diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-04-18 02:18:42 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-04-18 02:18:42 +0300 |
commit | 6cfedea4cc36200caab87301e0dd121e97ddc8f2 (patch) | |
tree | 3325980560fc5bb3fa8befea1b827a2ca945e9ea /UVtools.Core | |
parent | 4dae750e83987fde8b34087d26fc75407e88f55a (diff) |
v2.9.1v2.9.1
* **File formats:**
* PhotonS: Implement the write/encode method to allow to use this format and fix the thumbnail
* VDT: Allow to auto convert the .vdt to the target printer format using the Machine - Notes, using a flag: FILEFORMAT_YourPrinterExtension, for example: FILEFORMAT_CTB
* (Fix) Unable to convert files with no thumbnails to other file format that requires thumbnails
* **Tools:**
* (Add) Re-height: Option to Anti-Aliasing layers
* (Fix) Morph and Blur: The combobox was not setting to the selected item when preform a redo operation (Ctrl+Shift+Z)
* **GUI:**
* (Change) Progress window to be a grid element inside MainWindow, this allow to reuse the graphics and its elements without the need of spawning a Window instance everytime a progress is shown, resulting in better performance and more fluid transaction
* (Improvement) Clear issues when generating calibration tests
Diffstat (limited to 'UVtools.Core')
-rw-r--r-- | UVtools.Core/Extensions/BitExtensions.cs | 15 | ||||
-rw-r--r-- | UVtools.Core/Extensions/EnumExtensions.cs | 39 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/CXDLPFile.cs | 1 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 1 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/LGSFile.cs | 40 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/MDLPFile.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/PhotonSFile.cs | 294 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/PhotonWorkshopFile.cs | 24 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/VDTFile.cs | 44 | ||||
-rw-r--r-- | UVtools.Core/Objects/StringTag.cs | 100 | ||||
-rw-r--r-- | UVtools.Core/Objects/ValueDescription.cs | 73 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationBlur.cs | 42 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationLayerImport.cs | 34 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationLayerReHeight.cs | 142 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationMorph.cs | 51 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationProgress.cs | 19 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 2 |
17 files changed, 540 insertions, 383 deletions
diff --git a/UVtools.Core/Extensions/BitExtensions.cs b/UVtools.Core/Extensions/BitExtensions.cs new file mode 100644 index 0000000..7df1876 --- /dev/null +++ b/UVtools.Core/Extensions/BitExtensions.cs @@ -0,0 +1,15 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +namespace UVtools.Core.Extensions +{ + public static class BitExtensions + { + public static ushort ToUShortLittleEndian(byte byte1, byte byte2) => (ushort)(byte1 + (byte2 << 8)); + public static ushort ToUShortBigEndian(byte byte1, byte byte2) => (ushort)((byte1 << 8) + byte2); + } +} diff --git a/UVtools.Core/Extensions/EnumExtensions.cs b/UVtools.Core/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..493418c --- /dev/null +++ b/UVtools.Core/Extensions/EnumExtensions.cs @@ -0,0 +1,39 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using UVtools.Core.Objects; + +namespace UVtools.Core.Extensions +{ + public static class EnumExtensions + { + public static string GetDescription(this Enum value) + { + var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); + if (attributes.Any()) + return (attributes.First() as DescriptionAttribute)?.Description; + + // If no description is found, the least we can do is replace underscores with spaces + // You can add your own custom default formatting logic here + var ti = CultureInfo.CurrentCulture.TextInfo; + return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " "))); + } + + public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t) + { + if (!t.IsEnum) + throw new ArgumentException($"{nameof(t)} must be an enum type"); + + return Enum.GetValues(t).Cast<Enum>().Select(e => new ValueDescription(e, e.GetDescription())).ToList(); + } + } +} diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index 2e680a3..bd44c26 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -255,6 +255,7 @@ namespace UVtools.Core.FileFormats public string FooterValue { get; set; } = HEADER_VALUE; [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] public uint Unknown { get; set; } = 7; public void Validate() diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 2fe8a04..9b05d2e 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -1241,6 +1241,7 @@ namespace UVtools.Core.FileFormats /// <param name="images"></param> public void SetThumbnails(Mat[] images) { + if (images is null || images.Length == 0) return; byte imageIndex = 0; for (int i = 0; i < ThumbnailsCount; i++) { diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index cb3d38d..9a3a0a6 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -431,6 +431,26 @@ namespace UVtools.Core.FileFormats return bytes; } + public unsafe Mat PreviewDecode(byte[] data) + { + Mat mat = new((int)HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3); + var span = mat.GetBytePointer(); + int spanIndex = 0; + for (int i = 0; i < data.Length; i += 2) + { + ushort rgb15 = BitExtensions.ToUShortBigEndian(data[i], data[i + 1]); + byte r = (byte)((((rgb15 >> 11) & 0x1f) << 3) | 0x7); + byte g = (byte)((((rgb15 >> 6) & 0x1f) << 3) | 0x7); + byte b = (byte)((((rgb15 >> 0) & 0x1f) << 3) | 0x7); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + return mat; + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { if (ResolutionY >= 2560) // Longer Orange 30 @@ -474,26 +494,6 @@ namespace UVtools.Core.FileFormats Debug.WriteLine("-End-"); } - public unsafe Mat PreviewDecode(byte []data) - { - Mat mat = new Mat((int) HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3); - var span = mat.GetBytePointer(); - int spanIndex = 0; - for (int i = 0; i < data.Length; i+=2) - { - ushort rgb15 = (ushort) ((ushort)(data[i + 0] << 8) | data[i + 1]); - byte r = (byte) ((((rgb15 >> 11) & 0x1f) << 3) | 0x7); - byte g = (byte) ((((rgb15 >> 6) & 0x1f) << 3) | 0x7); - byte b = (byte) ((((rgb15 >> 0) & 0x1f) << 3) | 0x7); - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - return mat; - } - protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index 784ad28..475150f 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -420,7 +420,7 @@ namespace UVtools.Core.FileFormats int spanIndex = 0; for (int i = 0; i < previews[previewIndex].Length; i += 2) { - ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); + ushort rgb15 = BitExtensions.ToUShortBigEndian(previews[previewIndex][i], previews[previewIndex][i + 1]); byte r = (byte)((rgb15 >> 11) << 3); byte g = (byte)((rgb15 >> 5) << 2); byte b = (byte)((rgb15 >> 0) << 3); diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index e2391a7..6e6fe47 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -24,7 +24,7 @@ namespace UVtools.Core.FileFormats { public class PhotonSFile : FileFormat { - public const byte RLEEncodingLimit = 0x7f - 2; // 128; + public const byte RLEEncodingLimit = 128; #region Sub Classes @@ -37,7 +37,7 @@ namespace UVtools.Core.FileFormats public const float DisplayWidth = 68.04f; public const float DisplayHeight = 120.96f; - public const float BuildZ = 150f; + public const float BuildZ = 165f; public const uint TAG1 = 2; public const ushort TAG2 = 49; @@ -55,7 +55,7 @@ namespace UVtools.Core.FileFormats [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public double LiftSpeed { get; set; } // mm/s [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public double RetractSpeed { get; set; } // mm/s [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public double VolumeMl { get; set; } // ml - [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 225; + [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 224; [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 42; [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionY { get; set; } = 168; [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public uint Unknown4 { get; set; } = 10; @@ -88,7 +88,14 @@ namespace UVtools.Core.FileFormats [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = 1440; [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = 2560; [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DataSize { get; set; } - [Ignore] public uint RleDataSize => (DataSize >> 3) - 4; + [Ignore] public uint RleDataSize + { + get => (DataSize >> 3) - 4; + set => DataSize = (value + 4) << 3; + //get => DataSize / 8 - 4; + //set => DataSize = (value + 4) * 8; + } + [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public uint Unknown5 { get; set; } = 2684702720; [Ignore] public byte[] EncodedRle { get; set; } @@ -100,33 +107,34 @@ namespace UVtools.Core.FileFormats public unsafe byte[] Encode(Mat mat) { - List<byte> rawData = new List<byte>(); + List<byte> rawData = new(); var spanMat = mat.GetBytePointer(); var imageLength = mat.GetLength(); int rep = 0; byte color = 0; + int totalPixels = 0; void AddRep() { if (rep <= 0) return; + + totalPixels += rep; rep--; - byte rle = - (byte) (((rep & 1) > 0 ? 128 : 0) | - ((rep & 2) > 0 ? 64 : 0) | - ((rep & 4) > 0 ? 32 : 0) | - ((rep & 8) > 0 ? 16 : 0) | - ((rep & 16) > 0 ? 8 : 0) | - ((rep & 32) > 0 ? 4 : 0) | - ((rep & 64) > 0 ? 2 : 0) | color); + byte rle = (byte) (((rep & 1) > 0 ? 128 : 0) | + ((rep & 2) > 0 ? 64 : 0) | + ((rep & 4) > 0 ? 32 : 0) | + ((rep & 8) > 0 ? 16 : 0) | + ((rep & 16) > 0 ? 8 : 0) | + ((rep & 32) > 0 ? 4 : 0) | + ((rep & 64) > 0 ? 2 : 0) | color); rawData.Add(rle); } for (int i = 0; i < imageLength; i++) { - //color = color <= 127 ? 0 : 255; // Sanitize no AA - byte thisColor = spanMat[i] <= 127 ? byte.MinValue : byte.MaxValue; // Sanitize no AA + byte thisColor = spanMat[i] <= 127 ? (byte)0 : (byte)1; // Sanitize no AA if (thisColor != color) { AddRep(); @@ -146,8 +154,13 @@ namespace UVtools.Core.FileFormats AddRep(); + if (totalPixels != imageLength) + { + throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {totalPixels} pixels."); + } + EncodedRle = rawData.ToArray(); - DataSize = (uint)(EncodedRle.Length * 8 + 4); + RleDataSize = (uint) EncodedRle.Length; return EncodedRle; } @@ -157,31 +170,43 @@ namespace UVtools.Core.FileFormats var matSpan = mat.GetBytePointer(); var imageLength = mat.GetLength(); - int pixel = 0; + uint pixel = 0; foreach (var run in EncodedRle) { - byte col = (byte) ((run & 0x01) * 255); - - var numPixelsInRun = - (((run & 128) > 0 ? 1 : 0) | - ((run & 64) > 0 ? 2 : 0) | - ((run & 32) > 0 ? 4 : 0) | - ((run & 16) > 0 ? 8 : 0) | - ((run & 8) > 0 ? 16 : 0) | - ((run & 4) > 0 ? 32 : 0) | - ((run & 2) > 0 ? 64 : 0)) + 1; - + byte brightness = (byte) ((run & 0x01) * 255); + + uint numPixelsInRun = + (uint) ((((run & 128) > 0 ? 1 : 0) | + ((run & 64) > 0 ? 2 : 0) | + ((run & 32) > 0 ? 4 : 0) | + ((run & 16) > 0 ? 8 : 0) | + ((run & 8) > 0 ? 16 : 0) | + ((run & 4) > 0 ? 32 : 0) | + ((run & 2) > 0 ? 64 : 0)) + 1); + + if (brightness == 0) // Don't fill black pixels + { + pixel += numPixelsInRun; + continue; + } + for (; numPixelsInRun > 0; numPixelsInRun--) { if (pixel > imageLength) { mat.Dispose(); - throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels"); + throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels."); } - matSpan[pixel++] = col; + matSpan[pixel++] = brightness; } } + if (pixel != imageLength && pixel-1 != imageLength) + { + mat.Dispose(); + throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {pixel} pixels."); + } + // Not required as mat is all black by default //for (;pixel < imageLength; pixel++) matSpan[pixel] = 0; @@ -197,8 +222,8 @@ namespace UVtools.Core.FileFormats #region Properties - public Header HeaderSettings { get; protected internal set; } = new Header(); - public LayerHeader LayerSettings { get; protected internal set; } = new LayerHeader(); + public Header HeaderSettings { get; protected internal set; } = new(); + public LayerHeader LayerSettings { get; protected internal set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { @@ -221,7 +246,7 @@ namespace UVtools.Core.FileFormats }; - public override Size[] ThumbnailsOriginalSize { get; } = {new(225, 168) }; + public override Size[] ThumbnailsOriginalSize { get; } = {new(224, 168) }; public override uint ResolutionX { @@ -261,7 +286,7 @@ namespace UVtools.Core.FileFormats public override float LayerHeight { - get => (float) Math.Round(HeaderSettings.LayerHeight); + get => (float) Layer.RoundHeight(HeaderSettings.LayerHeight); set { HeaderSettings.LayerHeight = Layer.RoundHeight(value); @@ -269,6 +294,12 @@ namespace UVtools.Core.FileFormats } } + public override float MachineZ + { + get => Header.BuildZ; + set { } + } + public override uint LayerCount { get => base.LayerCount; @@ -293,7 +324,7 @@ namespace UVtools.Core.FileFormats set => base.ExposureTime = (float) (HeaderSettings.ExposureSeconds = Math.Round(value, 2)); } - public override float BottomLiftHeight => LightOffDelay; + public override float BottomLiftHeight => LiftHeight; public override float LiftHeight { @@ -306,21 +337,13 @@ namespace UVtools.Core.FileFormats public override float LiftSpeed { get => (float) Math.Round(HeaderSettings.LiftSpeed * 60.0, 2); - set - { - HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2); - base.LiftSpeed = value; - } + set => base.LiftSpeed = (float) (HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2)); } public override float RetractSpeed { get => (float)Math.Round(HeaderSettings.RetractSpeed * 60.0, 2); - set - { - HeaderSettings.RetractSpeed = (float) Math.Round(value / 60.0, 2); - base.RetractSpeed = value; - } + set => base.RetractSpeed = (float) (HeaderSettings.RetractSpeed = (float) Math.Round(value / 60.0, 2)); } public override float BottomLightOffDelay => LightOffDelay; @@ -365,14 +388,14 @@ namespace UVtools.Core.FileFormats int index = 0; for (int i = 0; i < imageLength; i+=3) { + byte r = span[i + 2]; // 60 + byte g = span[i + 1]; byte b = span[i]; - byte g = span[i+1]; - byte r = span[i+2]; - ushort rgb15 = (ushort) (((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 0)); + ushort color = (ushort)(((b & 0xF8) << 8) | ((g & 0xFC) << 3) | (r >> 3)); - bytes[index++] = (byte) (rgb15 >> 8); - bytes[index++] = (byte) (rgb15 & 0xff); + bytes[index++] = (byte)color; + bytes[index++] = (byte)(color >> 8); } if (index != bytes.Length) @@ -383,41 +406,62 @@ namespace UVtools.Core.FileFormats return bytes; } + public unsafe Mat PreviewDecode(byte[] data) + { + Mat mat = new((int)HeaderSettings.PreviewResolutionY, (int)HeaderSettings.PreviewResolutionX, DepthType.Cv8U, 3); + var span = mat.GetBytePointer(); + int spanIndex = 0; + for (int i = 0; i < data.Length; i += 2) + { + ushort color16 = BitExtensions.ToUShortLittleEndian(data[i], data[i + 1]); + + //var r = (byte)((color16 & 0x1F) << 3); + //var g = (byte)(((color16 >> 5) & 0x3F) << 2); + //var b = (byte)(((color16 >> 11) & 0x1f) << 3); + var r = (byte)((color16 << 3) & 0xF8); // Mask: 11111000 + var g = (byte)((color16 >> 3) & 0xFC); // Mask: 11111100 + var b = (byte)((color16 >> 8) & 0xF8); // Mask: 11111000 + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + return mat; + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - throw new NotSupportedException("PhotonS is read-only format, please use pws instead!"); + //throw new NotSupportedException("PhotonS is read-only format, please use pws instead!"); //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - outputFile.WriteSerialize(HeaderSettings); - outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); - LayerSettings.LayerCount = LayerCount; - outputFile.WriteSerialize(LayerSettings); + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + outputFile.WriteSerialize(HeaderSettings); + outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); + outputFile.WriteSerialize(LayerSettings); - LayerData[] layerData = new LayerData[LayerCount]; + var layerData = new LayerData[LayerCount]; - Parallel.For(0, LayerCount, layerIndex => + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) { - if (progress.Token.IsCancellationRequested) return; - using (var mat = this[layerIndex].LayerMat) - { - layerData[layerIndex] = new LayerData(); - layerData[layerIndex].Encode(mat); - } + layerData[layerIndex] = new LayerData(); + layerData[layerIndex].Encode(mat); + } - progress.LockAndIncrement(); - }); + progress.LockAndIncrement(); + }); - progress.ItemName = "Saving layers"; - progress.ProcessedItems = 0; + progress.ItemName = "Saving layers"; + progress.ProcessedItems = 0; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); - outputFile.WriteSerialize(layerData[layerIndex]); - outputFile.WriteBytes(layerData[layerIndex].EncodedRle); - progress++; - } + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + outputFile.WriteSerialize(layerData[layerIndex]); + outputFile.WriteBytes(layerData[layerIndex].EncodedRle); + progress++; } Debug.WriteLine("Encode Results:"); @@ -425,84 +469,57 @@ namespace UVtools.Core.FileFormats Debug.WriteLine("-End-"); } - public unsafe Mat PreviewDecode(byte []data) - { - Mat mat = new Mat((int) HeaderSettings.PreviewResolutionX, (int)HeaderSettings.PreviewResolutionY, DepthType.Cv8U, 3); - var span = mat.GetBytePointer(); - int spanIndex = 0; - for (int i = 0; i < data.Length; i+=2) - { - ushort color16 = (ushort)(data[i] + (data[i + 1] << 8)); - - var r = (color16 >> 11) & 0x1F; - var g = (color16 >> 5) & 0x3F; - var b = (color16 >> 0) & 0x1F; - - /*span[spanIndex++] = (byte)(b << 3); - span[spanIndex++] = (byte)(g << 2); - span[spanIndex++] = (byte)(r << 3);*/ - - span[spanIndex++] = (byte)((b << 3) | (b & 0x7)); - span[spanIndex++] = (byte)((g << 2) | (g & 0x3)); - span[spanIndex++] = (byte)((r << 3) | (r & 0x7)); - } - - return mat; - } - protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2) { - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2) - { - throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", fileFullPath); - } + throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", fileFullPath); + } - int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2); - byte[] previewData = new byte[previewSize]; + int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2); + byte[] previewData = new byte[previewSize]; - uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings); - currentOffset += inputFile.ReadBytes(previewData); - Thumbnails[0] = PreviewDecode(previewData); + uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings); + currentOffset += inputFile.ReadBytes(previewData); + Thumbnails[0] = PreviewDecode(previewData); - LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile); - currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings); + LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile); + currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine(LayerSettings); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine(LayerSettings); - LayerData[] layerData = new LayerData[LayerSettings.LayerCount]; - progress.Reset(OperationProgress.StatusGatherLayers, LayerSettings.LayerCount); + var layerData = new LayerData[LayerSettings.LayerCount]; + progress.Reset(OperationProgress.StatusGatherLayers, LayerSettings.LayerCount); - for (int layerIndex = 0; layerIndex < LayerSettings.LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); - layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile); - layerData[layerIndex].EncodedRle = new byte[layerData[layerIndex].RleDataSize]; - currentOffset += inputFile.ReadBytes(layerData[layerIndex].EncodedRle); - Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}"); - } + for (int layerIndex = 0; layerIndex < LayerSettings.LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile); + layerData[layerIndex].EncodedRle = new byte[layerData[layerIndex].RleDataSize]; + currentOffset += inputFile.ReadBytes(layerData[layerIndex].EncodedRle); + Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}"); + } - LayerManager.Init(LayerSettings.LayerCount); - progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + LayerManager.Init(LayerSettings.LayerCount); + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); - Parallel.For(0, LayerCount, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - layerIndex => + Parallel.For(0, LayerCount, + //new ParallelOptions{MaxDegreeOfParallelism = 1}, + layerIndex => { if (progress.Token.IsCancellationRequested) return; using var image = layerData[layerIndex].Decode(); - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); + this[layerIndex] = new Layer((uint) layerIndex, image, this); progress.LockAndIncrement(); }); - LayerManager.RebuildLayersProperties(); - } + LayerManager.RebuildLayersProperties(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) @@ -524,12 +541,9 @@ namespace UVtools.Core.FileFormats FileFullPath = filePath; } - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - } + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); } #endregion diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 4fbe61f..abff762 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -423,16 +423,16 @@ namespace UVtools.Core.FileFormats public unsafe Mat Decode(bool consumeData = true) { - Mat image = new Mat(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); + Mat image = new(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); var span = image.GetBytePointer(); int pixel = 0; for (uint i = 0; i < Data.Length; i += 2) { - ushort color16 = (ushort)(Data[i] + (Data[i+1] << 8)); - var r =(color16 >> 11) & 0x1f; - var g = (color16 >> 5) & 0x3f; - var b = (color16 >> 0) & 0x1f; + ushort color16 = BitExtensions.ToUShortLittleEndian(Data[i], Data[i+1]); + byte r = (byte)((color16 >> 11) & 0x1f); + byte g = (byte)((color16 >> 5) & 0x3f); + byte b = (byte)((color16 >> 0) & 0x1f); span[pixel++] = (byte) ((b << 3) | (b & 0x7)); span[pixel++] = (byte) ((g << 2) | (g & 0x3)); @@ -456,9 +456,9 @@ namespace UVtools.Core.FileFormats for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels) { // BGR - int b = span[pixel] >> 3; - int g = span[pixel+1] >> 2; - int r = span[pixel+2] >> 3; + byte b = (byte)(span[pixel] >> 3); + byte g = (byte)(span[pixel+1] >> 2); + byte r = (byte)(span[pixel+2] >> 3); ushort color = (ushort) ((r << 11) | (g << 5) | (b << 0)); @@ -699,19 +699,21 @@ namespace UVtools.Core.FileFormats case 0x0: color = 0x00; index++; - reps = reps * 256 + EncodedRle[index]; + //reps = reps * 256 + EncodedRle[index]; + reps = (reps << 8) + EncodedRle[index]; break; case 0xf: color = 0xff; index++; - reps = reps * 256 + EncodedRle[index]; + //reps = reps * 256 + EncodedRle[index]; + reps = (reps << 8) + EncodedRle[index]; break; default: color = (byte) ((code << 4) | code); break; } - color &= 0xff; + //color &= 0xff; // We only need to set the non-zero pixels if (color != 0) diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 5dfa02c..5dc0e6b 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -82,6 +82,7 @@ namespace UVtools.Core.FileFormats [JsonProperty("resolution_y")] public ushort ResolutionY { get; set; } [JsonProperty("x_mirror")] public bool XMirror { get; set; } [JsonProperty("y_mirror")] public bool YMirror { get; set; } + [JsonProperty("notes")] public string Notes { get; set; } } public sealed class VDTResin @@ -89,6 +90,7 @@ namespace UVtools.Core.FileFormats [JsonProperty("name")] public string Name { get; set; } [JsonProperty("cost")] public float Cost { get; set; } [JsonProperty("density")] public float Density { get; set; } = 1; + [JsonProperty("notes")] public string Notes { get; set; } } public sealed class VDTPrint @@ -513,6 +515,48 @@ namespace UVtools.Core.FileFormats //Decode(FileFullPath, progress); } + + public T LookupCustomValue<T>(string name, T defaultValue, bool existsOnly = false) + { + //if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue; + var result = string.Empty; + if (!existsOnly) name += '_'; + + foreach (var notes in new[] { ManifestFile.Machine.Notes }) + { + if (string.IsNullOrWhiteSpace(notes)) continue; + + var lines = notes.Split(new[] { "\\r\\n", "\\r", "\\n" }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + foreach (var line in lines) + { + if (!line.StartsWith(name)) continue; + if (existsOnly || line == name) return "true".Convert<T>(); + var value = line.Remove(0, name.Length); + foreach (var c in value) + { + if (typeof(T) == typeof(string)) + { + if (char.IsWhiteSpace(c)) break; + } + else + { + if (!char.IsLetterOrDigit(c) && c != '.') + { + break; + } + } + + + result += c; + } + } + + if (!string.IsNullOrEmpty(result)) break; // Found a candidate + } + + return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>(); + } #endregion } } diff --git a/UVtools.Core/Objects/StringTag.cs b/UVtools.Core/Objects/StringTag.cs deleted file mode 100644 index edaea6f..0000000 --- a/UVtools.Core/Objects/StringTag.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Collections.Generic; - -namespace UVtools.Core.Objects -{ - public class StringTag : BindableBase, IEquatable<StringTag>, IComparable<StringTag> - { - private string _content; - private object _tag; - - public string Content - { - get => _content; - set => RaiseAndSetIfChanged(ref _content, value); - } - - public object Tag - { - get => _tag; - set => RaiseAndSetIfChanged(ref _tag, value); - } - - public string TagString - { - get => Tag?.ToString(); - set => Tag = value; - } - - public StringTag(object content, object tag = null) - { - Content = content.ToString(); - Tag = tag; - } - public StringTag(string content, object tag = null) - { - Content = content; - Tag = tag; - } - - public bool Equals(StringTag other) - { - return _content == other._content && Equals(_tag, other._tag); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((StringTag) obj); - } - - public override int GetHashCode() - { - unchecked - { - return ((_content != null ? _content.GetHashCode() : 0) * 397) ^ (_tag != null ? _tag.GetHashCode() : 0); - } - } - - private sealed class ContentEqualityComparer : IEqualityComparer<StringTag> - { - public bool Equals(StringTag x, StringTag y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return x.Content == y.Content; - } - - public int GetHashCode(StringTag obj) - { - return (obj.Content != null ? obj.Content.GetHashCode() : 0); - } - } - - public static IEqualityComparer<StringTag> ContentComparer { get; } = new ContentEqualityComparer(); - - public int CompareTo(StringTag other) - { - if (ReferenceEquals(this, other)) return 0; - if (ReferenceEquals(null, other)) return 1; - return string.Compare(Content, other.Content, StringComparison.Ordinal); - } - - public override string ToString() - { - return Content; - } - } -} diff --git a/UVtools.Core/Objects/ValueDescription.cs b/UVtools.Core/Objects/ValueDescription.cs new file mode 100644 index 0000000..3f7372c --- /dev/null +++ b/UVtools.Core/Objects/ValueDescription.cs @@ -0,0 +1,73 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; + +namespace UVtools.Core.Objects +{ + public class ValueDescription : BindableBase, IEquatable<ValueDescription> + { + private object _value; + private string _description; + + public object Value + { + get => _value; + set => RaiseAndSetIfChanged(ref _value, value); + } + + public string Description + { + get => _description; + set => RaiseAndSetIfChanged(ref _description, value); + } + + public string ValueAsString + { + get => Value?.ToString(); + set => Value = value; + } + + public ValueDescription(object value, string description = null) + { + Value = value; + Description = description; + } + public ValueDescription(object value, object description = null) + { + Value = value; + Description = description?.ToString(); + } + + public bool Equals(ValueDescription other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(_value, other._value) && _description == other._description; + } + + 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((ValueDescription) obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(_value, _description); + } + + public override string ToString() + { + return Description; + } + } +} diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index ed65ac8..c803177 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -7,6 +7,7 @@ */ using System; +using System.ComponentModel; using System.Drawing; using System.Text; using System.Threading.Tasks; @@ -68,42 +69,30 @@ namespace UVtools.Core.Operations #region Enums public enum BlurAlgorithm { + [Description("Blur: Normalized box filter")] Blur, + [Description("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition")] Pyramid, + [Description("Median Blur: Each pixel becomes the median of its surrounding pixels")] MedianBlur, + [Description("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood")] GaussianBlur, + [Description("Filter 2D: Applies an arbitrary linear filter to an image")] Filter2D } #endregion #region Properties - public static StringTag[] BlurTypes => new[] - { - new StringTag("Blur: Normalized box filter", BlurAlgorithm.Blur), - new StringTag("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition", BlurAlgorithm.Pyramid), - new StringTag("Median Blur: Each pixel becomes the median of its surrounding pixels", BlurAlgorithm.MedianBlur), - new StringTag("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood", BlurAlgorithm.GaussianBlur), - new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D), - }; - - public byte BlurTypeIndex - { - get - { - for (byte i = 0; i < BlurTypes.Length; i++) - { - if ((BlurAlgorithm)BlurTypes[i].Tag == BlurOperation) return i; - } - - return 0; - } - } - public BlurAlgorithm BlurOperation { get => _blurOperation; - set => RaiseAndSetIfChanged(ref _blurOperation, value); + set + { + if(!RaiseAndSetIfChanged(ref _blurOperation, value)) return; + RaisePropertyChanged(nameof(IsSizeEnabled)); + RaisePropertyChanged(nameof(IsKernelVisible)); + } } public uint Size @@ -112,8 +101,13 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _size, value); } + public bool IsSizeEnabled => BlurOperation != BlurAlgorithm.Pyramid && + BlurOperation != BlurAlgorithm.Filter2D; + + public bool IsKernelVisible => BlurOperation == BlurAlgorithm.Filter2D; + [XmlIgnore] - public Kernel Kernel { get; set; } = new Kernel(); + public Kernel Kernel { get; set; } = new (); public override string ToString() { diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index fb69546..367e725 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -46,7 +46,7 @@ namespace UVtools.Core.Operations private bool _extendBeyondLayerCount = true; private bool _discardUnmodifiedLayers; private ushort _stackMargin = 50; - private ObservableCollection<StringTag> _files = new(); + private ObservableCollection<ValueDescription> _files = new(); #endregion #region Overrides @@ -161,7 +161,7 @@ namespace UVtools.Core.Operations } [XmlIgnore] - public ObservableCollection<StringTag> Files + public ObservableCollection<ValueDescription> Files { get => _files; set => RaiseAndSetIfChanged(ref _files, value); @@ -183,13 +183,13 @@ namespace UVtools.Core.Operations public void AddFile(string file) { - Files.Add(new StringTag(Path.GetFileNameWithoutExtension(file), file)); + Files.Add(new ValueDescription(file, Path.GetFileNameWithoutExtension(file))); } public void Sort() { var sortedFiles = Files.ToList(); - sortedFiles.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.TagString), Path.GetFileNameWithoutExtension(file2.TagString), StringComparison.Ordinal)); + sortedFiles.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.ValueAsString), Path.GetFileNameWithoutExtension(file2.ValueAsString), StringComparison.Ordinal)); Files.Clear(); foreach (var file in sortedFiles) { @@ -232,12 +232,12 @@ namespace UVtools.Core.Operations // Order raw images for (int i = 0; i < Count; i++) { - if (!Files[i].TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; - keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, Files[i].TagString)); + if (!Files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; + keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, Files[i].ValueAsString)); } // Create virtual file format with images @@ -264,15 +264,15 @@ namespace UVtools.Core.Operations // Order remaining possible file formats for (int i = 0; i < Count; i++) { - if (Files[i].TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; + if (Files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; - var fileFormat = FileFormat.FindByExtension(Files[i].TagString, true, true); + var fileFormat = FileFormat.FindByExtension(Files[i].ValueAsString, true, true); if (fileFormat is null) continue; - fileFormat.FileFullPath = Files[i].TagString; + fileFormat.FileFullPath = Files[i].ValueAsString; fileFormats.Add(fileFormat); } diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs index 6b45374..8a9d338 100644 --- a/UVtools.Core/Operations/OperationLayerReHeight.cs +++ b/UVtools.Core/Operations/OperationLayerReHeight.cs @@ -8,22 +8,35 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.ComponentModel; using System.Text; using System.Threading.Tasks; using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; -using UVtools.Core.Objects; namespace UVtools.Core.Operations { [Serializable] public sealed class OperationLayerReHeight : Operation { + #region Enums + + public enum OperationLayerReHeightAntiAliasingType : byte + { + //[Description("Hello")] + None, + [Description("Difference - Compute anti-aliasing by the layers difference and perform a down/up sample over pixels")] + Difference, + [Description("Average - Compute anti-aliasing by averaging the layers pixels")] + Average + } + #endregion #region Members - private OperationLayerReHeightItem _item; - private bool _antiAliasing = true; + private OperationLayerReHeightItem _selectedItem; + private OperationLayerReHeightAntiAliasingType _antiAliasingType; #endregion @@ -38,10 +51,10 @@ namespace UVtools.Core.Operations " than current height will reduce model detail.\n\n" + "Note: Using dedicated slicer software to re-slice will usually yeild better results."; public override string ConfirmationText => - $"adjust layer height to {Item.LayerHeight}mm?"; + $"adjust layer height to {SelectedItem.LayerHeight}mm?"; public override string ProgressTitle => - $"Adjusting layer height to {Item.LayerHeight}mm"; + $"Adjusting layer height to {SelectedItem.LayerHeight}mm"; public override string ProgressAction => "Height adjusted layers"; @@ -51,7 +64,7 @@ namespace UVtools.Core.Operations { var sb = new StringBuilder(); - if (Item is null) + if (SelectedItem is null) { sb.AppendLine("No valid configurations, unable to proceed."); } @@ -62,7 +75,7 @@ namespace UVtools.Core.Operations public override string ToString() { - var result = $"[Layer Count: {Item.LayerCount}] [Layer Height: {Item.LayerHeight}]" + LayerRangeString; + var result = $"[Layer Count: {SelectedItem.LayerCount}] [Layer Height: {SelectedItem.LayerHeight}]" + LayerRangeString; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -70,22 +83,24 @@ namespace UVtools.Core.Operations #region Properties - public OperationLayerReHeightItem Item + public OperationLayerReHeightItem[] Presets { get; } + + public OperationLayerReHeightItem SelectedItem { - get => _item; + get => _selectedItem; set { - if(!RaiseAndSetIfChanged(ref _item, value)) return; - RaisePropertyChanged(nameof(AntiAliasing)); + if(!RaiseAndSetIfChanged(ref _selectedItem, value)) return; + RaisePropertyChanged(nameof(CanAntiAliasing)); } } - public bool CanAntiAliasing => _item?.IsMultiply ?? false; + public bool CanAntiAliasing => _selectedItem?.IsMultiply ?? false; - public bool AntiAliasing + public OperationLayerReHeightAntiAliasingType AntiAliasingType { - get => _antiAliasing; - set => RaiseAndSetIfChanged(ref _antiAliasing, value); + get => _antiAliasingType; + set => RaiseAndSetIfChanged(ref _antiAliasingType, value); } @@ -120,7 +135,14 @@ namespace UVtools.Core.Operations public OperationLayerReHeight() { } - public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile) { } + public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile) + { + Presets = GetItems(slicerFile.LayerCount, (decimal) slicerFile.LayerHeight); + if (Presets is not null && Presets.Length > 0) + { + _selectedItem = Presets[0]; + } + } #endregion @@ -151,11 +173,11 @@ namespace UVtools.Core.Operations #region Methods protected override bool ExecuteInternally(OperationProgress progress) { - progress.ItemCount = _item.LayerCount; + progress.ItemCount = _selectedItem.LayerCount; - var layers = new Layer[_item.LayerCount]; + var layers = new Layer[_selectedItem.LayerCount]; - if (_item.IsDivision) + if (_selectedItem.IsDivision) { uint newLayerIndex = 0; for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) @@ -163,7 +185,7 @@ namespace UVtools.Core.Operations progress.Token.ThrowIfCancellationRequested(); var oldLayer = SlicerFile[layerIndex]; - for (byte i = 0; i < _item.Modifier; i++) + for (byte i = 0; i < _selectedItem.Modifier; i++) { var newLayer = oldLayer.Clone(); //newLayer.Index = newLayerIndex; @@ -176,48 +198,90 @@ namespace UVtools.Core.Operations } else { - var layerIndexes = new uint[SlicerFile.LayerCount / _item.Modifier]; + var layerIndexes = new uint[SlicerFile.LayerCount / _selectedItem.Modifier]; for (uint i = 0; i < layerIndexes.Length; i++) { - layerIndexes[i] = i * _item.Modifier; + layerIndexes[i] = i * _selectedItem.Modifier; } Parallel.ForEach(layerIndexes, layerIndex => { if (progress.Token.IsCancellationRequested) return; var oldLayer = SlicerFile[layerIndex]; - using var mat = oldLayer.LayerMat; - using var original = mat.Clone(); + using var matSum = oldLayer.LayerMat; + Mat matXorSum = null; + using Mat aaAverageSum = new(); + + if (_antiAliasingType == OperationLayerReHeightAntiAliasingType.Average) + { + matSum.ConvertTo(aaAverageSum, DepthType.Cv16U); + } - for (byte i = 1; i < Item.Modifier; i++) + for (byte i = 1; i < SelectedItem.Modifier; i++) { using var nextMat = SlicerFile[layerIndex+i].LayerMat; - CvInvoke.Add(mat, nextMat, mat); + + switch (_antiAliasingType) + { + case OperationLayerReHeightAntiAliasingType.None: + CvInvoke.Add(matSum, nextMat, matSum); + break; + case OperationLayerReHeightAntiAliasingType.Difference: + { + using var previousMat = SlicerFile[layerIndex + i - 1].LayerMat; + var matXor = new Mat(); + //CvInvoke.Threshold(previousMat, previousMat, 127, 255, ThresholdType.Binary); + //CvInvoke.Threshold(nextMat, nextMat, 127, 255, ThresholdType.Binary); + CvInvoke.BitwiseXor(previousMat, nextMat, matXor); + matXor.SetTo(new MCvScalar((byte)(byte.MaxValue / _selectedItem.Modifier)), matXor); + if (matXorSum is null) + { + matXorSum = matXor.Clone(); + } + else + { + CvInvoke.Add(matXorSum, matXorSum, matXorSum); + CvInvoke.Add(matXorSum, matXor, matXorSum); + } + break; + } + case OperationLayerReHeightAntiAliasingType.Average: + nextMat.ConvertTo(nextMat, DepthType.Cv16U); + CvInvoke.Add(aaAverageSum, nextMat, aaAverageSum); + break; + default: + throw new ArgumentOutOfRangeException(); + } } - /*if (_antiAliasing) + switch (_antiAliasingType) { - CvInvoke.Subtract(mat, original, mat); - CvInvoke.PyrDown(mat, mat); - CvInvoke.PyrUp(mat, mat); - CvInvoke.Add(original, mat, mat); - }*/ + case OperationLayerReHeightAntiAliasingType.Difference: + CvInvoke.Add(matSum, matXorSum, matSum); + CvInvoke.PyrDown(matSum, matSum); + CvInvoke.PyrUp(matSum, matSum); + matXorSum.Dispose(); + break; + case OperationLayerReHeightAntiAliasingType.Average: + aaAverageSum.ConvertTo(matSum, DepthType.Cv8U, 1.0 / _selectedItem.Modifier); + CvInvoke.PyrDown(matSum, matSum); + CvInvoke.PyrUp(matSum, matSum); + break; + } var newLayer = oldLayer.Clone(); //newLayer.Index = newLayerIndex; //newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); - newLayer.LayerMat = mat; - layers[layerIndex / _item.Modifier] = newLayer; + newLayer.LayerMat = matSum; + layers[layerIndex / _selectedItem.Modifier] = newLayer; progress.LockAndIncrement(); }); - - } - + progress.Token.ThrowIfCancellationRequested(); - SlicerFile.LayerHeight = (float)Item.LayerHeight; + SlicerFile.LayerHeight = (float)SelectedItem.LayerHeight; SlicerFile.LayerManager.Layers = layers; return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs index c3120cf..f2c6804 100644 --- a/UVtools.Core/Operations/OperationMorph.cs +++ b/UVtools.Core/Operations/OperationMorph.cs @@ -7,6 +7,7 @@ */ using System; +using System.ComponentModel; using System.Threading.Tasks; using System.Xml.Serialization; using Emgu.CV; @@ -19,8 +20,28 @@ namespace UVtools.Core.Operations [Serializable] public sealed class OperationMorph : Operation { + #region Enums + public enum MorphOperations + { + [Description("Erode: Contracts the boundaries within the object")] + Erode = MorphOp.Erode, + + [Description("Dilate: Expands the boundaries within the object")] + Dilate = MorphOp.Dilate, + + [Description("Gap Closing - Closes small holes inside the objects")] + Close = MorphOp.Close, + + [Description("Noise Removal - Removes small isolated pixels")] + Open = MorphOp.Open, + + [Description("Gradient - Removes the interior areas of objects")] + Gradient = MorphOp.Gradient, + } + #endregion + #region Members - private MorphOp _morphOperation = MorphOp.Erode; + private MorphOperations _morphOperation = MorphOperations.Erode; private uint _iterationsStart = 1; private uint _iterationsEnd = 1; private bool _chamfer; @@ -43,31 +64,7 @@ namespace UVtools.Core.Operations #endregion #region Properties - - - public static StringTag[] MorphOperations => new[] - { - new StringTag("Erode - Contracts the boundaries within the object", MorphOp.Erode), - new StringTag("Dilate - Expands the boundaries within the object", MorphOp.Dilate), - new StringTag("Gap Closing - Closes small holes inside the objects", MorphOp.Close), - new StringTag("Noise Removal - Removes small isolated pixels", MorphOp.Open), - new StringTag("Gradient - Removes the interior areas of objects", MorphOp.Gradient), - }; - - public byte MorphOperationIndex - { - get - { - for (byte i = 0; i < MorphOperations.Length; i++) - { - if ((MorphOp) MorphOperations[i].Tag == MorphOperation) return i; - } - - return 0; - } - } - - public MorphOp MorphOperation + public MorphOperations MorphOperation { get => _morphOperation; set => RaiseAndSetIfChanged(ref _morphOperation, value); @@ -185,7 +182,7 @@ namespace UVtools.Core.Operations using var original = mat.Clone(); var target = GetRoiOrDefault(mat); - CvInvoke.MorphologyEx(target, target, MorphOperation, Kernel.Matrix, Kernel.Anchor, iterations, BorderType.Reflect101, default); + CvInvoke.MorphologyEx(target, target, (MorphOp) MorphOperation, Kernel.Matrix, Kernel.Anchor, iterations, BorderType.Reflect101, default); ApplyMask(original, mat); return true; } diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 0da2586..1771536 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -34,7 +34,7 @@ namespace UVtools.Core.Operations public readonly object Mutex = new(); - public CancellationTokenSource TokenSource { get; } = new(); + public CancellationTokenSource TokenSource { get; private set; } public CancellationToken Token => TokenSource.Token; private bool _canCancel = true; @@ -47,14 +47,15 @@ namespace UVtools.Core.Operations public OperationProgress() { + Init(); } - public OperationProgress(string name, uint value = 0) + public OperationProgress(string name, uint value = 0) : this() { Reset(name, value); } - public OperationProgress(bool canCancel) + public OperationProgress(bool canCancel) : this() { _canCancel = canCancel; } @@ -164,6 +165,18 @@ namespace UVtools.Core.Operations return progress; } + public void Init(bool canCancel = true) + { + CanCancel = canCancel; + Title = "Operation"; + ItemName = "Initializing"; + ItemCount = 0; + ProcessedItems = 0; + + TokenSource = new(); + RaisePropertyChanged(nameof(CanCancel)); + } + public void Reset(string name = "", uint itemCount = 0, uint items = 0) { ItemName = name; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 63a0232..f3e2fba 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.9.0</Version> + <Version>2.9.1</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> |