diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee865b6b..cf7d3cf2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,22 @@
# Changelog
-## /01/2024 - v4.1.1
+## /01/2024 - v4.2.0
- **Layers:**
- - (Improvement) Use `ResizableMemory` instead of `MemoryStream` for GZip and Deflate compressions, this results in faster compressions and less memory pressure
+ - (Add) Brotli compression codec with good performance and compression ratio (Choose it if you have low available RAM)
+ - (Improvement) Use `ResizableMemory` instead of `MemoryStream` for `GZip` and `Deflate` compressions, this results in faster compressions and less memory pressure
+ - (Improvement) Changed how layers are cached into the memory, before they were compressed and save for the whole image. Now it crops the bitmap to the bounding rectangle of the current layer and save only that portion with pixels.
+ For example, if the image is empty the cached size will be 0 bytes, and in a 12K image but with a 100 x 100 usable area it will save only that area instead of requiring a 12K buffer.
+ The size of the buffer is now dynamic and will depends on layer data, as so, this method can reduce the memory usage greatly, specially when using large images with a lot of empty space, but also boosts the overall performance by relief the allocations and the required memory footprint.
+ Only in few special cases can have drawbacks, however they are very minimal and the performance impact is minimal in that case.
+ When decompressing, the full resolution image is still created and then the cached area is imported to the corresponding position, composing the final and original image. This is still faster than the old method because decompress a larger buffer is more costly.
+ In the end both writes/compresses and reads/decompresses are now faster and using less memory.
+ Note: When printing multiple objects it is recommended to place them close to each other as you can to take better advantage of this new method.
+- **Issues Detection:**
+ - (Fix) When detecting for Islands but not overhangs it will throw an exception about invalid roi
+ - (Fix) Huge memory leak when detecting resin traps (#830)
+- (Improvement) Core: Changed the way "Roi" method is returned and try to dispose all it instances
+- (Fix) EncryptedCTB, GOO, SVGX: Huge memory leak when decoding files that caused the program to crash (#830)
- (Fix) UI: Missing theme styles
- (Fix) PrusaSlicer profiles for Creality Halot Mage's: Enable the "Horizontal" mirror under the "Printer" tab to produce the correct orientation when printing (#827)
diff --git a/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs b/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs
index efb8e818..9a708049 100644
--- a/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs
+++ b/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs
@@ -50,13 +50,13 @@ public bool ScriptExecute()
var layersBelowCount = layerIndex > LayerBleed.Value ? LayerBleed.Value : layerIndex;
using var sourceMat = originalLayers[layerIndex].LayerMat;
- var source = sourceMat.GetDataByteSpan();
+ var source = sourceMat.GetDataByteReadOnlySpan();
using var targetMat = sourceMat.NewBlank();
var target = targetMat.GetDataByteSpan();
using var occupancyMat = sourceMat.NewBlank();
- var occupancy = occupancyMat.GetDataByteSpan();
+ var occupancy = occupancyMat.GetDataByteReadOnlySpan();
var sumRectangle = Rectangle.Empty;
for (int i = 0; i < layersBelowCount; i++)
diff --git a/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs b/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs
index 3f903b84..8f3ab7fb 100644
--- a/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs
+++ b/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs
@@ -102,7 +102,7 @@ public bool ScriptExecute()
using var erodeMat = new Mat(); // Creates a temporary mat for the eroded image
using var wallMat = new Mat(); // Creates a temporary mat for the wall image
- var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
+ using var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
// Erode original image by InsetMarginFromEdge pixels, so we get the offset margin from image and put new image on erodeMat
CvInvoke.Erode(target, erodeMat, kernel, EmguExtensions.AnchorCenter, InsetMarginFromEdge.Value, BorderType.Reflect101, default);
diff --git a/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs b/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs
index 3160e6b1..c8939ed9 100644
--- a/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs
+++ b/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs
@@ -94,7 +94,7 @@ public bool ScriptExecute()
using var mat = layer.LayerMat; // Gets this layer mat/image
var original = mat.Clone(); // Keep a original mat copy
- var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
+ using var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region
for (byte i = 0; i < brightnesses.Length; i++)
{
@@ -103,7 +103,7 @@ public bool ScriptExecute()
using var subtractMat = EmguExtensions.InitMat(target.Size, new MCvScalar(brightnesses[i]));
using var nextMat = SlicerFile[layerIndexNext].LayerMat;
- var nextMatRoi = Operation.GetRoiOrDefault(nextMat);
+ using var nextMatRoi = Operation.GetRoiOrDefault(nextMat);
CvInvoke.Subtract(target, subtractMat, target, nextMatRoi);
}
diff --git a/UVtools.Core/EmguCV/CMat.cs b/UVtools.Core/EmguCV/CMat.cs
new file mode 100644
index 00000000..b1c0f75c
--- /dev/null
+++ b/UVtools.Core/EmguCV/CMat.cs
@@ -0,0 +1,606 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using System.Drawing;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using UVtools.Core.Extensions;
+
+namespace UVtools.Core.EmguCV;
+
+///
+/// Represents a compressed that can be compressed and decompressed using multiple s.
+/// This allows to have a high count of s in memory without using too much memory.
+///
+public class CMat : IEquatable
+{
+ #region Properties
+ ///
+ /// Gets the compressed bytes that have been compressed with .
+ ///
+ private byte[] _compressedBytes = Array.Empty();
+
+ ///
+ /// Gets the compressed bytes that have been compressed with .
+ ///
+ public byte[] CompressedBytes
+ {
+ get => _compressedBytes;
+ private set
+ {
+ _compressedBytes = value;
+ IsInitialized = true;
+ IsCompressed = !IsEmpty;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the have ever been set.
+ ///
+ public bool IsInitialized { get; private set; }
+
+ ///
+ /// Gets a value indicating whether the are compressed or raw bytes.
+ ///
+ public bool IsCompressed { get; private set; }
+
+ ///
+ /// Gets or sets the threshold in bytes to compress the data. Mat's equal to or less than this size will not be compressed.
+ ///
+ public int ThresholdToCompress { get; set; } = 64;
+
+ ///
+ /// Gets the cached width of the that was compressed.
+ ///
+ public int Width { get; private set; }
+
+ ///
+ /// Gets the cached height of the that was compressed.
+ ///
+ public int Height { get; private set; }
+
+ ///
+ /// Gets the cached size of the that was compressed.
+ ///
+ public Size Size
+ {
+ get => new(Width, Height);
+ private set
+ {
+ Width = value.Width;
+ Height = value.Height;
+ }
+ }
+
+ ///
+ /// Gets the cached depth of the that was compressed.
+ ///
+ public DepthType Depth { get; private set; } = DepthType.Cv8U;
+
+ ///
+ /// Gets the cached number of channels of the that was compressed.
+ ///
+ public int Channels { get; private set; } = 1;
+
+ ///
+ /// Gets the cached ROI of the that was compressed.
+ ///
+ public Rectangle Roi { get; private set; }
+
+ ///
+ /// Gets or sets the that will be used to compress and decompress the .
+ ///
+ public MatCompressor Compressor { get; set; } = MatCompressorLz4.Instance;
+
+ ///
+ /// Gets the that will be used to decompress the .
+ ///
+ public MatCompressor Decompressor { get; private set; } = MatCompressorLz4.Instance;
+
+ ///
+ /// Gets a value indicating whether the are empty.
+ ///
+ public bool IsEmpty => _compressedBytes.Length == 0;
+
+ ///
+ /// Gets the length of the .
+ ///
+ public int Length => _compressedBytes.Length;
+
+ ///
+ /// Gets the uncompressed length of the aka bitmap size.
+ ///
+ public int UncompressedLength => (Roi.Size.IsEmpty ? Width * Height : Roi.Width * Roi.Height) * Channels;
+
+ ///
+ /// Gets the compression ratio of the to the .
+ ///
+ public float CompressionRatio
+ {
+ get
+ {
+ var uncompressedLength = UncompressedLength;
+ if (uncompressedLength == 0 || Length == uncompressedLength) return 1;
+ if (Length == 0) return uncompressedLength;
+ return (float)Math.Round((float)uncompressedLength / Length, 2, MidpointRounding.AwayFromZero);
+ }
+ }
+
+ ///
+ /// Gets the compression percentage of the to the .
+ ///
+ public float CompressionPercentage
+ {
+ get
+ {
+ var uncompressedLength = UncompressedLength;
+ if (uncompressedLength == 0 || Length == uncompressedLength) return 0;
+ if (Length == 0) return 100f;
+ return (float)Math.Round(100 - (Length * 100f / uncompressedLength), 2, MidpointRounding.AwayFromZero);
+ }
+ }
+
+ ///
+ /// Gets the compression efficiency percentage of the to the .
+ ///
+ public float CompressionEfficiency
+ {
+ get
+ {
+ var uncompressedLength = UncompressedLength;
+ if (uncompressedLength == 0) return 0;
+ if (Length == 0) return uncompressedLength;
+ return (float)Math.Round(uncompressedLength * 100f / Length, 2, MidpointRounding.AwayFromZero);
+ }
+ }
+
+ ///
+ /// Gets the number of bytes saved by compressing the .
+ ///
+ public int SavedBytes => UncompressedLength - Length;
+
+ ///
+ /// Gets or sets the that will be compressed and decompressed.
+ /// Every time the is accessed, it will be de/compressed.
+ ///
+ public Mat Mat
+ {
+ get => Decompress();
+ set => Compress(value);
+ }
+ #endregion
+
+ #region Constructors
+
+ public CMat(int width = 0, int height = 0, DepthType depth = DepthType.Cv8U, int channels = 1)
+ {
+ Width = width;
+ Height = height;
+ Depth = depth;
+ Channels = channels;
+ }
+
+ public CMat(Size size, DepthType depth = DepthType.Cv8U, int channels = 1) : this(size.Width, size.Height, depth, channels)
+ {
+ }
+
+ public CMat(MatCompressor compressor, int width = 0, int height = 0, DepthType depth = DepthType.Cv8U, int channels = 1) : this(width, height, depth, channels)
+ {
+ Compressor = compressor;
+ Decompressor = compressor;
+ }
+
+ public CMat(MatCompressor compressor, Size size, DepthType depth = DepthType.Cv8U, int channels = 1) : this(compressor, size.Width, size.Height, depth, channels)
+ {
+ }
+
+ public CMat(Mat mat)
+ {
+ Compress(mat);
+ }
+
+ public CMat(MatRoi matRoi)
+ {
+ Compress(matRoi);
+ }
+
+
+ public CMat(MatCompressor compressor, Mat mat)
+ {
+ Compressor = compressor;
+ Decompressor = compressor;
+ Compress(mat);
+ }
+
+ public CMat(MatCompressor compressor, MatRoi matRoi)
+ {
+ Compressor = compressor;
+ Decompressor = compressor;
+ Compress(matRoi);
+ }
+ #endregion
+
+ #region Compress/Decompress
+ ///
+ /// Changes the and optionally re-encodes the with the new if the is different from the set .
+ ///
+ /// New compressor
+ /// True to re-encodes the with the new , otherwise false.
+ ///
+ /// True if compressor has been changed, otherwise false.
+ public bool ChangeCompressor(MatCompressor compressor, bool reEncodeWithNewCompressor = false, object? argument = null)
+ {
+ bool willReEncode = reEncodeWithNewCompressor && !ReferenceEquals(Decompressor, compressor);
+ if (ReferenceEquals(Compressor, compressor) && !willReEncode) return false; // Nothing to change
+ Compressor = compressor;
+
+ if (willReEncode)
+ {
+ using var mat = Decompress(argument);
+ Compress(mat);
+ }
+
+ return true;
+ }
+
+ ///
+ /// Changes the and optionally re-encodes the with the new if the is different from the set .
+ ///
+ /// New compressor
+ /// True to re-encodes the with the new , otherwise false.
+ ///
+ ///
+ /// True if compressor has been changed, otherwise false.
+ public Task ChangeCompressorAsync(MatCompressor compressor, bool reEncodeWithNewCompressor = false, object? argument = null, CancellationToken cancellationToken = default)
+ {
+ bool willReEncode = reEncodeWithNewCompressor && !ReferenceEquals(Decompressor, compressor);
+ if (ReferenceEquals(Compressor, compressor) && !willReEncode) return Task.FromResult(false); // Nothing to change
+ Compressor = compressor;
+
+ if (!willReEncode) return Task.FromResult(true);
+
+ return Task.Run(() =>
+ {
+ using var mat = Decompress(argument);
+ Compress(mat);
+ return true;
+ }, cancellationToken);
+ }
+
+ ///
+ /// Sets the to an empty byte array and sets to false.
+ ///
+ public void SetEmptyCompressedBytes()
+ {
+ _compressedBytes = Array.Empty();
+ IsCompressed = false;
+ }
+
+ ///
+ /// Sets the to an empty byte array and sets to false.
+ ///
+ /// Sets the to a known state.
+ public void SetEmptyCompressedBytes(bool isInitialized)
+ {
+ SetEmptyCompressedBytes();
+ IsInitialized = isInitialized;
+ }
+
+ ///
+ /// Sets the and and .
+ ///
+ ///
+ ///
+ ///
+ public void SetCompressedBytes(byte[] compressedBytes, MatCompressor decompressor, bool setCompressor = true)
+ {
+ CompressedBytes = compressedBytes;
+ if (setCompressor) Compressor = decompressor;
+ Decompressor = decompressor;
+ }
+
+ /*
+ public void SetUncompressed(Mat src)
+ {
+ Width = src.Width;
+ Height = src.Height;
+ Depth = src.Depth;
+ Channels = src.NumberOfChannels;
+ Roi = Rectangle.Empty;
+
+ CompressedBytes = src.GetBytes();
+ IsCompressed = false;
+ Decompressor = MatCompressorNone.Instance;
+ }
+
+ public void SetUncompressed(MatRoi src)
+ {
+ Width = src.RoiMat.Width;
+ Height = src.RoiMat.Height;
+ Depth = src.RoiMat.Depth;
+ Channels = src.RoiMat.NumberOfChannels;
+ Roi = src.Roi;
+
+ CompressedBytes = src.RoiMat.GetBytes();
+ IsCompressed = false;
+ Decompressor = MatCompressorNone.Instance;
+ }
+ */
+
+ private void SetUncompressed(Mat src)
+ {
+ CompressedBytes = src.GetBytes();
+ IsCompressed = false;
+ Decompressor = MatCompressorNone.Instance;
+ }
+
+ ///
+ /// Compresses the into a byte array.
+ ///
+ ///
+ ///
+ public void Compress(Mat src, object? argument = null)
+ {
+ Width = src.Width;
+ Height = src.Height;
+ Depth = src.Depth;
+ Channels = src.NumberOfChannels;
+ Roi = Rectangle.Empty;
+
+ if (src.IsEmpty)
+ {
+ CompressedBytes = Array.Empty();
+ return;
+ }
+
+ var srcLength = src.GetLength();
+ if (srcLength <= ThresholdToCompress) // Do not compress if the size is smaller or equal to the threshold
+ {
+ SetUncompressed(src);
+ return;
+ }
+
+ try
+ {
+ CompressedBytes = Compressor.Compress(src, argument);
+ if (_compressedBytes.Length < srcLength) // Compressed ok
+ {
+ Decompressor = Compressor;
+ }
+ else if (ReferenceEquals(Compressor, MatCompressorNone.Instance)) // Special case for uncompressed bitmap
+ {
+ IsCompressed = false;
+ Decompressor = Compressor;
+ }
+ else // Compressed size is larger than uncompressed size
+ {
+ SetUncompressed(src);
+ }
+ }
+ catch (Exception) // Cannot compress due some error
+ {
+ SetUncompressed(src);
+ }
+ }
+
+ ///
+ /// Compresses the into a byte array.
+ ///
+ ///
+ ///
+ public void Compress(MatRoi src, object? argument = null)
+ {
+ if (src.RoiSize.IsEmpty)
+ {
+ Width = src.SourceMat.Width;
+ Height = src.SourceMat.Height;
+ Depth = src.SourceMat.Depth;
+ Channels = src.SourceMat.NumberOfChannels;
+ Roi = Rectangle.Empty;
+ CompressedBytes = Array.Empty();
+ return;
+ }
+
+ if (src.IsSourceSameSizeOfRoi)
+ {
+ Compress(src.SourceMat, argument);
+ }
+ else
+ {
+ Compress(src.RoiMat, argument);
+ Width = src.SourceMat.Width;
+ Height = src.SourceMat.Height;
+ Roi = src.Roi;
+ }
+ }
+
+ ///
+ /// Compresses the into a byte array.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Task CompressAsync(Mat src, object? argument = null, CancellationToken cancellationToken = default)
+ {
+ return Task.Run(() => Compress(src, argument), cancellationToken);
+ }
+
+ ///
+ /// Compresses the into a byte array.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Task CompressAsync(MatRoi src, object? argument = null, CancellationToken cancellationToken = default)
+ {
+ return Task.Run(() => Compress(src, argument), cancellationToken);
+ }
+
+ ///
+ /// Decompresses the into a new .
+ ///
+ ///
+ ///
+ public Mat Decompress(object? argument = null)
+ {
+ if (IsEmpty) return CreateMatZeros();
+
+ var mat = Roi.Size.IsEmpty ? CreateMat() : new Mat(Roi.Size, Depth, Channels);
+
+ if (IsCompressed)
+ {
+ Decompressor.Decompress(_compressedBytes, mat, argument);
+ }
+ else
+ {
+ mat.SetBytes(_compressedBytes);
+ }
+
+ if (Roi.Size.IsEmpty) return mat;
+
+ var fullMat = CreateMatZeros();
+ using var roi = new Mat(fullMat, Roi);
+ mat.CopyTo(roi);
+ mat.Dispose();
+ return fullMat;
+ }
+
+ ///
+ /// Decompresses the into a new .
+ ///
+ ///
+ ///
+ ///
+ public Task DecompressAsync(object? argument = null, CancellationToken cancellationToken = default)
+ {
+ return Task.Run(() => Decompress(argument), cancellationToken);
+ }
+ #endregion
+
+ #region Utilities
+
+ ///
+ /// Creates a new with the same size, depth, and channels as the .
+ ///
+ ///
+ public Mat CreateMat()
+ {
+ if (Width <= 0 || Height <= 0) return new Mat();
+ var mat = new Mat(Size, Depth, Channels);
+ return mat;
+ }
+
+ ///
+ /// Create a new with the same size, depth, and channels as the but with all bytes set to 0.
+ ///
+ ///
+ public Mat CreateMatZeros()
+ {
+ return EmguExtensions.InitMat(Size, Channels, Depth);
+ }
+
+ #endregion
+
+ #region Copy and Clone
+ ///
+ /// Copies the to the .
+ ///
+ ///
+ public void CopyTo(CMat dst)
+ {
+ dst._compressedBytes = _compressedBytes.ToArray();
+ dst.IsInitialized = IsInitialized;
+ dst.IsCompressed = IsCompressed;
+ dst.ThresholdToCompress = ThresholdToCompress;
+ dst.Compressor = Compressor;
+ dst.Decompressor = Decompressor;
+ dst.Width = Width;
+ dst.Height = Height;
+ dst.Depth = Depth;
+ dst.Channels = Channels;
+ dst.Roi = Roi;
+ }
+
+ ///
+ /// Creates a clone of the with the same .
+ ///
+ ///
+ public CMat Clone()
+ {
+ var clone = (CMat)MemberwiseClone();
+ clone._compressedBytes = _compressedBytes.ToArray();
+ return clone;
+ }
+ #endregion
+
+ #region Formaters
+ public override string ToString()
+ {
+ return $"{nameof(Decompressor)}: {Decompressor}, {nameof(Size)}: {Size}, {nameof(UncompressedLength)}: {UncompressedLength}, {nameof(Length)}: {Length}, {nameof(IsCompressed)}: {IsCompressed}, {nameof(CompressionRatio)}: {CompressionRatio}x, {nameof(CompressionPercentage)}: {CompressionPercentage}%";
+ }
+ #endregion
+
+ #region Equality
+
+ public bool Equals(CMat? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return IsInitialized == other.IsInitialized
+ && IsCompressed == other.IsCompressed
+ //&& ThresholdToCompress == other.ThresholdToCompress
+ && Width == other.Width
+ && Height == other.Height
+ && Depth == other.Depth
+ && Channels == other.Channels
+ && Roi.Equals(other.Roi)
+ && _compressedBytes.Length == other._compressedBytes.Length
+ && _compressedBytes.SequenceEqual(other._compressedBytes);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((CMat)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ var hashCode = new HashCode();
+ hashCode.Add(_compressedBytes);
+ hashCode.Add(IsInitialized);
+ hashCode.Add(IsCompressed);
+ hashCode.Add(ThresholdToCompress);
+ hashCode.Add(Width);
+ hashCode.Add(Height);
+ hashCode.Add((int)Depth);
+ hashCode.Add(Channels);
+ hashCode.Add(Roi);
+ return hashCode.ToHashCode();
+ }
+
+ public static bool operator ==(CMat? left, CMat? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(CMat? left, CMat? right)
+ {
+ return !Equals(left, right);
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/UVtools.Core/EmguCV/CompressedMat.cs b/UVtools.Core/EmguCV/CompressedMat.cs
deleted file mode 100644
index dda3c7b5..00000000
--- a/UVtools.Core/EmguCV/CompressedMat.cs
+++ /dev/null
@@ -1,316 +0,0 @@
-using System;
-using System.Drawing;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Emgu.CV;
-using Emgu.CV.CvEnum;
-using UVtools.Core.Extensions;
-
-namespace UVtools.Core.EmguCV;
-
-public class CompressedMat : IEquatable
-{
- ///
- /// Gets the compressed bytes that have been compressed with .
- ///
- private byte[] _compressedBytes = Array.Empty();
-
- ///
- /// Gets the compressed bytes that have been compressed with .
- ///
- public byte[] CompressedBytes
- {
- get => _compressedBytes;
- private set => _compressedBytes = value;
- }
-
- ///
- /// Gets the cached width of the that was compressed.
- ///
- public int Width { get; private set; }
-
- ///
- /// Gets the cached height of the that was compressed.
- ///
- public int Height { get; private set; }
-
- ///
- /// Gets the cached size of the that was compressed.
- ///
- public Size Size
- {
- get => new(Width, Height);
- private set
- {
- Width = value.Width;
- Height = value.Height;
- }
- }
-
- ///
- /// Gets the cached depth of the that was compressed.
- ///
- public DepthType Depth { get; private set; } = DepthType.Cv8U;
-
- ///
- /// Gets the cached number of channels of the that was compressed.
- ///
- public int Channels { get; private set; } = 1;
-
- ///
- /// Gets the cached ROI of the that was compressed.
- ///
- public Rectangle Roi { get; private set; } = default;
-
- ///
- /// Gets or sets the that will be used to compress and decompress the .
- ///
- public MatCompressor Compressor { get; set; } = MatCompressorLz4.Instance;
-
- ///
- /// Gets the that will be used to decompress the .
- ///
- public MatCompressor Decompressor { get; private set; } = MatCompressorLz4.Instance;
-
- ///
- /// Gets a value indicating whether the are empty.
- ///
- public bool IsEmpty => CompressedBytes.Length == 0;
-
- ///
- /// Gets a value indicating whether the are compressed or raw bytes.
- ///
- public bool IsCompressed { get; private set; }
-
- public Mat Mat
- {
- get => Decompress();
- set => Compress(value);
- }
-
- public CompressedMat(int width = 0, int height = 0, DepthType depth = DepthType.Cv8U, int channels = 1)
- {
- Width = width;
- Height = height;
- Depth = depth;
- Channels = channels;
- }
-
- public CompressedMat(Mat mat)
- {
- Compress(mat);
- }
-
- public CompressedMat(MatRoi matRoi)
- {
- Compress(matRoi);
- }
-
- public CompressedMat(MatCompressor compressor, int width = 0, int height = 0, DepthType depth = DepthType.Cv8U, int channels = 1) : this(width, height, depth, channels)
- {
- Compressor = compressor;
- Decompressor = compressor;
- }
-
- public CompressedMat(MatCompressor compressor, Mat mat)
- {
- Compressor = compressor;
- Decompressor = compressor;
- Compress(mat);
- }
-
- public CompressedMat(MatCompressor compressor, MatRoi matRoi)
- {
- Compressor = compressor;
- Decompressor = compressor;
- Compress(matRoi);
- }
-
- ///
- /// Creates a new with the same size, depth, and channels as the .
- ///
- ///
- public Mat CreateMat()
- {
- if (Width <= 0 || Height <= 0) return new Mat();
- var mat = new Mat(Height, Width, Depth, Channels);
- return mat;
- }
-
- ///
- /// Create a new with the same size, depth, and channels as the but with all bytes set to 0.
- ///
- ///
- public Mat CreateMatZeros()
- {
- return EmguExtensions.InitMat(Size, Channels, Depth);
- }
-
- ///
- /// Compresses the into a byte array.
- ///
- ///
- ///
- public void Compress(Mat src, object? tag = null)
- {
- Width = src.Width;
- Height = src.Height;
- Depth = src.Depth;
- Channels = src.NumberOfChannels;
- Roi = Rectangle.Empty;
-
- if (src.IsEmpty)
- {
- _compressedBytes = Array.Empty();
- return;
- }
-
- try
- {
- _compressedBytes = Compressor.Compress(src, tag);
- if (_compressedBytes.Length >= src.GetLength())
- {
- Decompressor = Compressor;
- _compressedBytes = src.GetBytes();
- IsCompressed = false;
- }
- else
- {
- IsCompressed = true;
- }
- }
- catch (Exception)
- {
- _compressedBytes = src.GetBytes();
- IsCompressed = false;
- }
-
- }
-
- ///
- /// Compresses the into a byte array.
- ///
- ///
- ///
- public void Compress(MatRoi src, object? tag = null)
- {
- if (src.IsSourceSameSizeOfRoi)
- {
- Compress(src.SourceMat, tag);
- }
- else
- {
- Compress(src.RoiMat, tag);
- Width = src.SourceMat.Width;
- Height = src.SourceMat.Height;
- Roi = src.Roi;
- }
- }
-
- ///
- /// Compresses the into a byte array.
- ///
- ///
- ///
- ///
- ///
- public Task CompressAsync(Mat src, object? tag = null, CancellationToken cancellationToken = default)
- {
- return Task.Run(() => Compress(src, tag), cancellationToken);
- }
-
- ///
- /// Compresses the into a byte array.
- ///
- ///
- ///
- ///
- ///
- public Task CompressAsync(MatRoi src, object? tag = null, CancellationToken cancellationToken = default)
- {
- return Task.Run(() => Compress(src, tag), cancellationToken);
- }
-
- ///
- /// Decompresses the into a new .
- ///
- ///
- ///
- public Mat Decompress(object? tag = null)
- {
- if (IsEmpty) return new Mat();
-
- var mat = Roi.IsEmpty ? CreateMat() : new Mat(Roi.Size, Depth, Channels);
-
- if (!IsCompressed)
- {
- mat.SetBytes(_compressedBytes);
- }
- else
- {
- Decompressor.Decompress(_compressedBytes, mat, tag);
- }
-
- if (Roi.IsEmpty) return mat;
-
- var fullMat = CreateMatZeros();
- using var roi = new Mat(fullMat, Roi);
- mat.CopyTo(roi);
- mat.Dispose();
- return fullMat;
- }
-
- ///
- /// Decompresses the into a new .
- ///
- ///
- ///
- ///
- public Task DecompressAsync(object? tag = null, CancellationToken cancellationToken = default)
- {
- return Task.Run(() => Decompress(tag), cancellationToken);
- }
-
-
- ///
- /// Creates a clone of the with the same .
- ///
- ///
- public CompressedMat Clone()
- {
- var clone = (CompressedMat)MemberwiseClone();
- clone._compressedBytes = _compressedBytes.ToArray();
- return clone;
- }
-
- public bool Equals(CompressedMat? other)
- {
- if (ReferenceEquals(null, other)) return false;
- if (ReferenceEquals(this, other)) return true;
- return Width == other.Width && Height == other.Height && Depth == other.Depth && Channels == other.Channels && Roi == other.Roi && IsCompressed == other.IsCompressed && _compressedBytes.SequenceEqual(other._compressedBytes);
- }
-
- public override bool Equals(object? obj)
- {
- if (ReferenceEquals(null, obj)) return false;
- if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
- return Equals((CompressedMat)obj);
- }
-
- public override int GetHashCode()
- {
- return HashCode.Combine(_compressedBytes, Width, Height, (int)Depth, Channels, Roi, IsCompressed);
- }
-
- public static bool operator ==(CompressedMat? left, CompressedMat? right)
- {
- return Equals(left, right);
- }
-
- public static bool operator !=(CompressedMat? left, CompressedMat? right)
- {
- return !Equals(left, right);
- }
-}
\ No newline at end of file
diff --git a/UVtools.Core/EmguCV/EmptyRoiBehaviour.cs b/UVtools.Core/EmguCV/EmptyRoiBehaviour.cs
new file mode 100644
index 00000000..6d53dbe0
--- /dev/null
+++ b/UVtools.Core/EmguCV/EmptyRoiBehaviour.cs
@@ -0,0 +1,22 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+namespace UVtools.Core.EmguCV;
+
+public enum EmptyRoiBehaviour
+{
+ ///
+ /// Allows the mat to be created with an empty roi.
+ ///
+ Continue,
+
+ ///
+ /// Sets the roi to the source mat size.
+ ///
+ CaptureSource,
+}
\ No newline at end of file
diff --git a/UVtools.Core/EmguCV/MatCompressor.cs b/UVtools.Core/EmguCV/MatCompressor.cs
index 70a851d0..1db19e27 100644
--- a/UVtools.Core/EmguCV/MatCompressor.cs
+++ b/UVtools.Core/EmguCV/MatCompressor.cs
@@ -1,4 +1,12 @@
-using System;
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
using System.Buffers;
using System.IO.Compression;
using System.IO;
@@ -17,28 +25,28 @@ public abstract class MatCompressor
/// Compresses the into a byte array.
///
///
- ///
+ ///
///
- public abstract byte[] Compress(Mat src, object? tag = null);
+ public abstract byte[] Compress(Mat src, object? argument = null);
///
/// Decompresses the from a byte array.
///
///
///
- ///
- public abstract void Decompress(byte[] compressedBytes, Mat dst, object? tag = null);
+ ///
+ public abstract void Decompress(byte[] compressedBytes, Mat dst, object? argument = null);
///
/// Compresses the into a byte array.
///
///
- ///
+ ///
///
///
- public Task CompressAsync(Mat src, object? tag = null, CancellationToken cancellationToken = default)
+ public Task CompressAsync(Mat src, object? argument = null, CancellationToken cancellationToken = default)
{
- return Task.Run(() => Compress(src, tag), cancellationToken);
+ return Task.Run(() => Compress(src, argument), cancellationToken);
}
///
@@ -46,15 +54,45 @@ public Task CompressAsync(Mat src, object? tag = null, CancellationToken
///
///
///
- ///
+ ///
///
///
- public Task DecompressAsync(byte[] compressedBytes, Mat dst, object? tag = null, CancellationToken cancellationToken = default)
+ public Task DecompressAsync(byte[] compressedBytes, Mat dst, object? argument = null, CancellationToken cancellationToken = default)
{
- return Task.Run(() => Decompress(compressedBytes, dst, tag), cancellationToken);
+ return Task.Run(() => Decompress(compressedBytes, dst, argument), cancellationToken);
}
}
+#region None
+public sealed class MatCompressorNone : MatCompressor
+{
+ ///
+ /// Instance of .
+ ///
+ public static readonly MatCompressorNone Instance = new();
+
+ private MatCompressorNone()
+ {
+ }
+
+ ///
+ public override byte[] Compress(Mat src, object? argument = null)
+ {
+ return src.GetBytes();
+ }
+
+ ///
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
+ {
+ dst.SetBytes(compressedBytes);
+ }
+
+ public override string ToString()
+ {
+ return "None";
+ }
+}
+#endregion
#region PNG
public sealed class MatCompressorPng : MatCompressor
@@ -69,13 +107,13 @@ private MatCompressorPng()
}
///
- public override byte[] Compress(Mat src, object? tag = null)
+ public override byte[] Compress(Mat src, object? argument = null)
{
return src.GetPngByes();
}
///
- public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = null)
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
{
CvInvoke.Imdecode(compressedBytes, ImreadModes.AnyColor, dst);
}
@@ -93,16 +131,21 @@ private MatCompressorPngGreyScale()
}
///
- public override byte[] Compress(Mat src, object? tag = null)
+ public override byte[] Compress(Mat src, object? argument = null)
{
return src.GetPngByes();
}
///
- public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = null)
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
{
CvInvoke.Imdecode(compressedBytes, ImreadModes.Grayscale, dst);
}
+
+ public override string ToString()
+ {
+ return "PNG";
+ }
}
#endregion
@@ -119,7 +162,7 @@ private MatCompressorDeflate()
}
///
-public override byte[] Compress(Mat src, object? tag = null)
+public override byte[] Compress(Mat src, object? argument = null)
{
UnmanagedMemoryStream srcStream;
if (src.IsContinuous)
@@ -140,9 +183,9 @@ public override byte[] Compress(Mat src, object? tag = null)
using var compressedStream = StreamExtensions.RecyclableMemoryStreamManager.GetStream();
- using (var gzipStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true))
+ using (var deflateStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true))
{
- srcStream.CopyTo(gzipStream);
+ srcStream.CopyTo(deflateStream);
}
srcStream.Dispose();
@@ -151,7 +194,7 @@ public override byte[] Compress(Mat src, object? tag = null)
}
///
- public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = null)
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
{
unsafe
{
@@ -164,6 +207,11 @@ public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = n
}
}
}
+
+ public override string ToString()
+ {
+ return "Deflate";
+ }
}
#endregion
@@ -180,7 +228,7 @@ private MatCompressorGZip()
}
///
- public override byte[] Compress(Mat src, object? tag = null)
+ public override byte[] Compress(Mat src, object? argument = null)
{
UnmanagedMemoryStream srcStream;
if (src.IsContinuous)
@@ -212,7 +260,7 @@ public override byte[] Compress(Mat src, object? tag = null)
}
///
- public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = null)
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
{
unsafe
{
@@ -220,11 +268,138 @@ public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = n
{
using var compressedStream = new UnmanagedMemoryStream(pBuffer, compressedBytes.Length);
using var matStream = dst.GetUnmanagedMemoryStream(FileAccess.Write);
- using var deflateStream = new GZipStream(compressedStream, CompressionMode.Decompress);
- deflateStream.CopyTo(matStream);
+ using var gZipStream = new GZipStream(compressedStream, CompressionMode.Decompress);
+ gZipStream.CopyTo(matStream);
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return "GZip";
+ }
+}
+#endregion
+
+#region ZLib
+public sealed class MatCompressorZLib : MatCompressor
+{
+ ///
+ /// Instance of .
+ ///
+ public static readonly MatCompressorZLib Instance = new();
+
+ private MatCompressorZLib()
+ {
+ }
+
+ ///
+ public override byte[] Compress(Mat src, object? argument = null)
+ {
+ UnmanagedMemoryStream srcStream;
+ if (src.IsContinuous)
+ {
+ srcStream = src.GetUnmanagedMemoryStream(FileAccess.Read);
+ }
+ else
+ {
+ var bytes = src.GetBytes(); // Need to copy the submatrix to get the full data in a contiguous block
+ unsafe
+ {
+ fixed (byte* p = bytes)
+ {
+ srcStream = new UnmanagedMemoryStream(p, bytes.Length);
+ }
+ }
+ }
+
+
+ using var compressedStream = StreamExtensions.RecyclableMemoryStreamManager.GetStream();
+ using (var zLibStream = new ZLibStream(compressedStream, CompressionLevel.Fastest, true))
+ {
+ srcStream.CopyTo(zLibStream);
+ }
+
+ srcStream.Dispose();
+
+ return compressedStream.ToArray();
+ }
+
+ ///
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
+ {
+ unsafe
+ {
+ fixed (byte* pBuffer = compressedBytes)
+ {
+ using var compressedStream = new UnmanagedMemoryStream(pBuffer, compressedBytes.Length);
+ using var matStream = dst.GetUnmanagedMemoryStream(FileAccess.Write);
+ using var zLibStream = new ZLibStream(compressedStream, CompressionMode.Decompress);
+ zLibStream.CopyTo(matStream);
}
}
}
+
+ public override string ToString()
+ {
+ return "ZLib";
+ }
+}
+#endregion
+
+#region Brotli
+public sealed class MatCompressorBrotli : MatCompressor
+{
+ ///
+ /// Instance of .
+ ///
+ public static readonly MatCompressorBrotli Instance = new();
+
+ private MatCompressorBrotli()
+ {
+ }
+
+ ///
+ public override byte[] Compress(Mat src, object? argument = null)
+ {
+ ReadOnlySpan srcSpan;
+ if (src.IsContinuous)
+ {
+ srcSpan = src.GetDataByteReadOnlySpan();
+ }
+ else
+ {
+ var bytes = src.GetBytes(); // Need to copy the submatrix to get the full data in a contiguous block
+ srcSpan = new ReadOnlySpan(bytes);
+ }
+
+ var rent = ArrayPool.Shared.Rent(srcSpan.Length - 1);
+ var rentSpan = rent.AsSpan();
+
+ bool result = BrotliEncoder.TryCompress(srcSpan, rentSpan, out var encodedLength, 0, 22);
+ if (!result) // Throw an exception if compression failed and let CMat handle it and use uncompressed data
+ {
+ ArrayPool.Shared.Return(rent);
+ throw new Exception("Failed to compress, buffer is too short?");
+ }
+
+ var target = rentSpan[..encodedLength].ToArray();
+
+ ArrayPool.Shared.Return(rent);
+
+ return target;
+ }
+
+ ///
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
+ {
+ BrotliDecoder.TryDecompress(new ReadOnlySpan(compressedBytes), dst.GetDataByteSpan(), out var bytesWritten);
+ }
+
+ public override string ToString()
+ {
+ return "Brotli";
+ }
}
#endregion
@@ -241,35 +416,46 @@ private MatCompressorLz4()
}
///
- public override byte[] Compress(Mat src, object? tag = null)
+ public override byte[] Compress(Mat src, object? argument = null)
{
- Span srcSpan;
+ ReadOnlySpan srcSpan;
if (src.IsContinuous)
{
- srcSpan = src.GetDataByteSpan();
+ srcSpan = src.GetDataByteReadOnlySpan();
}
else
{
var bytes = src.GetBytes(); // Need to copy the submatrix to get the full data in a contiguous block
- srcSpan = bytes.AsSpan();
+ srcSpan = new ReadOnlySpan(bytes);
}
-
- var rent = ArrayPool.Shared.Rent(srcSpan.Length);
+ var rent = ArrayPool.Shared.Rent(srcSpan.Length - 1);
var rentSpan = rent.AsSpan();
-
- var encodedLength = LZ4Codec.Encode(srcSpan, rentSpan);
- var target = rentSpan[..encodedLength].ToArray();
-
- ArrayPool.Shared.Return(rent);
+ byte[] target;
+ try
+ {
+ var encodedLength = LZ4Codec.Encode(srcSpan, rentSpan);
+ target = rentSpan[..encodedLength].ToArray();
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(rent);
+ }
+
+
return target;
}
///
- public override void Decompress(byte[] compressedBytes, Mat dst, object? tag = null)
+ public override void Decompress(byte[] compressedBytes, Mat dst, object? argument = null)
+ {
+ LZ4Codec.Decode(new ReadOnlySpan(compressedBytes), dst.GetDataByteSpan());
+ }
+
+ public override string ToString()
{
- LZ4Codec.Decode(compressedBytes.AsSpan(), dst.GetDataByteSpan());
+ return "LZ4";
}
}
#endregion
diff --git a/UVtools.Core/EmguCV/MatRoi.cs b/UVtools.Core/EmguCV/MatRoi.cs
index 8dc34642..41db6aa3 100644
--- a/UVtools.Core/EmguCV/MatRoi.cs
+++ b/UVtools.Core/EmguCV/MatRoi.cs
@@ -33,8 +33,14 @@ public class MatRoi : IDisposable, IEquatable
///
public bool DisposeSourceMat { get; set; }
-
+ ///
+ /// Gets the location
+ ///
public Point RoiLocation => Roi.Location;
+
+ ///
+ /// Gets the size
+ ///
public Size RoiSize => Roi.Size;
///
@@ -51,17 +57,29 @@ public class MatRoi : IDisposable, IEquatable
///
/// The source mat to apply the .
/// The roi rectangle.
+ /// Sets the behavior for an empty roi event.
/// True if you want to dispose the upon the call.
/// Use true when creating a directly on this constructor or not handling the disposal, otherwise false.
- public MatRoi(Mat sourceMat, Rectangle roi, bool disposeSourceMat = false)
+ public MatRoi(Mat sourceMat, Rectangle roi, EmptyRoiBehaviour emptyRoiBehaviour = EmptyRoiBehaviour.Continue, bool disposeSourceMat = false)
{
SourceMat = sourceMat;
Roi = roi;
- RoiMat = sourceMat.Roi(roi);
+ RoiMat = sourceMat.Roi(roi, emptyRoiBehaviour);
DisposeSourceMat = disposeSourceMat;
}
+ ///
+ ///
+ ///
+ /// The source mat to apply the .
+ /// The roi rectangle.
+ /// True if you want to dispose the upon the call.
+ /// Use true when creating a directly on this constructor or not handling the disposal, otherwise false.
+ public MatRoi(Mat sourceMat, Rectangle roi, bool disposeSourceMat) : this(sourceMat, roi, EmptyRoiBehaviour.Continue, disposeSourceMat)
+ {
+ }
+
public void Deconstruct(out Mat sourceMat, out Mat roiMat, out Rectangle roi)
{
sourceMat = SourceMat;
diff --git a/UVtools.Core/Extensions/ClassExtensions.cs b/UVtools.Core/Extensions/ClassExtensions.cs
index 83c57ebb..6cff2b38 100644
--- a/UVtools.Core/Extensions/ClassExtensions.cs
+++ b/UVtools.Core/Extensions/ClassExtensions.cs
@@ -6,6 +6,7 @@
* of this license document, but changing it is not allowed.
*/
+using System;
using System.IO;
using System.Text;
using System.Text.Json;
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 53a937ee..4d6b917a 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -231,7 +231,6 @@ public static unsafe Span2D GetDataSpan2D(this Mat mat)
///
public static Span GetDataByteSpan(this Mat mat, int length, int offset = 0) => GetDataSpan(mat, length, offset);
-
///
/// Gets the data span to manipulate or read pixels given a length and offset
///
@@ -303,6 +302,7 @@ public static unsafe Span GetRowSpan(this Mat mat, int y, int length = 0,
///
public static Span GetRowByteSpan(this Mat mat, int y, int length = 0, int offset = 0) => mat.GetRowSpan(y, length, offset);
+ /*
///
/// Gets a col span to manipulate or read pixels
///
@@ -317,7 +317,66 @@ public static unsafe Span GetColSpan(this Mat mat, int x, int length = 0,
// Fix with Span2D
var colMat = mat.Col(x);
return new(IntPtr.Add(colMat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length);
+ }*/
+
+
+
+ ///
+ /// Gets the data span to read pixels given a length and offset
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static unsafe ReadOnlySpan GetDataReadOnlySpan(this Mat mat, int length = 0, int offset = 0)
+ {
+ if (length <= 0)
+ {
+ if (mat.IsContinuous)
+ {
+ length = mat.GetLength();
+ }
+ else
+ {
+ length = mat.Step / mat.DepthToByteCount() * (mat.Height - 1) + mat.GetRealStep();
+ }
+ }
+ return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length);
}
+
+ ///
+ /// Gets the whole data span to manipulate or read pixels
+ ///
+ ///
+ ///
+ public static ReadOnlySpan GetDataByteReadOnlySpan(this Mat mat) => mat.GetDataReadOnlySpan();
+
+ ///
+ /// Gets a row span to read pixels
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static unsafe ReadOnlySpan GetRowReadOnlySpan(this Mat mat, int y, int length = 0, int offset = 0)
+ {
+ var originalStep = mat.Step;
+ if (length <= 0) length = mat.GetRealStep();
+ return new(IntPtr.Add(mat.DataPointer, y * originalStep + offset).ToPointer(), length);
+ }
+
+ ///
+ /// Gets a row span or read pixels
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static ReadOnlySpan GetRowByteReadOnlySpan(this Mat mat, int y, int length = 0, int offset = 0) => mat.GetRowReadOnlySpan(y, length, offset);
#endregion
#region Memory Fill
@@ -613,7 +672,7 @@ public static void CropByBounds(this Mat src, Mat dst)
/// Target to paste the
public static void CopyCenterToCenter(this Mat src, Size size, Mat dst)
{
- var srcRoi = src.RoiFromCenter(size);
+ using var srcRoi = src.RoiFromCenter(size);
CopyToCenter(srcRoi, dst);
}
@@ -625,7 +684,7 @@ public static void CopyCenterToCenter(this Mat src, Size size, Mat dst)
/// Target to paste the
public static void CopyRegionToCenter(this Mat src, Rectangle region, Mat dst)
{
- var srcRoi = src.Roi(region);
+ using var srcRoi = src.Roi(region);
CopyToCenter(srcRoi, dst);
}
@@ -720,34 +779,44 @@ public static void CopyAreasLargerThan(this Mat src, double threshold, Mat dst)
#region Roi methods
///
- /// Gets a Roi, but return source when roi is empty or have same size as source
+ /// Gets a Roi
///
///
///
+ ///
///
- public static Mat Roi(this Mat mat, Rectangle roi)
+ public static Mat Roi(this Mat mat, Rectangle roi, EmptyRoiBehaviour emptyRoiBehaviour = EmptyRoiBehaviour.Continue)
{
- return roi.IsEmpty || roi.Size == mat.Size ? mat : new Mat(mat, roi);
+ return emptyRoiBehaviour switch
+ {
+ EmptyRoiBehaviour.Continue => new Mat(mat, roi),
+ EmptyRoiBehaviour.CaptureSource => new Mat(mat, roi.IsEmpty ? new Rectangle(Point.Empty, mat.Size) : roi),
+ _ => throw new ArgumentOutOfRangeException(nameof(emptyRoiBehaviour), emptyRoiBehaviour, null)
+ };
}
///
- /// Gets a Roi at x=0 and y=0 given a size, but return source when roi is empty or have same size as source
+ /// Gets a Roi at x=0 and y=0 given a size
///
///
///
///
public static Mat Roi(this Mat mat, Size size)
{
- return size.IsEmpty || size == mat.Size ? mat : new Mat(mat, new(Point.Empty, size));
+ return new Mat(mat, new(Point.Empty, size));
}
///
- /// Gets a Roi from a mat size, but return source when roi is empty or have same size as source
+ /// Gets a Roi from a mat size
///
///
///
///
- public static Mat Roi(this Mat mat, Mat fromMat) => mat.Roi(fromMat.Size);
+ public static Mat Roi(this Mat mat, Mat fromMat)
+ {
+ return new Mat(mat, new(Point.Empty, fromMat.Size));
+ }
+
///
/// Calculates the bounding rectangle and return a object with it
@@ -761,14 +830,14 @@ public static MatRoi RoiFromAutoBoundingRectangle(this Mat mat)
}
///
- /// Gets a Roi from center, but return source when have same size as source
+ /// Gets a Roi from center
///
///
///
///
public static Mat RoiFromCenter(this Mat mat, Size size)
{
- if(mat.Size == size) return mat;
+ if(mat.Size == size) return mat.Roi(size);
var newRoi = mat.Roi(new Rectangle(
mat.Width / 2 - size.Width / 2,
@@ -791,11 +860,11 @@ public static Mat NewMatFromCenterRoi(this Mat mat, Size targetSize, Rectangle r
{
if (targetSize == mat.Size) return mat.Clone();
var newMat = InitMat(targetSize);
- var roiMat = mat.Roi(roi);
+ using var roiMat = mat.Roi(roi);
//int xStart = mat.Width / 2 - targetSize.Width / 2;
//int yStart = mat.Height / 2 - targetSize.Height / 2;
- var newMatRoi = newMat.RoiFromCenter(roi.Size);
+ using var newMatRoi = newMat.RoiFromCenter(roi.Size);
/*var newMatRoi = new Mat(newMat, new Rectangle(
targetSize.Width / 2 - roi.Width / 2,
targetSize.Height / 2 - roi.Height / 2,
@@ -859,7 +928,7 @@ public static int FindFirstPositivePixel(this Mat mat, int startPos = 0, int len
/// Pixel position in the span, or -1 if not found
public static int FindFirstPixelEqualTo(this Mat mat, byte value, int startPos = 0, int length = 0)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (length <= 0) length = span.Length;
for (var i = startPos; i < length; i++)
{
@@ -879,7 +948,7 @@ public static int FindFirstPixelEqualTo(this Mat mat, byte value, int startPos =
/// Pixel position in the span, or -1 if not found
public static int FindFirstPixelLessThan(this Mat mat, byte value, int startPos = 0, int length = 0)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (length <= 0) length = span.Length;
for (var i = startPos; i < length; i++)
{
@@ -899,7 +968,7 @@ public static int FindFirstPixelLessThan(this Mat mat, byte value, int startPos
/// Pixel position in the span, or -1 if not found
public static int FindFirstPixelEqualOrLessThan(this Mat mat, byte value, int startPos = 0, int length = 0)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (length <= 0) length = span.Length;
for (var i = startPos; i < length; i++)
{
@@ -919,7 +988,7 @@ public static int FindFirstPixelEqualOrLessThan(this Mat mat, byte value, int st
/// Pixel position in the span, or -1 if not found
public static int FindFirstPixelGreaterThan(this Mat mat, byte value, int startPos = 0, int length = 0)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (length <= 0) length = span.Length;
for (var i = startPos; i < length; i++)
{
@@ -939,7 +1008,7 @@ public static int FindFirstPixelGreaterThan(this Mat mat, byte value, int startP
/// Pixel position in the span, or -1 if not found
public static int FindFirstPixelEqualOrGreaterThan(this Mat mat, byte value, int startPos = 0, int length = 0)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (length <= 0) length = span.Length;
for (var i = startPos; i < length; i++)
{
@@ -981,7 +1050,7 @@ public static List ScanStrides(this Mat mat, uint strideLimit = 0, b
var maxWidth = mat.Width;
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (excludeBlacks || startOnFirstPositivePixel)
{
@@ -1109,7 +1178,7 @@ public static List ScanStrides(this Mat mat, Func greyFu
var maxWidth = mat.Width;
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
if (excludeBlacks || startOnFirstPositivePixel)
{
@@ -2104,5 +2173,15 @@ public static void DisposeIfSubMatrix(this Mat mat)
{
if(mat.IsSubmatrix) mat.Dispose();
}
+
+ ///
+ /// Dispose this if it's not the same reference as
+ ///
+ /// Mat to dispose
+ ///
+ public static void DisposeIfNot(this Mat mat, Mat otherMat)
+ {
+ if (!ReferenceEquals(mat, otherMat)) mat.Dispose();
+ }
#endregion
}
\ No newline at end of file
diff --git a/UVtools.Core/FileFormats/AnetFile.cs b/UVtools.Core/FileFormats/AnetFile.cs
index fcaf0cf1..7d0b7b53 100644
--- a/UVtools.Core/FileFormats/AnetFile.cs
+++ b/UVtools.Core/FileFormats/AnetFile.cs
@@ -177,7 +177,7 @@ void SetBits(List data, uint pos, uint value, uint count = 1)
uint compressedPos = 33;
var rawData = new List();
- var spanMat = mat.GetDataByteSpan();
+ var spanMat = mat.GetDataByteReadOnlySpan();
bool isWhitePrev = spanMat[0] > 127;
@@ -471,10 +471,14 @@ protected override void EncodeInternally(OperationProgress progress)
{
progress.PauseIfRequested();
var layer = this[layerIndex];
- using var mat = layer.LayerMat;
- layerData[layerIndex] = new LayerDef();
- layerData[layerIndex].SetFrom(layer);
- layerData[layerIndex].Encode(mat);
+
+ using (var mat = layer.LayerMat)
+ {
+ layerData[layerIndex] = new LayerDef();
+ layerData[layerIndex].SetFrom(layer);
+ layerData[layerIndex].Encode(mat);
+ }
+
progress.LockAndIncrement();
});
@@ -545,25 +549,31 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = layersDefinitions[layerIndex].Decode(out var resolutionX, out var resolutionY);
- if (layerIndex == 0) // Set file resolution from first layer RLE. Figure out other properties after that
- {
- ResolutionX = resolutionX;
- ResolutionY = resolutionY;
- var machine = Machine.Machines.FirstOrDefault(machine =>
- machine.Brand == PrinterBrand.Anet && machine.ResolutionX == resolutionX &&
- machine.ResolutionY == resolutionY);
-
- if (machine is not null)
+ using (var mat = layersDefinitions[layerIndex].Decode(out var resolutionX, out var resolutionY))
+ {
+ if (layerIndex ==
+ 0) // Set file resolution from first layer RLE. Figure out other properties after that
{
- DisplayWidth = machine.DisplayWidth;
- DisplayHeight = machine.DisplayHeight;
- MachineZ = machine.MachineZ;
- MachineName = machine.Name;
+ ResolutionX = resolutionX;
+ ResolutionY = resolutionY;
+
+ var machine = Machine.Machines.FirstOrDefault(machine =>
+ machine.Brand == PrinterBrand.Anet && machine.ResolutionX == resolutionX &&
+ machine.ResolutionY == resolutionY);
+
+ if (machine is not null)
+ {
+ DisplayWidth = machine.DisplayWidth;
+ DisplayHeight = machine.DisplayHeight;
+ MachineZ = machine.MachineZ;
+ MachineName = machine.Name;
+ }
}
+
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
}
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
progress.LockAndIncrement();
});
}
diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs
index 8082d3f5..9fb2efc2 100644
--- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs
+++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs
@@ -1181,7 +1181,8 @@ protected override void DecodeInternally(OperationProgress progress)
}
else
{
- _layers[layerIndex] = new Layer((uint) layerIndex, layerDef.DecodeImage((uint) layerIndex), this);
+ using var mat = layerDef.DecodeImage((uint)layerIndex);
+ _layers[layerIndex] = new Layer((uint) layerIndex, mat, this);
}
progress.LockAndIncrement();
diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs
index 71ddffc2..3229c766 100644
--- a/UVtools.Core/FileFormats/CXDLPFile.cs
+++ b/UVtools.Core/FileFormats/CXDLPFile.cs
@@ -694,7 +694,7 @@ protected override void EncodeInternally(OperationProgress progress)
layerLargestContourArea[layerIndex] = (uint)(EmguContours.GetLargestContourArea(contours) * pixelArea * 1000);
//Debug.WriteLine($"Area: {contourArea} ({contourArea * PixelArea * 1000}) BR: {max.Bounds.Area()} ({max.Bounds.Area() * PixelArea * 1000})");
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
layerBytes[layerIndex] = new();
diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs
index ac8c820f..5bcbdea8 100644
--- a/UVtools.Core/FileFormats/CXDLPv1File.cs
+++ b/UVtools.Core/FileFormats/CXDLPv1File.cs
@@ -531,7 +531,7 @@ protected override void EncodeInternally(OperationProgress progress)
var layer = this[layerIndex];
using (var mat = layer.LayerMat)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
layerBytes[layerIndex] = new();
diff --git a/UVtools.Core/FileFormats/CXDLPv4File.cs b/UVtools.Core/FileFormats/CXDLPv4File.cs
index 882997de..53b3f29c 100644
--- a/UVtools.Core/FileFormats/CXDLPv4File.cs
+++ b/UVtools.Core/FileFormats/CXDLPv4File.cs
@@ -1420,8 +1420,10 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = LayerDefinitionsEx[layerIndex].Decode(this, LayerDefinitions[layerIndex], (uint)layerIndex);
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ using (var mat = LayerDefinitionsEx[layerIndex].Decode(this, LayerDefinitions[layerIndex], (uint)layerIndex))
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs
index d07e21f1..00546905 100644
--- a/UVtools.Core/FileFormats/ChituboxFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxFile.cs
@@ -2146,8 +2146,11 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = LayerDefinitions[0, layerIndex].Decode((uint)layerIndex);
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
+ using (var mat = LayerDefinitions[0, layerIndex].Decode((uint)layerIndex))
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs
index cdcf60e7..ae5c24c8 100644
--- a/UVtools.Core/FileFormats/FDGFile.cs
+++ b/UVtools.Core/FileFormats/FDGFile.cs
@@ -1087,9 +1087,9 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- if (DecodeType == FileDecodeType.Full)
+
+ using (var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex))
{
- using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex);
_layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
}
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 72f2a589..763a0e5c 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -119,7 +119,8 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable
public static int DefaultParallelBatchCount => Environment.ProcessorCount * 10;
- #endregion
+
+ #endregion
#region Enums
@@ -180,10 +181,12 @@ public enum FileImageType : byte
Png8,
Png24,
Png32,
+
///
/// eg: Nova Bene4, Elfin Mono SE, Whale 1/2
///
Png24BgrAA,
+
///
/// eg: Uniformation GKone
///
@@ -194,54 +197,105 @@ public enum FileImageType : byte
#endregion
#region Sub Classes
+
///
/// Available Print Parameters to modify
///
public class PrintParameterModifier
{
-
+
#region Instances
- public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Layer absolute Z position", "mm", 0, 100000, 0.01m, Layer.HeightPrecision);
- public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layers", "Number of bottom/burn-in layers", "Ξ", 0, ushort.MaxValue, 1, 0);
- public static PrintParameterModifier TransitionLayerCount { get; } = new ("Transition layers", "Number of fade/transition layers", "Ξ", 0, ushort.MaxValue, 1, 0);
-
- public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off delay", "Total motor movement time + rest time to wait before cure a new bottom layer", "s");
- public static PrintParameterModifier LightOffDelay { get; } = new("Light-off delay", "Total motor movement time + rest time to wait before cure a new layer", "s");
- public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
- public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
-
- public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers exposure time", "s", 0.1M);
- public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Normal layers exposure time", "s", 0.1M);
-
- public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
- public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
-
- public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Lift/peel height between bottom layers", "mm");
- public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm");
-
- public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", "Lift speed of bottom layers", "mm/min", 10, 5000, 5);
- public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", "Lift speed of normal layers", "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier PositionZ { get; } = new("Position Z", "Layer absolute Z position", "mm",
+ 0, 100000, 0.01m, Layer.HeightPrecision);
+
+ public static PrintParameterModifier BottomLayerCount { get; } = new("Bottom layers",
+ "Number of bottom/burn-in layers", "Ξ", 0, ushort.MaxValue, 1, 0);
+
+ public static PrintParameterModifier TransitionLayerCount { get; } = new("Transition layers",
+ "Number of fade/transition layers", "Ξ", 0, ushort.MaxValue, 1, 0);
+
+ public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off delay",
+ "Total motor movement time + rest time to wait before cure a new bottom layer", "s");
+
+ public static PrintParameterModifier LightOffDelay { get; } = new("Light-off delay",
+ "Total motor movement time + rest time to wait before cure a new layer", "s");
+
+ public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new("Bottom wait before cure",
+ "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print",
+ "s");
+
+ public static PrintParameterModifier WaitTimeBeforeCure { get; } = new("Wait before cure",
+ "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
+
+ public static PrintParameterModifier BottomExposureTime { get; } =
+ new("Bottom exposure time", "Bottom layers exposure time", "s", 0.1M);
+
+ public static PrintParameterModifier ExposureTime { get; } =
+ new("Exposure time", "Normal layers exposure time", "s", 0.1M);
+
+ public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure",
+ "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print",
+ "s");
+
+ public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure",
+ "Time to wait/rest after cure a new layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
+
+ public static PrintParameterModifier BottomLiftHeight { get; } =
+ new("Bottom lift height", "Lift/peel height between bottom layers", "mm");
+
+ public static PrintParameterModifier LiftHeight { get; } =
+ new("Lift height", @"Lift/peel height between layers", "mm");
+
+ public static PrintParameterModifier BottomLiftSpeed { get; } = new("Bottom lift speed",
+ "Lift speed of bottom layers", "mm/min", 10, 5000, 5);
+
+ public static PrintParameterModifier LiftSpeed { get; } =
+ new("Lift speed", "Lift speed of normal layers", "mm/min", 10, 5000, 5);
+
+ public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height",
+ "Second lift/peel height between bottom layers", "mm");
+
+ public static PrintParameterModifier LiftHeight2 { get; } =
+ new("2) Lift height", @"Second lift/peel height between layers", "mm");
+
+ public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed",
+ "Lift speed of bottom layers for the second lift sequence (TSMC)", "mm/min", 0, 5000, 5);
- public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Second lift/peel height between bottom layers", "mm");
- public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm");
+ public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed",
+ "Lift speed of normal layers for the second lift sequence (TSMC)", "mm/min", 0, 5000, 5);
- public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", "Lift speed of bottom layers for the second lift sequence (TSMC)", "mm/min", 0, 5000, 5);
- public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", "Lift speed of normal layers for the second lift sequence (TSMC)", "mm/min", 0, 5000, 5);
+ public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift",
+ "Time to wait/rest after a lift/peel sequence on bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift",
+ "s");
- public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence on bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
- public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence on layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
-
- public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Down speed from lift height to next bottom layer cure position", "mm/min", 10, 5000, 5);
- public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
+ public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift",
+ "Time to wait/rest after a lift/peel sequence on layers\nChitubox: Rest after lift\nLychee: Wait after lift",
+ "s");
- public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", "Slow retract height of bottom layers (TSMC)", "mm");
- public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", "Slow retract height of normal layers (TSMC)", "mm");
- public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", "Slow retract speed of bottom layers (TSMC)", "mm/min", 0, 5000, 5);
- public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", "Slow retract speed of normal layers (TSMC)", "mm/min", 0, 5000, 5);
+ public static PrintParameterModifier BottomRetractSpeed { get; } = new("Bottom retract speed",
+ "Down speed from lift height to next bottom layer cure position", "mm/min", 10, 5000, 5);
- public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0);
- public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0);
+ public static PrintParameterModifier RetractSpeed { get; } = new("Retract speed",
+ "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
+
+ public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height",
+ "Slow retract height of bottom layers (TSMC)", "mm");
+
+ public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height",
+ "Slow retract height of normal layers (TSMC)", "mm");
+
+ public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed",
+ "Slow retract speed of bottom layers (TSMC)", "mm/min", 0, 5000, 5);
+
+ public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed",
+ "Slow retract speed of normal layers (TSMC)", "mm/min", 0, 5000, 5);
+
+ public static PrintParameterModifier BottomLightPWM { get; } = new("Bottom light PWM",
+ "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0);
+
+ public static PrintParameterModifier LightPWM { get; } =
+ new("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0);
/*public static PrintParameterModifier[] Parameters = {
BottomLayerCount,
@@ -266,6 +320,7 @@ public class PrintParameterModifier
BottomLightPWM,
LightPWM
};*/
+
#endregion
#region Properties
@@ -325,10 +380,13 @@ public decimal Value
/// Gets if the value has changed
///
public bool HasChanged => OldValue != NewValue;
+
#endregion
#region Constructor
- public PrintParameterModifier(string name, string? description = null, string? valueUnit = null, decimal minimum = 0, decimal maximum = 1000, decimal increment = 0.5m, byte decimalPlates = 2)
+
+ public PrintParameterModifier(string name, string? description = null, string? valueUnit = null,
+ decimal minimum = 0, decimal maximum = 1000, decimal increment = 0.5m, byte decimalPlates = 2)
{
Name = name;
Description = description ?? $"Modify '{name}'";
@@ -338,6 +396,7 @@ public PrintParameterModifier(string name, string? description = null, string? v
Increment = decimalPlates == 0 ? Math.Max(1, increment) : increment;
DecimalPlates = decimalPlates;
}
+
#endregion
#region Overrides
@@ -352,7 +411,7 @@ public override bool Equals(object? obj)
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
- return Equals((PrintParameterModifier) obj);
+ return Equals((PrintParameterModifier)obj);
}
public override int GetHashCode()
@@ -362,52 +421,56 @@ public override int GetHashCode()
public override string ToString()
{
- return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}, {nameof(DecimalPlates)}: {DecimalPlates}, {nameof(OldValue)}: {OldValue}, {nameof(NewValue)}: {NewValue}, {nameof(HasChanged)}: {HasChanged}";
+ return
+ $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}, {nameof(DecimalPlates)}: {DecimalPlates}, {nameof(OldValue)}: {OldValue}, {nameof(NewValue)}: {NewValue}, {nameof(HasChanged)}: {HasChanged}";
}
public PrintParameterModifier Clone()
{
return (PrintParameterModifier)MemberwiseClone();
}
+
#endregion
}
+
#endregion
#region Static Methods
+
///
/// Gets the available formats to process
///
public static FileFormat[] AvailableFormats { get; } =
{
- new SL1File(), // Prusa SL1
+ new SL1File(), // Prusa SL1
new ChituboxZipFile(), // Zip
new ChituboxFile(), // cbddlp, cbt, photon
new CTBEncryptedFile(), // encrypted ctb
new PhotonSFile(), // photons
new PHZFile(), // phz
- new PhotonWorkshopFile(), // PSW
- new CWSFile(), // CWS
+ new PhotonWorkshopFile(), // PSW
+ new CWSFile(), // CWS
new AnetFile(), // Anet N4, N7
- new LGSFile(), // LGS, LGS30
- new VDAFile(), // VDA
- new VDTFile(), // VDT
+ new LGSFile(), // LGS, LGS30
+ new VDAFile(), // VDA
+ new VDTFile(), // VDT
//new CXDLPv1File(), // Creality Box v1
- new CXDLPFile(), // Creality Box
- new CXDLPv4File(), // Creality Box
+ new CXDLPFile(), // Creality Box
+ new CXDLPv4File(), // Creality Box
new FDGFile(), // fdg
new GooFile(), // goo
- new ZCodeFile(), // zcode
- new JXSFile(), // jxs
- new ZCodexFile(), // zcodex
- new MDLPFile(), // MKS v1
- new GR1File(), // GR1 Workshop
+ new ZCodeFile(), // zcode
+ new JXSFile(), // jxs
+ new ZCodexFile(), // zcodex
+ new MDLPFile(), // MKS v1
+ new GR1File(), // GR1 Workshop
new FlashForgeSVGXFile(), // SVGX
new QDTFile(), // QDT
- new OSLAFile(), // OSLA
- new OSFFile(), // OSF
- new UVJFile(), // UVJ
- new GenericZIPFile(), // Generic zip files
- new ImageFile(), // images
+ new OSLAFile(), // OSLA
+ new OSFFile(), // OSF
+ new UVJFile(), // UVJ
+ new GenericZIPFile(), // Generic zip files
+ new ImageFile(), // images
};
public static string AllSlicerFiles => AvailableFormats.Aggregate("All slicer files|",
@@ -432,12 +495,12 @@ public static List>> AllFileFiltersAvalonia
{
new("All slicer files", new List())
};
-
+
foreach (var format in AvailableFormats)
{
foreach (var fileExtension in format.FileExtensions)
{
- if(!fileExtension.IsVisibleOnFileFilters) continue;
+ if (!fileExtension.IsVisibleOnFileFilters) continue;
result[0].Value.Add(fileExtension.Extension);
result.Add(new KeyValuePair>(fileExtension.Description, new List
{
@@ -448,7 +511,7 @@ public static List>> AllFileFiltersAvalonia
return result;
}
-
+
}
public static List AllFileExtensions
@@ -460,17 +523,21 @@ public static List AllFileExtensions
{
extensions.AddRange(slicerFile.FileExtensions);
}
+
return extensions;
}
}
- public static List AllFileExtensionsString => (from slicerFile in AvailableFormats from extension in slicerFile.FileExtensions select extension.Extension).ToList();
+ public static List AllFileExtensionsString => (from slicerFile in AvailableFormats
+ from extension in slicerFile.FileExtensions
+ select extension.Extension).ToList();
///
/// Gets the count of available file extensions
///
- public static byte FileExtensionsCount => AvailableFormats.Aggregate(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length));
+ public static byte FileExtensionsCount => AvailableFormats.Aggregate(0,
+ (current, fileFormat) => (byte)(current + fileFormat.FileExtensions.Length));
///
/// Find by an extension
@@ -488,14 +555,16 @@ public static List AllFileExtensions
/// Number of file formats sharing the input extension
/// True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose
/// object or null if not found
- public static FileFormat? FindByExtensionOrFilePath(string extensionOrFilePath, out byte fileFormatsSharingExt, bool createNewInstance = false)
+ public static FileFormat? FindByExtensionOrFilePath(string extensionOrFilePath, out byte fileFormatsSharingExt,
+ bool createNewInstance = false)
{
fileFormatsSharingExt = 0;
if (string.IsNullOrWhiteSpace(extensionOrFilePath)) return null;
bool isFilePath = false;
// Test for ext first
- var fileFormats = AvailableFormats.Where(fileFormat => fileFormat.IsExtensionValid(extensionOrFilePath)).ToArray();
+ var fileFormats = AvailableFormats.Where(fileFormat => fileFormat.IsExtensionValid(extensionOrFilePath))
+ .ToArray();
fileFormatsSharingExt = (byte)fileFormats.Length;
if (fileFormats.Length == 0) // Extension not found, can be filepath, try to find it
@@ -546,7 +615,8 @@ public static List AllFileExtensions
type += "File";
}
- var fileFormat = AvailableFormats.FirstOrDefault(format => string.Equals(format.GetType().Name, type, StringComparison.OrdinalIgnoreCase));
+ var fileFormat = AvailableFormats.FirstOrDefault(format =>
+ string.Equals(format.GetType().Name, type, StringComparison.OrdinalIgnoreCase));
if (fileFormat is null) return null;
return createNewInstance
? Activator.CreateInstance(fileFormat.GetType()) as FileFormat
@@ -584,7 +654,8 @@ public static List AllFileExtensions
public static FileExtension? FindExtension(string extension)
{
- return AvailableFormats.SelectMany(format => format.FileExtensions).FirstOrDefault(ext => ext.Equals(extension));
+ return AvailableFormats.SelectMany(format => format.FileExtensions)
+ .FirstOrDefault(ext => ext.Equals(extension));
}
public static IEnumerable FindExtensions(string extension)
@@ -596,13 +667,15 @@ public static IEnumerable FindExtensions(string extension)
{
if (filepath is null) return null;
//if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file);
- return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out _);
+ return PathExtensions.GetFileNameStripExtensions(filepath,
+ AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out _);
}
public static string GetFileNameStripExtensions(string filepath, out string strippedExtension)
{
//if (file.EndsWith(TemporaryFileAppend)) file = Path.GetFileNameWithoutExtension(file);
- return PathExtensions.GetFileNameStripExtensions(filepath, AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out strippedExtension);
+ return PathExtensions.GetFileNameStripExtensions(filepath,
+ AllFileExtensionsString.OrderByDescending(s => s.Length).ToList(), out strippedExtension);
}
public static FileFormat? Open(string fileFullPath, FileDecodeType decodeType, OperationProgress? progress = null)
@@ -616,12 +689,14 @@ public static string GetFileNameStripExtensions(string filepath, out string stri
public static FileFormat? Open(string fileFullPath, OperationProgress? progress = null) =>
Open(fileFullPath, FileDecodeType.Full, progress);
- public static Task OpenAsync(string fileFullPath, FileDecodeType decodeType, OperationProgress? progress = null)
+ public static Task OpenAsync(string fileFullPath, FileDecodeType decodeType,
+ OperationProgress? progress = null)
=> Task.Run(() => Open(fileFullPath, decodeType, progress), progress?.Token ?? default);
- public static Task OpenAsync(string fileFullPath, OperationProgress? progress = null) => OpenAsync(fileFullPath, FileDecodeType.Full, progress);
+ public static Task OpenAsync(string fileFullPath, OperationProgress? progress = null) =>
+ OpenAsync(fileFullPath, FileDecodeType.Full, progress);
- public static float RoundDisplaySize(float value) => (float) Math.Round(value, DisplayFloatPrecision);
+ public static float RoundDisplaySize(float value) => (float)Math.Round(value, DisplayFloatPrecision);
public static double RoundDisplaySize(double value) => Math.Round(value, DisplayFloatPrecision);
public static decimal RoundDisplaySize(decimal value) => Math.Round(value, DisplayFloatPrecision);
@@ -657,7 +732,7 @@ public static uint CopyParameters(FileFormat from, FileFormat to)
count++;
}
}
-
+
return count;
}
@@ -857,7 +932,7 @@ or DATATYPE_BGR888
case DATATYPE_RGB888:
span[pixel++] = bytes[i + 2]; // b
span[pixel++] = bytes[i + 1]; // g
- span[pixel++] = bytes[i]; // r
+ span[pixel++] = bytes[i]; // r
i += 3;
break;
case DATATYPE_BGR555:
@@ -899,7 +974,7 @@ or DATATYPE_BGR888
for (; pixel < span.Length; pixel++) // Fill leftovers
{
- span[pixel] = 0;
+ span[pixel] = 0;
}
return mat;
@@ -911,7 +986,8 @@ or DATATYPE_BGR888
public static Mat DecodeImage(string dataType, byte[] bytes, uint resolutionX = 0, uint resolutionY = 0)
=> DecodeImage(dataType, bytes, new Size((int)resolutionX, (int)resolutionY));
- public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endLayerIndex, int iterationsStart, int iterationsEnd, ref bool isFade, out float iterationSteps, out int maxIteration)
+ public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endLayerIndex, int iterationsStart,
+ int iterationsEnd, ref bool isFade, out float iterationSteps, out int maxIteration)
{
iterationSteps = 0;
maxIteration = 0;
@@ -921,7 +997,8 @@ public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endL
maxIteration = Math.Max(iterationsStart, iterationsEnd);
}
- public static int MutateGetIterationVar(bool isFade, int iterationsStart, int iterationsEnd, float iterationSteps, int maxIteration, uint startLayerIndex, uint layerIndex)
+ public static int MutateGetIterationVar(bool isFade, int iterationsStart, int iterationsEnd, float iterationSteps,
+ int maxIteration, uint startLayerIndex, uint layerIndex)
{
if (!isFade) return iterationsStart;
// calculate iterations based on range
@@ -933,86 +1010,89 @@ public static int MutateGetIterationVar(bool isFade, int iterationsStart, int it
return Math.Min(Math.Max(0, iterations), maxIteration);
}
- public static int MutateGetIterationChamfer(uint layerIndex, uint startLayerIndex, uint endLayerIndex, int iterationsStart,
+ public static int MutateGetIterationChamfer(uint layerIndex, uint startLayerIndex, uint endLayerIndex,
+ int iterationsStart,
int iterationsEnd, bool isFade)
{
MutateGetVarsIterationChamfer(startLayerIndex, endLayerIndex, iterationsStart, iterationsEnd, ref isFade,
out float iterationSteps, out int maxIteration);
- return MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration, startLayerIndex, layerIndex);
+ return MutateGetIterationVar(isFade, iterationsStart, iterationsEnd, iterationSteps, maxIteration,
+ startLayerIndex, layerIndex);
}
- ///
- /// Compares file a with file b
- ///
- /// Left file
- /// Right file
- /// True if you also want to compare layers
- /// A list of strict properties to compare
- ///
- public static FileFormatComparison Compare(FileFormat left, FileFormat right, bool compareLayers = true, params string[] onlyProperties)
+ ///
+ /// Compares file a with file b
+ ///
+ /// Left file
+ /// Right file
+ /// True if you also want to compare layers
+ /// A list of strict properties to compare
+ ///
+ public static FileFormatComparison Compare(FileFormat left, FileFormat right, bool compareLayers = true,
+ params string[] onlyProperties)
{
- FileFormatComparison comparison = new();
-
+ FileFormatComparison comparison = new();
+
void CheckAddProperties(object a, object b, uint? layerIndex = null)
- {
- var properties = ReflectionExtensions.GetProperties(a);
- foreach (var aProperty in properties)
- {
- if (aProperty.GetMethod is null || !(aProperty.PropertyType.IsPrimitive ||
- aProperty.PropertyType == typeof(decimal) ||
- aProperty.PropertyType == typeof(string) ||
- aProperty.PropertyType == typeof(Point) ||
- aProperty.PropertyType == typeof(PointF) ||
- aProperty.PropertyType == typeof(Size) ||
- aProperty.PropertyType == typeof(SizeF) ||
- aProperty.PropertyType == typeof(Rectangle) ||
- aProperty.PropertyType == typeof(RectangleF) ||
- aProperty.PropertyType == typeof(DateTime) ||
- aProperty.PropertyType == typeof(TimeSpan) ||
- aProperty.PropertyType == typeof(TimeOnly)
- )) continue;
-
- if (onlyProperties.Length > 0 && !onlyProperties.Contains(aProperty.Name)) continue;
-
- var bProperty = b.GetType().GetProperty(aProperty.Name);
- if (bProperty is null) continue;
-
- var aValue = aProperty.GetValue(a);
- var bValue = bProperty.GetValue(b);
-
- if (Equals(aValue, bValue)) continue;
-
- if (layerIndex is null)
- {
- comparison.Global.Add(new ComparisonItem(aProperty.Name, aValue, bValue));
- }
- else
- {
- if (!comparison.Layers.TryGetValue(layerIndex.Value, out var list))
- {
- list = new List();
+ {
+ var properties = ReflectionExtensions.GetProperties(a);
+ foreach (var aProperty in properties)
+ {
+ if (aProperty.GetMethod is null || !(aProperty.PropertyType.IsPrimitive ||
+ aProperty.PropertyType == typeof(decimal) ||
+ aProperty.PropertyType == typeof(string) ||
+ aProperty.PropertyType == typeof(Point) ||
+ aProperty.PropertyType == typeof(PointF) ||
+ aProperty.PropertyType == typeof(Size) ||
+ aProperty.PropertyType == typeof(SizeF) ||
+ aProperty.PropertyType == typeof(Rectangle) ||
+ aProperty.PropertyType == typeof(RectangleF) ||
+ aProperty.PropertyType == typeof(DateTime) ||
+ aProperty.PropertyType == typeof(TimeSpan) ||
+ aProperty.PropertyType == typeof(TimeOnly)
+ )) continue;
+
+ if (onlyProperties.Length > 0 && !onlyProperties.Contains(aProperty.Name)) continue;
+
+ var bProperty = b.GetType().GetProperty(aProperty.Name);
+ if (bProperty is null) continue;
+
+ var aValue = aProperty.GetValue(a);
+ var bValue = bProperty.GetValue(b);
+
+ if (Equals(aValue, bValue)) continue;
+
+ if (layerIndex is null)
+ {
+ comparison.Global.Add(new ComparisonItem(aProperty.Name, aValue, bValue));
+ }
+ else
+ {
+ if (!comparison.Layers.TryGetValue(layerIndex.Value, out var list))
+ {
+ list = new List();
comparison.Layers.Add(layerIndex.Value, list);
- }
-
- list.Add(new ComparisonItem(aProperty.Name, aValue, bValue));
- }
- }
+ }
+
+ list.Add(new ComparisonItem(aProperty.Name, aValue, bValue));
+ }
+ }
}
CheckAddProperties(left, right);
- if (compareLayers)
- {
- var commonLayers = Math.Min(left.LayerCount, right.LayerCount);
- //var diffLayers = Math.Abs(a.Count - b.Count);
- for (uint layerIndex = 0; layerIndex < commonLayers; layerIndex++)
- {
+ if (compareLayers)
+ {
+ var commonLayers = Math.Min(left.LayerCount, right.LayerCount);
+ //var diffLayers = Math.Abs(a.Count - b.Count);
+ for (uint layerIndex = 0; layerIndex < commonLayers; layerIndex++)
+ {
CheckAddProperties(left[layerIndex], right[layerIndex], layerIndex);
- }
- }
+ }
+ }
- return comparison;
+ return comparison;
}
///
@@ -1028,10 +1108,11 @@ public static bool IsFileNameValid(string filename, out string errorMessage, boo
var invalidFileNameChars = Path.GetInvalidFileNameChars();
var invalidChars = filename.Where(c => invalidFileNameChars.Contains(c)).Distinct();
-
+
if (invalidChars.Any())
{
- errorMessage = $"The file \"{filename}\" have invalid characters.\nThe following in-name characters are forbidden: {string.Join(", ", invalidChars)}.";
+ errorMessage =
+ $"The file \"{filename}\" have invalid characters.\nThe following in-name characters are forbidden: {string.Join(", ", invalidChars)}.";
return false;
}
@@ -1040,7 +1121,8 @@ public static bool IsFileNameValid(string filename, out string errorMessage, boo
var nonAscii = filename.Where(c => !char.IsAscii(c)).Distinct();
if (nonAscii.Any())
{
- errorMessage = $"The file \"{filename}\" have non-ASCII characters.\nThe following in-name characters are not allowed: {string.Join(", ", nonAscii)}.";
+ errorMessage =
+ $"The file \"{filename}\" have non-ASCII characters.\nThe following in-name characters are not allowed: {string.Join(", ", nonAscii)}.";
return false;
}
}
@@ -1058,9 +1140,11 @@ public static bool IsFileNameValid(string filename, bool onlyAsciiCharacters = f
{
return IsFileNameValid(filename, out _, onlyAsciiCharacters);
}
+
#endregion
#region Members
+
public object Mutex = new();
private string? _fileFullPath;
@@ -1095,7 +1179,7 @@ public static bool IsFileNameValid(string filename, bool onlyAsciiCharacters = f
private float _bottomWaitTimeAfterCure;
private float _waitTimeAfterCure;
-
+
private float _bottomLiftHeight = DefaultBottomLiftHeight;
private float _liftHeight = DefaultLiftHeight;
private float _bottomLiftSpeed = DefaultBottomLiftSpeed;
@@ -1131,7 +1215,7 @@ public static bool IsFileNameValid(string filename, bool onlyAsciiCharacters = f
private Rectangle _boundingRectangle = Rectangle.Empty;
- private readonly Timer _queueTimerPrintTime = new(QueueTimerPrintTime){AutoReset = false};
+ private readonly Timer _queueTimerPrintTime = new(QueueTimerPrintTime) { AutoReset = false };
#endregion
@@ -1143,16 +1227,20 @@ public static bool IsFileNameValid(string filename, bool onlyAsciiCharacters = f
/// Gets the file format type
///
public abstract FileFormatType FileType { get; }
-
+
///
/// Gets the manufacturing process this file and printer uses
///
- public virtual PrinterManufacturingProcess ManufacturingProcess => MachineName.Contains(" DLP", StringComparison.OrdinalIgnoreCase) ? PrinterManufacturingProcess.DLP : PrinterManufacturingProcess.mSLA;
+ public virtual PrinterManufacturingProcess ManufacturingProcess =>
+ MachineName.Contains(" DLP", StringComparison.OrdinalIgnoreCase)
+ ? PrinterManufacturingProcess.DLP
+ : PrinterManufacturingProcess.mSLA;
///
/// Gets the layer image data type used on this file format
///
- public virtual FileImageType LayerImageType => FileType == FileFormatType.Archive ? FileImageType.Png8 : FileImageType.Custom;
+ public virtual FileImageType LayerImageType =>
+ FileType == FileFormatType.Archive ? FileImageType.Png8 : FileImageType.Custom;
///
/// Gets the group name under convert menu to group all extensions, set to null to not group items
@@ -1168,7 +1256,7 @@ public static bool IsFileNameValid(string filename, bool onlyAsciiCharacters = f
/// The speed unit used by this file format in his internal data
///
public virtual SpeedUnit FormatSpeedUnit => CoreSpeedUnit;
-
+
///
/// Gets the available
///
@@ -1184,19 +1272,22 @@ public static bool IsFileNameValid(string filename, bool onlyAsciiCharacters = f
///
///
/// True if exists, otherwise false
- public bool HavePrintParameterModifier(PrintParameterModifier modifier) => PrintParameterModifiers.Contains(modifier);
+ public bool HavePrintParameterModifier(PrintParameterModifier modifier) =>
+ PrintParameterModifiers.Contains(modifier);
///
/// Checks if a exists on layer parameters
///
///
/// True if exists, otherwise false
- public bool HaveLayerParameterModifier(PrintParameterModifier modifier) => SupportPerLayerSettings && PrintParameterPerLayerModifiers.Contains(modifier);
+ public bool HaveLayerParameterModifier(PrintParameterModifier modifier) =>
+ SupportPerLayerSettings && PrintParameterPerLayerModifiers.Contains(modifier);
///
/// Gets the file filter for open and save dialogs
///
- public string FileFilter {
+ public string FileFilter
+ {
get
{
var result = string.Empty;
@@ -1207,6 +1298,7 @@ public string FileFilter {
{
result += '|';
}
+
result += fileExt.Filter;
}
@@ -1217,8 +1309,10 @@ public string FileFilter {
///
/// Gets all valid file extensions for Avalonia file dialog
///
- public List>> FileFilterAvalonia
- => FileExtensions.Select(fileExt => new KeyValuePair>(fileExt.Description, new List {fileExt.Extension})).ToList();
+ public List>> FileFilterAvalonia
+ => FileExtensions.Select(fileExt =>
+ new KeyValuePair>(fileExt.Description, new List { fileExt.Extension }))
+ .ToList();
///
/// Gets all valid file extensions in "*.extension1;*.extension2" format
@@ -1235,6 +1329,7 @@ public string FileFilterExtensionsOnly
{
result += "; ";
}
+
result += $"*.{fileExt.Extension}";
}
@@ -1265,7 +1360,7 @@ public string? FileFullPath
get => _fileFullPath;
set
{
- if(!RaiseAndSetIfChanged(ref _fileFullPath, value)) return;
+ if (!RaiseAndSetIfChanged(ref _fileFullPath, value)) return;
RaisePropertyChanged(DirectoryPath);
RaisePropertyChanged(Filename);
RaisePropertyChanged(FilenameNoExt);
@@ -1288,8 +1383,9 @@ public string? FileFullPath
///
/// Returns the file name without the extension(s)
///
- public string? FilenameStripExtensions => FileFullPath is null ? null : GetFileNameStripExtensions(FileFullPath, out _);
-
+ public string? FilenameStripExtensions =>
+ FileFullPath is null ? null : GetFileNameStripExtensions(FileFullPath, out _);
+
///
/// Returns the file extension. The returned value includes the period (".") character of the
/// extension except when you have a terminal period when you get string.Empty, such as ".exe" or ".cpp".
@@ -1308,7 +1404,7 @@ public string? FileAbsoluteExtension
{
get
{
- if(FileFullPath is null) return null;
+ if (FileFullPath is null) return null;
GetFileNameStripExtensions(FileFullPath, out var ext);
return ext;
}
@@ -1342,7 +1438,8 @@ public string? FileAbsoluteExtension
///
///
///
- public uint[] GetAvailableVersionsForFileName(string? fileName) => GetAvailableVersionsForExtension(Path.GetExtension(fileName));
+ public uint[] GetAvailableVersionsForFileName(string? fileName) =>
+ GetAvailableVersionsForExtension(Path.GetExtension(fileName));
///
/// Gets the default version to use in this file when not setting the version
@@ -1443,7 +1540,8 @@ public Layer[] Layers
_layers[layerIndex].Index = layerIndex;
_layers[layerIndex].SlicerFile = this;
- if (layerIndex >= oldLayerCount || layerIndex < oldLayerCount && !_layers[layerIndex].Equals(oldLayers[layerIndex]))
+ if (layerIndex >= oldLayerCount || layerIndex < oldLayerCount &&
+ !_layers[layerIndex].Equals(oldLayers[layerIndex]))
{
// Marks as modified only if layer image changed on this index
_layers[layerIndex].IsModified = true;
@@ -1473,6 +1571,27 @@ public Layer[] Layers
}
}
+ ///
+ /// Gets the layers cache/memory occupation size in bytes
+ ///
+ public long LayersCacheSize {
+ get
+ {
+ long size = 0;
+ for (int i = 0; i < LayerCount; i++)
+ {
+ if (this[i] is null) continue;
+ size += this[i].CompressedMat.Length;
+ }
+ return size;
+ }
+ }
+
+ ///
+ /// Gets the layers cache/memory occupation size in readable string format
+ ///
+ public string LayersCacheSizeString => SizeExtensions.SizeSuffix(LayersCacheSize);
+
///
/// First layer index, this is always 0
///
@@ -5644,10 +5763,13 @@ public void SaveAs(string? filePath = null, OperationProgress? progress = null)
/// Changes the compression method of all layers to a new method
///
/// The new method to change to
- ///
- public void ChangeLayersCompressionMethod(LayerCompressionCodec newCodec, OperationProgress? progress = null)
+ public void ChangeLayersCompressionMethod(LayerCompressionCodec newCodec)
{
- progress ??= new OperationProgress($"Changing layers compression codec to {newCodec}");
+ for (int i = 0; i < LayerCount; i++)
+ {
+ this[i].CompressionCodec = newCodec;
+ }
+ /*progress ??= new OperationProgress($"Changing layers compression codec to {newCodec}");
progress.Reset("Layers", LayerCount);
Parallel.ForEach(this, CoreSettings.GetParallelOptions(progress), layer =>
@@ -5655,7 +5777,7 @@ public void ChangeLayersCompressionMethod(LayerCompressionCodec newCodec, Operat
progress.PauseIfRequested();
layer.CompressionCodec = newCodec;
progress.LockAndIncrement();
- });
+ });*/
}
///
@@ -6195,17 +6317,6 @@ public void Reallocate(uint newLayerCount, bool initBlack = false, bool resetLay
{
Layers = _layers;
}, resetLayerProperties, resetLayerProperties);
-
- if (differenceLayerCount > 0 && initBlack)
- {
- using var blackMat = CreateMat();
- var layer = new Layer(0, blackMat, this);
-
- for (var layerIndex = oldLayerCount; layerIndex < newLayerCount; layerIndex++)
- {
- this[layerIndex].CompressedBytes = layer.CompressedBytes.ToArray();
- }
- }
}
///
@@ -6239,16 +6350,6 @@ public void ReallocateInsert(uint insertAtLayerIndex, uint layerCount, bool init
Layers = newLayers;
}, resetLayerProperties, resetLayerProperties);
- if (initBlack)
- {
- using var blackMat = CreateMat();
- var layer = new Layer(0, blackMat, this);
- for (var layerIndex = insertAtLayerIndex; layerIndex < rightDestinationIndex; layerIndex++)
- {
- this[layerIndex].CompressedBytes = layer.CompressedBytes.ToArray();
- }
- }
-
if (!resetLayerProperties && fixPositionZ)
{
var addedDistance = LayerHeight + this[rightDestinationIndex-1].PositionZ - this[insertAtLayerIndex].PositionZ;
diff --git a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs
index 67a3855d..a7c0b42b 100644
--- a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs
+++ b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs
@@ -621,79 +621,84 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.For(0, LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- var mat = EmguExtensions.InitMat(Resolution);
- var group = SVGDocument.Groups.FirstOrDefault(g => g.Id == $"layer-{layerIndex}");
-
- if (group is not null)
+ using (var mat = EmguExtensions.InitMat(Resolution))
{
- var pointsOfPoints = new List();
- var points = new List();
- foreach (var path in group.Paths)
- {
- progress.PauseOrCancelIfRequested();
- var spaceSplit = path.Value.Split(' ',
- StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+ var group = SVGDocument.Groups.FirstOrDefault(g => g.Id == $"layer-{layerIndex}");
- for (int i = 0; i < spaceSplit.Length; i++)
+ if (group is not null)
+ {
+ var pointsOfPoints = new List();
+ var points = new List();
+ foreach (var path in group.Paths)
{
- if (spaceSplit[i] == "M")
+ progress.PauseOrCancelIfRequested();
+ var spaceSplit = path.Value.Split(' ',
+ StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
+
+ for (int i = 0; i < spaceSplit.Length; i++)
{
- if (points.Count > 0)
+ if (spaceSplit[i] == "M")
{
- pointsOfPoints.Add(points.ToArray());
- points.Clear();
- }
+ if (points.Count > 0)
+ {
+ pointsOfPoints.Add(points.ToArray());
+ points.Clear();
+ }
- continue;
- }
+ continue;
+ }
- if (spaceSplit[i] == "Z")
- {
- if (points.Count > 0)
+ if (spaceSplit[i] == "Z")
{
- pointsOfPoints.Add(points.ToArray());
- points.Clear();
+ if (points.Count > 0)
+ {
+ pointsOfPoints.Add(points.ToArray());
+ points.Clear();
+ }
+
+ continue;
}
- continue;
- }
+ if (spaceSplit[i].Length == 1 && !char.IsDigit(spaceSplit[i][0]))
+ continue; // Ignore any other not processed 1 char that's not a digit (L)
- if (spaceSplit[i].Length == 1 && !char.IsDigit(spaceSplit[i][0]))
- continue; // Ignore any other not processed 1 char that's not a digit (L)
+ if (i + 1 >= spaceSplit.Length) break; // No more to see
- if (i + 1 >= spaceSplit.Length) break; // No more to see
+ if (!float.TryParse(spaceSplit[i], NumberStyles.Float, CultureInfo.InvariantCulture,
+ out var mmX)) continue;
+ if (!float.TryParse(spaceSplit[++i], NumberStyles.Float, CultureInfo.InvariantCulture,
+ out var mmY)) continue;
- if (!float.TryParse(spaceSplit[i], NumberStyles.Float, CultureInfo.InvariantCulture, out var mmX)) continue;
- if (!float.TryParse(spaceSplit[++i], NumberStyles.Float, CultureInfo.InvariantCulture, out var mmY)) continue;
+ var mmAbsX = Math.Clamp(halfDisplay.Width + mmX, 0, DisplayWidth);
+ var mmAbsY = Math.Clamp(halfDisplay.Height + mmY, 0, DisplayHeight);
- var mmAbsX = Math.Clamp(halfDisplay.Width + mmX, 0, DisplayWidth);
- var mmAbsY = Math.Clamp(halfDisplay.Height + mmY, 0, DisplayHeight);
+ int x = (int)(mmAbsX * ppmm.Width);
+ int y = (int)(mmAbsY * ppmm.Height);
- int x = (int)(mmAbsX * ppmm.Width);
- int y = (int)(mmAbsY * ppmm.Height);
+ points.Add(new Point(x, y));
+ }
- points.Add(new Point(x, y));
+ if (points.Count > 0) // Leftovers, still this should never happen!
+ {
+ pointsOfPoints.Add(points.ToArray());
+ points.Clear();
+ }
}
- if (points.Count > 0) // Leftovers, still this should never happen!
+ if (pointsOfPoints.Count > 0)
{
- pointsOfPoints.Add(points.ToArray());
- points.Clear();
+ using var vecPoints = new VectorOfVectorOfPoint(pointsOfPoints.ToArray());
+ CvInvoke.DrawContours(mat, vecPoints, -1, EmguExtensions.WhiteColor, -1);
}
- }
- if (pointsOfPoints.Count > 0)
- {
- using var vecPoints = new VectorOfVectorOfPoint(pointsOfPoints.ToArray());
- CvInvoke.DrawContours(mat, vecPoints, -1, EmguExtensions.WhiteColor, -1);
}
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
}
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
progress.LockAndIncrement();
});
}
diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs
index 9e604444..108b26d1 100644
--- a/UVtools.Core/FileFormats/GR1File.cs
+++ b/UVtools.Core/FileFormats/GR1File.cs
@@ -370,7 +370,7 @@ protected override void EncodeInternally(OperationProgress progress)
var layer = this[layerIndex];
using (var mat = layer.LayerMat)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
layerBytes[layerIndex] = new();
@@ -471,21 +471,23 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- 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);
- }
+ 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]);
- linesBytes[layerIndex] = null!;
+ CvInvoke.Line(mat, new Point(startX, startY), new Point(startX, endY), EmguExtensions.WhiteColor);
+ }
+
+ linesBytes[layerIndex] = null!;
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/FileFormats/GooFile.cs b/UVtools.Core/FileFormats/GooFile.cs
index a565356d..ff939a65 100644
--- a/UVtools.Core/FileFormats/GooFile.cs
+++ b/UVtools.Core/FileFormats/GooFile.cs
@@ -366,7 +366,7 @@ public byte[] EncodeImage(Mat image, uint layerIndex, bool useColorDifferenceCom
byte currentColor = 0;
uint stride = 0;
byte checkSum = 0;
- var span = image.GetDataByteSpan();
+ var span = image.GetDataByteReadOnlySpan();
void AddRep()
{
@@ -976,7 +976,12 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- _layers[layerIndex] = new Layer((uint) layerIndex, LayersDefinition[layerIndex].DecodeImage((uint) layerIndex), this);
+
+ using (var mat = LayersDefinition[layerIndex].DecodeImage((uint)layerIndex))
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
+
progress.LockAndIncrement();
});
}
diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs
index 402ad588..28b3886d 100644
--- a/UVtools.Core/FileFormats/LGSFile.cs
+++ b/UVtools.Core/FileFormats/LGSFile.cs
@@ -573,8 +573,11 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = layerData[layerIndex].Decode();
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
+ using (var mat = layerData[layerIndex].Decode())
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs
index 4a76ccd7..fe961060 100644
--- a/UVtools.Core/FileFormats/MDLPFile.cs
+++ b/UVtools.Core/FileFormats/MDLPFile.cs
@@ -332,7 +332,7 @@ protected override void EncodeInternally(OperationProgress progress)
var layer = this[layerIndex];
using (var mat = layer.LayerMat)
{
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
layerBytes[layerIndex] = new();
@@ -348,8 +348,7 @@ protected override void EncodeInternally(OperationProgress progress)
if (span[pos] < 128) // Black pixel
{
if (startY == -1) continue; // Keep ignoring
- layerBytes[layerIndex]
- .AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
+ layerBytes[layerIndex].AddRange(LayerLine.GetBytes((ushort)startY, (ushort)(y - 1), (ushort)x));
startY = -1;
lineCount++;
}
diff --git a/UVtools.Core/FileFormats/OSFFile.cs b/UVtools.Core/FileFormats/OSFFile.cs
index cdcbbab3..40b6acb1 100644
--- a/UVtools.Core/FileFormats/OSFFile.cs
+++ b/UVtools.Core/FileFormats/OSFFile.cs
@@ -846,8 +846,12 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = layerDef[layerIndex].DecodeImage(this);
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
+ using (var mat = layerDef[layerIndex].DecodeImage(this))
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
+
layerDef[layerIndex].EncodedRle = null!;
progress.LockAndIncrement();
diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs
index 56363bbf..6e81b01b 100644
--- a/UVtools.Core/FileFormats/OSLAFile.cs
+++ b/UVtools.Core/FileFormats/OSLAFile.cs
@@ -673,10 +673,12 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = DecodeImage(HeaderSettings.LayerDataType, layerBytes[layerIndex], Resolution);
- layerBytes[layerIndex] = null!; // Clean
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ using (var mat = DecodeImage(HeaderSettings.LayerDataType, layerBytes[layerIndex], Resolution))
+ {
+ layerBytes[layerIndex] = null!; // Clean
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs
index 443b656b..b9c4aa8d 100644
--- a/UVtools.Core/FileFormats/PHZFile.cs
+++ b/UVtools.Core/FileFormats/PHZFile.cs
@@ -1107,8 +1107,12 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex);
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
+ using (var mat = LayersDefinitions[layerIndex].Decode((uint)layerIndex))
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
+
progress.LockAndIncrement();
});
}
diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs
index 237185c6..42de5ddd 100644
--- a/UVtools.Core/FileFormats/PhotonSFile.cs
+++ b/UVtools.Core/FileFormats/PhotonSFile.cs
@@ -495,8 +495,12 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = layersDefinitions[layerIndex].Decode();
- _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+
+ using (var mat = layersDefinitions[layerIndex].Decode())
+ {
+ _layers[layerIndex] = new Layer((uint)layerIndex, mat, this);
+ }
+
progress.LockAndIncrement();
});
}
diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
index 42b1cd07..798ffee9 100644
--- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
+++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
@@ -2230,13 +2230,16 @@ protected override void DecodeInternally(OperationProgress progress)
Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
- using var mat = LayersDefinition[layerIndex].Decode(this);
- this[layerIndex] = new Layer((uint)layerIndex, mat, this)
+
+ using (var mat = LayersDefinition[layerIndex].Decode(this))
{
- PositionZ = LayersDefinition.Layers
- .Where((_, i) => i <= layerIndex)
- .Sum(def => def.LayerHeight),
- };
+ this[layerIndex] = new Layer((uint)layerIndex, mat, this)
+ {
+ PositionZ = LayersDefinition.Layers
+ .Where((_, i) => i <= layerIndex)
+ .Sum(def => def.LayerHeight),
+ };
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/FileFormats/QDTFile.cs b/UVtools.Core/FileFormats/QDTFile.cs
index 62c9cad8..7bace5be 100644
--- a/UVtools.Core/FileFormats/QDTFile.cs
+++ b/UVtools.Core/FileFormats/QDTFile.cs
@@ -295,62 +295,64 @@ void ProcessBatch()
{
progress.PauseIfRequested();
- using var mat = CreateMat();
-
- if (layerCoords[layerIndex].Count > 0)
+ using (var mat = CreateMat())
{
- Guard.IsEqualTo((int)layerCoords[layerIndex][0][2], 0);
- int zerosInRow = 1;
-
- var coords = layerCoords[layerIndex][0];
-
- var startPoint = new Point(coords[0], coords[1]);
- var endPoint = startPoint;
- var mirror = layerIndex % 2 != 0;
-
- for (var i = 1; i < layerCoords[layerIndex].Count; i++)
+ if (layerCoords[layerIndex].Count > 0)
{
- coords = layerCoords[layerIndex][i];
- if (coords[2] == 0)
- {
- zerosInRow++;
- continue;
- }
-
- Guard.IsBetweenOrEqualTo(zerosInRow, 1, 2);
+ Guard.IsEqualTo((int)layerCoords[layerIndex][0][2], 0);
+ int zerosInRow = 1;
- var lastCoords = layerCoords[layerIndex][i - 1];
+ var coords = layerCoords[layerIndex][0];
- if (zerosInRow == 1)
- {
- if (i > 1) startPoint = new Point(coords[0], endPoint.Y + lastCoords[1]);
- }
- else
- {
- startPoint = new Point(coords[0], lastCoords[1]);
- }
+ var startPoint = new Point(coords[0], coords[1]);
+ var endPoint = startPoint;
- endPoint = new Point(coords[0], startPoint.Y + coords[1]);
+ var mirror = layerIndex % 2 != 0;
- if (mirror)
+ for (var i = 1; i < layerCoords[layerIndex].Count; i++)
{
- CvInvoke.Line(mat,
- startPoint with { X = (int)(ResolutionX - startPoint.X + 1) },
- endPoint with { X = (int)(ResolutionX - endPoint.X + 1) },
- EmguExtensions.WhiteColor);
+ coords = layerCoords[layerIndex][i];
+ if (coords[2] == 0)
+ {
+ zerosInRow++;
+ continue;
+ }
+
+ Guard.IsBetweenOrEqualTo(zerosInRow, 1, 2);
+
+ var lastCoords = layerCoords[layerIndex][i - 1];
+
+ if (zerosInRow == 1)
+ {
+ if (i > 1) startPoint = new Point(coords[0], endPoint.Y + lastCoords[1]);
+ }
+ else
+ {
+ startPoint = new Point(coords[0], lastCoords[1]);
+ }
+
+ endPoint = new Point(coords[0], startPoint.Y + coords[1]);
+
+ if (mirror)
+ {
+ CvInvoke.Line(mat,
+ startPoint with { X = (int)(ResolutionX - startPoint.X + 1) },
+ endPoint with { X = (int)(ResolutionX - endPoint.X + 1) },
+ EmguExtensions.WhiteColor);
+ }
+ else
+ {
+ CvInvoke.Line(mat, startPoint, endPoint, EmguExtensions.WhiteColor);
+ }
+
+ zerosInRow = 0;
}
- else
- {
- CvInvoke.Line(mat, startPoint, endPoint, EmguExtensions.WhiteColor);
- }
-
- zerosInRow = 0;
}
- }
- layerCoords[layerIndex] = null!; // Clean
- _layers[layerIndex].LayerMat = mat;
+ layerCoords[layerIndex] = null!; // Clean
+ _layers[layerIndex].LayerMat = mat;
+ }
progress.LockAndIncrement();
});
diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs
index 1e512f9c..ce5d37a8 100644
--- a/UVtools.Core/Layers/Layer.cs
+++ b/UVtools.Core/Layers/Layer.cs
@@ -6,18 +6,13 @@
* of this license document, but changing it is not allowed.
*/
-using CommunityToolkit.Diagnostics;
using Emgu.CV;
using Emgu.CV.CvEnum;
-using K4os.Compression.LZ4;
using System;
-using System.Buffers;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
-using System.IO.Compression;
-using System.Linq;
using System.Text.Json.Serialization;
using System.Xml.Serialization;
using UVtools.Core.EmguCV;
@@ -34,11 +29,13 @@ public enum LayerCompressionCodec : byte
{
[Description("PNG: Compression=High | Speed=Slow (Use with low RAM)")]
Png,
- [Description("GZip: Compression=Medium | Speed=Medium (Optimal)")]
+ [Description("GZip: Compression=Medium | Speed=Medium")]
GZip,
- [Description("Deflate: Compression=Medium | Speed=Medium (Optimal)")]
+ [Description("Deflate: Compression=Medium | Speed=Medium")]
Deflate,
- [Description("LZ4: Compression=Low | Speed=Fast (Use with high RAM)")]
+ [Description("Brotli: Compression=High | Speed=Fast (Optimal)")]
+ Brotli,
+ [Description("LZ4: Compression=Low | Speed=VeryFast (Use with high RAM)")]
Lz4,
//[Description("None: Compression=None | Speed=Fastest (Your soul belongs to RAM)")]
//None
@@ -66,7 +63,7 @@ public class Layer : BindableBase, IEquatable, IEquatable
public object Mutex = new();
private LayerCompressionCodec _compressionCodec;
- private byte[] _compressedBytes = Array.Empty();
+ private CMat _compressedMat;
private uint _nonZeroPixelCount;
private Rectangle _boundingRectangle = Rectangle.Empty;
private uint _firstPixelIndex;
@@ -75,8 +72,6 @@ public class Layer : BindableBase, IEquatable, IEquatable
private Point _lastPixelPosition;
private bool _isModified;
private uint _index;
- private uint _resolutionX;
- private uint _resolutionY;
private float _positionZ;
private float _lightOffDelay;
private float _waitTimeBeforeCure;
@@ -94,6 +89,7 @@ public class Layer : BindableBase, IEquatable, IEquatable
private float _materialMilliliters;
private EmguContours? _contours;
+
#endregion
@@ -107,34 +103,17 @@ public class Layer : BindableBase, IEquatable, IEquatable
///
/// Image resolution X
///
- public uint ResolutionX
- {
- get => _resolutionX;
- set => RaiseAndSetIfChanged(ref _resolutionX, value);
- }
+ public uint ResolutionX => (uint)CompressedMat.Width;
///
/// Image resolution Y
///
- public uint ResolutionY
- {
- get => _resolutionY;
- set => RaiseAndSetIfChanged(ref _resolutionY, value);
- }
+ public uint ResolutionY => (uint)CompressedMat.Height;
///
/// Image resolution
///
- public Size Resolution
- {
- get => new((int)ResolutionX, (int)ResolutionY);
- set
- {
- ResolutionX = (uint)value.Width;
- ResolutionY = (uint)value.Height;
- RaisePropertyChanged();
- }
- }
+ public Size Resolution => CompressedMat.Size;
///
/// Gets the number of non zero pixels on this layer image
@@ -836,6 +815,28 @@ public float MaterialMilliliters
///
public string EndTimeStr => TimeSpan.FromSeconds(CalculateStartTime(30) + CalculateCompletionTime()).ToString(@"hh\h\:mm\m\:ss\s");
+ private void InvalidateImage()
+ {
+ IsModified = true;
+ SlicerFile.BoundingRectangle = Rectangle.Empty;
+ _contours?.Dispose();
+ _contours = null;
+ }
+
+ private MatCompressor LayerCompressionCodecToMatCompressor(LayerCompressionCodec compressionCodec)
+ {
+ return compressionCodec switch
+ {
+ LayerCompressionCodec.Png => MatCompressorPngGreyScale.Instance,
+ LayerCompressionCodec.GZip => MatCompressorGZip.Instance,
+ LayerCompressionCodec.Deflate => MatCompressorDeflate.Instance,
+ LayerCompressionCodec.Brotli => MatCompressorBrotli.Instance,
+ LayerCompressionCodec.Lz4 => MatCompressorLz4.Instance,
+ //LayerCompressionCodec.None => MatCompressorNone.Instance,
+ _ => throw new ArgumentOutOfRangeException(nameof(compressionCodec), compressionCodec, null)
+ };
+ }
+
///
/// Gets or sets the compression method used to cache the image
///
@@ -844,35 +845,18 @@ public LayerCompressionCodec CompressionCodec
get => _compressionCodec;
set
{
- if (!HaveImage)
- {
- RaiseAndSetIfChanged(ref _compressionCodec, value);
- return;
- }
-
- // Handle conversion
- if (_compressionCodec == value) return;
- using var mat = LayerMat;
- _compressionCodec = value;
- _compressedBytes = CompressMat(mat, value);
- RaisePropertyChanged();
+ CompressedMat.Compressor = LayerCompressionCodecToMatCompressor(value);
+ RaiseAndSetIfChanged(ref _compressionCodec, value);
}
}
- ///
- /// Gets or sets layer image compressed data
- ///
- public byte[] CompressedBytes
+ public CMat CompressedMat
{
- get => _compressedBytes;
- set
+ get => _compressedMat;
+ private set
{
- Guard.IsNotNull(value, nameof(CompressedBytes));
- _compressedBytes = value;
- IsModified = true;
- SlicerFile.BoundingRectangle = Rectangle.Empty;
- _contours?.Dispose();
- _contours = null;
+ _compressedMat = value;
+ InvalidateImage();
RaisePropertyChanged();
RaisePropertyChanged(nameof(CompressedPngBytes));
RaisePropertyChanged(nameof(HaveImage));
@@ -883,8 +867,7 @@ public byte[] CompressedPngBytes
{
get
{
- if (!HaveImage) return _compressedBytes;
- if (_compressionCodec == LayerCompressionCodec.Png) return _compressedBytes;
+ if (_compressionCodec == LayerCompressionCodec.Png || _compressedMat.IsEmpty) return _compressedMat.CompressedBytes;
using var mat = LayerMat;
return mat.GetPngByes();
@@ -893,7 +876,8 @@ public byte[] CompressedPngBytes
{
if (_compressionCodec == LayerCompressionCodec.Png || value.Length == 0)
{
- CompressedBytes = value;
+ _compressedMat.SetCompressedBytes(value, MatCompressorPngGreyScale.Instance);
+ InvalidateImage();
}
else
{
@@ -907,8 +891,8 @@ public byte[] CompressedPngBytes
///
/// True if this layer have a valid initialized image, otherwise false
///
- public bool HaveImage => _compressedBytes.Length > 0;
-
+ public bool HaveImage => _compressedMat.IsInitialized;
+
///
/// Gets or sets a new image instance
///
@@ -919,77 +903,29 @@ public Mat LayerMat
get
{
if (!HaveImage) return null!;
-
- Mat mat;
- switch (_compressionCodec)
- {
- case LayerCompressionCodec.Png:
- mat = new Mat();
- CvInvoke.Imdecode(_compressedBytes, ImreadModes.Grayscale, mat);
- break;
- case LayerCompressionCodec.Lz4:
- mat = CreateMat(false);
- LZ4Codec.Decode(_compressedBytes.AsSpan(), mat.GetDataByteSpan());
- break;
- case LayerCompressionCodec.GZip:
- {
- mat = CreateMat(false);
- unsafe
- {
- fixed (byte* pBuffer = _compressedBytes)
- {
- using var compressedStream = new UnmanagedMemoryStream(pBuffer, _compressedBytes.Length);
- using var matStream = mat.GetUnmanagedMemoryStream(FileAccess.Write);
- using var gZipStream = new GZipStream(compressedStream, CompressionMode.Decompress);
- gZipStream.CopyTo(matStream);
- }
- }
-
- break;
- }
- case LayerCompressionCodec.Deflate:
- {
- mat = CreateMat(false);
- unsafe
- {
- fixed (byte* pBuffer = _compressedBytes)
- {
- using var compressedStream = new UnmanagedMemoryStream(pBuffer, _compressedBytes.Length);
- using var matStream = mat.GetUnmanagedMemoryStream(FileAccess.Write);
- using var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress);
- deflateStream.CopyTo(matStream);
- }
- }
-
- break;
- }
- /*case LayerCompressionMethod.None:
- mat = new Mat(Resolution, DepthType.Cv8U, 1);
- //mat.SetBytes(_compressedBytes!);
- _compressedBytes.CopyTo(mat.GetDataByteSpan());
- break;*/
- default:
- throw new ArgumentOutOfRangeException(nameof(LayerMat));
- }
-
- return mat;
+ return _compressedMat.Mat;
}
set
{
- if (value is not null)
+ GetBoundingRectangle(value, true);
+ if (value is null)
{
- CompressedBytes = CompressMat(value, _compressionCodec);
- _resolutionX = (uint)value.Width;
- _resolutionY = (uint)value.Height;
+ _compressedMat.SetEmptyCompressedBytes();
+ }
+ else if (_nonZeroPixelCount == 0)
+ {
+ _compressedMat.SetEmptyCompressedBytes(true);
}
else
{
- _resolutionX = 0;
- _resolutionY = 0;
+ using var matRoi = new MatRoi(value, _boundingRectangle);
+ _compressedMat.Compress(matRoi);
}
-
- GetBoundingRectangle(value, true);
+
+ InvalidateImage();
RaisePropertyChanged();
+ RaisePropertyChanged(nameof(CompressedPngBytes));
+ RaisePropertyChanged(nameof(HaveImage));
}
}
@@ -1007,8 +943,9 @@ public Mat LayerMat
/// Gets the layer mat with a specified roi
///
/// Region of interest
+ ///
///
- public MatRoi GetLayerMat(Rectangle roi) => new(LayerMat, roi, true);
+ public MatRoi GetLayerMat(Rectangle roi, EmptyRoiBehaviour emptyRoiBehaviour = EmptyRoiBehaviour.Continue) => new(LayerMat, roi, emptyRoiBehaviour, true);
///
/// Gets the layer mat with bounding rectangle mat
@@ -1163,12 +1100,11 @@ public Layer(uint index, FileFormat slicerFile, LayerCompressionCodec? compressi
compressionMethod ??= CoreSettings.DefaultLayerCompressionCodec;
_compressionCodec = compressionMethod.Value;
+ _compressedMat = new CMat(LayerCompressionCodecToMatCompressor(_compressionCodec), slicerFile.Resolution);
+
SlicerFile = slicerFile;
_index = index;
- _resolutionX = slicerFile.ResolutionX;
- _resolutionY = slicerFile.ResolutionY;
-
//if (slicerFile is null) return;
_positionZ = SlicerFile.CalculatePositionZ(index);
ResetParameters();
@@ -1244,8 +1180,7 @@ public bool Equals(Layer? other)
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
if (_index != other._index) return false;
- if (_compressedBytes.Length != other._compressedBytes.Length) return false;
- return _compressedBytes.AsSpan().SequenceEqual(other._compressedBytes.AsSpan());
+ return _compressedMat.Equals(other.CompressedMat);
//return Equals(_compressedBytes, other._compressedBytes);
}
@@ -1259,7 +1194,7 @@ public override bool Equals(object? obj)
public override int GetHashCode()
{
- return _compressedBytes.GetHashCode();
+ return _compressedMat.GetHashCode();
}
private sealed class IndexRelationalComparer : IComparer
@@ -1527,11 +1462,11 @@ public Rectangle GetBoundingRectangle(Mat? mat = null, bool reCalculate = false)
}
else
{
- var roiMat = mat.Roi(_boundingRectangle);
+ using var roiMat = mat.Roi(_boundingRectangle);
NonZeroPixelCount = (uint)CvInvoke.CountNonZero(roiMat);
// Compute first and last pixel
- var span = roiMat.GetDataByteSpan();
+ var span = roiMat.GetDataByteReadOnlySpan();
var yOffset = mat.GetRealStep() * BoundingRectangle.Y;
for (var i = 0; i < span.Length; i++)
{
@@ -1548,8 +1483,6 @@ public Rectangle GetBoundingRectangle(Mat? mat = null, bool reCalculate = false)
LastPixelPosition = new Point(BoundingRectangle.Right - (span.Length - i), BoundingRectangle.Bottom - 1);
break;
}
-
- roiMat.DisposeIfSubMatrix();
}
/* // Test
@@ -1893,10 +1826,7 @@ public void CopyLiftTo(Layer layer)
///
public void CopyImageTo(Layer layer)
{
- if (!HaveImage) return;
- layer.ResolutionX = _resolutionX;
- layer.ResolutionY = _resolutionY;
- layer.CompressedBytes = _compressedBytes.ToArray();
+ _compressedMat.CopyTo(layer._compressedMat);
layer._contours = _contours?.Clone();
layer.BoundingRectangle = _boundingRectangle;
layer.NonZeroPixelCount = _nonZeroPixelCount;
@@ -1909,7 +1839,7 @@ public void CopyImageTo(Layer layer)
public Layer Clone()
{
var layer = (Layer)MemberwiseClone();
- layer._compressedBytes = _compressedBytes.ToArray();
+ layer._compressedMat = _compressedMat.Clone();
layer._contours = _contours?.Clone();
//Debug.WriteLine(ReferenceEquals(_compressedBytes, layer.CompressedBytes));
return layer;
@@ -1986,46 +1916,5 @@ public static Layer[] CloneLayers(Layer[] layers)
return clonedLayers;
}
- public static byte[] CompressMat(Mat mat, LayerCompressionCodec codec)
- {
- switch (codec)
- {
- case LayerCompressionCodec.Png:
- return mat.GetPngByes();
- case LayerCompressionCodec.Lz4:
- {
- /*var span = mat.GetDataByteSpan();
- var target = new byte[LZ4Codec.MaximumOutputSize(span.Length)];
- var encodedLength = LZ4Codec.Encode(span, target.AsSpan());
- Array.Resize(ref target, encodedLength);
- return target;*/
-
- var span = mat.GetDataByteSpan();
-
- var rent = ArrayPool.Shared.Rent(LZ4Codec.MaximumOutputSize(span.Length));
- var rentSpan = rent.AsSpan();
-
- var encodedLength = LZ4Codec.Encode(span, rentSpan);
- var target = rentSpan[..encodedLength].ToArray();
-
- ArrayPool.Shared.Return(rent);
- return target;
-
- /*var span = mat.GetDataByteSpan();
- using var buffer = new PooledBufferWriter();
- LZ4Frame.Encode(span, buffer, LZ4Level.L00_FAST);
- return buffer.WrittenMemory.ToArray();*/
- }
- case LayerCompressionCodec.GZip:
- return CompressionExtensions.GZipCompressToBytes(mat.GetUnmanagedMemoryStream(FileAccess.Read), CompressionLevel.Fastest);
- case LayerCompressionCodec.Deflate:
- return CompressionExtensions.DeflateCompressToBytes(mat.GetUnmanagedMemoryStream(FileAccess.Read), CompressionLevel.Fastest);
- /*case LayerCompressionMethod.None:
- return mat.GetBytes();*/
- default:
- throw new ArgumentOutOfRangeException(nameof(codec));
- }
- }
-
#endregion
}
\ No newline at end of file
diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs
index 02cb98b0..d7ca3af4 100644
--- a/UVtools.Core/Managers/IssueManager.cs
+++ b/UVtools.Core/Managers/IssueManager.cs
@@ -135,11 +135,11 @@ public List DetectIssues(IssuesDetectionConfiguration? config = null,
var result = new ConcurrentBag();
//var layerHollowAreas = new ConcurrentDictionary>();
- var resinTraps = new List[SlicerFile.LayerCount];
- var suctionCups = new List[SlicerFile.LayerCount];
- var externalContours = new VectorOfVectorOfPoint[SlicerFile.LayerCount];
- var hollows = new List[SlicerFile.LayerCount];
- var airContours = new List[SlicerFile.LayerCount];
+ var resinTraps = new List?[SlicerFile.LayerCount];
+ var suctionCups = new List?[SlicerFile.LayerCount];
+ var externalContours = new VectorOfVectorOfPoint?[SlicerFile.LayerCount];
+ var hollows = new List?[SlicerFile.LayerCount];
+ var airContours = new List?[SlicerFile.LayerCount];
var resinTrapsContoursArea = new double[SlicerFile.LayerCount][];
bool IsIgnored(MainIssue issue) => IgnoredIssues.Count > 0 && IgnoredIssues.Contains(issue);
@@ -470,7 +470,7 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
//stats[i][2]: Width of Connected Component
//stats[i][3]: Height of Connected Component
//stats[i][4]: Total Area (in pixels) in Connected Component
- var ccStats = stats.GetDataSpan();
+ var ccStats = stats.GetDataReadOnlySpan();
var labelSpan = labels.GetDataSpan2D();
for (int i = 1; i < numLabels; i++)
@@ -545,8 +545,10 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
using var previousIslandRoi = previousImage.RoiMat.Roi(rect);
var islandOverhangMat = overhangImage;
+ bool wasNull = false;
if (islandOverhangMat is null)
{
+ wasNull = true;
islandOverhangMat = new Mat();
CvInvoke.Subtract(islandRoi, previousIslandRoi, islandOverhangMat);
CvInvoke.Threshold(islandOverhangMat, islandOverhangMat, 127, 255, ThresholdType.Binary);
@@ -555,7 +557,7 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
EmguExtensions.AnchorCenter, overhangsIterations, BorderType.Default, default);
}
- using var subtractedImage = islandOverhangMat.Roi(rect);
+ using var subtractedImage = islandOverhangMat.Roi(wasNull ? Rectangle.Empty : rect, EmptyRoiBehaviour.CaptureSource);
var subtractedSpan = subtractedImage.GetDataByteSpan2D();
var subtractedStep = subtractedImage.GetRealStep();
@@ -598,15 +600,15 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
Mat resinTrapImage;
if (resinTrapConfig.BinaryThreshold > 0)
{
+ needDispose = true;
resinTrapImage = new Mat();
CvInvoke.Threshold(image.SourceMat, resinTrapImage, resinTrapConfig.BinaryThreshold, byte.MaxValue, ThresholdType.Binary);
}
else
{
- needDispose = true;
resinTrapImage = image.SourceMat;
}
- var contourLayer = resinTrapImage.Roi(SlicerFile.BoundingRectangle);
+ using var contourLayer = resinTrapImage.Roi(SlicerFile.BoundingRectangle);
using var contours = contourLayer.FindContours(out var hierarchy, RetrType.Tree);
externalContours[layerIndex] = EmguContours.GetExternalContours(contours, hierarchy);
@@ -657,21 +659,20 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
//if (progress.Token.IsCancellationRequested) return result.OrderBy(issue => issue.Type).ThenBy(issue => issue.LayerIndex).ThenBy(issue => issue.Area).ToList();
progress.Reset("Detection pass 1 of 2 (Resin traps)", SlicerFile.LayerCount, resinTrapConfig.StartLayerIndex);
- using var matCache = new MatCacheManager(SlicerFile, 0, 2)
+ using var matCache = new MatCacheManager(SlicerFile, 0, 2);
+ matCache.AfterCacheAction = mats =>
{
- AfterCacheAction = mats =>
+ mats[1] = mats[0].Roi(SlicerFile.BoundingRectangle);
+ if (resinTrapConfig.MaximumPixelBrightnessToDrain > 0)
{
- mats[1] = mats[0].Roi(SlicerFile.BoundingRectangle);
- if (resinTrapConfig.MaximumPixelBrightnessToDrain > 0)
- {
- CvInvoke.Threshold(mats[1], mats[1], resinTrapConfig.MaximumPixelBrightnessToDrain, byte.MaxValue, ThresholdType.Binary);
- }
+ CvInvoke.Threshold(mats[1], mats[1], resinTrapConfig.MaximumPixelBrightnessToDrain, byte.MaxValue, ThresholdType.Binary);
}
};
/* define all mats up front, reducing allocations */
- var currentAirMap = EmguExtensions.InitMat(SlicerFile.BoundingRectangle.Size);
- var layerAirMap = currentAirMap.NewBlank();
+
+ using var layerAirMap = new Mat();
+ Mat? currentAirMap = null;
/* the first pass does bottom to top, and tracks anything it thinks is a resin trap */
for (var layerIndex = resinTrapConfig.StartLayerIndex; layerIndex < SlicerFile.LayerCount; layerIndex++)
{
@@ -719,7 +720,7 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
/* intersect current contour, with the current airmap. */
using var currentContour = curLayer.NewBlank();
- using var airOverlap = curLayer.NewBlank();
+ using var airOverlap = new Mat();
CvInvoke.DrawContours(currentContour, hollows[layerIndex][i], -1, EmguExtensions.WhiteColor, -1);
CvInvoke.BitwiseAnd(currentAirMap, currentContour, airOverlap);
var overlapCount = CvInvoke.CountNonZero(airOverlap);
@@ -751,9 +752,6 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
}
});
-
- if (progress.Token.IsCancellationRequested)
- return GetResult();
}
//matCache[layerIndex].Dispose();
@@ -788,7 +786,7 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
{
/* this is subtly different that for the first pass, we don't use GenerateAirMap for the initial airmap */
/* instead we use a bitwise not, this way anything that is open/hollow on the top layer is treated as air */
- currentAirMap = curLayer.Clone();
+ currentAirMap = new Mat();
CvInvoke.BitwiseNot(curLayer, currentAirMap);
}
@@ -824,7 +822,7 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
/* check if each contour overlaps known air */
using var currentContour = curLayer.NewBlank();
- using var airOverlap = curLayer.NewBlank();
+ using var airOverlap = new Mat();
CvInvoke.DrawContours(currentContour, resinTraps[layerIndex][x], -1, EmguExtensions.WhiteColor, -1);
CvInvoke.BitwiseAnd(currentAirMap, currentContour, airOverlap);
@@ -951,6 +949,13 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO
progress++;
}
+
+ if (currentAirMap is not null)
+ {
+ currentAirMap.Dispose();
+ currentAirMap = null;
+ }
+
if (progress.Token.IsCancellationRequested) return GetResult();
/* translate all contour points by ROI x and y */
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index 8f8b9004..444ee308 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -14,6 +14,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Serialization;
+using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
@@ -635,7 +636,7 @@ public Size GetRoiSizeOrVolumeSize(Size fallbackSize)
///
public Mat GetRoiOrDefault(Mat src)
{
- return HaveROI && src.Size != _roi.Size ? src.Roi(_roi) : src;
+ return src.Roi(_roi, EmptyRoiBehaviour.CaptureSource);
}
///
@@ -646,9 +647,7 @@ public Mat GetRoiOrDefault(Mat src)
///
public Mat GetRoiOrDefault(Mat src, Rectangle fallbackRoi)
{
- if (HaveROI && src.Size != _roi.Size) return src.Roi(_roi);
- if (fallbackRoi.IsEmpty) return src;
- return src.Size != fallbackRoi.Size ? src.Roi(fallbackRoi) : src;
+ return HaveROI ? src.Roi(_roi) : src.Roi(fallbackRoi, EmptyRoiBehaviour.CaptureSource);
}
///
@@ -710,21 +709,28 @@ public void SetMasksIfEmpty(Point[][] points)
public void ApplyMask(Mat original, Mat result, Mat? mask)
{
if (mask is null) return;
- var originalRoi = GetRoiOrDefault(original);
+ using var originalRoi = GetRoiOrDefault(original);
var resultRoi = result;
+ bool needDisposeResultRoi = false;
if (originalRoi.Size != result.Size) // Accept a ROI mat
{
resultRoi = GetRoiOrDefault(result);
+ needDisposeResultRoi = true;
}
+ bool needDisposeMask = false;
if (mask.Size != resultRoi.Size) // Accept a full size mask
{
mask = GetRoiOrDefault(mask);
+ needDisposeMask = true;
}
using var tempMat = originalRoi.Clone();
resultRoi.CopyTo(tempMat, mask);
tempMat.CopyTo(resultRoi);
+
+ if (needDisposeResultRoi) resultRoi.Dispose();
+ if (needDisposeMask) mask.Dispose();
}
///
diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs
index 7347029b..d8bffa8a 100644
--- a/UVtools.Core/Operations/OperationBlur.cs
+++ b/UVtools.Core/Operations/OperationBlur.cs
@@ -154,7 +154,7 @@ public override bool Execute(Mat mat, params object[]? arguments)
if (anchor.IsEmpty) anchor = EmguExtensions.AnchorCenter;
//if (size.IsEmpty) size = new Size(3, 3);
//if (anchor.IsEmpty) anchor = EmguExtensions.AnchorCenter;
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
using var original = mat.Clone();
switch (BlurOperation)
{
diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs
index ef565d9d..28b74a31 100644
--- a/UVtools.Core/Operations/OperationFlip.cs
+++ b/UVtools.Core/Operations/OperationFlip.cs
@@ -116,7 +116,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
public override bool Execute(Mat mat, params object[]? arguments)
{
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
if (MakeCopy)
{
diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs
index b9c7ed11..8a1e57ba 100644
--- a/UVtools.Core/Operations/OperationInfill.cs
+++ b/UVtools.Core/Operations/OperationInfill.cs
@@ -429,7 +429,7 @@ or InfillAlgorithm.CubicCrossAlternating
points.Add(new Point(x, y));
maxY = Math.Max(maxY, y);
}
- var infillPatternRoi = infillPattern.Roi(new Size(infillPattern.Width, maxY + radius + 2 + _infillSpacing));
+ using var infillPatternRoi = infillPattern.Roi(new Size(infillPattern.Width, maxY + radius + 2 + _infillSpacing));
CvInvoke.Polylines(infillPatternRoi, points.ToArray(), false, infillColor, _infillThickness);
CvInvoke.Repeat(infillPatternRoi, target.Rows / infillPatternRoi.Rows + 1, 1, infillPattern);
@@ -443,7 +443,7 @@ or InfillAlgorithm.CubicCrossAlternating
points.Add(new Point(x, y));
maxY = Math.Max(maxY, x);
}
- var infillPatternRoi = infillPattern.Roi(new Size(maxY + radius + 2 + _infillSpacing, infillPattern.Height));
+ using var infillPatternRoi = infillPattern.Roi(new Size(maxY + radius + 2 + _infillSpacing, infillPattern.Height));
CvInvoke.Polylines(infillPatternRoi, points.ToArray(), false, infillColor, _infillThickness);
CvInvoke.Repeat(infillPatternRoi, 1, target.Cols / infillPatternRoi.Cols + 1, infillPattern);
diff --git a/UVtools.Core/Operations/OperationLayerExportHtml.cs b/UVtools.Core/Operations/OperationLayerExportHtml.cs
index a371d7a8..11922dff 100644
--- a/UVtools.Core/Operations/OperationLayerExportHtml.cs
+++ b/UVtools.Core/Operations/OperationLayerExportHtml.cs
@@ -578,7 +578,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
html.WriteLine(" const layerSvg = {");
var layerSvgPath = new string[SlicerFile.LayerCount];
- Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ Parallel.For(0, SlicerFile.LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
using var mat = SlicerFile[layerIndex].LayerMat;
diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs
index 83441f9e..7568c19c 100644
--- a/UVtools.Core/Operations/OperationLayerExportImage.cs
+++ b/UVtools.Core/Operations/OperationLayerExportImage.cs
@@ -184,9 +184,11 @@ protected override bool ExecuteInternally(OperationProgress progress)
progress.PauseIfRequested();
using var mat = SlicerFile[layerIndex].LayerMat;
var matRoi = mat;
+ bool needDispose = false;
if (_cropByRoi && HaveROI)
{
matRoi = GetRoiOrDefault(mat);
+ needDispose = true;
}
if (_flipDirection != FlipDirection.None)
@@ -282,6 +284,8 @@ protected override bool ExecuteInternally(OperationProgress progress)
tw.WriteLine("\t");
tw.WriteLine("");
+
+ if (needDispose) matRoi.Dispose();
}
progress.LockAndIncrement();
diff --git a/UVtools.Core/Operations/OperationLayerExportMesh.cs b/UVtools.Core/Operations/OperationLayerExportMesh.cs
index bc019657..5c97718f 100644
--- a/UVtools.Core/Operations/OperationLayerExportMesh.cs
+++ b/UVtools.Core/Operations/OperationLayerExportMesh.cs
@@ -251,7 +251,7 @@ protected override unsafe bool ExecuteInternally(OperationProgress progress)
Mat? aboveLayer;
using (var mat = SlicerFile.GetMergedMatForSequentialPositionedLayers(distinctLayers[0].Index, cacheManager))
{
- var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
+ using var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
if ((byte)_quality > 1)
{
@@ -316,7 +316,7 @@ void ExitCleanup()
if (layerIndex < distinctLayers.Length - 1)
{
using var mat = SlicerFile.GetMergedMatForSequentialPositionedLayers(distinctLayers[(int)layerIndex+1].Index, cacheManager);
- var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
+ using var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
if ((byte)_quality > 1)
{
diff --git a/UVtools.Core/Operations/OperationLightBleedCompensation.cs b/UVtools.Core/Operations/OperationLightBleedCompensation.cs
index 915c9981..1857161f 100644
--- a/UVtools.Core/Operations/OperationLightBleedCompensation.cs
+++ b/UVtools.Core/Operations/OperationLightBleedCompensation.cs
@@ -209,7 +209,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
var layer = SlicerFile[layerIndex];
using var mat = layer.LayerMat;
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
for (byte i = 0; i < dimMats.Length; i++)
{
@@ -250,6 +250,8 @@ protected override bool ExecuteInternally(OperationProgress progress)
previousMat?.Dispose();
nextMat?.Dispose();
+ previousMatRoi?.Dispose();
+ nextMatRoi?.Dispose();
}
// Apply the results only to the selected masked area, if user selected one
diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs
index 9ecbd845..c7b49d9e 100644
--- a/UVtools.Core/Operations/OperationMask.cs
+++ b/UVtools.Core/Operations/OperationMask.cs
@@ -110,7 +110,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
public override bool Execute(Mat mat, params object[]? arguments)
{
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
using var mask = GetMask(mat);
if (Mask!.Size != target.Size) return false;
//CvInvoke.BitwiseAnd(target, Mask, target, mask);
diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs
index f270384e..27a4b6cd 100644
--- a/UVtools.Core/Operations/OperationMorph.cs
+++ b/UVtools.Core/Operations/OperationMorph.cs
@@ -200,7 +200,7 @@ public override bool Execute(Mat mat, params object[]? arguments)
}
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
/*if (CoreSettings.CanUseCuda)
{
@@ -217,7 +217,7 @@ public override bool Execute(Mat mat, params object[]? arguments)
if (_morphOperation == MorphOperations.OffsetCrop)
{
- var originalRoi = GetRoiOrDefault(original);
+ using var originalRoi = GetRoiOrDefault(original);
originalRoi.CopyTo(target, target);
}
/*else if (_morphOperation == MorphOperations.IsolateFeatures)
diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs
index 1166d818..863129d8 100644
--- a/UVtools.Core/Operations/OperationPixelArithmetic.cs
+++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs
@@ -632,7 +632,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
// Difference
using var nextMat = SlicerFile[layerIndex + 1].LayerMat;
- var nextMatRoi = GetMatRoiCropped(nextMat);
+ using var nextMatRoi = GetMatRoiCropped(nextMat);
CvInvoke.Subtract(target, nextMatRoi, applyMask);
// 1px walls
diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs
index a01ec2b1..faf2fca3 100644
--- a/UVtools.Core/Operations/OperationPixelDimming.cs
+++ b/UVtools.Core/Operations/OperationPixelDimming.cs
@@ -635,7 +635,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
using var blankMat = EmguExtensions.InitMat(SlicerFile.Resolution);
using var matPattern = blankMat.NewBlank();
using var matAlternatePattern = blankMat.NewBlank();
- var target = GetRoiOrDefault(blankMat);
+ using var target = GetRoiOrDefault(blankMat);
CvInvoke.Repeat(Pattern, target.Rows / Pattern.Rows + 1, target.Cols / Pattern.Cols + 1, matPattern);
CvInvoke.Repeat(AlternatePattern, target.Rows / AlternatePattern.Rows + 1, target.Cols / AlternatePattern.Cols + 1, matAlternatePattern);
@@ -687,8 +687,8 @@ public override bool Execute(Mat mat, params object[]? arguments)
using Mat erode = new();
//using Mat diff = new();
var original = mat.Clone();
- var originalRoi = GetRoiOrDefault(original);
- var target = GetRoiOrDefault(mat);
+ using var originalRoi = GetRoiOrDefault(original);
+ using var target = GetRoiOrDefault(mat);
using var mask = GetMask(mat);
diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs
index b05e2dc7..a938c54f 100644
--- a/UVtools.Core/Operations/OperationRaftRelief.cs
+++ b/UVtools.Core/Operations/OperationRaftRelief.cs
@@ -400,7 +400,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
progress.PauseIfRequested();
using var mat = SlicerFile[layerIndex].LayerMat;
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
switch (ReliefType)
{
@@ -425,7 +425,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
case RaftReliefTypes.Tabs:
{
using var contours = new EmguContours(mat, RetrType.External);
- var span = mat.GetDataByteSpan();
+ var span = mat.GetDataByteReadOnlySpan();
var minX = 10;
var maxX = mat.Size.Width - 10;
diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs
index bd1607b5..228fc85d 100644
--- a/UVtools.Core/Operations/OperationRedrawModel.cs
+++ b/UVtools.Core/Operations/OperationRedrawModel.cs
@@ -192,8 +192,8 @@ protected override bool ExecuteInternally(OperationProgress progress)
using var nextLayerMat = otherFile[layerIndex + 1].LayerMat;
using var nextLayerMatRoi = GetRoiOrDefault(nextLayerMat);
var fullSpan = fullMatRoi.GetDataByteSpan();
- var supportsSpan = supportsMat.GetDataByteSpan();
- var nextSpan = nextLayerMatRoi.GetDataByteSpan();
+ var supportsSpan = supportsMat.GetDataByteReadOnlySpan();
+ var nextSpan = nextLayerMatRoi.GetDataByteReadOnlySpan();
for (int i = 0; i < contours.Size; i++)
{
var foundContour = false;
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index 15f0a99b..5b2d9740 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -330,7 +330,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
{
progress.PauseIfRequested();
using var mat = SlicerFile[group.Key].LayerMat;
- var matSpan = mat.GetDataByteSpan();
+ var matSpan = mat.GetDataByteReadOnlySpan();
var matCache = new Dictionary();
var matCacheModified = new Dictionary();
var startLayer = Math.Max(0, (int)group.Key - 2);
diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs
index e2b623e1..a04ba0bd 100644
--- a/UVtools.Core/Operations/OperationResize.cs
+++ b/UVtools.Core/Operations/OperationResize.cs
@@ -202,7 +202,7 @@ public override bool Execute(Mat mat, params object[]? arguments)
}
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
target.TransformFromCenter((double) xScale, (double) yScale);
ApplyMask(original, target);
return true;
diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs
index 2f8f41c6..47e9d73e 100644
--- a/UVtools.Core/Operations/OperationRotate.cs
+++ b/UVtools.Core/Operations/OperationRotate.cs
@@ -110,7 +110,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
public override bool Execute(Mat mat, params object[]? arguments)
{
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
target.Rotate((double)AngleDegrees);
ApplyMask(original, target);
return true;
diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs
index 34457b26..f4116748 100644
--- a/UVtools.Core/Operations/OperationSolidify.cs
+++ b/UVtools.Core/Operations/OperationSolidify.cs
@@ -103,7 +103,7 @@ public override bool Execute(Mat mat, params object[]? arguments)
{
using Mat filteredMat = new();
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
CvInvoke.Threshold(target, filteredMat, 127, 255, ThresholdType.Binary); // Clean AA
using var contours = filteredMat.FindContours(out var hierarchy, RetrType.Ccomp);
diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs
index 4190bfc6..ea53e174 100644
--- a/UVtools.Core/Operations/OperationThreshold.cs
+++ b/UVtools.Core/Operations/OperationThreshold.cs
@@ -100,7 +100,7 @@ protected override bool ExecuteInternally(OperationProgress progress)
public override bool Execute(Mat mat, params object[]? arguments)
{
using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
+ using var target = GetRoiOrDefault(mat);
CvInvoke.Threshold(target, target, Threshold, Maximum, Type);
ApplyMask(original, target);
return true;
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 4cccada2..70a85f78 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -1,7 +1,7 @@
- 4.1.1
+ 4.2.0
True
..\documentation\$(AssemblyName).xml
diff --git a/UVtools.Core/Voxel/Voxelizer.cs b/UVtools.Core/Voxel/Voxelizer.cs
index 689a5067..9d8f7a4b 100644
--- a/UVtools.Core/Voxel/Voxelizer.cs
+++ b/UVtools.Core/Voxel/Voxelizer.cs
@@ -50,7 +50,7 @@ public enum FaceOrientation : short
public static FaceOrientation GetOpenFaces(Mat layer, int x, int y, Mat? layerBelow = null, Mat? layerAbove = null)
{
- var layerSpan = layer.GetDataByteSpan();
+ var layerSpan = layer.GetDataByteReadOnlySpan();
var foundFaces = FaceOrientation.None;
var pixelPos = layer.GetPixelPos(x, y);
@@ -65,7 +65,7 @@ public static FaceOrientation GetOpenFaces(Mat layer, int x, int y, Mat? layerBe
}
else
{
- var belowSpan = layerBelow.GetDataByteSpan();
+ var belowSpan = layerBelow.GetDataByteReadOnlySpan();
if (belowSpan[pixelPos] == 0)
{
foundFaces |= FaceOrientation.Bottom;
@@ -78,7 +78,7 @@ public static FaceOrientation GetOpenFaces(Mat layer, int x, int y, Mat? layerBe
}
else
{
- var aboveSpan = layerAbove.GetDataByteSpan();
+ var aboveSpan = layerAbove.GetDataByteReadOnlySpan();
if (aboveSpan[pixelPos] == 0)
{
foundFaces |= FaceOrientation.Top;
diff --git a/UVtools.UI/Extensions/BitmapExtension.cs b/UVtools.UI/Extensions/BitmapExtension.cs
index 916075c3..17fe4d66 100644
--- a/UVtools.UI/Extensions/BitmapExtension.cs
+++ b/UVtools.UI/Extensions/BitmapExtension.cs
@@ -261,7 +261,7 @@ public static WriteableBitmap ToBitmapParallel(this Mat mat)
}
else if (mat.Depth == DepthType.Cv32S)
{
- mat.GetDataSpan().CopyTo(new Span(lockBuffer.Address.ToPointer(), dataCount));
+ mat.GetDataReadOnlySpan().CopyTo(new Span(lockBuffer.Address.ToPointer(), dataCount));
}
break;
diff --git a/UVtools.UI/MainWindow.axaml.cs b/UVtools.UI/MainWindow.axaml.cs
index 3d55cd88..d2259e59 100644
--- a/UVtools.UI/MainWindow.axaml.cs
+++ b/UVtools.UI/MainWindow.axaml.cs
@@ -1088,7 +1088,7 @@ public async void MenuFileSettingsClicked()
if (oldLayerCompressionCodec != Settings.General.LayerCompressionCodec && IsFileLoaded)
{
- IsGUIEnabled = false;
+ /*IsGUIEnabled = false;
ShowProgressWindow($"Changing layers compression codec from {oldLayerCompressionCodec.ToString().ToUpper()} to {Settings.General.LayerCompressionCodec.ToString().ToUpper()}");
await Task.Run(() =>
@@ -1106,7 +1106,9 @@ await Task.Run(() =>
return false;
});
- IsGUIEnabled = true;
+ IsGUIEnabled = true;*/
+
+ SlicerFile!.ChangeLayersCompressionMethod(Settings.General.LayerCompressionCodec);
}
_layerNavigationSliderDebounceTimer.Interval = Settings.LayerPreview.LayerSliderDebounce == 0 ? 1 : Settings.LayerPreview.LayerSliderDebounce;
diff --git a/UVtools.UI/UVtools.UI.csproj b/UVtools.UI/UVtools.UI.csproj
index 55e65c31..140c590e 100644
--- a/UVtools.UI/UVtools.UI.csproj
+++ b/UVtools.UI/UVtools.UI.csproj
@@ -2,7 +2,7 @@
WinExe
UVtools
- 4.1.1
+ 4.2.0
true
1701;1702;
diff --git a/UVtools.UI/Windows/BenchmarkWindow.axaml.cs b/UVtools.UI/Windows/BenchmarkWindow.axaml.cs
index f3c50a3a..3d0ad64b 100644
--- a/UVtools.UI/Windows/BenchmarkWindow.axaml.cs
+++ b/UVtools.UI/Windows/BenchmarkWindow.axaml.cs
@@ -11,7 +11,6 @@
using Avalonia.Media;
using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
-using UVtools.Core.Layers;
using UVtools.UI.Controls;
using UVtools.UI.Extensions;
using UVtools.UI.Structures;
@@ -68,10 +67,12 @@ public enum BenchmarkResolution
/*PW0 8K Encode*/ new BenchmarkTestResult(45.15f, 678.43f),
/*PNG 4K Compress*/ new BenchmarkTestResult(86.96f, 1479.29f),
/*PNG 8K Compress*/ new BenchmarkTestResult(22.17f, 369.00f),
- /*GZip 4K Compress*/ new BenchmarkTestResult(243.9f, 4000.00f),
- /*GZip 8K Compress*/ new BenchmarkTestResult(63.29f, 1020.41f),
- /*Deflate 4K Compress*/ new BenchmarkTestResult(246.91f, 4000.00f),
- /*Deflate 8K Compress*/ new BenchmarkTestResult(64.10f, 1033.06f),
+ /*GZip 4K Compress*/ new BenchmarkTestResult(250f, 4237.29f),
+ /*GZip 8K Compress*/ new BenchmarkTestResult(65.57f, 1039.5f),
+ /*Deflate 4K Compress*/ new BenchmarkTestResult(256.41f, 4201.68f),
+ /*Deflate 8K Compress*/ new BenchmarkTestResult(66.67f, 1072.96f),
+ /*Brotli 4K Compress*/ new BenchmarkTestResult(645.16f, 12820.51f),
+ /*Brotli 8K Compress*/ new BenchmarkTestResult(190.48f, 3787.88f),
/*LZ4 4K Compress*/ new BenchmarkTestResult(1111.11f, 20833.33f),
/*LZ4 8K Compress*/ new BenchmarkTestResult(312.5f, 6097.56f),
/*Stress CPU test*/ new BenchmarkTestResult(0f, 0f),
@@ -90,6 +91,8 @@ public enum BenchmarkResolution
/*GZip 8K Compress*/ new BenchmarkTestResult(45.77f, 397.47f),
/*Deflate 4K Compress*/ new BenchmarkTestResult(170.94f, 1592.36f),
/*Deflate 8K Compress*/ new BenchmarkTestResult(46.30f, 406.50f),
+ /*Brotli 4K Compress*/ new BenchmarkTestResult(0, 0),
+ /*Brotli 8K Compress*/ new BenchmarkTestResult(0, 0),
/*LZ4 4K Compress*/ new BenchmarkTestResult(665.12f, 2762.43f),
/*LZ4 8K Compress*/ new BenchmarkTestResult(148.15f, 907.44f),
/*Stress CPU test*/ new BenchmarkTestResult(0f, 0f),
@@ -115,6 +118,9 @@ public enum BenchmarkResolution
new BenchmarkTest("Deflate 4K Compress", "TestDeflateCompress", BenchmarkResolution.Resolution4K),
new BenchmarkTest("Deflate 8K Compress", "TestDeflateCompress", BenchmarkResolution.Resolution8K),
+ new BenchmarkTest("Brotli 4K Compress", "TestBrotliCompress", BenchmarkResolution.Resolution4K),
+ new BenchmarkTest("Brotli 8K Compress", "TestBrotliCompress", BenchmarkResolution.Resolution8K),
+
new BenchmarkTest("LZ4 4K Compress", "TestLZ4Compress", BenchmarkResolution.Resolution4K),
new BenchmarkTest("LZ4 8K Compress", "TestLZ4Compress", BenchmarkResolution.Resolution8K),
@@ -388,7 +394,7 @@ private void UpdateResults(bool isSingleThread, long milliseconds)
public byte[] EncodeCbddlpImage(Mat image, byte bit = 0)
{
List rawData = new();
- var span = image.GetDataByteSpan();
+ var span = image.GetDataByteReadOnlySpan();
bool obit = false;
int rep = 0;
@@ -450,7 +456,7 @@ private byte[] EncodeCbtImage(Mat image)
List rawData = new();
byte color = byte.MaxValue >> 1;
uint stride = 0;
- var span = image.GetDataByteSpan();
+ var span = image.GetDataByteReadOnlySpan();
void AddRep()
{
@@ -528,7 +534,7 @@ void AddRep()
public byte[] EncodePW0Image(Mat image)
{
List rawData = new();
- var span = image.GetDataByteSpan();
+ var span = image.GetDataByteReadOnlySpan();
int lastColor = -1;
int reps = 0;
@@ -692,6 +698,11 @@ public void TestDeflateCompress(BenchmarkResolution resolution)
public void TestDeflateDecompress(BenchmarkResolution resolution)
{ }
+ public void TestBrotliCompress(BenchmarkResolution resolution)
+ {
+ MatCompressorBrotli.Instance.Compress(Mats[resolution]);
+ }
+
public void TestLZ4Compress(BenchmarkResolution resolution)
{
//Layer.CompressMat(Mats[resolution], LayerCompressionCodec.Lz4);
diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml
index 2d5eacba..e7e5e3e4 100644
--- a/documentation/UVtools.Core.xml
+++ b/documentation/UVtools.Core.xml
@@ -127,128 +127,224 @@
Gets the default folder to save the settings
-
+
- Gets the compressed bytes that have been compressed with .
+ Represents a compressed that can be compressed and decompressed using multiple s.
+ This allows to have a high count of s in memory without using too much memory.
-
+
- Gets the compressed bytes that have been compressed with .
+ Gets the compressed bytes that have been compressed with .
-
+
- Gets the cached width of the that was compressed.
+ Gets the compressed bytes that have been compressed with .
-
+
- Gets the cached height of the that was compressed.
+ Gets a value indicating whether the have ever been set.
-
+
- Gets the cached size of the that was compressed.
+ Gets a value indicating whether the are compressed or raw bytes.
-
+
- Gets the cached depth of the that was compressed.
+ Gets or sets the threshold in bytes to compress the data. Mat's equal to or less than this size will not be compressed.
-
+
- Gets the cached number of channels of the that was compressed.
+ Gets the cached width of the that was compressed.
-
+
- Gets the cached ROI of the that was compressed.
+ Gets the cached height of the that was compressed.
-
+
- Gets or sets the that will be used to compress and decompress the .
+ Gets the cached size of the that was compressed.
-
+
- Gets the that will be used to decompress the .
+ Gets the cached depth of the that was compressed.
-
+
- Gets a value indicating whether the are empty.
+ Gets the cached number of channels of the that was compressed.
-
+
- Gets a value indicating whether the are compressed or raw bytes.
+ Gets the cached ROI of the that was compressed.
-
+
- Creates a new with the same size, depth, and channels as the .
+ Gets or sets the that will be used to compress and decompress the .
-
-
+
- Create a new with the same size, depth, and channels as the but with all bytes set to 0.
+ Gets the that will be used to decompress the .
+
+
+
+
+ Gets a value indicating whether the are empty.
+
+
+
+
+ Gets the length of the .
+
+
+
+
+ Gets the uncompressed length of the aka bitmap size.
+
+
+
+
+ Gets the compression ratio of the to the .
+
+
+
+
+ Gets the compression percentage of the to the .
-
-
+
- Compresses the into a byte array.
+ Gets the compression efficiency percentage of the to the .
+
+
+
+
+ Gets the number of bytes saved by compressing the .
+
+
+
+
+ Gets or sets the that will be compressed and decompressed.
+ Every time the is accessed, it will be de/compressed.
+
+
+
+
+ Changes the and optionally re-encodes the with the new if the is different from the set .
+
+ New compressor
+ True to re-encodes the with the new , otherwise false.
+
+ True if compressor has been changed, otherwise false.
+
+
+
+ Changes the and optionally re-encodes the with the new if the is different from the set .
+
+ New compressor
+ True to re-encodes the with the new , otherwise false.
+
+
+ True if compressor has been changed, otherwise false.
+
+
+
+ Sets the to an empty byte array and sets to false.
+
+
+
+
+ Sets the to an empty byte array and sets to false.
+
+ Sets the to a known state.
+
+
+
+ Sets the and and .
+
+
+
+
+
+
+
+ Compresses the into a byte array.
-
+
-
+
Compresses the into a byte array.
-
+
-
+
- Compresses the into a byte array.
+ Compresses the into a byte array.
-
+
-
+
- Compresses the into a byte array.
+ Compresses the into a byte array.
-
+
-
+
- Decompresses the into a new .
+ Decompresses the into a new .
-
+
-
+
- Decompresses the into a new .
+ Decompresses the into a new .
-
+
-
+
- Creates a clone of the with the same .
+ Creates a new with the same size, depth, and channels as the .
+
+
+
+
+
+ Create a new with the same size, depth, and channels as the but with all bytes set to 0.
+
+
+
+
+
+ Copies the to the .
+
+
+
+
+
+ Creates a clone of the with the same .
@@ -420,12 +516,22 @@
Contour 2
+
+
+ Allows the mat to be created with an empty roi.
+
+
+
+
+ Sets the roi to the source mat size.
+
+
Compresses the into a byte array.
-
+
@@ -434,14 +540,14 @@
-
+
Compresses the into a byte array.
-
+
@@ -451,10 +557,21 @@
-
+
+
+
+ Instance of .
+
+
+
+
+
+
+
+
Instance of .
@@ -499,6 +616,28 @@
+
+
+ Instance of .
+
+
+
+
+
+
+
+
+
+
+ Instance of .
+
+
+
+
+
+
+
+
Instance of .
@@ -520,11 +659,31 @@
True to dispose the on
+
+
+ Gets the location
+
+
+
+
+ Gets the size
+
+
Gets if the is the same size of the
+
+
+
+
+ The source mat to apply the .
+ The roi rectangle.
+ Sets the behavior for an empty roi event.
+ True if you want to dispose the upon the call.
+ Use true when creating a directly on this constructor or not handling the disposal, otherwise false.
+
@@ -911,13 +1070,40 @@
-
+
- Gets a col span to manipulate or read pixels
+ Gets the data span to read pixels given a length and offset
-
+
+
+
+
+
+
+ Gets the whole data span to manipulate or read pixels
+
+
+
+
+
+
+ Gets a row span to read pixels
+
+
+
+
+
+
+
+
+
+
+ Gets a row span or read pixels
+
+
+
@@ -1104,17 +1290,18 @@
Source to be copied to
Target to paste the
-
+
- Gets a Roi, but return source when roi is empty or have same size as source
+ Gets a Roi
+
- Gets a Roi at x=0 and y=0 given a size, but return source when roi is empty or have same size as source
+ Gets a Roi at x=0 and y=0 given a size
@@ -1122,7 +1309,7 @@
- Gets a Roi from a mat size, but return source when roi is empty or have same size as source
+ Gets a Roi from a mat size
@@ -1137,7 +1324,7 @@
- Gets a Roi from center, but return source when have same size as source
+ Gets a Roi from center
@@ -1501,6 +1688,13 @@
Mat to dispose
+
+
+ Dispose this if it's not the same reference as
+
+ Mat to dispose
+
+
Read from current position to the end of the file
@@ -3240,6 +3434,16 @@
Layers List
+
+
+ Gets the layers cache/memory occupation size in bytes
+
+
+
+
+ Gets the layers cache/memory occupation size in readable string format
+
+
First layer index, this is always 0
@@ -4476,12 +4680,11 @@
TThe converted file if successful, otherwise null
-
+
Changes the compression method of all layers to a new method
The new method to change to
-
@@ -6290,11 +6493,6 @@
Gets or sets the compression method used to cache the image
-
-
- Gets or sets layer image compressed data
-
-
True if this layer have a valid initialized image, otherwise false
@@ -6315,11 +6513,12 @@
Gets the layer mat with roi of model bounding rectangle
-
+
Gets the layer mat with a specified roi
Region of interest
+
diff --git a/wiki/UVtools_Compression.xlsx b/wiki/UVtools_Compression.xlsx
index 69ac25f2..1a8102f7 100644
Binary files a/wiki/UVtools_Compression.xlsx and b/wiki/UVtools_Compression.xlsx differ