diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d68c05..f1dc8d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 17/03/2022 - v3.1.0 + +- **Benchmark:** + - (Add) PNG, GZip, Deflate and LZ4 compress tests + - (Change) Test against a known image instead of random noise + - (Change) Single-thread tests from 100 to 200 and multi-thread tests from 1000 to 5000 + - (Improvement) Same image instance is shared between tests instead of create new per test + - (Fix) Encode typo +- **Core:** + - (Add) Layer compression method: Allow to choose the compression method for layer image + - **PNG:** Compression=High Speed=Slow (Use with low RAM) + - **GZip:** Compression=Medium Speed=Medium (Optimal) + - **Deflate:** Compression=Medium Speed=Medium (Optimal) + - **LZ4:** Compression=Low Speed=Fast (Use with high RAM) + - (Improvement) Better handling on cancel operations and more immediate response + - (Fix) Extract: Zip Slip Vulnerability (CWE-22) +- **File formats:** + - (Improvement) Better handling of encode/decoding layers from zip files + - (Fix) ZCode: Canceling the file load can trigger an error + - (Fix) VDA: Unable to open vda zip files +- **Tools:** + - (Improvement) Allow operations to be aware of ROI and Masks before execution (#436) + - (Improvement) Scripting: Allow save and load profiles (#436) + - (Fix) Adjust layer height: When using the Offset type the last layer in the range was not taken in account (#435) +- **UI:** + - (Improvement) Allow layer zoom levels of 0.1x and 64x but constrain minimum zoom to the level of image fit + - (Improvement) Update change log now shows with markdown style and more readable + - (Fix) Windows MSI upgrade to this version (#432) + - (Fix) Auto-updater for Mac ARM, was downloading x64 instead + ## 12/03/2022 - v3.0.0 - **(Add) Suggestions:** diff --git a/CREDITS.md b/CREDITS.md index 7f9976e..3f78f2d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -70,4 +70,6 @@ - Tim Anderson - Sakari Toivonen - Ed Wagaman -- Marcin Chomiczuk \ No newline at end of file +- Marcin Chomiczuk +- Patrick Hofmann +- Ajilus \ No newline at end of file diff --git a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs index f8c88c2..71d8ed8 100644 --- a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs +++ b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs @@ -113,7 +113,7 @@ public class AdvancedImageBox : UserControl /// public static ZoomLevelCollection Default => new(new[] { - 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200 + 7, 10, 15, 20, 25, 30, 50, 70, 100, 150, 200, 300, 400, 500, 600, 700, 800, 1200, 1600, 3200, 6400 }); #endregion @@ -276,32 +276,28 @@ public class AdvancedImageBox : UserControl /// Returns the next increased zoom level for the given current zoom. /// /// The current zoom level. + /// When positive, constrain maximum zoom to this value /// The next matching increased zoom level for the given current zoom if applicable, otherwise the nearest zoom. - public int NextZoom(int zoomLevel) + public int NextZoom(int zoomLevel, int constrainZoomLevel = 0) { var index = IndexOf(FindNearest(zoomLevel)); - if (index < Count - 1) - { - index++; - } + if (index < Count - 1) index++; - return this[index]; + return constrainZoomLevel > 0 && this[index] >= constrainZoomLevel ? constrainZoomLevel : this[index]; } /// /// Returns the next decreased zoom level for the given current zoom. /// /// The current zoom level. + /// When positive, constrain minimum zoom to this value /// The next matching decreased zoom level for the given current zoom if applicable, otherwise the nearest zoom. - public int PreviousZoom(int zoomLevel) + public int PreviousZoom(int zoomLevel, int constrainZoomLevel = 0) { var index = IndexOf(FindNearest(zoomLevel)); - if (index > 0) - { - index--; - } + if (index > 0) index--; - return this[index]; + return constrainZoomLevel > 0 && this[index] <= constrainZoomLevel ? constrainZoomLevel : this[index]; } /// @@ -842,7 +838,7 @@ public class AdvancedImageBox : UserControl } public static readonly StyledProperty MaxZoomProperty = - AvaloniaProperty.Register(nameof(MaxZoom), 3500); + AvaloniaProperty.Register(nameof(MaxZoom), 6400); /// /// Gets or sets the maximum possible zoom. @@ -854,6 +850,18 @@ public class AdvancedImageBox : UserControl set => SetValue(MaxZoomProperty, value); } + public static readonly StyledProperty ConstrainZoomOutToFitLevelProperty = + AvaloniaProperty.Register(nameof(ConstrainZoomOutToFitLevel), true); + + /// + /// Gets or sets if the zoom out should constrain to fit image as the lowest zoom level. + /// + public bool ConstrainZoomOutToFitLevel + { + get => GetValue(ConstrainZoomOutToFitLevelProperty); + set => SetValue(ConstrainZoomOutToFitLevelProperty, value); + } + public static readonly DirectProperty OldZoomProperty = AvaloniaProperty.RegisterDirect( @@ -884,12 +892,14 @@ public class AdvancedImageBox : UserControl get => GetValue(ZoomProperty); set { - var newZoom = Math.Clamp(value, MinZoom, MaxZoom); + var minZoom = MinZoom; + if (ConstrainZoomOutToFitLevel) minZoom = Math.Max(ZoomLevelToFit, minZoom); + var newZoom = Math.Clamp(value, minZoom, MaxZoom); var previousZoom = Zoom; if (previousZoom == newZoom) return; OldZoom = previousZoom; - SetValue(ZoomProperty, value); + SetValue(ZoomProperty, newZoom); UpdateViewPort(); TriggerRender(); @@ -899,13 +909,58 @@ public class AdvancedImageBox : UserControl } } + /// + /// Gets if the image have zoom. + /// True if zoomed in or out + /// False if no zoom applied + /// public bool IsActualSize => Zoom == 100; /// - /// Gets the zoom factor, the zoom / 100 + /// Gets the zoom factor, the zoom / 100.0 /// public double ZoomFactor => Zoom / 100.0; + /// + /// Gets the zoom to fit level which shows all the image + /// + public int ZoomLevelToFit + { + get + { + if (!IsImageLoaded) return 100; + var image = Image!; + + double zoom; + double aspectRatio; + + if (image.Size.Width > image.Size.Height) + { + aspectRatio = ViewPortSize.Width / image.Size.Width; + zoom = aspectRatio * 100.0; + + if (ViewPortSize.Height < image.Size.Height * zoom / 100.0) + { + aspectRatio = ViewPortSize.Height / image.Size.Height; + zoom = aspectRatio * 100.0; + } + } + else + { + aspectRatio = ViewPortSize.Height / image.Size.Height; + zoom = aspectRatio * 100.0; + + if (ViewPortSize.Width < image.Size.Width * zoom / 100.0) + { + aspectRatio = ViewPortSize.Width / image.Size.Width; + zoom = aspectRatio * 100.0; + } + } + + return (int) zoom; + } + } + /// /// Gets the width of the scaled image. /// @@ -1442,36 +1497,8 @@ public class AdvancedImageBox : UserControl /// public void ZoomToFit() { - var image = Image; - if (image is null) return; - - double zoom; - double aspectRatio; - - if (image.Size.Width > image.Size.Height) - { - aspectRatio = ViewPortSize.Width / image.Size.Width; - zoom = aspectRatio * 100.0; - - if (ViewPortSize.Height < image.Size.Height * zoom / 100.0) - { - aspectRatio = ViewPortSize.Height / image.Size.Height; - zoom = aspectRatio * 100.0; - } - } - else - { - aspectRatio = ViewPortSize.Height / image.Size.Height; - zoom = aspectRatio * 100.0; - - if (ViewPortSize.Width < image.Size.Width * zoom / 100.0) - { - aspectRatio = ViewPortSize.Width / image.Size.Width; - zoom = aspectRatio * 100.0; - } - } - - Zoom = (int)zoom; + if (!IsImageLoaded) return; + Zoom = ZoomLevelToFit; } /// diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj index 5974565..5a23b57 100644 --- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj +++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj @@ -10,11 +10,14 @@ https://github.com/sn4k3/UVtools/tree/master/UVtools.AvaloniaControls Git - Advanced image box + Advanced image box; Avalonia true - AvaloniaUI Controls - 1.0.1 + AvaloniaUI Controls +- AdvancedImageBox: Pan, zoom, cursor, pixel grid and selections image box + 2.0.0 enable + https://github.com/sn4k3/UVtools + README.md @@ -49,4 +52,11 @@ + + + True + \ + + + diff --git a/UVtools.Core/CoreSettings.cs b/UVtools.Core/CoreSettings.cs index e7b54fb..d6ef78a 100644 --- a/UVtools.Core/CoreSettings.cs +++ b/UVtools.Core/CoreSettings.cs @@ -10,7 +10,10 @@ using Emgu.CV.Cuda; using System; using System.Diagnostics; using System.IO; +using System.Threading; using System.Threading.Tasks; +using UVtools.Core.Layers; +using UVtools.Core.Operations; namespace UVtools.Core; @@ -40,6 +43,21 @@ public static class CoreSettings /// public static ParallelOptions ParallelOptions => new() {MaxDegreeOfParallelism = _maxDegreeOfParallelism}; + /// + /// Gets the ParallelOptions with and the set + /// + public static ParallelOptions GetParallelOptions(CancellationToken token = default) + { + var options = ParallelOptions; + options.CancellationToken = token; + return options; + } + + /// + /// Gets the ParallelOptions with and the set + /// + public static ParallelOptions GetParallelOptions(OperationProgress progress) => GetParallelOptions(progress.Token); + /// /// Gets or sets if operations run via CUDA when possible /// @@ -50,6 +68,11 @@ public static class CoreSettings /// public static bool CanUseCuda => EnableCuda && CudaInvoke.HasCuda; + /// + /// Gets or sets the default compression type for layers + /// + public static Layer.LayerCompressionMethod DefaultLayerCompressionMethod { get; set; } = Layer.LayerCompressionMethod.Png; + /// /// Gets the default folder to save the settings /// diff --git a/UVtools.Core/Enumerations.cs b/UVtools.Core/Enumerations.cs index c7b9a96..d82c942 100644 --- a/UVtools.Core/Enumerations.cs +++ b/UVtools.Core/Enumerations.cs @@ -14,6 +14,15 @@ namespace UVtools.Core; public class Enumerations { + /// + /// Gets index start number + /// + public enum IndexStartNumber : byte + { + Zero, + One + } + public enum LayerRangeSelection : byte { None, diff --git a/UVtools.Core/Extensions/StreamExtensions.cs b/UVtools.Core/Extensions/StreamExtensions.cs index d55a6a3..d8b6e7f 100644 --- a/UVtools.Core/Extensions/StreamExtensions.cs +++ b/UVtools.Core/Extensions/StreamExtensions.cs @@ -60,4 +60,9 @@ public static class StreamExtensions { await CopyToAsync(source, destination, DefaultCopyBufferSize, progress, cancellationToken); } + + public static MemoryStream ToStream(this byte[] arr) + { + return new MemoryStream(arr); + } } \ No newline at end of file diff --git a/UVtools.Core/Extensions/ZipArchiveExtensions.cs b/UVtools.Core/Extensions/ZipArchiveExtensions.cs index 8446283..c021783 100644 --- a/UVtools.Core/Extensions/ZipArchiveExtensions.cs +++ b/UVtools.Core/Extensions/ZipArchiveExtensions.cs @@ -103,16 +103,12 @@ public static class ZipArchiveExtensions { //Gets the complete path for the destination file, including any //relative paths that were in the zip file - string destinationFileName = Path.Combine(destinationPath, file.FullName); - - //Gets just the new path, minus the file name so we can create the - //directory if it does not exist - string? destinationFilePath = Path.GetDirectoryName(destinationFileName); - - destinationFilePath ??= string.Empty; + var destFileName = Path.Combine(destinationPath, file.FullName); + var fullDestDirPath = Path.GetFullPath(destinationPath + Path.DirectorySeparatorChar); + if (!destFileName.StartsWith(fullDestDirPath)) return; // Entry is outside the target dir //Creates the directory (if it doesn't exist) for the new path - Directory.CreateDirectory(destinationFilePath); + Directory.CreateDirectory(fullDestDirPath); //Determines what to do with the file based upon the //method of overwriting chosen @@ -120,26 +116,28 @@ public static class ZipArchiveExtensions { case Overwrite.Always: //Just put the file in and overwrite anything that is found - file.ExtractToFile(destinationFileName, true); + file.ExtractToFile(destFileName, true); break; case Overwrite.IfNewer: //Checks to see if the file exists, and if so, if it should //be overwritten - if (!File.Exists(destinationFileName) || File.GetLastWriteTime(destinationFileName) < file.LastWriteTime) + if (!File.Exists(destFileName) || File.GetLastWriteTime(destFileName) < file.LastWriteTime) { //Either the file didn't exist or this file is newer, so //we will extract it and overwrite any existing file - file.ExtractToFile(destinationFileName, true); + file.ExtractToFile(destFileName, true); } break; case Overwrite.Never: //Put the file in if it is new but ignores the //file if it already exists - if (!File.Exists(destinationFileName)) + if (!File.Exists(destFileName)) { - file.ExtractToFile(destinationFileName); + file.ExtractToFile(destFileName); } break; + default: + throw new ArgumentOutOfRangeException(nameof(overwriteMethod), overwriteMethod, null); } } @@ -194,7 +192,7 @@ public static class ZipArchiveExtensions //Throws an error if the file exists if (archiveExists) { - throw new IOException(String.Format("The zip file {0} already exists.", archiveFullName)); + throw new IOException(string.Format("The zip file {0} already exists.", archiveFullName)); } break; case ArchiveAction.Ignore: diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index c1b9b2a..b86a406 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -1169,7 +1169,7 @@ public class CTBEncryptedFile : FileFormat LayersPointer = new LayerPointer[Settings.LayerCount]; for (uint layerIndex = 0; layerIndex < Settings.LayerCount; layerIndex++) { - progress.Token.ThrowIfCancellationRequested(); + progress.ThrowIfCancellationRequested(); LayersPointer[layerIndex] = Helpers.Deserialize(inputFile); Debug.WriteLine($"pointer[{layerIndex}]: {LayersPointer[layerIndex]}"); progress++; @@ -1184,7 +1184,7 @@ public class CTBEncryptedFile : FileFormat { foreach (var layerIndex in batch) { - progress.Token.ThrowIfCancellationRequested(); + progress.ThrowIfCancellationRequested(); inputFile.Seek(LayersPointer[layerIndex].LayerOffset, SeekOrigin.Begin); LayersDefinition[layerIndex] = Helpers.Deserialize(inputFile); @@ -1195,10 +1195,8 @@ public class CTBEncryptedFile : FileFormat if (DecodeType == FileDecodeType.Full) { - Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex => + Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { - if (progress.Token.IsCancellationRequested) return; - var layerDef = LayersDefinition[layerIndex]; @@ -1359,9 +1357,8 @@ public class CTBEncryptedFile : FileFormat outputFile.Seek(outputFile.Position + layerTableSize, SeekOrigin.Begin); progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); - Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex => + Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { - if (progress.Token.IsCancellationRequested) return; var layerDef = new LayerDef(this, this[layerIndex]); using (var mat = this[layerIndex].LayerMat) { @@ -1375,6 +1372,7 @@ public class CTBEncryptedFile : FileFormat progress.Reset(OperationProgress.StatusWritingFile, LayerCount); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { + progress.ThrowIfCancellationRequested(); var layerDef = LayersDefinition[layerIndex]; LayersPointer[layerIndex] = new LayerPointer((uint)outputFile.Position); diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 3bb3d99..bd7622c 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -17,6 +17,7 @@ using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml.Serialization; using UVtools.Core.Extensions; @@ -624,7 +625,7 @@ public class CWSFile : FileFormat for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - manifest.Slices[layerIndex] = new CWSManifest.Slice(this[layerIndex].FormatFileName(filename)); + manifest.Slices[layerIndex] = new CWSManifest.Slice(this[layerIndex].FormatFileNameWithLayerDigits(filename)); } var entry = outputFile.CreateEntry(CWSManifest.FileName); @@ -659,16 +660,27 @@ public class CWSFile : FileFormat if (Printer == PrinterType.BeneMono) { - Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, + EncodeLayersInZip(outputFile, filename, LayerDigits, Enumerations.IndexStartNumber.Zero, progress, matGenFunc: + (_, mat) => + { + var matEncode = new Mat(mat.Height, mat.GetRealStep() / 3, DepthType.Cv8U, 3); + var span = mat.GetDataByteSpan(); + var spanEncode = matEncode.GetDataByteSpan(); + for (int i = 0; i < span.Length; i++) + { + spanEncode[i] = span[i]; + } + + return matEncode; + }); + /*Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), //new ParallelOptions { MaxDegreeOfParallelism = Printer == PrinterType.BeneMono ? 1 : 1 }, layerIndex => { - if (progress.Token.IsCancellationRequested) return; - var layer = this[layerIndex]; - var layerImagePath = layer.FormatFileName(filename); + var layerImagePath = layer.FormatFileNameWithLayerDigits(filename); - using var mat = layer.LayerMat!; + using var mat = layer.LayerMat; using var matEncode = new Mat(mat.Height, mat.GetRealStep() / 3, DepthType.Cv8U, 3); var span = mat.GetDataByteSpan(); var spanEncode = matEncode.GetDataByteSpan(); @@ -683,18 +695,11 @@ public class CWSFile : FileFormat outputFile.PutFileContent(layerImagePath, bytes, ZipArchiveMode.Create); progress++; } - }); + });*/ } else { - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); - var layer = this[layerIndex]; - var layerImagePath = layer.FormatFileName(filename); - outputFile.PutFileContent(layerImagePath, layer.CompressedBytes, ZipArchiveMode.Create); - progress++; - } + EncodeLayersInZip(outputFile, filename, LayerDigits, Enumerations.IndexStartNumber.Zero, progress); } RebuildGCode(); @@ -822,70 +827,65 @@ public class CWSFile : FileFormat Init(OutputSettings.LayersNum, DecodeType == FileDecodeType.Partial); - progress.ItemCount = OutputSettings.LayersNum; - - if(LayerCount > 0) + if (LayerCount <= 0) return; + + // Must discover png depth grayscale or color + if (DecodeType == FileDecodeType.Full && Printer == PrinterType.Unknown) { - if (DecodeType == FileDecodeType.Full) + var inputFilename = Path.GetFileNameWithoutExtension(FileFullPath)!; + foreach (var pngEntry in inputFile.Entries) { - var inputFilename = Path.GetFileNameWithoutExtension(FileFullPath)!; - foreach (var pngEntry in inputFile.Entries) - { - if (!pngEntry.Name.EndsWith(".png")) continue; - var filename = Path.GetFileNameWithoutExtension(pngEntry.Name).Replace(inputFilename, string.Empty, StringComparison.Ordinal); - - var layerIndexStr = string.Empty; - var layerStr = filename; - for (int i = layerStr.Length - 1; i >= 0; i--) - { - if (layerStr[i] < '0' || layerStr[i] > '9') break; - layerIndexStr = $"{layerStr[i]}{layerIndexStr}"; - } - - if (string.IsNullOrEmpty(layerIndexStr)) continue; - if (!uint.TryParse(layerIndexStr, out var layerIndex)) continue; - using var stream = pngEntry.Open(); - this[layerIndex] = new Layer(layerIndex, stream, this); - } - } + if (!pngEntry.Name.EndsWith(".png")) continue; + var match = Regex.Match(pngEntry.Name, @"(\d+).png"); + if (!match.Success || match.Groups.Count < 2) continue; + if (!uint.TryParse(match.Groups[1].Value, out var layerIndex)) continue; - GCode.ParseLayersFromGCode(this); + /*var filename = Path.GetFileNameWithoutExtension(pngEntry.Name).Replace(inputFilename, string.Empty, StringComparison.Ordinal); - var firstLayer = FirstLayer; - if (firstLayer is not null && DecodeType == FileDecodeType.Full) - { - if (Printer == PrinterType.Unknown) + var layerIndexStr = string.Empty; + var layerStr = filename; + for (int i = layerStr.Length - 1; i >= 0; i--) { - using Mat mat = new (); - CvInvoke.Imdecode(firstLayer.CompressedBytes, ImreadModes.AnyColor, mat); - Printer = mat.NumberOfChannels == 1 ? PrinterType.Elfin : PrinterType.BeneMono; + if (layerStr[i] < '0' || layerStr[i] > '9') break; + layerIndexStr = $"{layerStr[i]}{layerIndexStr}"; } - if (Printer == PrinterType.BeneMono) - { - Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex => - { - if (progress.Token.IsCancellationRequested) return; - var layer = this[layerIndex]; - using Mat mat = new(); - CvInvoke.Imdecode(layer.CompressedBytes, ImreadModes.Color, mat); - using Mat matDecode = new(mat.Height, mat.GetRealStep(), DepthType.Cv8U, 1); - var span = mat.GetDataByteSpan(); - var spanDecode = matDecode.GetDataByteSpan(); - for (int i = 0; i < span.Length; i++) - { - spanDecode[i] = span[i]; - } - - layer.LayerMat = matDecode; - layer.IsModified = false; - progress.LockAndIncrement(); - }); - } + if (string.IsNullOrEmpty(layerIndexStr)) continue; + if (!uint.TryParse(layerIndexStr, out var layerIndex)) continue;*/ + + using var stream = pngEntry.Open(); + using var mat = new Mat(); + CvInvoke.Imdecode(stream.ToArray(), ImreadModes.AnyColor, mat); + Printer = mat.NumberOfChannels == 1 ? PrinterType.Elfin : PrinterType.BeneMono; + break; } } - GetBoundingRectangle(progress); + if (Printer == PrinterType.BeneMono) + { + DecodeLayersFromZipRegex(inputFile, @"(\d+).png", Enumerations.IndexStartNumber.Zero, progress, + (layerIndex, pngBytes) => + { + using Mat mat = new(); + CvInvoke.Imdecode(pngBytes, ImreadModes.AnyColor, mat); + var matDecode = new Mat(mat.Height, mat.GetRealStep(), DepthType.Cv8U, 1); + var span = mat.GetDataByteSpan(); + var spanDecode = matDecode.GetDataByteSpan(); + for (int i = 0; i < span.Length; i++) + { + spanDecode[i] = span[i]; + } + + return matDecode; + }); + } + else + { + DecodeLayersFromZipRegex(inputFile, @"(\d+).png", Enumerations.IndexStartNumber.Zero, progress); + } + + GCode.ParseLayersFromGCode(this); + } public override void RebuildGCode() diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index ecd7fd6..e92316a 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -657,9 +657,8 @@ public class CXDLPFile : FileFormat var previews = new byte[ThumbnailsOriginalSize!.Length][]; // Previews - Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex => + Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { - if (progress.Token.IsCancellationRequested) return; var encodeLength = ThumbnailsOriginalSize[previewIndex].Area() * 2; if (Thumbnails[previewIndex] is null) { @@ -707,11 +706,8 @@ public class CXDLPFile : FileFormat var layerBytes = new List[LayerCount]; foreach (var batch in BatchLayersIndexes()) { - progress.Token.ThrowIfCancellationRequested(); - - Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex => + Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { - if (progress.Token.IsCancellationRequested) return; var layer = this[layerIndex]; using (var mat = layer.LayerMat) { @@ -762,8 +758,6 @@ public class CXDLPFile : FileFormat progress.LockAndIncrement(); }); - progress.Token.ThrowIfCancellationRequested(); - foreach (var layerIndex in batch) { outputFile.WriteBytes(layerBytes[layerIndex].ToArray()); @@ -892,7 +886,7 @@ public class CXDLPFile : FileFormat inputFile.Seek(2, SeekOrigin.Current); } - Parallel.For(0, previews.Length, CoreSettings.ParallelOptions, previewIndex => + Parallel.For(0, previews.Length, CoreSettings.GetParallelOptions(progress), previewIndex => { Thumbnails[previewIndex] = DecodeImage(DATATYPE_RGB565_BE, previews[previewIndex], ThumbnailsOriginalSize[previewIndex]); previews[previewIndex] = null!; @@ -919,23 +913,20 @@ public class CXDLPFile : FileFormat var linesBytes = new byte[LayerCount][]; foreach (var batch in BatchLayersIndexes()) { - progress.Token.ThrowIfCancellationRequested(); - foreach (var layerIndex in batch) { + progress.ThrowIfCancellationRequested(); + inputFile.Seek(4, SeekOrigin.Current); var lineCount = BitExtensions.ToUIntBigEndian(inputFile.ReadBytes(4)); linesBytes[layerIndex] = new byte[lineCount * 6]; inputFile.ReadBytes(linesBytes[layerIndex]); inputFile.Seek(2, SeekOrigin.Current); - - progress.Token.ThrowIfCancellationRequested(); } - Parallel.ForEach(batch, CoreSettings.ParallelOptions, layerIndex => + Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { - if (progress.Token.IsCancellationRequested) return; using (var mat = EmguExtensions.InitMat(Resolution)) { @@ -973,10 +964,6 @@ public class CXDLPFile : FileFormat inputFile.Seek(-Helpers.Serializer.SizeOf(FooterSettings), SeekOrigin.End); } - progress.Token.ThrowIfCancellationRequested(); - - - FooterSettings = Helpers.Deserialize