diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-08-17 22:17:08 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-08-17 22:17:08 +0300 |
commit | 95020ce2f103b781d65318059adc6768e16e4a6f (patch) | |
tree | 695a4f1cfeae946317ab8074d87b3de050362630 /UVtools.Core/FileFormats | |
parent | 44de1f3bd8ce0910765f1f51807f3ff0bd2dfbd9 (diff) |
v2.19.0v2.19.0
- **File formats:**
- Add and remove some image types that can be open
- (Add) `CanProcess` method to know if a file can be read under a format and to allow diferent formats with same extension
- (Fix) `LiftHeightTotal` and `RetractHeight` was rounding to no decimals and returning wrong values
- (Improvement) Round all float setters on `Layer` class
- (Improvement) Decode/encode RAM usage and performance by processing in batch groups
- **Pixel Dimming:** (#262)
- (Add) Option "Lightening pixels" to add brightness/lightening instead of dimming/subtract pixels
- (Fix) "Dim walls only" would reset body brightness by increase pixel brightness two times it value
- **Pixel Arithmetic:**
- (Change) Transpose "Pixel Dimming" to "Pixel Arithmetic"
- (Improvement) New options and manipulations
- **(Fix) Exposure time finder:**
- Generate top staircase based on selected measure (px or mm)
- Zebra bars when used in mm measures, it was using X density instead Y to calculate the thickness
- Move 'Unit of measure' to 'Object configuration'
- Custom text with wrong Y position when using out of portion resolutions/LCDs
- **CTBv4:**
- (Fix) More Unknown fields discovered and implemented
- (Fix) Reserved table is 384 bytes instead of 420
- (Fix) When full encoding it was forcing to change to version 3. This also affected convertions. (#263)
- (Fix) `BottomRetractHeight2` was being set to `BottomRetractSpeed2`
- (Fix) `RetractHeight2` was being set to `RetracSpeed2`
- (Fix) The PrintParametersV4 table address
- (Fix) Generates invalid files to open with Chitubox and printers (#263)
- (Fix) Better progress report
- **(Add) PrusaSlicer printer notes variables:**
- BottomLiftHeight2
- BottomLiftSpeed2
- LiftHeight2
- LiftSpeed2
- BottomRetractSpeed
- BottomRetractSpeed2
- BottomRetractHeight2
- BottomRetractSpeed2
- RetractHeight2
- RetractSpeed2
- **UI:**
- (Add) File - Open current file folder (Ctrl+Shift+L): Locate and open the folder that contain the current loaded file
- (Improvement) Hide some virtual extensions from file open dialog filters
- (Improvement) UI: Refresh active thumbnail when changed
- (Change) Icon for File - Open and Open in a new file
- (Change) Rename File - Extract to: Extract file contents
- (Upgrade) AvaloniaUI from 0.10.6 to 0.10.7
- (Fix) PW0, PWM, PWMX, PWMO, PWMS: Unable to decode some files with AntiAliasing (#143)
Diffstat (limited to 'UVtools.Core/FileFormats')
22 files changed, 1592 insertions, 1487 deletions
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 1dae648..44f3c8a 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -426,9 +426,8 @@ namespace UVtools.Core.FileFormats get => (byte) OutputSettings.AntiAliasingValue; set { - OutputSettings.AntiAliasingValue = value.Clamp(1, 16); + base.AntiAliasing = (byte)(OutputSettings.AntiAliasingValue = value.Clamp(1, 16)); OutputSettings.AntiAliasing = OutputSettings.AntiAliasingValue > 1; - RaisePropertyChanged(); } } diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index a81ad75..fb0f1bb 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -36,7 +36,7 @@ namespace UVtools.Core.FileFormats #region Header public sealed class Header { - private string _printerModel = "CL-89"; + //private string _printerModel = "CL-89"; /// <summary> /// Gets the size of the header @@ -447,12 +447,6 @@ namespace UVtools.Core.FileFormats } } - public override byte AntiAliasing - { - get => 8; - set {} - } - public override float LayerHeight { get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray())); @@ -608,33 +602,24 @@ namespace UVtools.Core.FileFormats Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; - for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) - { - previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; - } + var previews = new byte[ThumbnailsOriginalSize.Length][]; + // Previews Parallel.For(0, previews.Length, previewIndex => { if (progress.Token.IsCancellationRequested) return; - if (Thumbnails[previewIndex] is null) return; - var span = Thumbnails[previewIndex].GetDataByteSpan(); - int index = 0; - for (int i = 0; i < span.Length; i += 3) + var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; + if (Thumbnails[previewIndex] is null) { - byte b = span[i]; - byte g = span[i + 1]; - byte r = span[i + 2]; - - ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); - - previews[previewIndex][index++] = (byte)(rgb15 >> 8); - previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + previews[previewIndex] = new byte[encodeLength]; + return; } - if (index != previews[previewIndex].Length) + previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]); + + if (encodeLength != previews[previewIndex].Length) { - throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}"); } }); @@ -642,7 +627,7 @@ namespace UVtools.Core.FileFormats { Helpers.SerializeWriteFileStream(outputFile, previews[i]); outputFile.WriteBytes(pageBreak); - //Helpers.SerializeWriteFileStream(outputFile, pageBreak); + previews[i] = null; } Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); @@ -662,10 +647,8 @@ namespace UVtools.Core.FileFormats //Helpers.SerializeWriteFileStream(outputFile, pageBreak); outputFile.WriteBytes(pageBreak); - var range = Enumerable.Range(0, (int) LayerCount); - var layerBytes = new List<byte>[LayerCount]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -673,48 +656,52 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; var layer = this[layerIndex]; - using var mat = layer.LayerMat; - var span = mat.GetDataByteSpan(); + using (var mat = layer.LayerMat) + { + var span = mat.GetDataByteSpan(); - layerBytes[layerIndex] = new(); + layerBytes[layerIndex] = new(); - uint lineCount = 0; + uint lineCount = 0; - for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) - { - int y = layer.BoundingRectangle.Y; - int startY = -1; - byte lastColor = 0; - for (; y < layer.BoundingRectangle.Bottom; y++) + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) { - int pos = mat.GetPixelPos(x, y); - byte color = span[pos]; + int y = layer.BoundingRectangle.Y; + int startY = -1; + byte lastColor = 0; + for (; y < layer.BoundingRectangle.Bottom; y++) + { + int pos = mat.GetPixelPos(x, y); + byte color = span[pos]; + + if (lastColor == color && color != 0) continue; - if (lastColor == color && color != 0) continue; + if (startY >= 0) + { + layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), + (ushort)x, lastColor)); + lineCount++; + } + + startY = color == 0 ? -1 : y; + + lastColor = color; + } if (startY >= 0) { - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); + layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), + (ushort)x, lastColor)); lineCount++; } - - startY = color == 0 ? -1 : y; - - lastColor = color; } - if (startY >= 0) - { - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); - lineCount++; - } + layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes( + (uint)Math.Round(layer.BoundingRectangleMillimeters.Area() * 1000), + lineCount)); + layerBytes[layerIndex].AddRange(pageBreak); } - layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes( - (uint)Math.Round(layer.BoundingRectangleMillimeters.Area()*1000), - lineCount)); - layerBytes[layerIndex].AddRange(pageBreak); - progress.LockAndIncrement(); }); @@ -842,23 +829,8 @@ namespace UVtools.Core.FileFormats Parallel.For(0, previews.Length, previewIndex => { - var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); - var span = mat.GetDataByteSpan(); - - int spanIndex = 0; - for (int i = 0; i < previews[previewIndex].Length; i += 2) - { - ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); - byte r = (byte)((rgb15 >> 11) << 3); - byte g = (byte)((rgb15 >> 5) << 2); - byte b = (byte)((rgb15 >> 0) << 3); - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - Thumbnails[previewIndex] = mat; + Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); + previews[previewIndex] = null; }); @@ -877,10 +849,9 @@ namespace UVtools.Core.FileFormats }*/ //inputFile.Seek(2, SeekOrigin.Current); - var range = Enumerable.Range(0, (int)LayerCount); var linesBytes = new byte[LayerCount][]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -899,29 +870,32 @@ namespace UVtools.Core.FileFormats Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = EmguExtensions.InitMat(Resolution); - - for (int i = 0; i < linesBytes[layerIndex].Length; i++) + using (var mat = EmguExtensions.InitMat(Resolution)) { - LayerLine line = new() + + for (int i = 0; i < linesBytes[layerIndex].Length; i++) { - Coordinates = + LayerLine line = new() { - [0] = linesBytes[layerIndex][i++], - [1] = linesBytes[layerIndex][i++], - [2] = linesBytes[layerIndex][i++], - [3] = linesBytes[layerIndex][i++], - [4] = linesBytes[layerIndex][i++] - }, - Gray = linesBytes[layerIndex][i] - }; - - CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), new MCvScalar(line.Gray)); - } + Coordinates = + { + [0] = linesBytes[layerIndex][i++], + [1] = linesBytes[layerIndex][i++], + [2] = linesBytes[layerIndex][i++], + [3] = linesBytes[layerIndex][i++], + [4] = linesBytes[layerIndex][i++] + }, + Gray = linesBytes[layerIndex][i] + }; + + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), + new MCvScalar(line.Gray)); + } - linesBytes[layerIndex] = null; + linesBytes[layerIndex] = null; - this[layerIndex] = new Layer((uint)layerIndex, mat, this); + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + } progress.LockAndIncrement(); }); diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs index 137f839..445396c 100644 --- a/UVtools.Core/FileFormats/CXDLPv1File.cs +++ b/UVtools.Core/FileFormats/CXDLPv1File.cs @@ -397,12 +397,6 @@ namespace UVtools.Core.FileFormats } } - public override byte AntiAliasing - { - get => 8; - set { } - } - public override float LayerHeight { get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray())); @@ -543,33 +537,24 @@ namespace UVtools.Core.FileFormats Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; - for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) - { - previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; - } + var previews = new byte[ThumbnailsOriginalSize.Length][]; + // Previews Parallel.For(0, previews.Length, previewIndex => { if (progress.Token.IsCancellationRequested) return; - if (Thumbnails[previewIndex] is null) return; - var span = Thumbnails[previewIndex].GetDataByteSpan(); - int index = 0; - for (int i = 0; i < span.Length; i += 3) + var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; + if (Thumbnails[previewIndex] is null) { - byte b = span[i]; - byte g = span[i + 1]; - byte r = span[i + 2]; - - ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); - - previews[previewIndex][index++] = (byte)(rgb15 >> 8); - previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + previews[previewIndex] = new byte[encodeLength]; + return; } - if (index != previews[previewIndex].Length) + previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]); + + if (encodeLength != previews[previewIndex].Length) { - throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}"); } }); @@ -577,6 +562,7 @@ namespace UVtools.Core.FileFormats { Helpers.SerializeWriteFileStream(outputFile, previews[i]); outputFile.WriteBytes(pageBreak); + previews[i] = null; } Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); @@ -589,10 +575,8 @@ namespace UVtools.Core.FileFormats } outputFile.WriteBytes(pageBreak); - var range = Enumerable.Range(0, (int)LayerCount); - var layerBytes = new List<byte>[LayerCount]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -600,46 +584,50 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; var layer = this[layerIndex]; - using var mat = layer.LayerMat; - var span = mat.GetDataByteSpan(); + using (var mat = layer.LayerMat) + { + var span = mat.GetDataByteSpan(); - layerBytes[layerIndex] = new(); + layerBytes[layerIndex] = new(); - uint lineCount = 0; + uint lineCount = 0; - for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) - { - int y = layer.BoundingRectangle.Y; - int startY = -1; - byte lastColor = 0; - for (; y < layer.BoundingRectangle.Bottom; y++) + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) { - int pos = mat.GetPixelPos(x, y); - byte color = span[pos]; + int y = layer.BoundingRectangle.Y; + int startY = -1; + byte lastColor = 0; + for (; y < layer.BoundingRectangle.Bottom; y++) + { + int pos = mat.GetPixelPos(x, y); + byte color = span[pos]; - if (lastColor == color && color != 0) continue; + if (lastColor == color && color != 0) continue; + + if (startY >= 0) + { + layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), + (ushort)x, lastColor)); + lineCount++; + } + + startY = color == 0 ? -1 : y; + + lastColor = color; + } if (startY >= 0) { - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); + layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), + (ushort)x, lastColor)); lineCount++; } - - startY = color == 0 ? -1 : y; - - lastColor = color; } - if (startY >= 0) - { - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x, lastColor)); - lineCount++; - } + layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(layer.NonZeroPixelCount, lineCount)); + layerBytes[layerIndex].AddRange(pageBreak); } - layerBytes[layerIndex].InsertRange(0, LayerDef.GetHeaderBytes(layer.NonZeroPixelCount, lineCount)); - layerBytes[layerIndex].AddRange(pageBreak); - progress.LockAndIncrement(); }); @@ -679,23 +667,8 @@ namespace UVtools.Core.FileFormats Parallel.For(0, previews.Length, previewIndex => { - var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); - var span = mat.GetDataByteSpan(); - - int spanIndex = 0; - for (int i = 0; i < previews[previewIndex].Length; i += 2) - { - ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); - byte r = (byte)((rgb15 >> 11) << 3); - byte g = (byte)((rgb15 >> 5) << 2); - byte b = (byte)((rgb15 >> 0) << 3); - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - Thumbnails[previewIndex] = mat; + Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); + previews[previewIndex] = null; }); @@ -711,7 +684,7 @@ namespace UVtools.Core.FileFormats var range = Enumerable.Range(0, (int)LayerCount); var linesBytes = new byte[LayerCount][]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -730,29 +703,31 @@ namespace UVtools.Core.FileFormats Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = EmguExtensions.InitMat(Resolution); - - for (int i = 0; i < linesBytes[layerIndex].Length; i++) + using (var mat = EmguExtensions.InitMat(Resolution)) { - LayerLine line = new() + for (int i = 0; i < linesBytes[layerIndex].Length; i++) { - Coordinates = + LayerLine line = new() { - [0] = linesBytes[layerIndex][i++], - [1] = linesBytes[layerIndex][i++], - [2] = linesBytes[layerIndex][i++], - [3] = linesBytes[layerIndex][i++], - [4] = linesBytes[layerIndex][i++] - }, - Gray = linesBytes[layerIndex][i] - }; - - CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), new MCvScalar(line.Gray)); - } + Coordinates = + { + [0] = linesBytes[layerIndex][i++], + [1] = linesBytes[layerIndex][i++], + [2] = linesBytes[layerIndex][i++], + [3] = linesBytes[layerIndex][i++], + [4] = linesBytes[layerIndex][i++] + }, + Gray = linesBytes[layerIndex][i] + }; + + CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), + new MCvScalar(line.Gray)); + } - linesBytes[layerIndex] = null; + linesBytes[layerIndex] = null; - this[layerIndex] = new Layer((uint)layerIndex, mat, this); + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + } progress.LockAndIncrement(); }); diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 046aea8..92ba36b 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -14,10 +14,12 @@ using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; +using MoreLinq; using UVtools.Core.Extensions; using UVtools.Core.Operations; @@ -45,15 +47,8 @@ namespace UVtools.Core.FileFormats private const string CTBv4_DISCLAIMER = "Layout and record format for the ctb and cbddlp file types are the copyrighted programs or codes of CBD Technology (China) Inc..The Customer or User shall not in any manner reproduce, distribute, modify, decompile, disassemble, decrypt, extract, reverse engineer, lease, assign, or sublicense the said programs or codes."; private const ushort CTBv4_DISCLAIMER_SIZE = 320; + private const ushort CTBv4_RESERVED_SIZE = 384; - public static readonly byte[] SomethingNew1 = { - 0xD0, 0x5B, 0x8E, 0x33, 0x71, 0xDE, 0x3D, 0x1A, 0xE5, 0x4F, 0x22, 0xDD, 0xDF, 0x5B, 0xFD, 0x94, - 0xAB, 0x5D, 0x64, 0x3A, 0x9D, 0x7E, 0xBF, 0xAF, 0x42, 0x03, 0xF3, 0x10, 0xD8, 0x52, 0x2A, 0xEA - }; - - public static readonly byte[] SomethingNew2 = { - 0x0F, 0x01, 0x0A, 0x05, 0x05, 0x0B, 0x06, 0x07, 0x08, 0x06, 0x0A, 0x0C, 0x0C, 0x0D, 0x09, 0x0F - }; #endregion #region Sub Classes @@ -176,6 +171,7 @@ namespace UVtools.Core.FileFormats /// This is used to implement antialiasing in cbddlp files. When greater than 1, /// the layer table will actually contain layer_table_count * level_set_count entries. /// See the section on antialiasing for details. + /// 1 for ctb /// </summary> [FieldOrder(23)] public uint AntiAliasLevel { get; set; } = 1; @@ -330,7 +326,7 @@ namespace UVtools.Core.FileFormats [FieldOrder(13)] public float RestTimeAfterRetract { get; set; } [FieldOrder(14)] public float RestTimeAfterLift2 { get; set; } [FieldOrder(15)] public uint TransitionLayerCount { get; set; } // CTB not all printers - [FieldOrder(16)] public uint Padding1 { get; set; } + [FieldOrder(16)] public uint PrintParametersV4Address { get; set; } // V4 Only [FieldOrder(17)] public uint Padding2 { get; set; } [FieldOrder(18)] public uint Padding3 { get; set; } @@ -354,7 +350,7 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(MachineName)}: {MachineName}"; + return $"{nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(PrintParametersV4Address)}: {PrintParametersV4Address}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(MachineName)}: {MachineName}"; } } @@ -363,9 +359,10 @@ namespace UVtools.Core.FileFormats #region PrintParametersV4 public sealed class PrintParametersV4 { - [FieldOrder(0)] - [FieldLength(CTBv4_DISCLAIMER_SIZE)] + /*[FieldOrder(0)] + [FieldLength(nameof(DisclaimerLength))] public string Disclaimer { get; set; } = CTBv4_DISCLAIMER; // 320 bytes + */ [FieldOrder(1)] public float BottomRetractSpeed { get; set; } @@ -407,7 +404,7 @@ namespace UVtools.Core.FileFormats public uint Unknown3 { get; set; } = 5; // 5? [FieldOrder(14)] - public uint Unknown4 { get; set; } // 139 but changes + public uint LastLayerIndex { get; set; } [FieldOrder(15)] public uint Padding3 { get; set; } @@ -422,18 +419,17 @@ namespace UVtools.Core.FileFormats public uint Padding6 { get; set; } [FieldOrder(19)] - public uint Unknown5 { get; set; } // 23047 but changes + public uint DisclaimerAddress { get; set; } - [FieldOrder(20)] - public uint Unknown6 { get; set; } // 320 but changes + [FieldOrder(20)] public uint DisclaimerLength { get; set; } = CTBv4_DISCLAIMER_SIZE; [FieldOrder(21)] - [FieldLength(384)] - public byte[] Reserved { get; set; } = new byte[384]; // 384 bytes + [FieldLength(CTBv4_RESERVED_SIZE)] + public byte[] Reserved { get; set; } = new byte[CTBv4_RESERVED_SIZE]; // 384 bytes public override string ToString() { - return $"{nameof(Disclaimer)}: {Disclaimer}, {nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Padding1)}: {Padding1}, {nameof(Four1)}: {Four1}, {nameof(Padding2)}: {Padding2}, {nameof(Four2)}: {Four2}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(Unknown5)}: {Unknown5}, {nameof(Unknown6)}: {Unknown6}, {nameof(Reserved)}: {Reserved}"; + return $"{nameof(BottomRetractSpeed)}: {BottomRetractSpeed}, {nameof(BottomRetractSpeed2)}: {BottomRetractSpeed2}, {nameof(Padding1)}: {Padding1}, {nameof(Four1)}: {Four1}, {nameof(Padding2)}: {Padding2}, {nameof(Four2)}: {Four2}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(BottomRetractHeight2)}: {BottomRetractHeight2}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(Padding3)}: {Padding3}, {nameof(Padding4)}: {Padding4}, {nameof(Padding5)}: {Padding5}, {nameof(Padding6)}: {Padding6}, {nameof(DisclaimerAddress)}: {DisclaimerAddress}, {nameof(DisclaimerLength)}: {DisclaimerLength}, {nameof(Reserved)}: {Reserved}"; } } #endregion @@ -589,12 +585,12 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the build platform Z position for this layer, measured in millimeters. /// </summary> - [FieldOrder(0)] public float LayerPositionZ { get; set; } + [FieldOrder(0)] public float PositionZ { get; set; } /// <summary> /// Gets the exposure time for this layer, in seconds. /// </summary> - [FieldOrder(1)] public float LayerExposure { get; set; } + [FieldOrder(1)] public float ExposureTime { get; set; } /// <summary> /// Gets how long to keep the light off after exposing this layer, in seconds. @@ -626,7 +622,7 @@ namespace UVtools.Core.FileFormats public LayerDef(ChituboxFile parent, Layer layer) { Parent = parent; - RefreshLayerData(layer); + SetFrom(layer); if (parent.HeaderSettings.Version >= 3 && Unknown2 == 0) { @@ -634,20 +630,32 @@ namespace UVtools.Core.FileFormats } } - public void RefreshLayerData(Layer layer) + public void SetFrom(Layer layer) { - LayerPositionZ = layer.PositionZ; - LayerExposure = layer.ExposureTime; + PositionZ = layer.PositionZ; + ExposureTime = layer.ExposureTime; LightOffSeconds = layer.LightOffDelay; } + public void CopyTo(Layer layer) + { + layer.PositionZ = PositionZ; + layer.ExposureTime = ExposureTime; + layer.LightOffDelay = LightOffSeconds; + } + public Mat Decode(uint layerIndex, bool consumeData = true) { var image = Parent.IsCtbFile ? DecodeCtbImage(layerIndex) : DecodeCbddlpImage(Parent, layerIndex); if (consumeData) - EncodedRle = null; + { + for (byte aaIndex = 0; aaIndex < Parent.HeaderSettings.AntiAliasLevel; aaIndex++) + { + Parent.LayerDefinitions[aaIndex, layerIndex].EncodedRle = null; + } + } return image; } @@ -945,10 +953,8 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LightOffSeconds)}: {LightOffSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; + return $"{nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffSeconds)}: {LightOffSeconds}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; } - - } public class LayerDefEx @@ -974,21 +980,19 @@ namespace UVtools.Core.FileFormats [FieldOrder(11)] public float RestTimeAfterRetract { get; set; } // 28672 v3? [FieldOrder(12)] public float LightPWM { get; set; } - public LayerDefEx() - { - } + public LayerDefEx() { } public LayerDefEx(LayerDef layerDef, Layer layer) { LayerDef = layerDef; - if (layerDef.Parent is not null && layer is not null) + if (layer is not null) { LiftHeight = layer.LiftHeight; LiftSpeed = layer.LiftSpeed; RetractSpeed = layer.RetractSpeed; LightPWM = layer.LightPWM; - if (layerDef.Parent.HeaderSettings.Version >= 4) + if (layerDef.Parent is not null && layerDef.Parent.HeaderSettings.Version >= 4) { LiftHeight2 = layer.LiftHeight2; LiftSpeed2 = layer.LiftSpeed2; @@ -1008,10 +1012,35 @@ namespace UVtools.Core.FileFormats } } + public void CopyTo(Layer layer) + { + LayerDef.CopyTo(layer); + + layer.LiftHeight = LiftHeight; + layer.LiftSpeed = LiftSpeed; + layer.RetractSpeed = RetractSpeed; + layer.LightPWM = (byte)LightPWM; + + if (LayerDef.Parent is not null && LayerDef.Parent.HeaderSettings.Version >= 4) + { + layer.LiftHeight2 = LiftHeight2; + layer.LiftSpeed2 = LiftSpeed2; + + layer.RetractHeight2 = RetractHeight2; + layer.RetractSpeed2 = RetractSpeed2; + + layer.WaitTimeBeforeCure = RestTimeAfterRetract; + layer.WaitTimeAfterCure = RestTimeBeforeLift; + layer.WaitTimeAfterLift = RestTimeAfterLift; + } + } + public override string ToString() { return $"{nameof(LayerDef)}: {LayerDef}, {nameof(TotalSize)}: {TotalSize}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeBeforeLift)}: {RestTimeBeforeLift}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(LightPWM)}: {LightPWM}"; } + + } #endregion @@ -1080,8 +1109,6 @@ namespace UVtools.Core.FileFormats public LayerDef[,] LayerDefinitions { get; private set; } - public Dictionary<string, LayerDef> LayersHash { get; } = new(); - public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { @@ -1289,14 +1316,13 @@ namespace UVtools.Core.FileFormats { if (IsCtbFile) { - SlicerInfoSettings.AntiAliasLevel = value; + base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = value.Clamp(1, 16)); } - else + else if(IsCbddlpFile) { - HeaderSettings.AntiAliasLevel = value.Clamp(1, 16); + base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel = value.Clamp(1, 16)); ValidateAntiAliasingLevel(); } - RaisePropertyChanged(); } } @@ -1319,7 +1345,11 @@ namespace UVtools.Core.FileFormats public override uint LayerCount { get => base.LayerCount; - set => base.LayerCount = HeaderSettings.LayerCount = base.LayerCount; + set + { + base.LayerCount = HeaderSettings.LayerCount = base.LayerCount; + PrintParametersV4Settings.LastLayerIndex = LastLayerIndex; + } } public override ushort BottomLayerCount @@ -1526,7 +1556,8 @@ namespace UVtools.Core.FileFormats set { if (HeaderSettings.Version < 4) return; - base.BottomRetractHeight2 = PrintParametersV4Settings.BottomRetractHeight2 = (float)Math.Round(value, 2); + value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); + base.BottomRetractHeight2 = PrintParametersV4Settings.BottomRetractHeight2 = value; } } @@ -1543,7 +1574,12 @@ namespace UVtools.Core.FileFormats public override float RetractHeight2 { get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractHeight2 : 0; - set => base.RetractHeight2 = SlicerInfoSettings.RetractHeight2 = (float)Math.Round(value, 2); + set + { + if (HeaderSettings.Version < 4) return; + value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + base.RetractHeight2 = SlicerInfoSettings.RetractHeight2 = value; + } } public override float RetractSpeed2 @@ -1623,7 +1659,7 @@ namespace UVtools.Core.FileFormats public bool IsCbddlpFile => HeaderSettings.Magic == MAGIC_CBDDLP; public bool IsCtbFile => HeaderSettings.Magic is MAGIC_CTB or MAGIC_CTBv4; - public bool CanHash => !IsCtbFile && HeaderSettings.Version <= 2; + public bool CanHash => IsCbddlpFile && HeaderSettings.Version <= 2; #endregion #region Constructors @@ -1702,8 +1738,6 @@ namespace UVtools.Core.FileFormats protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - LayersHash.Clear(); - if (FileEndsWith(".ctb")) { if (HeaderSettings.Magic is not MAGIC_CTB and not MAGIC_CTBv4) @@ -1734,7 +1768,6 @@ namespace UVtools.Core.FileFormats HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings); - SanitizeProperties(); if (IsCtbFile) { @@ -1751,10 +1784,10 @@ namespace UVtools.Core.FileFormats SlicerInfoSettings.EncryptionMode = ENCRYPTYION_MODE_CBDDLP; } - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); + //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount]; using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); + outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin); Mat[] thumbnails = {GetThumbnail(true), GetThumbnail(false)}; for (byte i = 0; i < thumbnails.Length; i++) @@ -1774,105 +1807,127 @@ namespace UVtools.Core.FileFormats if (i == 0) { - HeaderSettings.PreviewLargeOffsetAddress = currentOffset; + HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position; } else { - HeaderSettings.PreviewSmallOffsetAddress = currentOffset; + HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position; } - currentOffset += (uint) Helpers.Serializer.SizeOf(preview); - preview.ImageOffset = currentOffset; + preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview)); Helpers.SerializeWriteFileStream(outputFile, preview); - currentOffset += outputFile.WriteBytes(previewBytes); + outputFile.WriteBytes(previewBytes); } if (HeaderSettings.Version >= 2) { - HeaderSettings.PrintParametersOffsetAddress = currentOffset; + HeaderSettings.PrintParametersOffsetAddress = (uint)outputFile.Position; - currentOffset += Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings); + Helpers.SerializeWriteFileStream(outputFile, PrintParametersSettings); - HeaderSettings.SlicerOffset = currentOffset; + HeaderSettings.SlicerOffset = (uint)outputFile.Position; HeaderSettings.SlicerSize = (uint) Helpers.Serializer.SizeOf(SlicerInfoSettings) - SlicerInfoSettings.MachineNameSize; - SlicerInfoSettings.MachineNameAddress = currentOffset + HeaderSettings.SlicerSize; + SlicerInfoSettings.MachineNameAddress = HeaderSettings.SlicerOffset + HeaderSettings.SlicerSize; + if (HeaderSettings.Version >= 4) + { + SlicerInfoSettings.PrintParametersV4Address = (uint)(HeaderSettings.SlicerOffset + + Helpers.Serializer.SizeOf(SlicerInfoSettings) + + CTBv4_DISCLAIMER_SIZE); + } - currentOffset += Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); + + Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); if (HeaderSettings.Version >= 4) { - currentOffset += Helpers.SerializeWriteFileStream(outputFile, PrintParametersV4Settings); + PrintParametersV4Settings.DisclaimerAddress = (uint)outputFile.Position; + PrintParametersV4Settings.DisclaimerLength = (uint)CTBv4_DISCLAIMER.Length; + outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTBv4_DISCLAIMER)); + Helpers.SerializeWriteFileStream(outputFile, PrintParametersV4Settings); } } - HeaderSettings.LayersDefinitionOffsetAddress = currentOffset; - uint layerDataCurrentOffset = currentOffset + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * HeaderSettings.LayerCount * HeaderSettings.AntiAliasLevel; - - progress.ItemCount *= 2 * HeaderSettings.AntiAliasLevel; + HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position; + uint layerDefSize = (uint)Helpers.Serializer.SizeOf(new LayerDef()); + //uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress; + uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + layerDefSize * HeaderSettings.LayerCount * HeaderSettings.AntiAliasLevel; - for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) + var layersHash = new Dictionary<string, LayerDef>(); + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + + foreach (var batch in BatchLayersIndexes()) { - progress.Token.ThrowIfCancellationRequested(); - Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => + Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - var layerDef = new LayerDef(this, this[layerIndex]); - using (var image = this[layerIndex].LayerMat) + using (var mat = this[layerIndex].LayerMat) { - layerDef.Encode(image, aaIndex, (uint) layerIndex); - LayerDefinitions[aaIndex, layerIndex] = layerDef; + for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) + { + var layerDef = new LayerDef(this, this[layerIndex]); + layerDef.Encode(mat, aaIndex, (uint)layerIndex); + LayerDefinitions[aaIndex, layerIndex] = layerDef; + } } - progress.LockAndIncrement(); }); - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + foreach (var layerIndex in batch) { - progress.Token.ThrowIfCancellationRequested(); - var layerDef = LayerDefinitions[aaIndex, layerIndex]; - LayerDef layerDefHash = null; - - if (CanHash) + if (layerIndex == 0) layerDefSize = (uint)Helpers.Serializer.SizeOf(LayerDefinitions[0, layerIndex]); + for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) { - string hash = Helpers.ComputeSHA1Hash(layerDef.EncodedRle); - if (LayersHash.TryGetValue(hash, out layerDefHash)) - { - layerDef.DataAddress = layerDefHash.DataAddress; - layerDef.DataSize = layerDefHash.DataSize; - } - else + progress.Token.ThrowIfCancellationRequested(); + + var layerDef = LayerDefinitions[aaIndex, layerIndex]; + LayerDef layerDefHash = null; + + if (CanHash) { - LayersHash.Add(hash, layerDef); + var hash = Helpers.ComputeSHA1Hash(layerDef.EncodedRle); + if (layersHash.TryGetValue(hash, out layerDefHash)) + { + layerDef.DataAddress = layerDefHash.DataAddress; + layerDef.DataSize = layerDefHash.DataSize; + } + else + { + layersHash.Add(hash, layerDef); + } } - } - - if (layerDefHash is null) - { - layerDef.DataAddress = layerDataCurrentOffset; - outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin); - if (HeaderSettings.Version >= 3) + if (layerDefHash is null) { - var layerDataEx = new LayerDefEx(layerDef, this[layerIndex]); - layerDataCurrentOffset += (uint)Helpers.Serializer.SizeOf(layerDataEx); layerDef.DataAddress = layerDataCurrentOffset; - Helpers.SerializeWriteFileStream(outputFile, layerDataEx); + outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin); + + if (HeaderSettings.Version >= 3) + { + var layerDataEx = new LayerDefEx(layerDef, this[layerIndex]); + layerDataCurrentOffset += (uint)Helpers.Serializer.SizeOf(layerDataEx); + layerDef.DataAddress = layerDataCurrentOffset; + Helpers.SerializeWriteFileStream(outputFile, layerDataEx); + } + + layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle); } - layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle); - } - - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef); + outputFile.Seek(HeaderSettings.LayersDefinitionOffsetAddress + + aaIndex * HeaderSettings.LayerCount * layerDefSize + + layerDefSize * layerIndex + , SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, layerDef); - progress++; + layerDef.EncodedRle = null; // Free this + } } } + outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); @@ -1928,7 +1983,7 @@ namespace UVtools.Core.FileFormats inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); byte[] rawImageData = new byte[Previews[i].ImageLength]; inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength); - + Thumbnails[i] = Previews[i].Decode(rawImageData); progress++; } @@ -1959,13 +2014,29 @@ namespace UVtools.Core.FileFormats if (HeaderSettings.Version >= 4) { + if (SlicerInfoSettings.PrintParametersV4Address == 0) + { + throw new FileLoadException( + $"Malformed file, PrintParametersV4Address is missing", + fileFullPath); + } + + inputFile.Seek(SlicerInfoSettings.PrintParametersV4Address, SeekOrigin.Begin); PrintParametersV4Settings = Helpers.Deserialize<PrintParametersV4>(inputFile); Debug.Write("Print Parameters V4 -> "); Debug.WriteLine(PrintParametersV4Settings); + + if (PrintParametersV4Settings.Four1 != 4 && PrintParametersV4Settings.Four2 != 4) + { + throw new FileLoadException( + $"Malformed file, PrintParametersV4 found invalid validation values, expected (4, 4) " + + $"but got ({PrintParametersV4Settings.Four1}, {PrintParametersV4Settings.Four2})", + fileFullPath); + } } LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount]; - var LayerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDefEx[HeaderSettings.LayerCount] : null; + var layerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDefEx[HeaderSettings.LayerCount] : null; uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; @@ -1977,35 +2048,31 @@ namespace UVtools.Core.FileFormats Debug.WriteLine($"-Image GROUP {aaIndex}-"); for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) { + progress.Token.ThrowIfCancellationRequested(); inputFile.Seek(layerOffset, SeekOrigin.Begin); - LayerDef layerDef = Helpers.Deserialize<LayerDef>(inputFile); + var layerDef = Helpers.Deserialize<LayerDef>(inputFile); layerDef.Parent = this; LayerDefinitions[aaIndex, layerIndex] = layerDef; + LayerDefinitions[aaIndex, layerIndex].Parent = this; layerOffset += (uint) Helpers.Serializer.SizeOf(layerDef); Debug.Write($"LAYER {layerIndex} -> "); Debug.WriteLine(layerDef); - layerDef.EncodedRle = new byte[layerDef.DataSize]; + //layerDef.EncodedRle = new byte[layerDef.DataSize]; - - if (HeaderSettings.Version < 3) + if (HeaderSettings.Version >= 3) { - inputFile.Seek(layerDef.DataAddress, SeekOrigin.Begin); - } - else - { - inputFile.Seek(layerDef.DataAddress - 84, SeekOrigin.Begin); - LayerDefinitionsEx[layerIndex] = Helpers.Deserialize<LayerDefEx>(inputFile); - Debug.Write($"LAYER {layerIndex} -> "); - Debug.WriteLine(LayerDefinitionsEx[layerIndex]); + inputFile.SeekDoWorkAndRewind(layerDef.DataAddress - 84, () => + { + layerDefinitionsEx[layerIndex] = Helpers.Deserialize<LayerDefEx>(inputFile); + layerDefinitionsEx[layerIndex].LayerDef.Parent = this; + Debug.Write($"LAYER {layerIndex} -> "); + Debug.WriteLine(layerDefinitionsEx[layerIndex]); + }); } - - inputFile.Read(layerDef.EncodedRle, 0, (int) layerDef.DataSize); - progress++; - progress.Token.ThrowIfCancellationRequested(); } } @@ -2013,46 +2080,40 @@ namespace UVtools.Core.FileFormats progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); - Parallel.For(0, LayerCount, layerIndex => - //for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) + foreach (var batch in BatchLayersIndexes()) { - if (progress.Token.IsCancellationRequested) - { - return; - } - - using var image = LayerDefinitions[0, layerIndex].Decode((uint) layerIndex); - var layer = new Layer((uint) layerIndex, image, LayerManager) - { - PositionZ = LayerDefinitions[0, layerIndex].LayerPositionZ, - ExposureTime = LayerDefinitions[0, layerIndex].LayerExposure, - LightOffDelay = LayerDefinitions[0, layerIndex].LightOffSeconds, - }; - - if (LayerDefinitionsEx is not null) + foreach (var layerIndex in batch) { - layer.LiftHeight = LayerDefinitionsEx[layerIndex].LiftHeight; - layer.LiftSpeed = LayerDefinitionsEx[layerIndex].LiftSpeed; - layer.RetractSpeed = LayerDefinitionsEx[layerIndex].RetractSpeed; - layer.LightPWM = (byte) LayerDefinitionsEx[layerIndex].LightPWM; - - if (HeaderSettings.Version >= 4) + for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++) { - layer.LiftHeight2 = LayerDefinitionsEx[layerIndex].LiftHeight2; - layer.LiftSpeed2 = LayerDefinitionsEx[layerIndex].LiftSpeed2; - layer.RetractHeight2 = LayerDefinitionsEx[layerIndex].RetractHeight2; - layer.RetractSpeed2 = LayerDefinitionsEx[layerIndex].RetractSpeed2; - layer.WaitTimeBeforeCure = LayerDefinitionsEx[layerIndex].RestTimeAfterRetract; - layer.WaitTimeAfterCure = LayerDefinitionsEx[layerIndex].RestTimeBeforeLift; - layer.WaitTimeAfterLift = LayerDefinitionsEx[layerIndex].RestTimeAfterLift; + progress.Token.ThrowIfCancellationRequested(); + + inputFile.Seek(LayerDefinitions[aaIndex, layerIndex].DataAddress, SeekOrigin.Begin); + LayerDefinitions[aaIndex, layerIndex].EncodedRle = inputFile.ReadBytes(LayerDefinitions[aaIndex, layerIndex].DataSize); } } - this[layerIndex] = layer; + Parallel.ForEach(batch, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = LayerDefinitions[0, layerIndex].Decode((uint)layerIndex)) + { + var layer = new Layer((uint)layerIndex, mat, this); + if (layerDefinitionsEx is not null) // CTBv4 + { + layerDefinitionsEx[layerIndex].CopyTo(layer); + } + else // others + { + LayerDefinitions[0, layerIndex].CopyTo(layer); + } + this[layerIndex] = layer; + } - progress.LockAndIncrement(); - }); + progress.LockAndIncrement(); + }); + } } public override void SaveAs(string filePath = null, OperationProgress progress = null) @@ -2092,7 +2153,7 @@ namespace UVtools.Core.FileFormats for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) { var layer = this[layerIndex]; - LayerDefinitions[aaIndex, layerIndex].RefreshLayerData(layer); + LayerDefinitions[aaIndex, layerIndex].SetFrom(layer); outputFile.Seek(layerOffset, SeekOrigin.Begin); layerOffset += Helpers.SerializeWriteFileStream(outputFile, LayerDefinitions[aaIndex, layerIndex]); diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index cc1da56..3982e02 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -8,6 +8,7 @@ using System; using System.ComponentModel; +using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Compression; @@ -197,11 +198,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => HeaderSettings.AntiAliasing; - set - { - HeaderSettings.AntiAliasing = value.Clamp(1, 16); - RaisePropertyChanged(); - } + set => base.AntiAliasing = HeaderSettings.AntiAliasing = value.Clamp(1, 16); } public override float LayerHeight @@ -367,6 +364,7 @@ namespace UVtools.Core.FileFormats public bool IsPHZZip; #endregion + #region Constructor public ChituboxZipFile() { GCode = new GCodeBuilder @@ -380,9 +378,28 @@ namespace UVtools.Core.FileFormats EndGCodeMoveCommand = GCodeBuilder.GCodeMoveCommands.G1 }; } + #endregion #region Methods + public override bool CanProcess(string fileFullPath) + { + if (!base.CanProcess(fileFullPath)) return false; + + try + { + var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read); + if (zip.Entries.Any(entry => entry.Name.EndsWith(".gcode"))) return true; + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + + return false; + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { using (ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create)) diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index b67cc0e..2f02dc4 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -405,7 +405,7 @@ namespace UVtools.Core.FileFormats #endregion #region Layer - public class LayerData + public class LayerDef { /// <summary> /// Gets the build platform Z position for this layer, measured in millimeters. @@ -440,21 +440,28 @@ namespace UVtools.Core.FileFormats [Ignore] public FDGFile Parent { get; set; } - public LayerData() + public LayerDef() { } - public LayerData(FDGFile parent, uint layerIndex) + public LayerDef(FDGFile parent, Layer layer) { Parent = parent; - RefreshLayerData(layerIndex); + SetFrom(layer); } - public void RefreshLayerData(uint layerIndex) + public void SetFrom(Layer layer) { - LayerPositionZ = Parent[layerIndex].PositionZ; - LayerExposure = Parent[layerIndex].ExposureTime; - LightOffDelay = Parent[layerIndex].LightOffDelay; + LayerPositionZ = layer.PositionZ; + LayerExposure = layer.ExposureTime; + LightOffDelay = layer.LightOffDelay; + } + + public void CopyTo(Layer layer) + { + layer.PositionZ = LayerPositionZ; + layer.ExposureTime = LayerExposure; + layer.LightOffDelay = LightOffDelay; } public unsafe Mat Decode(uint layerIndex, bool consumeData = true) @@ -522,7 +529,7 @@ namespace UVtools.Core.FileFormats return image; } - public void Encode(Mat image, uint layerIndex) + public void Encode(Mat mat, uint layerIndex) { List<byte> rawData = new(); @@ -550,12 +557,12 @@ namespace UVtools.Core.FileFormats } } - int halfWidth = image.Width / 2; + int halfWidth = mat.Width / 2; //int pixel = 0; - for (int y = 0; y < image.Height; y++) + for (int y = 0; y < mat.Height; y++) { - var span = image.GetRowSpan<byte>(y); + var span = mat.GetRowSpan<byte>(y); for (int x = 0; x < span.Length; x++) { @@ -589,7 +596,7 @@ namespace UVtools.Core.FileFormats if (Parent.HeaderSettings.EncryptionKey > 0) { - KeyRing kr = new(Parent.HeaderSettings.EncryptionKey, layerIndex); + var kr = new KeyRing(Parent.HeaderSettings.EncryptionKey, layerIndex); EncodedRle = kr.Read(rawData).ToArray(); } else @@ -664,9 +671,7 @@ namespace UVtools.Core.FileFormats public Preview[] Previews { get; protected internal set; } - public LayerData[] LayersDefinitions { get; private set; } - - public Dictionary<string, LayerData> LayersHash { get; } = new(); + public LayerDef[] LayersDefinitions { get; private set; } public override FileFormatType FileType => FileFormatType.Binary; @@ -764,11 +769,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => (byte) HeaderSettings.AntiAliasLevelInfo; - set - { - HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16); - RaisePropertyChanged(); - } + set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16)); } public override float LayerHeight @@ -955,221 +956,201 @@ namespace UVtools.Core.FileFormats protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - LayersHash.Clear(); - /*if (HeaderSettings.EncryptionKey == 0) { Random rnd = new Random(); HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue); }*/ - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - LayersDefinitions = new LayerData[HeaderSettings.LayerCount]; - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin); - for (byte i = 0; i < ThumbnailsCount; i++) - { - var image = Thumbnails[i]; + for (byte i = 0; i < ThumbnailsCount; i++) + { + var image = Thumbnails[i]; - var bytes = Preview.Encode(image); + var bytes = Preview.Encode(image); - if (bytes.Length == 0) continue; + if (bytes.Length == 0) continue; - if (i == (byte) FileThumbnailSize.Small) - { - HeaderSettings.PreviewSmallOffsetAddress = currentOffset; - } - else - { - HeaderSettings.PreviewLargeOffsetAddress = currentOffset; - } + if (i == (byte) FileThumbnailSize.Small) + { + HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position; + } + else + { + HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position; + } - Preview preview = new() - { - ResolutionX = (uint) image.Width, - ResolutionY = (uint) image.Height, - ImageLength = (uint)bytes.Length, - }; + Preview preview = new() + { + ResolutionX = (uint) image.Width, + ResolutionY = (uint) image.Height, + ImageLength = (uint)bytes.Length, + }; - currentOffset += (uint) Helpers.Serializer.SizeOf(preview); - preview.ImageOffset = currentOffset; + preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview)); - Helpers.SerializeWriteFileStream(outputFile, preview); + Helpers.SerializeWriteFileStream(outputFile, preview); - currentOffset += (uint)bytes.Length; - outputFile.WriteBytes(bytes); - } + outputFile.WriteBytes(bytes); + } - if (HeaderSettings.MachineNameSize > 0) - { - HeaderSettings.MachineNameAddress = currentOffset; - var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName); - outputFile.Write(machineBytes, 0, machineBytes.Length); - currentOffset += (uint)machineBytes.Length; - } + if (HeaderSettings.MachineNameSize > 0) + { + HeaderSettings.MachineNameAddress = (uint)outputFile.Position; + var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName); + outputFile.Write(machineBytes, 0, machineBytes.Length); + } + + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layersHash = new Dictionary<string, LayerDef>(); + LayersDefinitions = new LayerDef[HeaderSettings.LayerCount]; + HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position; + uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress; + uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount; - Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => + foreach (var batch in BatchLayersIndexes()) + { + Parallel.ForEach(batch, layerIndex => { - if(progress.Token.IsCancellationRequested) return; - LayerData layer = new(this, (uint) layerIndex); - using (var image = this[layerIndex].LayerMat) + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) { - layer.Encode(image, (uint) layerIndex); - LayersDefinitions[layerIndex] = layer; + LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]); + LayersDefinitions[layerIndex].Encode(mat, (uint)layerIndex); } - progress.LockAndIncrement(); }); - progress.Reset(OperationProgress.StatusWritingFile, LayerCount); - - HeaderSettings.LayersDefinitionOffsetAddress = currentOffset; - uint layerDataCurrentOffset = currentOffset + (uint) Helpers.Serializer.SizeOf(LayersDefinitions[0]) * LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + foreach (var layerIndex in batch) { progress.Token.ThrowIfCancellationRequested(); - LayerData layerData = LayersDefinitions[layerIndex]; - LayerData layerDataHash = null; + + var layerDef = LayersDefinitions[layerIndex]; + LayerDef layerDefHash = null; if (HeaderSettings.EncryptionKey == 0) { - string hash = Helpers.ComputeSHA1Hash(layerData.EncodedRle); - if (LayersHash.TryGetValue(hash, out layerDataHash)) + string hash = Helpers.ComputeSHA1Hash(layerDef.EncodedRle); + if (layersHash.TryGetValue(hash, out layerDefHash)) { - layerData.DataAddress = layerDataHash.DataAddress; - layerData.DataSize = layerDataHash.DataSize; + layerDef.DataAddress = layerDefHash.DataAddress; + layerDef.DataSize = layerDefHash.DataSize; } else { - LayersHash.Add(hash, layerData); + layersHash.Add(hash, layerDef); } } - if (ReferenceEquals(layerDataHash, null)) + if (layerDefHash is null) { - layerData.DataAddress = layerDataCurrentOffset; + layerDef.DataAddress = layerDataCurrentOffset; outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin); - layerDataCurrentOffset += outputFile.WriteBytes(layerData.EncodedRle); + layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle); } - LayersDefinitions[layerIndex] = layerData; + outputFile.Seek(layerDefCurrentOffset, SeekOrigin.Begin); + layerDefCurrentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef); - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layerData); - progress++; + layerDef.EncodedRle = null; // Free } + } - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - Debug.WriteLine("Encode Results:"); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine(Previews[0]); - Debug.WriteLine(Previews[1]); - Debug.WriteLine("-End-"); - } + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine(Previews[0]); + Debug.WriteLine(Previews[1]); + Debug.WriteLine("-End-"); } protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); + //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + if (HeaderSettings.Magic != MAGIC) { + throw new FileLoadException("Not a valid FDG file!", fileFullPath); + } - //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); - //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Magic != MAGIC) - { - throw new FileLoadException("Not a valid FDG file!", fileFullPath); - } - - HeaderSettings.AntiAliasLevel = 1; - - FileFullPath = fileFullPath; - - - progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); - Debug.Write("Header -> "); - Debug.WriteLine(HeaderSettings); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - uint offsetAddress = i == 0 - ? HeaderSettings.PreviewSmallOffsetAddress - : HeaderSettings.PreviewLargeOffsetAddress; - if (offsetAddress == 0) continue; + HeaderSettings.AntiAliasLevel = 1; - inputFile.Seek(offsetAddress, SeekOrigin.Begin); - Previews[i] = Helpers.Deserialize<Preview>(inputFile); + FileFullPath = fileFullPath; - Debug.Write($"Preview {i} -> "); - Debug.WriteLine(Previews[i]); - inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); - byte[] rawImageData = new byte[Previews[i].ImageLength]; - inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength); + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); + Debug.Write("Header -> "); + Debug.WriteLine(HeaderSettings); - Thumbnails[i] = Previews[i].Decode(rawImageData); - progress++; - } + for (byte i = 0; i < ThumbnailsCount; i++) + { + uint offsetAddress = i == 0 + ? HeaderSettings.PreviewSmallOffsetAddress + : HeaderSettings.PreviewLargeOffsetAddress; + if (offsetAddress == 0) continue; - if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) - { - inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); - byte[] buffer = new byte[HeaderSettings.MachineNameSize]; - inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize); - HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer); - } + inputFile.Seek(offsetAddress, SeekOrigin.Begin); + Previews[i] = Helpers.Deserialize<Preview>(inputFile); + Debug.Write($"Preview {i} -> "); + Debug.WriteLine(Previews[i]); - LayersDefinitions = new LayerData[HeaderSettings.LayerCount]; + inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); + byte[] rawImageData = new byte[Previews[i].ImageLength]; + inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength); - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; + Thumbnails[i] = Previews[i].Decode(rawImageData); + progress++; + } - progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount); + if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) + { + inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); + var buffer = new byte[HeaderSettings.MachineNameSize]; + inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize); + HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer); + } - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) + LayerManager.Init(HeaderSettings.LayerCount); + LayersDefinitions = new LayerDef[HeaderSettings.LayerCount]; + + progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); + foreach (var batch in BatchLayersIndexes()) + { + foreach (var layerIndex in batch) { - inputFile.Seek(layerOffset, SeekOrigin.Begin); - LayerData layerData = Helpers.Deserialize<LayerData>(inputFile); - layerData.Parent = this; - LayersDefinitions[layerIndex] = layerData; + progress.Token.ThrowIfCancellationRequested(); - layerOffset += (uint) Helpers.Serializer.SizeOf(layerData); + var layerDef = Helpers.Deserialize<LayerDef>(inputFile); + layerDef.Parent = this; + LayersDefinitions[layerIndex] = layerDef; + Debug.Write($"LAYER {layerIndex} -> "); - Debug.WriteLine(layerData); + Debug.WriteLine(layerDef); - layerData.EncodedRle = new byte[layerData.DataSize]; - inputFile.Seek(layerData.DataAddress, SeekOrigin.Begin); - inputFile.Read(layerData.EncodedRle, 0, (int) layerData.DataSize); - - progress++; - progress.Token.ThrowIfCancellationRequested(); + inputFile.SeekDoWorkAndRewind(layerDef.DataAddress, () => + { + layerDef.EncodedRle = inputFile.ReadBytes(layerDef.DataSize); + }); } - LayerManager.Init(HeaderSettings.LayerCount); - - progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); - - Parallel.For(0, LayerCount, layerIndex => + Parallel.ForEach(batch, layerIndex => { - if (progress.Token.IsCancellationRequested) + if (progress.Token.IsCancellationRequested) return; + using (var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex)) { - return; - } - - using (var image = LayersDefinitions[layerIndex].Decode((uint) layerIndex, true)) - { - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager) - { - PositionZ = LayersDefinitions[layerIndex].LayerPositionZ, - ExposureTime = LayersDefinitions[layerIndex].LayerExposure, - LightOffDelay = LayersDefinitions[layerIndex].LightOffDelay, - }; + var layer = new Layer((uint)layerIndex, mat, this); + LayersDefinitions[layerIndex].CopyTo(layer); + this[layerIndex] = layer; } progress.LockAndIncrement(); @@ -1195,26 +1176,24 @@ namespace UVtools.Core.FileFormats FileFullPath = filePath; } - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) + /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) { outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); byte[] buffer = new byte[HeaderSettings.MachineNameSize]; outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize); }*/ - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - LayersDefinitions[layerIndex].RefreshLayerData(layerIndex); - outputFile.Seek(layerOffset, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]); - layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]); - } + uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; + for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) + { + LayersDefinitions[layerIndex].SetFrom(this[layerIndex]); + outputFile.Seek(layerOffset, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]); + layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]); } } diff --git a/UVtools.Core/FileFormats/FileExtension.cs b/UVtools.Core/FileFormats/FileExtension.cs index 693a450..b83ad9f 100644 --- a/UVtools.Core/FileFormats/FileExtension.cs +++ b/UVtools.Core/FileFormats/FileExtension.cs @@ -32,7 +32,15 @@ namespace UVtools.Core.FileFormats /// </summary> public string Description { get; } - public bool IsVisible { get; } + /// <summary> + /// Gets if the extension shows up on open file dialog filters + /// </summary> + public bool IsVisibleOnFileFilters { get; } + + /// <summary> + /// Gets if the extension shows up on convert to menu + /// </summary> + public bool IsVisibleOnConvertMenu { get; } /// <summary> /// Gets a tag object @@ -53,14 +61,16 @@ namespace UVtools.Core.FileFormats /// <param name="fileFormatType">The exact <see cref="FileFormat"/> type</param> /// <param name="extension">The extension name without the dot (.)</param> /// <param name="description">The extension description</param> - /// <param name="isVisible">True if this extension is visible on open dialog filters</param> + /// <param name="isVisibleOnFileFilters">True if this extension is visible on open file dialog filters</param> + /// <param name="isVisibleOnConvertMenu">True if this extension is visible on convert to menu</param> /// <param name="tag">Tag object</param> - public FileExtension(Type fileFormatType, string extension, string description, bool isVisible = true, object tag = null) + public FileExtension(Type fileFormatType, string extension, string description, bool isVisibleOnFileFilters = true, bool isVisibleOnConvertMenu = true, object tag = null) { FileFormatType = fileFormatType; Extension = extension; Description = description; - IsVisible = isVisible; + IsVisibleOnFileFilters = isVisibleOnFileFilters; + IsVisibleOnConvertMenu = isVisibleOnConvertMenu; Tag = tag; } #endregion diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index b528ccc..b0eaa67 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -73,6 +73,31 @@ namespace UVtools.Core.FileFormats public const float MaximumLayerHeight = 0.20f; private const ushort QueueTimerPrintTime = 250; // ms + + public const string DATATYPE_PNG = "PNG"; + public const string DATATYPE_JPG = "JPG"; + public const string DATATYPE_JPEG = "JPEG"; + public const string DATATYPE_JP2 = "JP2"; + public const string DATATYPE_BMP = "BMP"; + public const string DATATYPE_TIF = "TIF"; + public const string DATATYPE_TIFF = "TIFF"; + public const string DATATYPE_PPM = "PPM"; + public const string DATATYPE_PMG = "PMG"; + public const string DATATYPE_SR = "SR"; + public const string DATATYPE_RAS = "RAS"; + + public const string DATATYPE_RGB555 = "RGB555"; + public const string DATATYPE_RGB565 = "RGB565"; + public const string DATATYPE_RGB555_BE = "RGB555-BE"; + public const string DATATYPE_RGB565_BE = "RGB565-BE"; + public const string DATATYPE_RGB888 = "RGB888"; + + + public const string DATATYPE_BGR555 = "BGR555"; + public const string DATATYPE_BGR565 = "BGR565"; + public const string DATATYPE_BGR555_BE = "BGR555-BE"; + public const string DATATYPE_BGR565_BE = "BGR565-BE"; + public const string DATATYPE_BGR888 = "BGR888"; #endregion #region Enums @@ -299,6 +324,7 @@ namespace UVtools.Core.FileFormats { foreach (var fileExtension in AvailableFormats[i].FileExtensions) { + if(!fileExtension.IsVisibleOnFileFilters) continue; result[0].Value.Add(fileExtension.Extension); result.Add(new KeyValuePair<string, List<string>>(fileExtension.Description, new List<string> { @@ -410,12 +436,260 @@ namespace UVtools.Core.FileFormats //if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file); return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out strippedExtension); } + + + public static byte[] EncodeImage(string dataType, Mat mat) + { + dataType = dataType.ToUpperInvariant(); + if (dataType + is DATATYPE_PNG + or DATATYPE_JPG + or DATATYPE_JPEG + or DATATYPE_JP2 + or DATATYPE_BMP + or DATATYPE_TIF + or DATATYPE_TIFF + or DATATYPE_PPM + or DATATYPE_PMG + or DATATYPE_SR + or DATATYPE_RAS + ) + { + return CvInvoke.Imencode($".{dataType.ToLowerInvariant()}", mat); + } + + if (dataType + is DATATYPE_RGB555 + or DATATYPE_RGB565 + or DATATYPE_RGB555_BE + or DATATYPE_RGB565_BE + or DATATYPE_RGB888 + + or DATATYPE_BGR555 + or DATATYPE_BGR565 + or DATATYPE_BGR555_BE + or DATATYPE_BGR565_BE + or DATATYPE_BGR888 + ) + { + var bytesPerPixel = dataType is "RGB888" or "BGR888" ? 3 : 2; + var bytes = new byte[mat.Width * mat.Height * bytesPerPixel]; + uint index = 0; + var span = mat.GetDataByteSpan(); + for (int i = 0; i < span.Length;) + { + byte b = span[i++]; + byte g; + byte r; + + if (mat.NumberOfChannels == 1) // 8 bit safe-guard + { + r = g = b; + } + else + { + g = span[i++]; + r = span[i++]; + } + + if (mat.NumberOfChannels == 4) i++; // skip alpha + + switch (dataType) + { + case DATATYPE_RGB555: + var rgb555 = (ushort)(((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3)); + BitExtensions.ToBytesLittleEndian(rgb555, bytes, index); + index += 2; + break; + case DATATYPE_RGB565: + var rgb565 = (ushort)(((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3)); + BitExtensions.ToBytesLittleEndian(rgb565, bytes, index); + index += 2; + break; + case DATATYPE_RGB555_BE: + var rgb555Be = (ushort)(((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3)); + BitExtensions.ToBytesBigEndian(rgb555Be, bytes, index); + index += 2; + break; + case DATATYPE_RGB565_BE: + var rgb565Be = (ushort)(((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3)); + BitExtensions.ToBytesBigEndian(rgb565Be, bytes, index); + index += 2; + break; + case DATATYPE_RGB888: + bytes[index++] = r; + bytes[index++] = g; + bytes[index++] = b; + break; + case DATATYPE_BGR555: + var bgr555 = (ushort)(((b & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (r >> 3)); + BitExtensions.ToBytesLittleEndian(bgr555, bytes, index); + index += 2; + break; + case DATATYPE_BGR565: + var bgr565 = (ushort)(((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3)); + BitExtensions.ToBytesLittleEndian(bgr565, bytes, index); + index += 2; + break; + case DATATYPE_BGR555_BE: + var bgr555Be = (ushort)(((b & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (r >> 3)); + BitExtensions.ToBytesBigEndian(bgr555Be, bytes, index); + index += 2; + break; + case DATATYPE_BGR565_BE: + var bgr565Be = (ushort)(((b & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (r >> 3)); + BitExtensions.ToBytesBigEndian(bgr565Be, bytes, index); + index += 2; + break; + case DATATYPE_BGR888: + bytes[index++] = b; + bytes[index++] = g; + bytes[index++] = r; + break; + } + } + + return bytes; + } + + throw new NotSupportedException($"The encode type: {dataType} is not supported."); + } + + public static Mat DecodeImage(string dataType, byte[] bytes, Size resolution) + { + if (dataType + is DATATYPE_PNG + or DATATYPE_JPG + or DATATYPE_JPEG + or DATATYPE_JP2 + or DATATYPE_BMP + or DATATYPE_TIF + or DATATYPE_TIFF + or DATATYPE_PPM + or DATATYPE_PMG + or DATATYPE_SR + or DATATYPE_RAS + ) + { + var mat = new Mat(); + CvInvoke.Imdecode(bytes, ImreadModes.AnyColor, mat); + return mat; + } + + if (dataType + is DATATYPE_RGB555 + or DATATYPE_RGB565 + or DATATYPE_RGB555_BE + or DATATYPE_RGB565_BE + or DATATYPE_RGB888 + + or DATATYPE_BGR555 + or DATATYPE_BGR565 + or DATATYPE_BGR555_BE + or DATATYPE_BGR565_BE + or DATATYPE_BGR888 + ) + { + var mat = new Mat(resolution, DepthType.Cv8U, 3); + var span = mat.GetDataByteSpan(); + var pixel = 0; + for (int i = 0; i < bytes.Length;) + { + switch (dataType) + { + case DATATYPE_RGB555: + ushort rgb555 = BitExtensions.ToUShortLittleEndian(bytes, i); + // 0b0rrrrrgggggbbbbb + span[pixel++] = (byte)((rgb555 & 0b00000000_00011111) << 3); // b + span[pixel++] = (byte)((rgb555 & 0b00000011_11100000) >> 2); // g + span[pixel++] = (byte)((rgb555 & 0b01111100_00000000) >> 7); // r + /*span[pixel++] = (byte)((rgb555 << 3) & 0b11111000); // b + span[pixel++] = (byte)((rgb555 >> 2) & 0b11111000); // g + span[pixel++] = (byte)((rgb555 >> 7) & 0b11111000); // r*/ + i += 2; + break; + case DATATYPE_RGB565: + // 0brrrrrggggggbbbbb + ushort rgb565 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((rgb565 & 0b00000000_00011111) << 3); // b + span[pixel++] = (byte)((rgb565 & 0b00000111_11100000) >> 3); // g + span[pixel++] = (byte)((rgb565 & 0b11111000_00000000) >> 8); // r + i += 2; + break; + case DATATYPE_RGB555_BE: + ushort rgb555Be = BitExtensions.ToUShortBigEndian(bytes, i); + span[pixel++] = (byte)((rgb555Be & 0b00000000_00011111) << 3); // b + span[pixel++] = (byte)((rgb555Be & 0b00000011_11100000) >> 2); // g + span[pixel++] = (byte)((rgb555Be & 0b01111100_00000000) >> 7); // r + i += 2; + break; + case DATATYPE_RGB565_BE: + ushort rgb565Be = BitExtensions.ToUShortBigEndian(bytes, i); + span[pixel++] = (byte)((rgb565Be & 0b00000000_00011111) << 3); // b + span[pixel++] = (byte)((rgb565Be & 0b00000111_11100000) >> 3); // g + span[pixel++] = (byte)((rgb565Be & 0b11111000_00000000) >> 8); // r + i += 2; + break; + case DATATYPE_RGB888: + span[pixel++] = bytes[i + 2]; // b + span[pixel++] = bytes[i + 1]; // g + span[pixel++] = bytes[i]; // r + i += 3; + break; + case DATATYPE_BGR555: + ushort bgr555 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((bgr555 & 0b01111100_00000000) >> 7); // b + span[pixel++] = (byte)((bgr555 & 0b00000011_11100000) >> 2); // g + span[pixel++] = (byte)((bgr555 & 0b00000000_00011111) << 3); // r + i += 2; + break; + case DATATYPE_BGR565: + ushort bgr565 = BitExtensions.ToUShortLittleEndian(bytes, i); + span[pixel++] = (byte)((bgr565 & 0b11111000_00000000) >> 8); // b + span[pixel++] = (byte)((bgr565 & 0b00000111_11100000) >> 3); // g + span[pixel++] = (byte)((bgr565 & 0b00000000_00011111) << 3); // r + i += 2; + break; + case DATATYPE_BGR555_BE: + ushort bgr555Be = BitExtensions.ToUShortBigEndian(bytes, i); + span[pixel++] = (byte)((bgr555Be & 0b01111100_00000000) >> 7); // b + span[pixel++] = (byte)((bgr555Be & 0b00000011_11100000) >> 2); // g + span[pixel++] = (byte)((bgr555Be & 0b00000000_00011111) << 3); // r + i += 2; + break; + case DATATYPE_BGR565_BE: + ushort bgr565Be = BitExtensions.ToUShortBigEndian(bytes, i); + span[pixel++] = (byte)((bgr565Be & 0b11111000_00000000) >> 8); // b + span[pixel++] = (byte)((bgr565Be & 0b00000111_11100000) >> 3); // g + span[pixel++] = (byte)((bgr565Be & 0b00000000_00011111) << 3); // r + i += 2; + break; + case DATATYPE_BGR888: + span[pixel++] = bytes[i]; // b + span[pixel++] = bytes[i + 1]; // g + span[pixel++] = bytes[i + 2]; // r + i += 3; + break; + } + } + return mat; + } + + throw new NotSupportedException($"The decode type: {dataType} is not supported."); + } + + public static Mat DecodeImage(string dataType, byte[] bytes, uint resolutionX = 0, uint resolutionY = 0) + => DecodeImage(dataType, bytes, new Size((int)resolutionX, (int)resolutionY)); #endregion #region Members + public object Mutex = new(); + private bool _haveModifiedLayers; private LayerManager _layerManager; + private byte _antiAliasing = 1; + private ushort _bottomLayerCount = DefaultBottomLayerCount; private float _bottomLightOffDelay; @@ -468,8 +742,6 @@ namespace UVtools.Core.FileFormats #region Properties - public object Mutex = new(); - /// <summary> /// Gets the file format type /// </summary> @@ -752,6 +1024,7 @@ namespace UVtools.Core.FileFormats { RaisePropertyChanged(nameof(Xppmm)); RaisePropertyChanged(nameof(Ppmm)); + RaisePropertyChanged(nameof(PpmmMax)); } } @@ -765,6 +1038,7 @@ namespace UVtools.Core.FileFormats { RaisePropertyChanged(nameof(Yppmm)); RaisePropertyChanged(nameof(Ppmm)); + RaisePropertyChanged(nameof(PpmmMax)); } } @@ -782,14 +1056,19 @@ namespace UVtools.Core.FileFormats } /// <summary> + /// Gets the maximum (Width or Height) pixels per mm + /// </summary> + public float PpmmMax => Ppmm.Max(); + + /// <summary> /// Gets the pixel width in millimeters /// </summary> - public float PixelWidth => DisplayWidth > 0 ? (float) Math.Round(DisplayWidth / ResolutionX, 3) : 0; + public float PixelWidth => DisplayWidth > 0 && ResolutionX > 0 ? (float) Math.Round(DisplayWidth / ResolutionX, 3) : 0; /// <summary> /// Gets the pixel height in millimeters /// </summary> - public float PixelHeight => DisplayHeight > 0 ? (float) Math.Round(DisplayHeight / ResolutionY, 3) : 0; + public float PixelHeight => DisplayHeight > 0 && ResolutionY > 0 ? (float) Math.Round(DisplayHeight / ResolutionY, 3) : 0; /// <summary> /// Gets the pixel size in millimeters @@ -844,7 +1123,11 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets or sets the AntiAliasing level /// </summary> - public abstract byte AntiAliasing { get; set; } + public virtual byte AntiAliasing + { + get => _antiAliasing; + set => RaiseAndSet(ref _antiAliasing, value); + } /// <summary> /// Gets Layer Height in mm @@ -862,7 +1145,7 @@ namespace UVtools.Core.FileFormats /// </summary> public virtual float PrintHeight { - get => LayerCount == 0 ? 0 : this[LayerCount - 1]?.PositionZ ?? 0; + get => LayerCount == 0 ? 0 : LastLayer?.PositionZ ?? 0; set => RaisePropertyChanged(); } @@ -881,7 +1164,7 @@ namespace UVtools.Core.FileFormats /// </summary> public virtual uint LayerCount { - get => LayerManager?.LayerCount ?? 0; + get => _layerManager?.LayerCount ?? 0; set { RaisePropertyChanged(); RaisePropertyChanged(nameof(NormalLayerCount)); @@ -1019,10 +1302,10 @@ namespace UVtools.Core.FileFormats /// </summary> public float BottomLiftHeightTotal { - get => (float)Math.Round(_bottomLiftHeight + _bottomLiftHeight2); + get => (float)Math.Round(BottomLiftHeight + BottomLiftHeight2, 2); set { - BottomLiftHeight = value; + BottomLiftHeight = (float)Math.Round(value, 2); BottomLiftHeight2 = 0; } } @@ -1033,10 +1316,10 @@ namespace UVtools.Core.FileFormats /// </summary> public float LiftHeightTotal { - get => (float)Math.Round(_liftHeight + _liftHeight2); + get => (float)Math.Round(LiftHeight + LiftHeight2, 2); set { - LiftHeight = value; + LiftHeight = (float)Math.Round(value, 2); LiftHeight2 = 0; } } @@ -1195,7 +1478,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the bottom retract height in mm /// </summary> - public float BottomRetractHeight => (float)Math.Round(BottomLiftHeightTotal - _bottomRetractHeight2); + public float BottomRetractHeight => (float)Math.Round(BottomLiftHeightTotal - BottomRetractHeight2, 2); /// <summary> /// Gets the speed in mm/min for the bottom retracts @@ -1213,7 +1496,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the retract height in mm /// </summary> - public float RetractHeight => (float)Math.Round(LiftHeightTotal - _retractHeight2); + public float RetractHeight => (float)Math.Round(LiftHeightTotal - RetractHeight2, 2); /// <summary> /// Gets the speed in mm/min for the retracts @@ -1407,19 +1690,32 @@ namespace UVtools.Core.FileFormats var haveBottomLiftHeight2 = CanUseBottomLiftHeight2; var haveLiftHeight2 = CanUseLiftHeight2; + var haveBottomLiftSpeed2 = CanUseBottomLiftSpeed2; + var haveLiftSpeed2 = CanUseLiftSpeed2; + if (!haveBottomLiftHeight && !haveLiftHeight && !haveBottomLiftHeight2 && !haveLiftHeight2) return str; // Sequence 1 if (haveBottomLiftHeight) { str += BottomLiftHeight.ToString(CultureInfo.InvariantCulture); + if (haveBottomLiftHeight2 && BottomLiftHeight2 > 0) + { + str += $"+{BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}"; + } } + if (haveLiftHeight) { if (!string.IsNullOrEmpty(str)) str += '/'; str += LiftHeight.ToString(CultureInfo.InvariantCulture); - } + if (haveLiftHeight2 && LiftHeight2 > 0) + { + str += $"+{LiftHeight2.ToString(CultureInfo.InvariantCulture)}"; + } + } + if (string.IsNullOrEmpty(str)) return str; str += "mm @ "; @@ -1429,16 +1725,24 @@ namespace UVtools.Core.FileFormats if (haveBottomLiftSpeed) { str += BottomLiftSpeed.ToString(CultureInfo.InvariantCulture); + if (haveBottomLiftSpeed2 && haveBottomLiftHeight2 && BottomLiftHeight2 > 0) + { + str += $"+{BottomLiftSpeed2.ToString(CultureInfo.InvariantCulture)}"; + } } if (haveLiftSpeed) { if (haveBottomLiftSpeed) str += '/'; str += LiftSpeed.ToString(CultureInfo.InvariantCulture); + if (haveLiftSpeed2 && haveLiftHeight2 && LiftHeight2 > 0) + { + str += $"+{LiftSpeed2.ToString(CultureInfo.InvariantCulture)}"; + } } str += "mm/min"; - // Sequence 2 + /*// Sequence 2 if (haveBottomLiftHeight2) { str += $"\n2th: {BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}"; @@ -1465,7 +1769,7 @@ namespace UVtools.Core.FileFormats str += LiftSpeed2.ToString(CultureInfo.InvariantCulture); } - str += "mm/min"; + str += "mm/min";*/ return str; } @@ -1492,11 +1796,19 @@ namespace UVtools.Core.FileFormats if (haveBottomRetractHeight) { str += BottomRetractHeight.ToString(CultureInfo.InvariantCulture); + if (haveBottomRetractHeight2 && BottomRetractHeight2 > 0) + { + str += $"+{BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}"; + } } if (haveRetractHeight) { if (!string.IsNullOrEmpty(str)) str += '/'; str += RetractHeight.ToString(CultureInfo.InvariantCulture); + if (haveRetractHeight2 && RetractHeight2 > 0) + { + str += $"+{RetractHeight2.ToString(CultureInfo.InvariantCulture)}"; + } } if (string.IsNullOrEmpty(str)) return str; @@ -1507,17 +1819,25 @@ namespace UVtools.Core.FileFormats if (haveBottomRetractSpeed) { str += BottomRetractSpeed.ToString(CultureInfo.InvariantCulture); + if (haveBottomRetractSpeed2 && haveBottomRetractHeight2 && BottomRetractHeight2 > 0) + { + str += $"+{BottomRetractSpeed2.ToString(CultureInfo.InvariantCulture)}"; + } } if (haveRetractSpeed) { if (haveBottomRetractSpeed) str += '/'; str += RetractSpeed.ToString(CultureInfo.InvariantCulture); + if (haveRetractSpeed2 && haveRetractHeight2 && RetractHeight2 > 0) + { + str += $"+{RetractSpeed2.ToString(CultureInfo.InvariantCulture)}"; + } } str += "mm/min"; // Sequence 2 - if (haveBottomRetractHeight2) + /*if (haveBottomRetractHeight2) { str += $"\n2th: {BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}"; } @@ -1541,7 +1861,7 @@ namespace UVtools.Core.FileFormats str += RetractSpeed2.ToString(CultureInfo.InvariantCulture); } - str += "mm/min"; + str += "mm/min";*/ return str; } @@ -1589,6 +1909,18 @@ namespace UVtools.Core.FileFormats } } + public IEnumerable<IEnumerable<int>> BatchLayersIndexes(int batchSize = 0) + { + if (batchSize <= 0) batchSize = Environment.ProcessorCount * 10; + return MoreLinq.MoreEnumerable.Batch(Enumerable.Range(0, (int)LayerCount), batchSize); + } + + public IEnumerable<IEnumerable<Layer>> BatchLayers(int batchSize = 0) + { + if (batchSize <= 0) batchSize = Environment.ProcessorCount * 10; + return MoreLinq.MoreEnumerable.Batch(this, batchSize); + } + #endregion /// <summary> @@ -1723,8 +2055,13 @@ namespace UVtools.Core.FileFormats { if (value <= 0) { - value = (float)Math.Round(this.Where(layer => layer is not null).Sum(layer => layer.MaterialMilliliters), 3); ; + value = (float)Math.Round(this.Where(layer => layer is not null).Sum(layer => layer.MaterialMilliliters), 3); + } + else + { + value = (float)Math.Round(value, 3); } + RaiseAndSetIfChanged(ref _materialMilliliters, value); } } @@ -2094,6 +2431,8 @@ namespace UVtools.Core.FileFormats CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]); } } + + RaisePropertyChanged(nameof(Thumbnails)); } /// <summary> @@ -2112,6 +2451,7 @@ namespace UVtools.Core.FileFormats CvInvoke.Resize(Thumbnails[i], Thumbnails[i], ThumbnailsOriginalSize[i]); } } + RaisePropertyChanged(nameof(Thumbnails)); } /// <summary> @@ -2126,6 +2466,7 @@ namespace UVtools.Core.FileFormats { CvInvoke.Resize(Thumbnails[index], Thumbnails[index], ThumbnailsOriginalSize[index]); } + RaisePropertyChanged(nameof(Thumbnails)); } /// <summary> @@ -3041,8 +3382,8 @@ namespace UVtools.Core.FileFormats /// </summary> public byte ValidateAntiAliasingLevel() { - if (AntiAliasing < 2) return 1; - if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing)); + if (AntiAliasing <= 1) return 1; + //if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing)); return AntiAliasing; } diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index c0a6441..ad0a7ff 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -232,12 +232,6 @@ namespace UVtools.Core.FileFormats public override bool DisplayMirror { get; set; } - public override byte AntiAliasing - { - get => 1; - set { } - } - public override float LayerHeight { get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray())); @@ -368,33 +362,24 @@ namespace UVtools.Core.FileFormats Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - byte[][] previews = new byte[ThumbnailsOriginalSize.Length][]; - for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) - { - previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; - } + var previews = new byte[ThumbnailsOriginalSize.Length][]; + // Previews Parallel.For(0, previews.Length, previewIndex => { if (progress.Token.IsCancellationRequested) return; - if (Thumbnails[previewIndex] is null) return; - var span = Thumbnails[previewIndex].GetDataByteSpan(); - int index = 0; - for (int i = 0; i < span.Length; i += 3) + var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; + if (Thumbnails[previewIndex] is null) { - byte b = span[i]; - byte g = span[i + 1]; - byte r = span[i + 2]; - - ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); - - previews[previewIndex][index++] = (byte)(rgb15 >> 8); - previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + previews[previewIndex] = new byte[encodeLength]; + return; } - if (index != previews[previewIndex].Length) + previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]); + + if (encodeLength != previews[previewIndex].Length) { - throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}"); } }); @@ -402,15 +387,15 @@ namespace UVtools.Core.FileFormats { Helpers.SerializeWriteFileStream(outputFile, previews[i]); outputFile.WriteBytes(pageBreak); + //Helpers.SerializeWriteFileStream(outputFile, pageBreak); + previews[i] = null; } Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - var range = Enumerable.Range(0, (int)LayerCount); - var layerBytes = new List<byte>[LayerCount]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -418,44 +403,47 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; var layer = this[layerIndex]; - using var mat = layer.LayerMat; - var span = mat.GetDataByteSpan(); + using (var mat = layer.LayerMat) + { + var span = mat.GetDataByteSpan(); - layerBytes[layerIndex] = new(); + layerBytes[layerIndex] = new(); - uint lineCount = 0; + uint lineCount = 0; - for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) - { - int y = layer.BoundingRectangle.Y; - int startY = -1; - for (; y < layer.BoundingRectangle.Bottom; y++) + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) { - int pos = mat.GetPixelPos(x, y); - if (span[pos] < 128) // Black pixel + int y = layer.BoundingRectangle.Y; + int startY = -1; + for (; y < layer.BoundingRectangle.Bottom; y++) { - if (startY == -1) continue; // Keep ignoring - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); - startY = -1; - lineCount++; + int pos = mat.GetPixelPos(x, y); + if (span[pos] < 128) // Black pixel + { + if (startY == -1) continue; // Keep ignoring + layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); + startY = -1; + lineCount++; + } + else + { + if (startY >= 0) continue; // Keep sum + startY = y; + } } - else + + if (startY >= 0) { - if (startY >= 0) continue; // Keep sum - startY = y; + layerBytes[layerIndex] + .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); + lineCount++; } } - if (startY >= 0) - { - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); - lineCount++; - } + layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount)); + layerBytes[layerIndex].AddRange(pageBreak); } - layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount)); - layerBytes[layerIndex].AddRange(pageBreak); - progress.LockAndIncrement(); }); @@ -497,25 +485,10 @@ namespace UVtools.Core.FileFormats Parallel.For(0, previews.Length, previewIndex => { - var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); - var span = mat.GetDataByteSpan(); - - int spanIndex = 0; - for (int i = 0; i < previews[previewIndex].Length; i += 2) - { - ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); - byte r = (byte)((rgb15 >> 11) << 3); - byte g = (byte)((rgb15 >> 5) << 2); - byte b = (byte)((rgb15 >> 0) << 3); - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - Thumbnails[previewIndex] = mat; + Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); + previews[previewIndex] = null; }); - + SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile); @@ -527,7 +500,7 @@ namespace UVtools.Core.FileFormats var range = Enumerable.Range(0, (int)LayerCount); var linesBytes = new byte[LayerCount][]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index 45f69fb..59ce84a 100644 --- a/UVtools.Core/FileFormats/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -72,12 +72,6 @@ namespace UVtools.Core.FileFormats set { } } - public override byte AntiAliasing - { - get => 1; - set { } - } - public override float LayerHeight { get; set; } = 0.01f; /*public override float PrintTime { get; } = 0; public override float UsedMaterial { get; } = 0; diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 71fb3a4..5c79a18 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -126,7 +126,7 @@ namespace UVtools.Core.FileFormats #region LayerData - public class LayerData + public class LayerDef { [Ignore] public LGSFile Parent { get; set; } @@ -137,9 +137,9 @@ namespace UVtools.Core.FileFormats [FieldLength(nameof(DataSize))] public byte[] EncodedRle { get; set; } - public LayerData() { } + public LayerDef() { } - public LayerData(LGSFile parent) + public LayerDef(LGSFile parent) { Parent = parent; } @@ -211,7 +211,7 @@ namespace UVtools.Core.FileFormats { var b = EncodedRle[i]; byte colorNibble = (byte)(b >> 4); - byte color = (byte)(colorNibble << 0x4 | colorNibble); + byte color = (byte)(colorNibble << 4 | colorNibble); int repeat = b & 0xf; while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble) @@ -350,12 +350,6 @@ namespace UVtools.Core.FileFormats set { } } - public override byte AntiAliasing - { - get => 4; - set { } - } - public override float LayerHeight { get => HeaderSettings.LayerHeight; @@ -492,54 +486,7 @@ namespace UVtools.Core.FileFormats #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) + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { if (FileEndsWith(".lgs")) // Longer Orange 10 { @@ -566,7 +513,7 @@ namespace UVtools.Core.FileFormats using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) { outputFile.WriteSerialize(HeaderSettings); - outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); + outputFile.WriteBytes(EncodeImage(DATATYPE_RGB565_BE, Thumbnails[0])); if (HeaderSettings.PrinterModel == 120) { @@ -577,19 +524,30 @@ namespace UVtools.Core.FileFormats outputFile.WriteSerialize(pngPreview); } - var layerData = new LayerData[LayerCount]; + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layerData = new LayerDef[LayerCount]; - Parallel.For(0, LayerCount, layerIndex => + foreach (var batch in BatchLayersIndexes()) { - if (progress.Token.IsCancellationRequested) return; - using (var mat = this[layerIndex].LayerMat) + Parallel.ForEach(batch, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) + { + layerData[layerIndex] = new LayerDef(this); + layerData[layerIndex].Encode(mat); + } + progress.LockAndIncrement(); + }); + + foreach (var layerIndex in batch) { - layerData[layerIndex] = new LayerData(this); - layerData[layerIndex].Encode(mat); + progress.Token.ThrowIfCancellationRequested(); + outputFile.WriteSerialize(layerData[layerIndex]); + layerData[layerIndex].EncodedRle = null; // Free this } + } - progress.LockAndIncrement(); - }); progress.ItemName = "Saving layers"; progress.ProcessedItems = 0; @@ -625,44 +583,41 @@ namespace UVtools.Core.FileFormats } //} - 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); + var previewSize = HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2; + var previewData = inputFile.ReadBytes(previewSize); + Thumbnails[0] = DecodeImage(DATATYPE_RGB565_BE, previewData, HeaderSettings.PreviewSizeX, HeaderSettings.PreviewSizeY); 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); + var layerData = new LayerDef[LayerCount]; + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + foreach (var batch in BatchLayersIndexes()) + { + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); + + layerData[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile); + layerData[layerIndex].Parent = this; + } - Parallel.For(0, LayerCount, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - layerIndex => + Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - - using var image = layerData[layerIndex].Decode(); - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); + using (var mat = layerData[layerIndex].Decode()) + { + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + } progress.LockAndIncrement(); }); + } LayerManager.RebuildLayersProperties(); } diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index 86da4af..d3516b8 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -235,13 +235,6 @@ namespace UVtools.Core.FileFormats } public override bool DisplayMirror { get; set; } - - public override byte AntiAliasing - { - get => 1; - set { } - } - public override float LayerHeight { get => float.Parse(Encoding.ASCII.GetString(SlicerInfoSettings.LayerHeightBytes.Where(b => b != 0).ToArray())); @@ -328,33 +321,24 @@ namespace UVtools.Core.FileFormats Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - byte[][] previews = new byte[2][]; - for (int i = 0; i < ThumbnailsOriginalSize.Length; i++) - { - previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2]; - } + var previews = new byte[ThumbnailsOriginalSize.Length][]; + // Previews Parallel.For(0, previews.Length, previewIndex => { if (progress.Token.IsCancellationRequested) return; - if (Thumbnails[previewIndex] is null) return; - var span = Thumbnails[previewIndex].GetDataByteSpan(); - int index = 0; - for (int i = 0; i < span.Length; i += 3) + var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; + if (Thumbnails[previewIndex] is null) { - byte b = span[i]; - byte g = span[i + 1]; - byte r = span[i + 2]; - - ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0)); - - previews[previewIndex][index++] = (byte)(rgb15 >> 8); - previews[previewIndex][index++] = (byte)(rgb15 & 0xff); + previews[previewIndex] = new byte[encodeLength]; + return; } - if (index != previews[previewIndex].Length) + previews[previewIndex] = EncodeImage(DATATYPE_RGB565_BE, Thumbnails[previewIndex]); + + if (encodeLength != previews[previewIndex].Length) { - throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}"); + throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {encodeLength}"); } }); @@ -362,6 +346,7 @@ namespace UVtools.Core.FileFormats { Helpers.SerializeWriteFileStream(outputFile, previews[i]); outputFile.WriteBytes(pageBreak); + previews[i] = null; } Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings); @@ -369,7 +354,7 @@ namespace UVtools.Core.FileFormats var range = Enumerable.Range(0, (int)LayerCount); var layerBytes = new List<byte>[LayerCount]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -377,44 +362,47 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; var layer = this[layerIndex]; - using var mat = layer.LayerMat; - var span = mat.GetDataByteSpan(); + using (var mat = layer.LayerMat) + { + var span = mat.GetDataByteSpan(); - layerBytes[layerIndex] = new(); + layerBytes[layerIndex] = new(); - uint lineCount = 0; + uint lineCount = 0; - for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) - { - int y = layer.BoundingRectangle.Y; - int startY = -1; - for (; y < layer.BoundingRectangle.Bottom; y++) + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) { - int pos = mat.GetPixelPos(x, y); - if (span[pos] < 128) // Black pixel + int y = layer.BoundingRectangle.Y; + int startY = -1; + for (; y < layer.BoundingRectangle.Bottom; y++) { - if (startY == -1) continue; // Keep ignoring - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); - startY = -1; - lineCount++; + int pos = mat.GetPixelPos(x, y); + if (span[pos] < 128) // Black pixel + { + if (startY == -1) continue; // Keep ignoring + layerBytes[layerIndex] + .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); + startY = -1; + lineCount++; + } + else + { + if (startY >= 0) continue; // Keep sum + startY = y; + } } - else + + if (startY >= 0) { - if (startY >= 0) continue; // Keep sum - startY = y; + layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); + lineCount++; } } - if (startY >= 0) - { - layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x)); - lineCount++; - } + layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount)); + layerBytes[layerIndex].AddRange(pageBreak); } - layerBytes[layerIndex].InsertRange(0, BitExtensions.ToBytesBigEndian(lineCount)); - layerBytes[layerIndex].AddRange(pageBreak); - progress.LockAndIncrement(); }); @@ -454,25 +442,10 @@ namespace UVtools.Core.FileFormats Parallel.For(0, previews.Length, previewIndex => { - var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3); - var span = mat.GetDataByteSpan(); - - int spanIndex = 0; - for (int i = 0; i < previews[previewIndex].Length; i += 2) - { - ushort rgb15 = BitExtensions.ToUShortBigEndian(previews[previewIndex][i], previews[previewIndex][i + 1]); - byte r = (byte)((rgb15 >> 11) << 3); - byte g = (byte)((rgb15 >> 5) << 2); - byte b = (byte)((rgb15 >> 0) << 3); - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - Thumbnails[previewIndex] = mat; + Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); + previews[previewIndex] = null; }); - + SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile); @@ -484,7 +457,7 @@ namespace UVtools.Core.FileFormats var range = Enumerable.Range(0, (int)LayerCount); var linesBytes = new byte[LayerCount][]; - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { progress.Token.ThrowIfCancellationRequested(); @@ -502,20 +475,22 @@ namespace UVtools.Core.FileFormats Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = EmguExtensions.InitMat(Resolution); - - for (int i = 0; i < linesBytes[layerIndex].Length; i++) + using (var mat = EmguExtensions.InitMat(Resolution)) { - var startY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]); - var endY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]); - var startX = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i]); - - CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY), EmguExtensions.WhiteColor); - } - linesBytes[layerIndex] = null; + for (int i = 0; i < linesBytes[layerIndex].Length; i++) + { + var startY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]); + var endY = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i++]); + var startX = BitExtensions.ToUShortBigEndian(linesBytes[layerIndex][i++], linesBytes[layerIndex][i]); + + CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY), EmguExtensions.WhiteColor); + } + + linesBytes[layerIndex] = null; - this[layerIndex] = new Layer((uint)layerIndex, mat, this); + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + } progress.LockAndIncrement(); }); diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs index 0ddd9a8..ef0e6de 100644 --- a/UVtools.Core/FileFormats/OSLAFile.cs +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -13,12 +13,10 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; -using System.Linq; using System.Threading.Tasks; using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; -using MoreLinq; using UVtools.Core.Extensions; using UVtools.Core.GCode; using UVtools.Core.Operations; @@ -30,6 +28,7 @@ namespace UVtools.Core.FileFormats #region Constants public const string MARKER = "OSLATiCo"; + #endregion #region Sub Classes @@ -209,7 +208,7 @@ namespace UVtools.Core.FileFormats BoundingRectangleHeight = (uint)layer.BoundingRectangle.Height; } - public void SetTo(Layer layer) + public void CopyTo(Layer layer) { layer.PositionZ = PositionZ; layer.LiftHeight = LiftHeight; @@ -376,12 +375,6 @@ namespace UVtools.Core.FileFormats } } - public override byte AntiAliasing - { - get => 8; - set => RaisePropertyChanged(); - } - public override float LayerHeight { get => HeaderSettings.LayerHeight; @@ -495,7 +488,7 @@ namespace UVtools.Core.FileFormats progress.Token.ThrowIfCancellationRequested(); - var bytes = EncodeImage(image, HeaderSettings.PreviewDataType); + var bytes = EncodeImage(HeaderSettings.PreviewDataType, image); if (bytes.Length == 0) continue; var preview = new Preview { @@ -528,18 +521,20 @@ namespace UVtools.Core.FileFormats outputFile.Seek(HeaderSettings.LayerTableSize * LayerCount, SeekOrigin.Current); // Start of layer data - var layerHash = new Dictionary<string, uint>(); + var layersHash = new Dictionary<string, uint>(); - var range = Enumerable.Range(0, (int)LayerCount); - foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { var layerBytes = new byte[LayerCount][]; Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = this[layerIndex].LayerMat; - layerBytes[layerIndex] = EncodeImage(mat, HeaderSettings.LayerDataType); + using (var mat = this[layerIndex].LayerMat) + { + layerBytes[layerIndex] = EncodeImage(HeaderSettings.LayerDataType, mat); + } + progress.LockAndIncrement(); }); @@ -549,7 +544,7 @@ namespace UVtools.Core.FileFormats // Try to reuse layers var hash = Helpers.ComputeSHA1Hash(layerBytes[layerIndex]); - if (layerHash.TryGetValue(hash, out var address)) + if (layersHash.TryGetValue(hash, out var address)) { layerDataAddresses[layerIndex] = address; } @@ -558,10 +553,10 @@ namespace UVtools.Core.FileFormats layerDataAddresses[layerIndex] = (uint)outputFile.Position; outputFile.WriteUIntLittleEndian((uint)layerBytes[layerIndex].Length); outputFile.WriteBytes(layerBytes[layerIndex]); - layerHash.Add(hash, layerDataAddresses[layerIndex]); + layersHash.Add(hash, layerDataAddresses[layerIndex]); } - layerBytes[layerIndex] = null; // Clean + layerBytes[layerIndex] = null; // Free this } } @@ -646,7 +641,7 @@ namespace UVtools.Core.FileFormats var bytes = inputFile.ReadBytes((int)Previews[i].ImageLength); - Thumbnails[i] = DecodeImage(bytes, HeaderSettings.PreviewDataType, Previews[i].ResolutionX, Previews[i].ResolutionY); + Thumbnails[i] = DecodeImage(HeaderSettings.PreviewDataType, bytes, Previews[i].ResolutionX, Previews[i].ResolutionY); progress++; } @@ -680,8 +675,7 @@ namespace UVtools.Core.FileFormats progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); - var range = Enumerable.Range(0, (int)LayerCount); - foreach (var batch in MoreEnumerable.Batch(range, Environment.ProcessorCount * 10)) + foreach (var batch in BatchLayersIndexes()) { var layerBytes = new byte[LayerCount][]; @@ -696,11 +690,14 @@ namespace UVtools.Core.FileFormats Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var mat = DecodeImage(layerBytes[layerIndex], HeaderSettings.LayerDataType, Resolution); - var layer = new Layer((uint)layerIndex, mat, this); - layerDef[layerIndex].SetTo(layer); - this[layerIndex] = layer; - layerBytes[layerIndex] = null; // Clean + using (var mat = DecodeImage(HeaderSettings.LayerDataType, layerBytes[layerIndex], Resolution)) + { + layerBytes[layerIndex] = null; // Clean + + var layer = new Layer((uint)layerIndex, mat, this); + layerDef[layerIndex].CopyTo(layer); + this[layerIndex] = layer; + } progress.LockAndIncrement(); }); @@ -761,134 +758,7 @@ namespace UVtools.Core.FileFormats #region Static Methods - public static byte[] EncodeImage(Mat mat, string encodeTo) - { - encodeTo = encodeTo.ToUpperInvariant(); - if (encodeTo is "PNG" or "JPG" or "JPEG" or "JP2" or "BMP" or "TIF" or "TIFF" or "PPM" or "PMG" or "SR" or "RAS") - { - return CvInvoke.Imencode($".{encodeTo.ToLowerInvariant()}", mat); - } - - if (encodeTo is "RGB555" or "RGB565" or "RGB888" - or "BGR555" or "BGR565" or "BGR888") - { - var bytesPerPixel = encodeTo is "RGB888" or "BGR888" ? 3 : 2; - var bytes = new byte[mat.Width * mat.Height * bytesPerPixel]; - uint index = 0; - var span = mat.GetDataByteSpan(); - for (int i = 0; i < span.Length;) - { - byte b = span[i++]; - byte g; - byte r; - - if (mat.NumberOfChannels == 1) // 8 bit safe-guard - { - r = g = b; - } - else - { - g = span[i++]; - r = span[i++]; - } - - if (mat.NumberOfChannels == 4) i++; // skip alpha - - switch (encodeTo) - { - case "RGB555": - var rgb555 = (ushort) (((r & 0b11111000) << 7) | ((g & 0b11111000) << 2) | (b >> 3)); - BitExtensions.ToBytesLittleEndian(rgb555, bytes, index); - index += 2; - break; - case "RGB565": - var rgb565 = (ushort) (((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3)); - BitExtensions.ToBytesLittleEndian(rgb565, bytes, index); - index += 2; - break; - case "RGB888": - bytes[index++] = r; - bytes[index++] = g; - bytes[index++] = b; - break; - } - } - - return bytes; - } - - throw new NotSupportedException($"The encode type: {encodeTo} is not supported."); - } - - public static Mat DecodeImage(byte[] bytes, string decodeFrom, Size resolution) - { - if (decodeFrom is "PNG" or "JPG" or "JPEG" or "JP2" or "BMP" or "TIF" or "TIFF" or "PPM" or "PMG" or "SR" or "RAS") - { - var mat = new Mat(); - CvInvoke.Imdecode(bytes, ImreadModes.AnyColor, mat); - return mat; - } - - if (decodeFrom is "RGB555" or "RGB565" or "RGB888" - or "BGR555" or "BGR565" or "BGR888") - { - var mat = new Mat(resolution, DepthType.Cv8U, 3); - var span = mat.GetDataByteSpan(); - var pixel = 0; - for (int i = 0; i < bytes.Length;) - { - switch (decodeFrom) - { - case "RGB555": - ushort rgb555 = BitExtensions.ToUShortLittleEndian(bytes, i); - span[pixel++] = (byte)((rgb555 & 0b00000000_00011111) << 3); // b - span[pixel++] = (byte)((rgb555 & 0b00000011_11100000) >> 2); // g - span[pixel++] = (byte)((rgb555 & 0b01111100_00000000) >> 7); // r - i += 2; - break; - case "RGB565": - ushort rgb565 = BitExtensions.ToUShortLittleEndian(bytes, i); - span[pixel++] = (byte)((rgb565 & 0b00000000_00011111) << 3); // b - span[pixel++] = (byte)((rgb565 & 0b00000111_11100000) >> 3); // g - span[pixel++] = (byte)((rgb565 & 0b11111000_00000000) >> 8); // r - i += 2; - break; - case "RGB888": - span[pixel++] = bytes[i + 2]; // b - span[pixel++] = bytes[i + 1]; // g - span[pixel++] = bytes[i]; // r - i += 3; - break; - case "BGR555": - ushort bgr555 = BitExtensions.ToUShortLittleEndian(bytes, i); - span[pixel++] = (byte)((bgr555 & 0b01111100_00000000) >> 7); // b - span[pixel++] = (byte)((bgr555 & 0b00000011_11100000) >> 2); // g - span[pixel++] = (byte)((bgr555 & 0b00000000_00011111) << 3); // r - i += 2; - break; - case "BGR565": - ushort bgr565 = BitExtensions.ToUShortLittleEndian(bytes, i); - span[pixel++] = (byte)((bgr565 & 0b11111000_00000000) >> 8); // b - span[pixel++] = (byte)((bgr565 & 0b00000111_11100000) >> 3); // g - span[pixel++] = (byte)((bgr565 & 0b00000000_00011111) << 3); // r - i += 2; - break; - case "BGR888": - span[pixel++] = bytes[i]; // b - span[pixel++] = bytes[i+1]; // g - span[pixel++] = bytes[i+2]; // r - i += 3; - break; - } - } - return mat; - } - - throw new NotSupportedException($"The decode type: {decodeFrom} is not supported."); - } - - public static Mat DecodeImage(byte[] bytes, string decodeFrom, uint resolutionX = 0, uint resolutionY = 0) - => DecodeImage(bytes, decodeFrom, new Size((int) resolutionX, (int) resolutionY)); + #endregion diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index fc507d3..e0ca04b 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -422,17 +422,17 @@ namespace UVtools.Core.FileFormats #endregion #region Layer - public class LayerData + public class LayerDef { /// <summary> /// Gets the build platform Z position for this layer, measured in millimeters. /// </summary> - [FieldOrder(0)] public float LayerPositionZ { get; set; } + [FieldOrder(0)] public float PositionZ { get; set; } /// <summary> /// Gets the exposure time for this layer, in seconds. /// </summary> - [FieldOrder(1)] public float LayerExposure { get; set; } + [FieldOrder(1)] public float ExposureTime { get; set; } /// <summary> /// Gets how long to keep the light off after exposing this layer, in seconds. @@ -457,21 +457,28 @@ namespace UVtools.Core.FileFormats [Ignore] public PHZFile Parent { get; set; } - public LayerData() + public LayerDef() { } - public LayerData(PHZFile parent, uint layerIndex) + public LayerDef(PHZFile parent, Layer layer) { Parent = parent; - RefreshLayerData(layerIndex); + SetFrom(layer); } - public void RefreshLayerData(uint layerIndex) + public void SetFrom(Layer layer) { - LayerPositionZ = Parent[layerIndex].PositionZ; - LayerExposure = Parent[layerIndex].ExposureTime; - LightOffDelay = Parent[layerIndex].LightOffDelay; + PositionZ = layer.PositionZ; + ExposureTime = layer.ExposureTime; + LightOffDelay = layer.LightOffDelay; + } + + public void CopyTo(Layer layer) + { + layer.PositionZ = PositionZ; + layer.ExposureTime = ExposureTime; + layer.LightOffDelay = LightOffDelay; } public unsafe Mat Decode(uint layerIndex, bool consumeData = true) @@ -619,7 +626,7 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(LayerPositionZ)}: {LayerPositionZ}, {nameof(LayerExposure)}: {LayerExposure}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; + return $"{nameof(PositionZ)}: {PositionZ}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(DataAddress)}: {DataAddress}, {nameof(DataSize)}: {DataSize}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; } } #endregion @@ -682,9 +689,7 @@ namespace UVtools.Core.FileFormats public Preview[] Previews { get; protected internal set; } - public LayerData[] LayersDefinitions { get; private set; } - - public Dictionary<string, LayerData> LayersHash { get; } = new (); + public LayerDef[] LayersDefinitions { get; private set; } public override FileFormatType FileType => FileFormatType.Binary; @@ -788,11 +793,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => (byte) HeaderSettings.AntiAliasLevelInfo; - set - { - HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16); - RaisePropertyChanged(); - } + set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16)); } public override float LayerHeight @@ -979,221 +980,201 @@ namespace UVtools.Core.FileFormats protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - LayersHash.Clear(); - /*if (HeaderSettings.EncryptionKey == 0) { Random rnd = new Random(); HeaderSettings.EncryptionKey = (uint)rnd.Next(short.MaxValue, int.MaxValue); }*/ + LayersDefinitions = new LayerDef[HeaderSettings.LayerCount]; + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin); - uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - LayersDefinitions = new LayerData[HeaderSettings.LayerCount]; - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) + for (byte i = 0; i < ThumbnailsCount; i++) { - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); + var image = Thumbnails[i]; - for (byte i = 0; i < ThumbnailsCount; i++) - { - var image = Thumbnails[i]; + var bytes = Preview.Encode(image); - var bytes = Preview.Encode(image); + if (bytes.Length == 0) continue; - if (bytes.Length == 0) continue; + if (i == (byte) FileThumbnailSize.Small) + { + HeaderSettings.PreviewSmallOffsetAddress = (uint)outputFile.Position; + } + else + { + HeaderSettings.PreviewLargeOffsetAddress = (uint)outputFile.Position; + } - if (i == (byte) FileThumbnailSize.Small) - { - HeaderSettings.PreviewSmallOffsetAddress = currentOffset; - } - else - { - HeaderSettings.PreviewLargeOffsetAddress = currentOffset; - } + Preview preview = new() + { + ResolutionX = (uint) image.Width, + ResolutionY = (uint) image.Height, + ImageLength = (uint)bytes.Length, + }; - Preview preview = new() - { - ResolutionX = (uint) image.Width, - ResolutionY = (uint) image.Height, - ImageLength = (uint)bytes.Length, - }; + preview.ImageOffset = (uint)(outputFile.Position + Helpers.Serializer.SizeOf(preview)); - currentOffset += (uint) Helpers.Serializer.SizeOf(preview); - preview.ImageOffset = currentOffset; + Helpers.SerializeWriteFileStream(outputFile, preview); - Helpers.SerializeWriteFileStream(outputFile, preview); + outputFile.WriteBytes(bytes); + } - currentOffset += (uint)bytes.Length; - outputFile.WriteBytes(bytes); - } + if (HeaderSettings.MachineNameSize > 0) + { + HeaderSettings.MachineNameAddress = (uint)outputFile.Position; + var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName); + outputFile.WriteBytes(machineBytes); + } - if (HeaderSettings.MachineNameSize > 0) - { - HeaderSettings.MachineNameAddress = currentOffset; - var machineBytes = Encoding.ASCII.GetBytes(HeaderSettings.MachineName); - outputFile.Write(machineBytes, 0, machineBytes.Length); - currentOffset += (uint)machineBytes.Length; - } + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layersHash = new Dictionary<string, LayerDef>(); + LayersDefinitions = new LayerDef[HeaderSettings.LayerCount]; + HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position; + uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress; + uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + (uint)Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount; - Parallel.For(0, LayerCount, /*new ParallelOptions{MaxDegreeOfParallelism = 1},*/ layerIndex => + foreach (var batch in BatchLayersIndexes()) + { + Parallel.ForEach(batch, layerIndex => { - if(progress.Token.IsCancellationRequested) return; - LayerData layer = new(this, (uint) layerIndex); - using (var image = this[layerIndex].LayerMat) + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) { - layer.Encode(image, (uint) layerIndex); - LayersDefinitions[layerIndex] = layer; + LayersDefinitions[layerIndex] = new LayerDef(this, this[layerIndex]); + LayersDefinitions[layerIndex].Encode(mat, (uint)layerIndex); } - progress.LockAndIncrement(); }); - progress.Reset(OperationProgress.StatusWritingFile, LayerCount); - - HeaderSettings.LayersDefinitionOffsetAddress = currentOffset; - uint layerDataCurrentOffset = currentOffset + (uint) Helpers.Serializer.SizeOf(LayersDefinitions[0]) * LayerCount; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + foreach (var layerIndex in batch) { progress.Token.ThrowIfCancellationRequested(); - LayerData layerData = LayersDefinitions[layerIndex]; - LayerData layerDataHash = null; + + var layerDef = LayersDefinitions[layerIndex]; + LayerDef layerDefHash = null; if (HeaderSettings.EncryptionKey == 0) { - string hash = Helpers.ComputeSHA1Hash(layerData.EncodedRle); - if (LayersHash.TryGetValue(hash, out layerDataHash)) + string hash = Helpers.ComputeSHA1Hash(layerDef.EncodedRle); + if (layersHash.TryGetValue(hash, out layerDefHash)) { - layerData.DataAddress = layerDataHash.DataAddress; - layerData.DataSize = layerDataHash.DataSize; + layerDef.DataAddress = layerDefHash.DataAddress; + layerDef.DataSize = layerDefHash.DataSize; } else { - LayersHash.Add(hash, layerData); + layersHash.Add(hash, layerDef); } } - if (ReferenceEquals(layerDataHash, null)) + if (layerDefHash is null) { - layerData.DataAddress = layerDataCurrentOffset; + layerDef.DataAddress = layerDataCurrentOffset; outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin); - layerDataCurrentOffset += outputFile.WriteBytes(layerData.EncodedRle); + layerDataCurrentOffset += outputFile.WriteBytes(layerDef.EncodedRle); } - LayersDefinitions[layerIndex] = layerData; + outputFile.Seek(layerDefCurrentOffset, SeekOrigin.Begin); + layerDefCurrentOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef); - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layerData); - progress++; + layerDef.EncodedRle = null; // Free } + } - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - Debug.WriteLine("Encode Results:"); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine(Previews[0]); - Debug.WriteLine(Previews[1]); - Debug.WriteLine("-End-"); - } + Debug.WriteLine("Encode Results:"); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine(Previews[0]); + Debug.WriteLine(Previews[1]); + Debug.WriteLine("-End-"); } protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); + //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + if (HeaderSettings.Magic != MAGIC_PHZ) { + throw new FileLoadException("Not a valid PHZ file!", fileFullPath); + } - //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile); - //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header)))); - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Magic != MAGIC_PHZ) - { - throw new FileLoadException("Not a valid PHZ file!", fileFullPath); - } - - HeaderSettings.AntiAliasLevel = 1; - - progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); - Debug.Write("Header -> "); - Debug.WriteLine(HeaderSettings); - - for (byte i = 0; i < ThumbnailsCount; i++) - { - uint offsetAddress = i == 0 - ? HeaderSettings.PreviewSmallOffsetAddress - : HeaderSettings.PreviewLargeOffsetAddress; - if (offsetAddress == 0) continue; + HeaderSettings.AntiAliasLevel = 1; - inputFile.Seek(offsetAddress, SeekOrigin.Begin); - Previews[i] = Helpers.Deserialize<Preview>(inputFile); + progress.Reset(OperationProgress.StatusDecodePreviews, ThumbnailsCount); + Debug.Write("Header -> "); + Debug.WriteLine(HeaderSettings); - Debug.Write($"Preview {i} -> "); - Debug.WriteLine(Previews[i]); + for (byte i = 0; i < ThumbnailsCount; i++) + { + uint offsetAddress = i == 0 + ? HeaderSettings.PreviewSmallOffsetAddress + : HeaderSettings.PreviewLargeOffsetAddress; + if (offsetAddress == 0) continue; - inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); - byte[] rawImageData = new byte[Previews[i].ImageLength]; - inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength); + inputFile.Seek(offsetAddress, SeekOrigin.Begin); + Previews[i] = Helpers.Deserialize<Preview>(inputFile); - Thumbnails[i] = Previews[i].Decode(rawImageData); - progress++; - } + Debug.Write($"Preview {i} -> "); + Debug.WriteLine(Previews[i]); - if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) - { - inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); - byte[] buffer = new byte[HeaderSettings.MachineNameSize]; - inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize); - HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer); - } + inputFile.Seek(Previews[i].ImageOffset, SeekOrigin.Begin); + byte[] rawImageData = new byte[Previews[i].ImageLength]; + inputFile.Read(rawImageData, 0, (int) Previews[i].ImageLength); + Thumbnails[i] = Previews[i].Decode(rawImageData); + progress++; + } - LayersDefinitions = new LayerData[HeaderSettings.LayerCount]; + if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) + { + inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); + byte[] buffer = new byte[HeaderSettings.MachineNameSize]; + inputFile.Read(buffer, 0, (int) HeaderSettings.MachineNameSize); + HeaderSettings.MachineName = Encoding.ASCII.GetString(buffer); + } - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount); + LayerManager.Init(HeaderSettings.LayerCount); + LayersDefinitions = new LayerDef[HeaderSettings.LayerCount]; - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) + progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); + foreach (var batch in BatchLayersIndexes()) + { + foreach (var layerIndex in batch) { - inputFile.Seek(layerOffset, SeekOrigin.Begin); - LayerData layerData = Helpers.Deserialize<LayerData>(inputFile); - layerData.Parent = this; - LayersDefinitions[layerIndex] = layerData; - - layerOffset += (uint) Helpers.Serializer.SizeOf(layerData); - Debug.Write($"LAYER {layerIndex} -> "); - Debug.WriteLine(layerData); - - layerData.EncodedRle = new byte[layerData.DataSize]; - inputFile.Seek(layerData.DataAddress, SeekOrigin.Begin); - inputFile.Read(layerData.EncodedRle, 0, (int) layerData.DataSize); - - progress++; progress.Token.ThrowIfCancellationRequested(); - } - LayerManager.Init(HeaderSettings.LayerCount); + var layerDef = Helpers.Deserialize<LayerDef>(inputFile); + layerDef.Parent = this; + LayersDefinitions[layerIndex] = layerDef; - progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); + Debug.Write($"LAYER {layerIndex} -> "); + Debug.WriteLine(layerDef); - Parallel.For(0, LayerCount, layerIndex => - { - if (progress.Token.IsCancellationRequested) + inputFile.SeekDoWorkAndRewind(layerDef.DataAddress, () => { - return; - } + layerDef.EncodedRle = inputFile.ReadBytes(layerDef.DataSize); + }); + } - using (var image = LayersDefinitions[layerIndex].Decode((uint) layerIndex, true)) + Parallel.ForEach(batch, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex)) { - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager) - { - PositionZ = LayersDefinitions[layerIndex].LayerPositionZ, - ExposureTime = LayersDefinitions[layerIndex].LayerExposure, - LightOffDelay = LayersDefinitions[layerIndex].LightOffDelay, - }; + var layer = new Layer((uint)layerIndex, mat, this); + LayersDefinitions[layerIndex].CopyTo(layer); + this[layerIndex] = layer; } progress.LockAndIncrement(); @@ -1219,26 +1200,24 @@ namespace UVtools.Core.FileFormats FileFullPath = filePath; } - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) + /*if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) { outputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); byte[] buffer = new byte[HeaderSettings.MachineNameSize]; outputFile.Write(Encoding.ASCII.GetBytes(HeaderSettings.MachineName), 0, (int)HeaderSettings.MachineNameSize); }*/ - uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; - for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) - { - LayersDefinitions[layerIndex].RefreshLayerData(layerIndex); - outputFile.Seek(layerOffset, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]); - layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]); - } + uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress; + for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) + { + LayersDefinitions[layerIndex].SetFrom(this[layerIndex]); + outputFile.Seek(layerOffset, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, LayersDefinitions[layerIndex]); + layerOffset += (uint)Helpers.Serializer.SizeOf(LayersDefinitions[layerIndex]); } } diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index 9e4fb9d..da588cb 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -14,7 +14,6 @@ using System.IO; using System.Threading.Tasks; using BinarySerialization; using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; using UVtools.Core.Operations; @@ -22,21 +21,31 @@ namespace UVtools.Core.FileFormats { public class PhotonSFile : FileFormat { + #region Constants public const byte RLEEncodingLimit = 128; + public const ushort RESOLUTION_X = 1440; + public const ushort RESOLUTION_Y = 2560; + + public const float DISPLAY_WIDTH = 68.04f; + public const float DISPLAY_HEIGHT = 120.96f; + public const float MACHINE_Z = 165f; + + #endregion + + #region Members + + private uint _resolutionX = RESOLUTION_X; + private uint _resolutionY = RESOLUTION_Y; + + #endregion + #region Sub Classes #region Header public class Header { - public const uint ResolutionX = 1440; - public const uint ResolutionY = 2560; - - public const float DisplayWidth = 68.04f; - public const float DisplayHeight = 120.96f; - public const float BuildZ = 165f; - public const uint TAG1 = 2; public const ushort TAG2 = 49; @@ -78,13 +87,13 @@ namespace UVtools.Core.FileFormats #region LayerDef - public class LayerData + public class LayerDef { [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint Unknown1 { get; set; } = 44944; [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 0; [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint Unknown3 { get; set; } = 0; - [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = 1440; - [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = 2560; + [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = RESOLUTION_X; + [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = RESOLUTION_Y; [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DataSize { get; set; } [Ignore] public uint RleDataSize { @@ -98,9 +107,19 @@ namespace UVtools.Core.FileFormats [Ignore] public byte[] EncodedRle { get; set; } + public LayerDef() + { + } + + public LayerDef(Mat mat) + { + ResolutionX = (uint)mat.Width; + ResolutionY = (uint)mat.Height; + } + public override string ToString() { - return $"{nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(DataSize)}: {DataSize}, {nameof(RleDataSize)}: {RleDataSize}, {nameof(Unknown5)}: {Unknown5}, {nameof(EncodedRle)}: {EncodedRle.Length}"; + return $"{nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(DataSize)}: {DataSize}, {nameof(RleDataSize)}: {RleDataSize}, {nameof(Unknown5)}: {Unknown5}, {nameof(EncodedRle)}: {EncodedRle?.Length}"; } public unsafe byte[] Encode(Mat mat) @@ -162,7 +181,7 @@ namespace UVtools.Core.FileFormats return EncodedRle; } - public unsafe Mat Decode(bool consumeRle = true) + public Mat Decode(bool consumeRle = true) { var mat = EmguExtensions.InitMat(new Size((int) ResolutionX, (int) ResolutionY)); //var matSpan = mat.GetBytePointer(); @@ -259,25 +278,33 @@ namespace UVtools.Core.FileFormats public override uint ResolutionX { - get => Header.ResolutionX; - set { } + get => _resolutionX; + set + { + if(!RaiseAndSetIfChanged(ref _resolutionX, value)) return; + HeaderSettings.XYPixelSize = PixelSizeMax; + } } public override uint ResolutionY { - get => Header.ResolutionY; - set { } + get => _resolutionY; + set + { + if (!RaiseAndSetIfChanged(ref _resolutionY, value)) return; + HeaderSettings.XYPixelSize = PixelSizeMax; + } } public override float DisplayWidth { - get => Header.DisplayWidth; + get => DISPLAY_WIDTH; set { } } public override float DisplayHeight { - get => Header.DisplayHeight; + get => DISPLAY_HEIGHT; set { } } @@ -286,13 +313,7 @@ namespace UVtools.Core.FileFormats get => true; set { } } - - public override byte AntiAliasing - { - get => 1; - set { } - } - + public override float LayerHeight { get => (float) Layer.RoundHeight(HeaderSettings.LayerHeight); @@ -305,7 +326,7 @@ namespace UVtools.Core.FileFormats public override float MachineZ { - get => Header.BuildZ; + get => MACHINE_Z; set { } } @@ -409,90 +430,40 @@ namespace UVtools.Core.FileFormats #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 r = span[i + 2]; // 60 - byte g = span[i + 1]; - byte b = span[i]; - - ushort color = (ushort)(((b & 0xF8) << 8) | ((g & 0xFC) << 3) | (r >> 3)); - - bytes[index++] = (byte)color; - bytes[index++] = (byte)(color >> 8); - } - - 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.PreviewResolutionY, (int)HeaderSettings.PreviewResolutionX, DepthType.Cv8U, 3); - var span = mat.GetBytePointer(); - int spanIndex = 0; - for (int i = 0; i < data.Length; i += 2) - { - ushort color16 = BitExtensions.ToUShortLittleEndian(data[i], data[i + 1]); - - //var r = (byte)((color16 & 0x1F) << 3); - //var g = (byte)(((color16 >> 5) & 0x3F) << 2); - //var b = (byte)(((color16 >> 11) & 0x1f) << 3); - var r = (byte)((color16 << 3) & 0xF8); // Mask: 11111000 - var g = (byte)((color16 >> 3) & 0xFC); // Mask: 11111100 - var b = (byte)((color16 >> 8) & 0xF8); // Mask: 11111000 - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - return mat; - } - + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { //throw new NotSupportedException("PhotonS is read-only format, please use pws instead!"); - //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])); + outputFile.WriteBytes(EncodeImage(DATATYPE_BGR565, Thumbnails[0])); outputFile.WriteSerialize(LayerSettings); - var layerData = new LayerData[LayerCount]; + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + var layerData = new LayerDef[LayerCount]; - Parallel.For(0, LayerCount, layerIndex => + foreach (var batch in BatchLayersIndexes()) { - if (progress.Token.IsCancellationRequested) return; - using (var mat = this[layerIndex].LayerMat) + Parallel.ForEach(batch, layerIndex => { - layerData[layerIndex] = new LayerData(); - layerData[layerIndex].Encode(mat); - } + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) + { + layerData[layerIndex] = new LayerDef(mat); + layerData[layerIndex].Encode(mat); + } + progress.LockAndIncrement(); + }); - progress.LockAndIncrement(); - }); + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); - progress.ItemName = "Saving layers"; - progress.ProcessedItems = 0; + outputFile.WriteSerialize(layerData[layerIndex]); + outputFile.WriteBytes(layerData[layerIndex].EncodedRle); - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); - outputFile.WriteSerialize(layerData[layerIndex]); - outputFile.WriteBytes(layerData[layerIndex].EncodedRle); - progress++; + layerData[layerIndex].EncodedRle = null; // Free + } } Debug.WriteLine("Encode Results:"); @@ -512,43 +483,52 @@ namespace UVtools.Core.FileFormats int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2); byte[] previewData = new byte[previewSize]; - - uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings); - currentOffset += inputFile.ReadBytes(previewData); - Thumbnails[0] = PreviewDecode(previewData); + inputFile.ReadBytes(previewData); + Thumbnails[0] = DecodeImage(DATATYPE_BGR565, previewData, HeaderSettings.PreviewResolutionX, HeaderSettings.PreviewResolutionY); LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile); - currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings); - + Debug.WriteLine(HeaderSettings); Debug.WriteLine(LayerSettings); - var layerData = new LayerData[LayerSettings.LayerCount]; - progress.Reset(OperationProgress.StatusGatherLayers, LayerSettings.LayerCount); + LayerManager.Init(LayerSettings.LayerCount); + var layersDefinitions = new LayerDef[LayerSettings.LayerCount]; - for (int layerIndex = 0; layerIndex < LayerSettings.LayerCount; layerIndex++) + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + foreach (var batch in BatchLayersIndexes()) { - progress.Token.ThrowIfCancellationRequested(); - layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile); - layerData[layerIndex].EncodedRle = new byte[layerData[layerIndex].RleDataSize]; - currentOffset += inputFile.ReadBytes(layerData[layerIndex].EncodedRle); - Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}"); - } + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); - LayerManager.Init(LayerSettings.LayerCount); - progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + var layerDef = Helpers.Deserialize<LayerDef>(inputFile); + layersDefinitions[layerIndex] = layerDef; - Parallel.For(0, LayerCount, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - layerIndex => + layerDef.EncodedRle = inputFile.ReadBytes(layerDef.RleDataSize); + + Debug.Write($"LAYER {layerIndex} -> "); + Debug.WriteLine(layerDef); + + if (layerIndex == 1) + { + // Auto fix resolution if needed + ResolutionX = layersDefinitions[layerIndex].ResolutionX; + ResolutionY = layersDefinitions[layerIndex].ResolutionY; + } + } + + Parallel.ForEach(batch, layerIndex => { if (progress.Token.IsCancellationRequested) return; + using (var mat = layersDefinitions[layerIndex].Decode()) + { + this[layerIndex] = new Layer((uint)layerIndex, mat, this); + } - using var image = layerData[layerIndex].Decode(); - this[layerIndex] = new Layer((uint) layerIndex, image, this); progress.LockAndIncrement(); }); + } LayerManager.RebuildLayersProperties(); } diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 0663d91..0a23b7f 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -121,7 +121,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// 18 /// </summary> - [FieldOrder(4)] public uint Offset1 { get; set; } + [FieldOrder(4)] public uint Padding1 { get; set; } /// <summary> /// Gets the preview start offset @@ -132,7 +132,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// 20 /// </summary> - [FieldOrder(6)] public uint Offset2 { get; set; } + [FieldOrder(6)] public uint Padding2 { get; set; } /// <summary> /// Gets the layer definition start address @@ -143,7 +143,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// 28 /// </summary> - [FieldOrder(8)] public uint Offset3 { get; set; } + [FieldOrder(8)] public uint Padding3 { get; set; } /// <summary> /// Gets layer image start address @@ -153,7 +153,7 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(Mark)}: {Mark}, {nameof(Version)}: {Version}, {nameof(AreaNum)}: {AreaNum}, {nameof(HeaderAddress)}: {HeaderAddress}, {nameof(Offset1)}: {Offset1}, {nameof(PreviewAddress)}: {PreviewAddress}, {nameof(Offset2)}: {Offset2}, {nameof(LayerDefinitionAddress)}: {LayerDefinitionAddress}, {nameof(Offset3)}: {Offset3}, {nameof(LayerImageAddress)}: {LayerImageAddress}"; + return $"{nameof(Mark)}: {Mark}, {nameof(Version)}: {Version}, {nameof(AreaNum)}: {AreaNum}, {nameof(HeaderAddress)}: {HeaderAddress}, {nameof(Padding1)}: {Padding1}, {nameof(PreviewAddress)}: {PreviewAddress}, {nameof(Padding2)}: {Padding2}, {nameof(LayerDefinitionAddress)}: {LayerDefinitionAddress}, {nameof(Padding3)}: {Padding3}, {nameof(LayerImageAddress)}: {LayerImageAddress}"; } } #endregion @@ -341,19 +341,19 @@ namespace UVtools.Core.FileFormats /// <summary> /// 88 /// </summary> - [FieldOrder(19)] public uint Offset1 { get; set; } + [FieldOrder(19)] public uint Padding1 { get; set; } /// <summary> /// 8C /// </summary> - [FieldOrder(20)] public uint Offset2 { get; set; } + [FieldOrder(20)] public uint Padding2 { get; set; } public Header() { Section = new SectionHeader(SectionMark, this); } - public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(PixelSizeUm)}: {PixelSizeUm}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(LayerExposureTime)}: {LayerExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(AntiAliasing)}: {AntiAliasing}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(WeightG)}: {WeightG}, {nameof(Price)}: {Price}, {nameof(PriceCurrencyDec)}: {PriceCurrencyDec}, {nameof(PerLayerOverride)}: {PerLayerOverride}, {nameof(PrintTime)}: {PrintTime}, {nameof(Offset1)}: {Offset1}, {nameof(Offset2)}: {Offset2}"; + public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(PixelSizeUm)}: {PixelSizeUm}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(LayerExposureTime)}: {LayerExposureTime}, {nameof(LightOffDelay)}: {LightOffDelay}, {nameof(BottomExposureSeconds)}: {BottomExposureSeconds}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(RetractSpeed)}: {RetractSpeed}, {nameof(VolumeMl)}: {VolumeMl}, {nameof(AntiAliasing)}: {AntiAliasing}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(WeightG)}: {WeightG}, {nameof(Price)}: {Price}, {nameof(PriceCurrencyDec)}: {PriceCurrencyDec}, {nameof(PerLayerOverride)}: {PerLayerOverride}, {nameof(PrintTime)}: {PrintTime}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}"; public void Validate() { @@ -382,26 +382,26 @@ namespace UVtools.Core.FileFormats /// Gets the image width, in pixels. /// A0 /// </summary> - [FieldOrder(1)] public uint Width { get; set; } = 224; + [FieldOrder(1)] public uint ResolutionX { get; set; } = 224; /// <summary> /// Gets the resolution of the image, in dpi. /// A4 /// </summary> - [FieldOrder(2)] public uint Resolution { get; set; } = 42; + [FieldOrder(2)] public uint DpiResolution { get; set; } = 42; /// <summary> /// Gets the image height, in pixels. /// A8 /// </summary> - [FieldOrder(3)] public uint Height { get; set; } = 168; + [FieldOrder(3)] public uint ResolutionY { get; set; } = 168; - [FieldOrder(4)] public uint Offset1 { get; set; } - [FieldOrder(5)] public uint Offset2 { get; set; } - [FieldOrder(6)] public uint Offset3 { get; set; } - [FieldOrder(7)] public uint Offset4 { get; set; } + [FieldOrder(4)] public uint Unknown1 { get; set; } + [FieldOrder(5)] public uint Unknown2 { get; set; } + [FieldOrder(6)] public uint Unknown3 { get; set; } + [FieldOrder(7)] public uint Unknown4 { get; set; } - [Ignore] public uint DataSize => Width * Height * 2; + [Ignore] public uint DataSize => ResolutionX * ResolutionY * 2; // little-endian 16bit colors, RGB 565 encoded. //[FieldOrder(4)] [FieldLength(nameof(Section)+"."+nameof(SectionHeader.Length))] @@ -412,16 +412,16 @@ namespace UVtools.Core.FileFormats Section = new SectionHeader(SectionMark, this); } - public Preview(uint width, uint height, uint resolution = 42) : this() + public Preview(uint resolutionX, uint resolutionY, uint dpiResolution = 42) : this() { - Width = width; - Height = height; - Resolution = resolution; + ResolutionX = resolutionX; + ResolutionY = resolutionY; + DpiResolution = dpiResolution; Data = new byte[DataSize]; Section.Length += (uint)Data.Length; } - public unsafe Mat Decode(bool consumeData = true) + /*public unsafe Mat Decode(bool consumeData = true) { Mat image = new(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); var span = image.GetBytePointer(); @@ -469,10 +469,10 @@ namespace UVtools.Core.FileFormats return preview; } - + */ public override string ToString() { - return $"{nameof(Section)}: {Section}, {nameof(Width)}: {Width}, {nameof(Resolution)}: {Resolution}, {nameof(Height)}: {Height}, {nameof(Data)}: {Data?.Length ?? 0}"; + return $"{nameof(Section)}: {Section}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(DpiResolution)}: {DpiResolution}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(Data)}: {Data?.Length ?? 0}"; } public void Validate(int size) @@ -485,7 +485,7 @@ namespace UVtools.Core.FileFormats #region Layer - public class LayerData + public class LayerDef { public const byte ClassSize = 32; /// <summary> @@ -505,38 +505,43 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the exposure time for this layer, in seconds. /// </summary> - [FieldOrder(4)] - public float ExposureTime { get; set; } + [FieldOrder(4)] public float ExposureTime { get; set; } /// <summary> /// Gets the layer height for this layer, measured in millimeters. /// </summary> - [FieldOrder(5)] - public float LayerHeight { get; set; } + [FieldOrder(5)] public float LayerHeight { get; set; } [FieldOrder(6)] public uint NonZeroPixelCount { get; set; } - [FieldOrder(7)] public uint Offset1 { get; set; } + [FieldOrder(7)] public uint Padding1 { get; set; } [Ignore] public byte[] EncodedRle { get; set; } [Ignore] public PhotonWorkshopFile Parent { get; set; } - public LayerData() + public LayerDef() { } - public LayerData(PhotonWorkshopFile parent, uint layerIndex) + public LayerDef(PhotonWorkshopFile parent, Layer layer) { Parent = parent; - RefreshLayerData(layerIndex); + SetFrom(layer); + } + + public void SetFrom(Layer layer) + { + LayerHeight = layer.LayerHeight; + ExposureTime = layer.ExposureTime; + LiftHeight = layer.LiftHeight; + LiftSpeed = (float) Math.Round(layer.LiftSpeed / 60, 2); + NonZeroPixelCount = layer.NonZeroPixelCount; } - public void RefreshLayerData(uint layerIndex) + public void CopyTo(Layer layer) { - LayerHeight = Parent[layerIndex].LayerHeight; - ExposureTime = Parent[layerIndex].ExposureTime; - LiftHeight = Parent[layerIndex].LiftHeight; - LiftSpeed = (float) Math.Round(Parent[layerIndex].LiftSpeed / 60, 2); - NonZeroPixelCount = Parent[layerIndex].NonZeroPixelCount; + layer.ExposureTime = ExposureTime; + layer.LiftHeight = LiftHeight; + layer.LiftSpeed = (float)Math.Round(LiftSpeed * 60, 2); } public Mat Decode(bool consumeData = true) @@ -681,72 +686,79 @@ namespace UVtools.Core.FileFormats return rawData.ToArray(); } - private unsafe Mat DecodePW0() + private Mat DecodePW0() { - var image = EmguExtensions.InitMat(Parent.Resolution); - var span = image.GetBytePointer(); - var imageLength = image.GetLength(); + var mat = EmguExtensions.InitMat(Parent.Resolution); + var imageLength = mat.GetLength(); - uint n = 0; - for (int index = 0; index < EncodedRle.Length; index++) + int pixelPos = 0; + for (int i = 0; i < EncodedRle.Length; i++) { - byte b = EncodedRle[index]; + byte b = EncodedRle[i]; int code = b >> 4; - uint reps = (uint) (b & 0xf); + int repeat = b & 0xf; byte color; switch (code) { case 0x0: - color = 0x00; - index++; - //reps = reps * 256 + EncodedRle[index]; - reps = (reps << 8) + EncodedRle[index]; + color = 0; + i++; + //reps = reps * 256 + EncodedRle[i]; + if (i >= EncodedRle.Length) + { + repeat = imageLength - pixelPos; + break; + } + + repeat = (repeat << 8) + EncodedRle[i]; break; case 0xf: - color = 0xff; - index++; - //reps = reps * 256 + EncodedRle[index]; - reps = (reps << 8) + EncodedRle[index]; + color = 255; + i++; + //reps = reps * 256 + EncodedRle[i]; + if (i >= EncodedRle.Length) + { + repeat = imageLength - pixelPos; + break; + } + + repeat = (repeat << 8) + EncodedRle[i]; break; default: color = (byte) ((code << 4) | code); + if (i >= EncodedRle.Length) + { + repeat = imageLength - pixelPos; + } break; } //color &= 0xff; // We only need to set the non-zero pixels - if (color != 0) - { - for (int i = 0; i < reps; i++) - { - span[(int) (n + i)] |= color; - } - } - - n += reps; + mat.FillSpan(ref pixelPos, repeat, color); - if (n == imageLength) + if (pixelPos == imageLength) { - //index++; + //i++; break; } - if (n > imageLength) + if (pixelPos > imageLength) { - image.Dispose(); - throw new FileLoadException($"Error image ran off the end: {n - reps}({reps}) of {imageLength}"); + mat.Dispose(); + throw new FileLoadException($"Error image ran off the end: {pixelPos - repeat}({repeat}) of {imageLength}"); } } - if (n > 0 && n != imageLength) + if (pixelPos > 0 && pixelPos != imageLength) { - image.Dispose(); - throw new FileLoadException($"Error image ended short: {n} of {imageLength}"); + mat.Dispose(); + throw new FileLoadException($"Error image ended short: {pixelPos} of {imageLength}"); } - return image; + return mat; } public unsafe byte[] EncodePW0(Mat image) @@ -840,7 +852,7 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(DataAddress)}: {DataAddress}, {nameof(DataLength)}: {DataLength}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, {nameof(Offset1)}: {Offset1}, {nameof(EncodedRle)}: {EncodedRle?.Length ?? 0}"; + return $"{nameof(DataAddress)}: {DataAddress}, {nameof(DataLength)}: {DataLength}, {nameof(LiftHeight)}: {LiftHeight}, {nameof(LiftSpeed)}: {LiftSpeed}, {nameof(ExposureTime)}: {ExposureTime}, {nameof(LayerHeight)}: {LayerHeight}, {nameof(NonZeroPixelCount)}: {NonZeroPixelCount}, {nameof(Padding1)}: {Padding1}, {nameof(EncodedRle)}: {EncodedRle?.Length ?? 0}"; } } @@ -858,7 +870,7 @@ namespace UVtools.Core.FileFormats [FieldOrder(1)] public uint LayerCount { get; set; } - [Ignore] public LayerData[] Layers { get; set; } + [Ignore] public LayerDef[] Layers { get; set; } public LayerDefinition() { @@ -868,19 +880,19 @@ namespace UVtools.Core.FileFormats public LayerDefinition(uint layerCount) : this() { LayerCount = layerCount; - Layers = new LayerData[layerCount]; - Section.Length += (uint) Helpers.Serializer.SizeOf(new LayerData()) * LayerCount; + Layers = new LayerDef[layerCount]; + Section.Length += (uint) Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount; } [Ignore] - public LayerData this[uint index] + public LayerDef this[uint index] { get => Layers[index]; set => Layers[index] = value; } [Ignore] - public LayerData this[int index] + public LayerDef this[int index] { get => Layers[index]; set => Layers[index] = value; @@ -888,7 +900,7 @@ namespace UVtools.Core.FileFormats public void Validate() { - Section.Validate(SectionMark, (int) (LayerCount * Helpers.Serializer.SizeOf(new LayerData()) - Helpers.Serializer.SizeOf(Section)), this); + Section.Validate(SectionMark, (int) (LayerCount * Helpers.Serializer.SizeOf(new LayerDef()) - Helpers.Serializer.SizeOf(Section)), this); } public override string ToString() => $"{nameof(Section)}: {Section}, {nameof(LayerCount)}: {LayerCount}"; @@ -907,8 +919,6 @@ namespace UVtools.Core.FileFormats public LayerDefinition LayersDefinition { get; protected internal set; } = new(); - public Dictionary<string, LayerData> LayersHash { get; } = new(); - public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { @@ -1054,9 +1064,8 @@ namespace UVtools.Core.FileFormats get => (byte) HeaderSettings.AntiAliasing; set { - HeaderSettings.AntiAliasing = value.Clamp(1, 16); + base.AntiAliasing = (byte)(HeaderSettings.AntiAliasing = value.Clamp(1, 16)); ValidateAntiAliasingLevel(); - RaisePropertyChanged(); } } @@ -1272,9 +1281,6 @@ namespace UVtools.Core.FileFormats protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - LayersHash.Clear(); - LayersDefinition = new LayerDefinition(LayerCount); - switch (PrinterModel) { case AnyCubicMachine.AnyCubicPhotonS: @@ -1303,65 +1309,72 @@ namespace UVtools.Core.FileFormats HeaderSettings.PerLayerOverride = (byte)(LayerManager.AllLayersHaveGlobalParameters ? 0 : 1); - uint currentOffset = FileMarkSettings.HeaderAddress = (uint) Helpers.Serializer.SizeOf(FileMarkSettings); + FileMarkSettings.HeaderAddress = (uint) Helpers.Serializer.SizeOf(FileMarkSettings); using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); - outputFile.Seek((int) currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); + outputFile.Seek((int)FileMarkSettings.HeaderAddress, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); if (CreatedThumbnailsCount > 0) { - FileMarkSettings.PreviewAddress = currentOffset; - Preview preview = Preview.Encode(Thumbnails[0]); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, preview); - currentOffset += outputFile.WriteBytes(preview.Data); + FileMarkSettings.PreviewAddress = (uint)outputFile.Position; + //Preview preview = Preview.Encode(Thumbnails[0]); + var preview = new Preview((uint)Thumbnails[0].Width, (uint)Thumbnails[0].Height) + { + Data = EncodeImage(DATATYPE_RGB565, Thumbnails[0]) + }; + Helpers.SerializeWriteFileStream(outputFile, preview); + outputFile.WriteBytes(preview.Data); } - FileMarkSettings.LayerDefinitionAddress = currentOffset; + progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); + FileMarkSettings.LayerDefinitionAddress = (uint)outputFile.Position; + LayersDefinition = new LayerDefinition(LayerCount); + Helpers.SerializeWriteFileStream(outputFile, LayersDefinition); + uint layerDefOffset = (uint)outputFile.Position; + uint layerRleOffset = FileMarkSettings.LayerImageAddress = (uint)(layerDefOffset + Helpers.Serializer.SizeOf(new LayerDef()) * LayerCount); + var layersHash = new Dictionary<string, LayerDef>(); - Parallel.For(0, LayerCount, layerIndex => + foreach (var batch in BatchLayersIndexes()) { - if (progress.Token.IsCancellationRequested) return; - LayerData layer = new(this, (uint) layerIndex); - using (var image = this[layerIndex].LayerMat) + Parallel.ForEach(batch, layerIndex => { - layer.Encode(image); - LayersDefinition.Layers[layerIndex] = layer; - } - progress.LockAndIncrement(); - }); + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) + { + LayersDefinition.Layers[layerIndex] = new LayerDef(this, this[layerIndex]); + LayersDefinition.Layers[layerIndex].Encode(mat); + } + progress.LockAndIncrement(); + }); - uint offsetLayerRle = FileMarkSettings.LayerImageAddress = (uint) (currentOffset + Helpers.Serializer.SizeOf(LayersDefinition.Section) + LayersDefinition.Section.Length); + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, LayersDefinition); + var layerDef = LayersDefinition.Layers[layerIndex]; - progress.Reset(OperationProgress.StatusWritingFile, LayerCount); + var hash = Helpers.ComputeSHA1Hash(layerDef.EncodedRle); - foreach (var layer in LayersDefinition.Layers) - { - progress.Token.ThrowIfCancellationRequested(); - string hash = Helpers.ComputeSHA1Hash(layer.EncodedRle); + if (layersHash.TryGetValue(hash, out var layerDataHash)) + { + layerDef.DataAddress = layerDataHash.DataAddress; + layerDef.DataLength = (uint)layerDataHash.EncodedRle.Length; + } + else + { + layersHash.Add(hash, layerDef); - if (LayersHash.TryGetValue(hash, out var layerDataHash)) - { - layer.DataAddress = layerDataHash.DataAddress; - layer.DataLength = (uint)layerDataHash.EncodedRle.Length; - } - else - { - LayersHash.Add(hash, layer); + layerDef.DataAddress = layerRleOffset; - layer.DataAddress = offsetLayerRle; + outputFile.Seek(layerRleOffset, SeekOrigin.Begin); + layerRleOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef.EncodedRle); + } - outputFile.Seek(offsetLayerRle, SeekOrigin.Begin); - offsetLayerRle += Helpers.SerializeWriteFileStream(outputFile, layer.EncodedRle); + outputFile.Seek(layerDefOffset, SeekOrigin.Begin); + layerDefOffset += Helpers.SerializeWriteFileStream(outputFile, layerDef); } - - outputFile.Seek(currentOffset, SeekOrigin.Begin); - currentOffset += Helpers.SerializeWriteFileStream(outputFile, layer); - - progress++; } - + // Rewind outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, FileMarkSettings); @@ -1410,7 +1423,8 @@ namespace UVtools.Core.FileFormats PreviewSettings.Data = new byte[PreviewSettings.DataSize]; inputFile.ReadBytes(PreviewSettings.Data); - Thumbnails[0] = PreviewSettings.Decode(true); + Thumbnails[0] = DecodeImage(DATATYPE_RGB565, PreviewSettings.Data, PreviewSettings.ResolutionX, PreviewSettings.ResolutionY); + PreviewSettings.Data = null; } inputFile.Seek(FileMarkSettings.LayerDefinitionAddress, SeekOrigin.Begin); @@ -1420,54 +1434,44 @@ namespace UVtools.Core.FileFormats Debug.WriteLine(LayersDefinition); LayerManager.Init(LayersDefinition.LayerCount); - LayersDefinition.Layers = new LayerData[LayerCount]; - - + LayersDefinition.Layers = new LayerDef[LayerCount]; + LayersDefinition.Validate(); - for (int i = 0; i < LayerCount; i++) + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + foreach (var batch in BatchLayersIndexes()) { - LayersDefinition[i] = Helpers.Deserialize<LayerData>(inputFile); - LayersDefinition[i].Parent = this; - Debug.WriteLine($"Layer {i}: {LayersDefinition[i]}"); - } - - progress.Reset(OperationProgress.StatusGatherLayers, LayerCount); + foreach (var layerIndex in batch) + { + progress.Token.ThrowIfCancellationRequested(); - for (int i = 0; i < LayerCount; i++) - { - var layer = LayersDefinition[i]; - //layer.Parent = this; - inputFile.Seek(layer.DataAddress, SeekOrigin.Begin); - layer.EncodedRle = new byte[layer.DataLength]; - inputFile.ReadBytes(layer.EncodedRle); - - progress++; - progress.Token.ThrowIfCancellationRequested(); - } + LayersDefinition[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile); + LayersDefinition[layerIndex].Parent = this; + Debug.WriteLine($"Layer {layerIndex}: {LayersDefinition[layerIndex]}"); - progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); - Parallel.For(0, LayerCount, layerIndex => - { - if (progress.Token.IsCancellationRequested) - { - return; + inputFile.SeekDoWorkAndRewind(LayersDefinition[layerIndex].DataAddress, () => + { + LayersDefinition[layerIndex].EncodedRle = inputFile.ReadBytes(LayersDefinition[layerIndex].DataLength); + }); } - using (var image = LayersDefinition[(uint) layerIndex].Decode()) + Parallel.ForEach(batch, layerIndex => { - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager) + if (progress.Token.IsCancellationRequested) return; + using (var mat = LayersDefinition[layerIndex].Decode()) { - PositionZ = Layer.RoundHeight(LayersDefinition[(uint)layerIndex].LayerHeight), - ExposureTime = LayersDefinition[(uint)layerIndex].ExposureTime, - LiftHeight = LayersDefinition[(uint)layerIndex].LiftHeight, - LiftSpeed = (float)Math.Round(LayersDefinition[(uint)layerIndex].LiftSpeed * 60, 2), - }; - } + var layer = new Layer((uint)layerIndex, mat, this) + { + PositionZ = Layer.RoundHeight(LayersDefinition[(uint)layerIndex].LayerHeight), + }; + LayersDefinition[layerIndex].CopyTo(layer); + this[layerIndex] = layer; + } - progress.LockAndIncrement(); - }); + progress.LockAndIncrement(); + }); + } // Fix position z height values if (LayerCount > 0) @@ -1508,7 +1512,7 @@ namespace UVtools.Core.FileFormats outputFile.Seek(FileMarkSettings.LayerDefinitionAddress + Helpers.Serializer.SizeOf(LayersDefinition), SeekOrigin.Begin); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - LayersDefinition[layerIndex].RefreshLayerData(layerIndex); + LayersDefinition[layerIndex].SetFrom(this[layerIndex]); Helpers.SerializeWriteFileStream(outputFile, LayersDefinition[layerIndex]); } } diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 1d58802..33d7555 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -40,9 +40,18 @@ namespace UVtools.Core.FileFormats public const string Keyword_BottomLiftSpeed = "BottomLiftSpeed"; public const string Keyword_LiftHeight = "LiftHeight"; public const string Keyword_LiftSpeed = "LiftSpeed"; + public const string Keyword_BottomLiftHeight2 = "BottomLiftHeight2"; + public const string Keyword_BottomLiftSpeed2 = "BottomLiftSpeed2"; + public const string Keyword_LiftHeight2 = "LiftHeight2"; + public const string Keyword_LiftSpeed2 = "LiftSpeed2"; public const string Keyword_BottomWaitTimeAfterLift = "BottomWaitAfterLift"; public const string Keyword_WaitTimeAfterLift = "WaitAfterLift"; + public const string Keyword_BottomRetractSpeed = "BottomRetractSpeed"; public const string Keyword_RetractSpeed = "RetractSpeed"; + public const string Keyword_BottomRetractHeight2 = "BottomRetractHeight2"; + public const string Keyword_BottomRetractSpeed2 = "BottomRetractSpeed2"; + public const string Keyword_RetractHeight2 = "RetractHeight2"; + public const string Keyword_RetractSpeed2 = "RetractSpeed2"; public const string Keyword_BottomLightPWM = "BottomLightPWM"; public const string Keyword_LightPWM = "LightPWM"; #endregion @@ -652,10 +661,22 @@ namespace UVtools.Core.FileFormats LiftHeight = LookupCustomValue(Keyword_LiftHeight, DefaultLiftHeight); LiftSpeed = LookupCustomValue(Keyword_LiftSpeed, DefaultLiftSpeed); + BottomLiftHeight2 = LookupCustomValue(Keyword_BottomLiftHeight2, DefaultBottomLiftHeight2); + BottomLiftSpeed2 = LookupCustomValue(Keyword_BottomLiftSpeed2, DefaultBottomLiftSpeed2); + + LiftHeight2 = LookupCustomValue(Keyword_LiftHeight2, DefaultLiftHeight2); + LiftSpeed2 = LookupCustomValue(Keyword_LiftSpeed2, DefaultLiftSpeed2); + BottomWaitTimeAfterLift = LookupCustomValue(Keyword_BottomWaitTimeAfterLift, 0f); WaitTimeAfterLift = LookupCustomValue(Keyword_WaitTimeAfterLift, 0f); + BottomRetractSpeed = LookupCustomValue(Keyword_BottomRetractSpeed, DefaultBottomRetractSpeed); RetractSpeed = LookupCustomValue(Keyword_RetractSpeed, DefaultRetractSpeed); + + BottomRetractHeight2 = LookupCustomValue(Keyword_BottomRetractHeight2, DefaultBottomRetractHeight2); + RetractHeight2 = LookupCustomValue(Keyword_RetractHeight2, DefaultRetractHeight2); + BottomRetractSpeed2 = LookupCustomValue(Keyword_BottomRetractSpeed2, DefaultBottomRetractSpeed2); + RetractSpeed2 = LookupCustomValue(Keyword_RetractSpeed2, DefaultRetractSpeed2); BottomLightPWM = LookupCustomValue(Keyword_BottomLightPWM, DefaultLightPWM); LightPWM = LookupCustomValue(Keyword_LightPWM, DefaultBottomLightPWM); }); @@ -664,7 +685,7 @@ namespace UVtools.Core.FileFormats progress.ItemCount = LayerCount; - foreach (ZipArchiveEntry entity in inputFile.Entries) + foreach (var entity in inputFile.Entries) { if (!entity.Name.EndsWith(".png")) continue; if (entity.Name.StartsWith("thumbnail")) diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index c97c286..7ae70da 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -216,12 +216,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => JsonSettings.Properties.AntiAliasLevel; - set - { - JsonSettings.Properties.AntiAliasLevel = value.Clamp(1, 16); - RaisePropertyChanged(); - } - + set => base.AntiAliasing = JsonSettings.Properties.AntiAliasLevel = value.Clamp(1, 16); } public override float LayerHeight @@ -363,7 +358,7 @@ namespace UVtools.Core.FileFormats { LiftHeight = layer.LiftHeight, LiftSpeed = layer.LiftSpeed, - RetractHeight = layer.LiftHeight+1, + RetractHeight = layer.LiftHeight, RetractSpeed = layer.RetractSpeed, LightOffTime = layer.LightOffDelay, LightOnTime = layer.ExposureTime, @@ -372,7 +367,7 @@ namespace UVtools.Core.FileFormats }); } - using ZipArchive outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create); + using var outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create); outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings, Formatting.Indented), ZipArchiveMode.Create); if (CreatedThumbnailsCount > 0) @@ -407,7 +402,7 @@ namespace UVtools.Core.FileFormats using (var inputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Read)) { var entry = inputFile.GetEntry(FileConfigName); - if (ReferenceEquals(entry, null)) + if (entry is null) { Clear(); throw new FileLoadException($"{FileConfigName} not found", fileFullPath); @@ -418,21 +413,19 @@ namespace UVtools.Core.FileFormats LayerManager.Init(JsonSettings.Properties.Size.Layers); entry = inputFile.GetEntry(FilePreviewTinyName); - if (!ReferenceEquals(entry, null)) + if (entry is not null) { - using (Stream stream = entry.Open()) - { - Mat image = new(); - CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image); - Thumbnails[0] = image; - stream.Close(); - } + using var stream = entry.Open(); + Mat image = new(); + CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image); + Thumbnails[0] = image; + stream.Close(); } entry = inputFile.GetEntry(FilePreviewHugeName); if (entry is not null) { - using Stream stream = entry.Open(); + using var stream = entry.Open(); Mat image = new(); CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, image); Thumbnails[1] = image; @@ -482,10 +475,8 @@ namespace UVtools.Core.FileFormats } - using (var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update)) - { - outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings, Formatting.Indented), ZipArchiveMode.Update); - } + using var outputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Update); + outputFile.PutFileContent(FileConfigName, JsonConvert.SerializeObject(JsonSettings, Formatting.Indented), ZipArchiveMode.Update); //Decode(FileFullPath, progress); } diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs index bba398b..0deabf9 100644 --- a/UVtools.Core/FileFormats/VDAFile.cs +++ b/UVtools.Core/FileFormats/VDAFile.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; @@ -152,7 +153,7 @@ namespace UVtools.Core.FileFormats public override FileFormatType FileType => FileFormatType.Archive; public override FileExtension[] FileExtensions { get; } = { - new(typeof(VDAFile), "vda.zip", "Voxeldance Additive Zip") + new(typeof(VDAFile), "zip", "Voxeldance Additive Zip") }; public override uint ResolutionX @@ -247,11 +248,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => ManifestFile.Machines.AntiAliasing; - set - { - ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16); - RaisePropertyChanged(); - } + set => base.AntiAliasing = ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16); } public override float LayerHeight @@ -286,6 +283,24 @@ namespace UVtools.Core.FileFormats #region Methods + public override bool CanProcess(string fileFullPath) + { + if(!base.CanProcess(fileFullPath)) return false; + + try + { + using var zip = ZipFile.Open(fileFullPath, ZipArchiveMode.Read); + if (zip.Entries.Any(entry => entry.Name.EndsWith(".xml"))) return true; + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + + return false; + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { using var outputFile = ZipFile.Open(fileFullPath, ZipArchiveMode.Create); diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 2b3790d..df2030b 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -288,12 +288,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => ManifestFile.AdvancedParameters.AntialasingLevel; - set - { - ManifestFile.AdvancedParameters.AntialasingLevel = value.Clamp(1, 16); - RaisePropertyChanged(); - } - + set => base.AntiAliasing = ManifestFile.AdvancedParameters.AntialasingLevel = value.Clamp(1, 16); } public override float LayerHeight @@ -370,18 +365,18 @@ namespace UVtools.Core.FileFormats set => base.BottomLiftHeight = ManifestFile.Print.BottomLiftHeight = (float)Math.Round(value, 2); } - public override float LiftHeight - { - get => ManifestFile.Print.LiftHeight; - set => base.LiftHeight = ManifestFile.Print.LiftHeight = (float)Math.Round(value, 2); - } - public override float BottomLiftSpeed { get => ManifestFile.Print.BottomLiftSpeed; set => base.BottomLiftSpeed = ManifestFile.Print.BottomLiftSpeed = (float)Math.Round(value, 2); } + public override float LiftHeight + { + get => ManifestFile.Print.LiftHeight; + set => base.LiftHeight = ManifestFile.Print.LiftHeight = (float)Math.Round(value, 2); + } + public override float LiftSpeed { get => ManifestFile.Print.LiftSpeed; @@ -394,18 +389,18 @@ namespace UVtools.Core.FileFormats set => base.BottomLiftHeight2 = ManifestFile.Print.BottomLiftHeight2 = (float)Math.Round(value, 2); } - public override float LiftHeight2 - { - get => ManifestFile.Print.LiftHeight2; - set => base.LiftHeight2 = ManifestFile.Print.LiftHeight2 = (float)Math.Round(value, 2); - } - public override float BottomLiftSpeed2 { get => ManifestFile.Print.BottomLiftSpeed2; set => base.BottomLiftSpeed2 = ManifestFile.Print.BottomLiftSpeed2 = (float)Math.Round(value, 2); } + public override float LiftHeight2 + { + get => ManifestFile.Print.LiftHeight2; + set => base.LiftHeight2 = ManifestFile.Print.LiftHeight2 = (float)Math.Round(value, 2); + } + public override float LiftSpeed2 { get => ManifestFile.Print.LiftSpeed2; @@ -438,21 +433,29 @@ namespace UVtools.Core.FileFormats public override float BottomRetractHeight2 { get => ManifestFile.Print.BottomRetractHeight2; - set => base.BottomRetractHeight2 = ManifestFile.Print.BottomRetractHeight2 = (float)Math.Round(value, 2); - } - - public override float RetractHeight2 - { - get => ManifestFile.Print.RetractHeight2; - set => base.RetractHeight2 = ManifestFile.Print.RetractHeight2 = (float)Math.Round(value, 2); + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); + base.BottomRetractHeight2 = ManifestFile.Print.BottomRetractHeight2 = value; + } } - + public override float BottomRetractSpeed2 { get => ManifestFile.Print.BottomRetractSpeed2; set => base.BottomRetractSpeed2 = ManifestFile.Print.BottomRetractSpeed2 = (float)Math.Round(value, 2); } + public override float RetractHeight2 + { + get => ManifestFile.Print.RetractHeight2; + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + base.RetractHeight2 = ManifestFile.Print.RetractHeight2 = value; + } + } + public override float RetractSpeed2 { get => ManifestFile.Print.RetractSpeed2; @@ -523,11 +526,11 @@ namespace UVtools.Core.FileFormats public void RebuildVDTLayers() { ManifestFile.CreateDateTime = DateTime.UtcNow.ToString("u"); - var layers = new VDTLayer[LayerCount]; + ManifestFile.Layers = new VDTLayer[LayerCount]; for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { var layer = this[layerIndex]; - layers[layerIndex] = new VDTLayer + ManifestFile.Layers[layerIndex] = new VDTLayer { PositionZ = layer.PositionZ, LightOffDelay = layer.LightOffDelay, @@ -545,8 +548,6 @@ namespace UVtools.Core.FileFormats LightPWM = layer.LightPWM }; } - - ManifestFile.Layers = layers; } protected override void EncodeInternally(string fileFullPath, OperationProgress progress) diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index db06f9e..9b25c50 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -292,11 +292,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => ManifestFile.Profile.Slice.AntiAliasing; - set - { - ManifestFile.Profile.Slice.AntiAliasing = value.Clamp(1, 16); - RaisePropertyChanged(); - } + set => base.AntiAliasing = ManifestFile.Profile.Slice.AntiAliasing = value.Clamp(1, 16); } public override float LayerHeight diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 9a397df..72027f4 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -216,12 +216,7 @@ namespace UVtools.Core.FileFormats public override byte AntiAliasing { get => UserSettings.AntiAliasing; - set - { - UserSettings.AntiAliasing = value.Clamp(1, 16); - RaisePropertyChanged(); - } - + set => base.AntiAliasing = UserSettings.AntiAliasing = value.Clamp(1, 16); } public override float LayerHeight |