mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-09 01:52:32 +02:00
80a9adbcd0
- **Command line arguments:** - (Add) Convert files: UVtools.exe -c \<inputfile\> \<outputfile1/ext1\> [outputfile2/ext2] - (Add) Extract files: UVtools.exe -e \<inputfile\> [output_folder] - https://github.com/sn4k3/UVtools#command-line-arguments - **File formats:** - (Add) Implement TSMC (Two Stage Motor Control) for the supported formats - (Add) Implement 'Bottom retract speed' for the supported formats - (Add) LGS: Support for lgs120 and lg4k (#218) - (Add) CTB: Special/virtual file extensions .v2.ctb, .v3.ctb, .v4.ctb to force a convertion to the set version (2 to 4). The .ctb is Version 3 by default when creating/converting files - (Improvement) Better performance for file formats that decode images in sequential pixels groups - **GCode:** - (Improvement) Better parsing of the movements / lifts - (Improvement) Better handling of lifts performed after cure the layer - (Improvement) More fail-safe checks and sanitize of gcode while parsing - (Improvement) CTBv3: Enable per layer settings if disabled when fast save without reencode - (Upgrade) .NET from 5.0.8 to 5.0.9 - (Fix) PrusaSlicer printer: Longer Orange 4k with correct resolution and display size - (Fix) Odd error when changing properties too fast in multi-thread
697 lines
24 KiB
C#
697 lines
24 KiB
C#
/*
|
|
* GNU AFFERO GENERAL PUBLIC LICENSE
|
|
* Version 3, 19 November 2007
|
|
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
* Everyone is permitted to copy and distribute verbatim copies
|
|
* of this license document, but changing it is not allowed.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using BinarySerialization;
|
|
using Emgu.CV;
|
|
using Emgu.CV.CvEnum;
|
|
using UVtools.Core.Extensions;
|
|
using UVtools.Core.Operations;
|
|
|
|
namespace UVtools.Core.FileFormats
|
|
{
|
|
public class LGSFile : FileFormat
|
|
{
|
|
#region Sub Classes
|
|
|
|
#region Header
|
|
|
|
public class Header
|
|
{
|
|
public const string NameValue = "Longer3D";
|
|
|
|
/// <summary>
|
|
/// Gets the model name
|
|
/// </summary>
|
|
[FieldOrder(0)] [FieldLength(8)] public string Name { get; set; } = NameValue; // 0x00:
|
|
[FieldOrder(1)] public uint Uint_08 { get; set; } = 1; // 0x08: 0xff000001 ?
|
|
[FieldOrder(2)] public uint Uint_0c { get; set; } = 1; // 0x0c: 1 ?
|
|
[FieldOrder(3)] public uint PrinterModel { get; set; } = 30; // 10, 30, 120, 4000 (4k), 4500 (4k mono)
|
|
[FieldOrder(4)] public uint Uint_14 { get; set; } = 0; // 0x14: 0 ?
|
|
[FieldOrder(5)] public uint MagicKey { get; set; } = 34; // 0x18: 34
|
|
[FieldOrder(6)] public float PixelPerMmX { get; set; } = 15.404f;
|
|
[FieldOrder(7)] public float PixelPerMmY { get; set; } = 4.866f;
|
|
[FieldOrder(8)] public float ResolutionX { get; set; }
|
|
[FieldOrder(9)] public float ResolutionY { get; set; }
|
|
[FieldOrder(10)] public float LayerHeight { get; set; }
|
|
[FieldOrder(11)] public float ExposureTimeMs { get; set; }
|
|
[FieldOrder(12)] public float BottomExposureTimeMs { get; set; }
|
|
[FieldOrder(13)] public float Float_38 { get; set; } = 10; // 0x38: 10
|
|
[FieldOrder(14)] public float LightOffDelayMs { get; set; } = 2000;
|
|
[FieldOrder(15)] public float BottomLightOffDelayMs { get; set; }
|
|
[FieldOrder(16)] public float BottomHeight { get; set; }
|
|
[FieldOrder(17)] public float Float_48 { get; set; } = 0.6f; // 0x48: 0.6
|
|
[FieldOrder(18)] public float BottomLiftHeight { get; set; } = 4;
|
|
[FieldOrder(19)] public float LiftHeight { get; set; }
|
|
[FieldOrder(20)] public float LiftSpeed { get; set; } = 150;
|
|
[FieldOrder(21)] public float LiftSpeed_ { get; set; } = 150;
|
|
[FieldOrder(22)] public float BottomLiftSpeed { get; set; } = 90;
|
|
[FieldOrder(23)] public float BottomLiftSpeed_ { get; set; } = 90;
|
|
[FieldOrder(24)] public float Float_64 { get; set; } = 5; // 0x64: 5?
|
|
[FieldOrder(25)] public float Float_68 { get; set; } = 60; // 0x68: 60?
|
|
[FieldOrder(26)] public float Float_6c { get; set; } = 10; // 0x6c: 10?
|
|
[FieldOrder(27)] public float Float_70 { get; set; } = 600; // 0x70: 600?
|
|
[FieldOrder(28)] public float Float_74 { get; set; } = 600; // 0x70: 600?
|
|
[FieldOrder(29)] public float Float_78 { get; set; } = 2; // 0x78: 2?
|
|
[FieldOrder(30)] public float Float_7c { get; set; } = 0.2f; // 0x7c: 0.2?
|
|
[FieldOrder(31)] public float Float_80 { get; set; } = 60; // 0x80: 60?
|
|
[FieldOrder(32)] public float Float_84 { get; set; } = 1; // 0x84: 1?
|
|
[FieldOrder(33)] public float Float_88 { get; set; } = 6; // 0x88: 6?
|
|
[FieldOrder(34)] public float Float_8c { get; set; } = 150; // 0x8c: 150 ?
|
|
[FieldOrder(35)] public float Float_90 { get; set; } = 1001; // 0x90: 1001 ?
|
|
[FieldOrder(36)] public float MachineZ { get; set; } = 140;// 0x94: 140 for lgs10, 170 for lgs30, 150 for lgs120, 190 for lgs4k
|
|
[FieldOrder(37)] public uint Uint_98 { get; set; } // 0x98: 0 ?
|
|
[FieldOrder(38)] public uint Uint_9c { get; set; } // 0x9c: 0 ?
|
|
[FieldOrder(39)] public uint Uint_a0 { get; set; } // 0xa0: 0 ?
|
|
[FieldOrder(40)] public uint LayerCount { get; set; }
|
|
[FieldOrder(41)] public uint Uint_a8 { get; set; } = 4; // 0xa8: 4 ?
|
|
[FieldOrder(42)] public uint PreviewSizeX { get; set; } = 120;
|
|
[FieldOrder(43)] public uint PreviewSizeY { get; set; } = 150;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region LGS120PngPreview
|
|
|
|
public class LGS120PngPreview
|
|
{
|
|
public const ushort ResolutionX = 1200;
|
|
public const ushort ResolutionY = 1600;
|
|
|
|
[FieldOrder(0)] public uint DataSize { get; set; }
|
|
|
|
[FieldOrder(1)]
|
|
[FieldLength(nameof(DataSize))]
|
|
public byte[] PngBytes { get; set; }
|
|
|
|
[FieldOrder(2)] public ushort Padding { get; set; }
|
|
|
|
public void Encode(Mat mat)
|
|
{
|
|
mat ??= EmguExtensions.InitMat(new Size(ResolutionX, ResolutionY), 3);
|
|
|
|
if (mat.Width != ResolutionX || mat.Height != ResolutionY)
|
|
{
|
|
using var resizeMat = new Mat();
|
|
CvInvoke.Resize(mat, resizeMat, new Size(ResolutionX, ResolutionY));
|
|
PngBytes = resizeMat.GetPngByes();
|
|
}
|
|
else
|
|
{
|
|
PngBytes = mat.GetPngByes();
|
|
}
|
|
}
|
|
|
|
public Mat Decode(bool consumeRle = true)
|
|
{
|
|
var mat = new Mat();
|
|
CvInvoke.Imdecode(PngBytes, ImreadModes.AnyColor, mat);
|
|
if (consumeRle)
|
|
PngBytes = null;
|
|
return mat;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region LayerData
|
|
|
|
public class LayerData
|
|
{
|
|
[Ignore] public LGSFile Parent { get; set; }
|
|
|
|
[FieldOrder(0)]
|
|
public uint DataSize { get; set; }
|
|
|
|
[FieldOrder(1)]
|
|
[FieldLength(nameof(DataSize))]
|
|
public byte[] EncodedRle { get; set; }
|
|
|
|
public LayerData() { }
|
|
|
|
public LayerData(LGSFile parent)
|
|
{
|
|
Parent = parent;
|
|
}
|
|
|
|
public unsafe byte[] Encode(Mat mat)
|
|
{
|
|
List<byte> rawData = new();
|
|
List<byte> chunk = new();
|
|
|
|
if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
|
|
{
|
|
CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90Clockwise);
|
|
}
|
|
|
|
var spanMat = mat.GetBytePointer();
|
|
var imageLength = mat.GetLength();
|
|
|
|
uint span = 0;
|
|
byte lc = 0;
|
|
|
|
void addSpan(){
|
|
chunk.Clear();
|
|
for (; span > 0; span >>= 4) {
|
|
chunk.Insert(0, (byte)((byte)(span & 0xf) | (lc & 0xf0)));
|
|
}
|
|
rawData.AddRange(chunk.ToArray());
|
|
}
|
|
|
|
for (int i = 0; i < imageLength; i++)
|
|
{
|
|
byte c = (byte) (spanMat[i] & 0xf0);
|
|
|
|
if (c == lc)
|
|
{
|
|
span++;
|
|
}
|
|
else
|
|
{
|
|
addSpan();
|
|
span = 1;
|
|
}
|
|
|
|
lc = c;
|
|
}
|
|
|
|
addSpan();
|
|
EncodedRle = rawData.ToArray();
|
|
DataSize = (uint) EncodedRle.Length;
|
|
|
|
if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
|
|
{
|
|
CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
|
|
}
|
|
|
|
return EncodedRle;
|
|
}
|
|
|
|
public Mat Decode(bool consumeRle = true)
|
|
{
|
|
// lgs10/30 -------->
|
|
// lgs120/4k From Y bottom to top Y
|
|
var mat = EmguExtensions.InitMat(Parent.HeaderSettings.PrinterModel is 4000 or 4500 ? Parent.Resolution.Exchange() : Parent.Resolution);
|
|
//var matSpan = mat.GetBytePointer();
|
|
var imageLength = mat.GetLength();
|
|
|
|
int pixelPos = 0;
|
|
|
|
for (var i = 0; i < EncodedRle.Length; i++)
|
|
{
|
|
var b = EncodedRle[i];
|
|
byte colorNibble = (byte)(b >> 4);
|
|
byte color = (byte)(colorNibble << 0x4 | colorNibble);
|
|
int repeat = b & 0xf;
|
|
|
|
while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble)
|
|
{
|
|
i++;
|
|
repeat = (repeat << 4) | (EncodedRle[i] & 0xf);
|
|
}
|
|
|
|
if (pixelPos >= imageLength)
|
|
{
|
|
throw new FileLoadException($"Too much buffer, expected: {imageLength}, got: {pixelPos}");
|
|
}
|
|
|
|
mat.FillSpan(ref pixelPos, repeat, color);
|
|
|
|
//if (repeat <= 0) continue;
|
|
/*while (repeat-- > 0)
|
|
{
|
|
matSpan[pixel++] = color;
|
|
}*/
|
|
|
|
}
|
|
|
|
if (pixelPos != imageLength)
|
|
{
|
|
throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {pixelPos}");
|
|
}
|
|
|
|
if (consumeRle)
|
|
EncodedRle = null;
|
|
|
|
if (Parent.HeaderSettings.PrinterModel is 4000 or 4500)
|
|
{
|
|
CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise);
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public Header HeaderSettings { get; protected internal set; } = new();
|
|
public override FileFormatType FileType => FileFormatType.Binary;
|
|
|
|
public override FileExtension[] FileExtensions { get; } = {
|
|
new ("lgs", "Longer Orange 10"),
|
|
new ("lgs30", "Longer Orange 30"),
|
|
new ("lgs120", "Longer Orange 120"),
|
|
new ("lgs4k", "Longer Orange 4k"),
|
|
};
|
|
|
|
public override PrintParameterModifier[] PrintParameterModifiers { get; } =
|
|
{
|
|
PrintParameterModifier.BottomLayerCount,
|
|
|
|
PrintParameterModifier.BottomLightOffDelay,
|
|
PrintParameterModifier.LightOffDelay,
|
|
|
|
PrintParameterModifier.BottomExposureTime,
|
|
PrintParameterModifier.ExposureTime,
|
|
|
|
PrintParameterModifier.BottomLiftHeight,
|
|
PrintParameterModifier.BottomLiftSpeed,
|
|
PrintParameterModifier.LiftHeight,
|
|
PrintParameterModifier.LiftSpeed,
|
|
|
|
};
|
|
|
|
public override Size[] ThumbnailsOriginalSize { get; } = {new(120, 150)};
|
|
|
|
public override uint ResolutionX
|
|
{
|
|
get => (uint) HeaderSettings.ResolutionX;
|
|
set
|
|
{
|
|
HeaderSettings.ResolutionX = value;
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override uint ResolutionY
|
|
{
|
|
get => (uint)HeaderSettings.ResolutionY;
|
|
set
|
|
{
|
|
HeaderSettings.ResolutionY = value;
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override float Xppmm
|
|
{
|
|
get => HeaderSettings.PixelPerMmX > 0 ? HeaderSettings.PixelPerMmX : base.Xppmm;
|
|
set
|
|
{
|
|
HeaderSettings.PixelPerMmX = value;
|
|
base.Xppmm = value;
|
|
}
|
|
}
|
|
|
|
public override float Yppmm
|
|
{
|
|
get => HeaderSettings.PixelPerMmY > 0 ? HeaderSettings.PixelPerMmY : base.Yppmm;
|
|
set
|
|
{
|
|
HeaderSettings.PixelPerMmY = value;
|
|
base.Yppmm = value;
|
|
}
|
|
}
|
|
|
|
public override float DisplayWidth
|
|
{
|
|
get => ResolutionX / HeaderSettings.PixelPerMmX;
|
|
set { }
|
|
}
|
|
|
|
public override float DisplayHeight
|
|
{
|
|
get => ResolutionY / HeaderSettings.PixelPerMmY;
|
|
set { }
|
|
}
|
|
|
|
public override float MachineZ
|
|
{
|
|
get => HeaderSettings.MachineZ > 0 ? HeaderSettings.MachineZ : base.MachineZ;
|
|
set => base.MachineZ = HeaderSettings.MachineZ = (float)Math.Round(value, 2);
|
|
}
|
|
|
|
public override bool DisplayMirror
|
|
{
|
|
get => true;
|
|
set { }
|
|
}
|
|
|
|
public override byte AntiAliasing
|
|
{
|
|
get => 4;
|
|
set { }
|
|
}
|
|
|
|
public override float LayerHeight
|
|
{
|
|
get => HeaderSettings.LayerHeight;
|
|
set
|
|
{
|
|
HeaderSettings.LayerHeight = Layer.RoundHeight(value);
|
|
RaisePropertyChanged();
|
|
}
|
|
}
|
|
|
|
public override uint LayerCount
|
|
{
|
|
get => base.LayerCount;
|
|
set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount;
|
|
}
|
|
|
|
public override ushort BottomLayerCount
|
|
{
|
|
get => (ushort) (HeaderSettings.BottomHeight / LayerHeight);
|
|
set
|
|
{
|
|
HeaderSettings.BottomHeight = value * LayerHeight;
|
|
base.BottomLayerCount = value;
|
|
}
|
|
}
|
|
|
|
public override float BottomLightOffDelay
|
|
{
|
|
get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomLightOffDelayMs);
|
|
set
|
|
{
|
|
HeaderSettings.BottomLightOffDelayMs = TimeExtensions.SecondsToMilliseconds(value);
|
|
base.BottomLightOffDelay = value;
|
|
}
|
|
}
|
|
|
|
public override float LightOffDelay
|
|
{
|
|
get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.LightOffDelayMs);
|
|
set
|
|
{
|
|
HeaderSettings.LightOffDelayMs = TimeExtensions.SecondsToMilliseconds(value);
|
|
base.LightOffDelay = value;
|
|
}
|
|
}
|
|
|
|
public override float BottomWaitTimeBeforeCure
|
|
{
|
|
get => base.BottomWaitTimeBeforeCure;
|
|
set
|
|
{
|
|
SetBottomLightOffDelay(value);
|
|
base.BottomWaitTimeBeforeCure = value;
|
|
}
|
|
}
|
|
|
|
public override float WaitTimeBeforeCure
|
|
{
|
|
get => base.WaitTimeBeforeCure;
|
|
set
|
|
{
|
|
SetNormalLightOffDelay(value);
|
|
base.WaitTimeBeforeCure = value;
|
|
}
|
|
}
|
|
|
|
public override float BottomExposureTime
|
|
{
|
|
get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.BottomExposureTimeMs);
|
|
set
|
|
{
|
|
HeaderSettings.BottomExposureTimeMs = TimeExtensions.SecondsToMilliseconds(value);
|
|
base.BottomExposureTime = value;
|
|
}
|
|
}
|
|
|
|
public override float ExposureTime
|
|
{
|
|
get => TimeExtensions.MillisecondsToSeconds(HeaderSettings.ExposureTimeMs);
|
|
set
|
|
{
|
|
HeaderSettings.ExposureTimeMs = TimeExtensions.SecondsToMilliseconds(value);
|
|
base.ExposureTime = value;
|
|
}
|
|
}
|
|
|
|
public override float BottomLiftHeight
|
|
{
|
|
get => HeaderSettings.BottomLiftHeight;
|
|
set => base.BottomLiftHeight = HeaderSettings.BottomLiftHeight = value;
|
|
}
|
|
|
|
public override float LiftHeight
|
|
{
|
|
get => HeaderSettings.LiftHeight;
|
|
set => base.LiftHeight = HeaderSettings.LiftHeight = value;
|
|
}
|
|
|
|
public override float BottomLiftSpeed
|
|
{
|
|
get => HeaderSettings.BottomLiftSpeed;
|
|
set => base.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed = HeaderSettings.BottomLiftSpeed_ = value;
|
|
}
|
|
|
|
public override float LiftSpeed
|
|
{
|
|
get => HeaderSettings.LiftSpeed;
|
|
set => base.LiftSpeed = HeaderSettings.LiftSpeed = HeaderSettings.LiftSpeed_ = value;
|
|
}
|
|
|
|
public override float BottomRetractSpeed => RetractSpeed;
|
|
|
|
public override float RetractSpeed => LiftSpeed;
|
|
|
|
/*public override float PrintTime => 0;
|
|
|
|
public override float UsedMaterial => 0;
|
|
|
|
public override float MaterialCost => 0;
|
|
|
|
public override string MaterialName => "Unknown";
|
|
public override string MachineName => null;*/
|
|
|
|
|
|
public override object[] Configs => new object[] { HeaderSettings };
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
public LGSFile()
|
|
{
|
|
}
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
public unsafe byte[] PreviewEncode(Mat mat)
|
|
{
|
|
byte[] bytes = new byte[mat.Width * mat.Height * 2];
|
|
var span = mat.GetBytePointer();
|
|
var imageLength = mat.GetLength();
|
|
|
|
int index = 0;
|
|
for (int i = 0; i < imageLength; i+=3)
|
|
{
|
|
byte b = span[i];
|
|
byte g = span[i+1];
|
|
byte r = span[i+2];
|
|
|
|
ushort rgb15 = (ushort) (((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 0));
|
|
|
|
bytes[index++] = (byte) (rgb15 >> 8);
|
|
bytes[index++] = (byte) (rgb15 & 0xff);
|
|
}
|
|
|
|
if (index != bytes.Length)
|
|
{
|
|
throw new FileLoadException($"Preview encode incomplete encode, expected: {bytes.Length}, encoded: {index}");
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
public unsafe Mat PreviewDecode(byte[] data)
|
|
{
|
|
Mat mat = new((int)HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3);
|
|
var span = mat.GetBytePointer();
|
|
int spanIndex = 0;
|
|
for (int i = 0; i < data.Length; i += 2)
|
|
{
|
|
ushort rgb15 = BitExtensions.ToUShortBigEndian(data[i], data[i + 1]);
|
|
byte r = (byte)((((rgb15 >> 11) & 0x1f) << 3) | 0x7);
|
|
byte g = (byte)((((rgb15 >> 6) & 0x1f) << 3) | 0x7);
|
|
byte b = (byte)((((rgb15 >> 0) & 0x1f) << 3) | 0x7);
|
|
|
|
span[spanIndex++] = b;
|
|
span[spanIndex++] = g;
|
|
span[spanIndex++] = r;
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
|
|
protected override void EncodeInternally(string fileFullPath, OperationProgress progress)
|
|
{
|
|
if (FileEndsWith(".lgs")) // Longer Orange 10
|
|
{
|
|
MachineZ = 140;
|
|
HeaderSettings.PrinterModel = 10;
|
|
}
|
|
else if (FileEndsWith(".lgs30")) // Longer Orange 30
|
|
{
|
|
MachineZ = 170;
|
|
HeaderSettings.PrinterModel = 30;
|
|
}
|
|
else if (FileEndsWith(".lgs120")) // Longer Orange 120
|
|
{
|
|
MachineZ = 150;
|
|
HeaderSettings.PrinterModel = 120;
|
|
}
|
|
else if (FileEndsWith(".lgs4k")) // Longer Orange 4K & Mono
|
|
{
|
|
MachineZ = 190;
|
|
if(HeaderSettings.PrinterModel is not 4000 and not 4500) HeaderSettings.PrinterModel = 4500;
|
|
}
|
|
|
|
//uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
|
|
using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write))
|
|
{
|
|
outputFile.WriteSerialize(HeaderSettings);
|
|
outputFile.WriteBytes(PreviewEncode(Thumbnails[0]));
|
|
|
|
if (HeaderSettings.PrinterModel == 120)
|
|
{
|
|
// Insert PNG here
|
|
var mat = GetThumbnail(true);
|
|
var pngPreview = new LGS120PngPreview();
|
|
pngPreview.Encode(mat);
|
|
outputFile.WriteSerialize(pngPreview);
|
|
}
|
|
|
|
var layerData = new LayerData[LayerCount];
|
|
|
|
Parallel.For(0, LayerCount, layerIndex =>
|
|
{
|
|
if (progress.Token.IsCancellationRequested) return;
|
|
using (var mat = this[layerIndex].LayerMat)
|
|
{
|
|
layerData[layerIndex] = new LayerData(this);
|
|
layerData[layerIndex].Encode(mat);
|
|
}
|
|
|
|
progress.LockAndIncrement();
|
|
});
|
|
|
|
progress.ItemName = "Saving layers";
|
|
progress.ProcessedItems = 0;
|
|
|
|
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
|
|
{
|
|
progress.Token.ThrowIfCancellationRequested();
|
|
outputFile.WriteSerialize(layerData[layerIndex]);
|
|
progress++;
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine("Encode Results:");
|
|
Debug.WriteLine(HeaderSettings);
|
|
Debug.WriteLine("-End-");
|
|
}
|
|
|
|
protected override void DecodeInternally(string fileFullPath, OperationProgress progress)
|
|
{
|
|
using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read);
|
|
HeaderSettings = Helpers.Deserialize<Header>(inputFile);
|
|
if (HeaderSettings.Name != Header.NameValue)
|
|
{
|
|
throw new FileLoadException("Not a valid LGS file!", fileFullPath);
|
|
}
|
|
|
|
//if (HeaderSettings.PrinterModel is 10 or 30 or 120)
|
|
//{
|
|
// Fix inconsistencies found of different version of plugin and slicers
|
|
if (ResolutionX > ResolutionY)
|
|
{
|
|
(ResolutionX, ResolutionY) = (ResolutionY, ResolutionX);
|
|
}
|
|
//}
|
|
|
|
int previewSize = (int) (HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2);
|
|
byte[] previewData = new byte[previewSize];
|
|
|
|
|
|
uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings);
|
|
currentOffset += inputFile.ReadBytes(previewData);
|
|
Thumbnails[0] = PreviewDecode(previewData);
|
|
|
|
if (HeaderSettings.PrinterModel == 120)
|
|
{
|
|
var pngPreview = Helpers.Deserialize<LGS120PngPreview>(inputFile);
|
|
}
|
|
|
|
|
|
var layerData = new LayerData[HeaderSettings.LayerCount];
|
|
progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount);
|
|
|
|
for (int layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
|
|
{
|
|
progress.Token.ThrowIfCancellationRequested();
|
|
layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile);
|
|
layerData[layerIndex].Parent = this;
|
|
}
|
|
|
|
LayerManager.Init(HeaderSettings.LayerCount);
|
|
progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
|
|
|
|
Parallel.For(0, LayerCount,
|
|
//new ParallelOptions{MaxDegreeOfParallelism = 1},
|
|
layerIndex =>
|
|
{
|
|
if (progress.Token.IsCancellationRequested) return;
|
|
|
|
using var image = layerData[layerIndex].Decode();
|
|
this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager);
|
|
|
|
progress.LockAndIncrement();
|
|
});
|
|
|
|
LayerManager.RebuildLayersProperties();
|
|
}
|
|
|
|
public override void SaveAs(string filePath = null, OperationProgress progress = null)
|
|
{
|
|
if (RequireFullEncode)
|
|
{
|
|
if (!string.IsNullOrEmpty(filePath))
|
|
{
|
|
FileFullPath = filePath;
|
|
}
|
|
Encode(FileFullPath, progress);
|
|
return;
|
|
}
|
|
|
|
|
|
if (!string.IsNullOrEmpty(filePath))
|
|
{
|
|
File.Copy(FileFullPath, filePath, true);
|
|
FileFullPath = filePath;
|
|
}
|
|
|
|
using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
|
|
outputFile.Seek(0, SeekOrigin.Begin);
|
|
Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|