diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d2e9c6..f9f1ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ * (Improvement) GUI is now scalable * (Fix) Some bug found and fixed during convertion +## /10/2020 - v0.8.4.2 + +* (Fix) pws and pw0: Error when try to save or copy to clipboard the slicer information / properties +* (Fix) phton, ctb, cbbdlp, pws, pw0, phz: Rare cases were decoding image generate noise and malformed image + ## 10/10/2020 - v0.8.4.1 * (Add) Tool - Modify print parameters: Value unit to confirmation text diff --git a/README.md b/README.md index 6a61408..f0f41c3 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ But also, i need victims for test subject. Proceed at your own risk! * ZCodex (Z-Suite) * CWS (NovaMaker) * LGS (Longer Orange 10) -* LGS30 (onger Orange 30) -* UVJ +* LGS30 (Longer Orange 30) +* UVJ (Zip file for manual manipulation format) * Image files (png, jpg, jpeg, gif, bmp) ## Available printers for PrusaSlicer diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 4cbb507..e022816 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -11,11 +11,14 @@ using System.Drawing; using System.Runtime.InteropServices; using Emgu.CV; using Emgu.CV.CvEnum; +using Emgu.CV.Structure; namespace UVtools.Core.Extensions { public static class EmguExtensions { + public static readonly MCvScalar BlackByte = new MCvScalar(0); + public static unsafe byte* GetBytePointer(this Mat mat) { return (byte*)mat.DataPointer.ToPointer(); @@ -181,7 +184,7 @@ namespace UVtools.Core.Extensions /// Blanked public static Mat CloneBlank(this Mat mat) { - return new Mat(new Size(mat.Width, mat.Height), mat.Depth, mat.NumberOfChannels); + return InitMat(mat.Size, mat.Depth, mat.NumberOfChannels); } public static byte GetByte(this Mat mat, int pos) @@ -210,5 +213,12 @@ namespace UVtools.Core.Extensions public static void SetBytes(this Mat mat, byte[] value) => Marshal.Copy(value, 0, mat.DataPointer, value.Length); + public static Mat InitMat(Size size, DepthType depthType = DepthType.Cv8U, int channels = 1) + { + var mat = new Mat(size, depthType, channels); + mat.SetTo(BlackByte); + return mat; + } + } } diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 002a830..05362b4 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -18,6 +18,7 @@ using System.Threading.Tasks; using BinarySerialization; using Emgu.CV; using Emgu.CV.CvEnum; +using Emgu.CV.Structure; using UVtools.Core.Extensions; using UVtools.Core.Operations; @@ -359,13 +360,10 @@ namespace UVtools.Core.FileFormats [FieldOrder(6)] public uint Unknown3 { get; set; } [FieldOrder(7)] public uint Unknown4 { get; set; } - public Mat Decode(byte[] rawImageData) + public unsafe Mat Decode(byte[] rawImageData) { var image = new Mat(new Size((int) ResolutionX, (int) ResolutionY), DepthType.Cv8U, 3); - var span = image.GetPixelSpan(); - //var image = EmguExtensions.CreateMat(out var bytes, new Size((int) ResolutionX, (int) ResolutionY), 3); - //var image = new MatBytes(new Size((int)ResolutionX, (int)ResolutionY), 3); - //var bytes = image.Bytes; + var span = image.GetBytePointer(); int pixel = 0; for (int n = 0; n < rawImageData.Length; n++) @@ -398,13 +396,14 @@ namespace UVtools.Core.FileFormats return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(ImageOffset)}: {ImageOffset}, {nameof(ImageLength)}: {ImageLength}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}"; } - public byte[] Encode(Mat image) + public unsafe byte[] Encode(Mat image) { List rawData = new List(); ushort color15 = 0; uint rep = 0; - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); void RleRGB15() { @@ -434,7 +433,7 @@ namespace UVtools.Core.FileFormats } int pixel = 0; - while (pixel < span.Length) + while (pixel < imageLength) { var ncolor15 = // bgr @@ -541,10 +540,11 @@ namespace UVtools.Core.FileFormats return image; } - public static Mat DecodeCbddlpImage(ChituboxFile parent, uint layerIndex) + public static unsafe Mat DecodeCbddlpImage(ChituboxFile parent, uint layerIndex) { - var image = new Mat(new Size((int)parent.HeaderSettings.ResolutionX, (int)parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); - var span = image.GetPixelSpan(); + var image = EmguExtensions.InitMat(parent.Resolution); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); for (byte bit = 0; bit < parent.AntiAliasing; bit++) { @@ -568,12 +568,12 @@ namespace UVtools.Core.FileFormats n += reps; - if (n == span.Length) + if (n == imageLength) { break; } - if (n > span.Length) + if (n > imageLength) { image.Dispose(); throw new FileLoadException("Error image ran off the end"); @@ -581,7 +581,7 @@ namespace UVtools.Core.FileFormats } } - for (int i = 0; i < span.Length; i++) + for (int i = 0; i < imageLength; i++) { int newC = span[i] * (256 / parent.AntiAliasing); @@ -598,13 +598,10 @@ namespace UVtools.Core.FileFormats return image; } - private Mat DecodeCbtImage(uint layerIndex) + private unsafe Mat DecodeCbtImage(uint layerIndex) { - //Mat image = new Mat(new Size((int)Parent.HeaderSettings.ResolutionX, (int)Parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); - //var bytes = image.GetBytes(); - //var image = EmguExtensions.CreateMat(out var bytes, new Size((int)Parent.HeaderSettings.ResolutionX, (int)Parent.HeaderSettings.ResolutionY)); - var image = new Mat(new Size((int)Parent.HeaderSettings.ResolutionX, (int)Parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); - var span = image.GetPixelSpan(); + var image = EmguExtensions.InitMat(Parent.Resolution); + var span = image.GetBytePointer(); if (Parent.HeaderSettings.EncryptionKey > 0) { @@ -616,7 +613,7 @@ namespace UVtools.Core.FileFormats for (var n = 0; n < EncodedRle.Length; n++) { byte code = EncodedRle[n]; - uint stride = 1; + int stride = 1; if ((code & 0x80) == 0x80) // It's a run { @@ -631,18 +628,17 @@ namespace UVtools.Core.FileFormats } else if ((slen & 0xc0) == 0x80) { - stride = (uint)(((slen & 0x3f) << 8) + EncodedRle[n + 1]); + stride = ((slen & 0x3f) << 8) + EncodedRle[n + 1]; n++; } else if ((slen & 0xe0) == 0xc0) { - stride = (uint)(((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2]); + stride = ((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2]; n += 2; } else if ((slen & 0xf0) == 0xe0) { - stride = (uint)(((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3]); - + stride = ((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3]; n += 3; } else @@ -662,11 +658,11 @@ namespace UVtools.Core.FileFormats if (code == 0) // Ignore blacks, spare cycles { - pixel += (int)stride; + pixel += stride; continue; } - while (stride-- > 0) + for (; stride > 0; stride--) { span[pixel] = code; pixel++; @@ -681,10 +677,11 @@ namespace UVtools.Core.FileFormats return Parent.IsCbtFile ? EncodeCbtImage(image, layerIndex) : EncodeCbddlpImage(image, aaIndex); } - public byte[] EncodeCbddlpImage(Mat image, byte bit) + public unsafe byte[] EncodeCbddlpImage(Mat image, byte bit) { List rawData = new List(); - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); bool obit = false; int rep = 0; @@ -712,7 +709,7 @@ namespace UVtools.Core.FileFormats rawData.Add(by); } - for (int pixel = 0; pixel < span.Length; pixel++) + for (int pixel = 0; pixel < imageLength; pixel++) { var nbit = span[pixel] >= threshold; @@ -743,12 +740,13 @@ namespace UVtools.Core.FileFormats return EncodedRle; } - private byte[] EncodeCbtImage(Mat image, uint layerIndex) + private unsafe byte[] EncodeCbtImage(Mat image, uint layerIndex) { List rawData = new List(); byte color = byte.MaxValue >> 1; uint stride = 0; - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); void AddRep() { @@ -801,7 +799,7 @@ namespace UVtools.Core.FileFormats } - for (int pixel = 0; pixel < span.Length; pixel++) + for (int pixel = 0; pixel < imageLength; pixel++) { var grey7 = (byte) (span[pixel] >> 1); @@ -1424,6 +1422,7 @@ namespace UVtools.Core.FileFormats inputFile.Read(layerData.EncodedRle, 0, (int) layerData.DataSize); + progress++; progress.Token.ThrowIfCancellationRequested(); } @@ -1434,6 +1433,7 @@ namespace UVtools.Core.FileFormats progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); Parallel.For(0, LayerCount, layerIndex => + //for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++) { if (progress.Token.IsCancellationRequested) { diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 74317d0..1d9a059 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -97,11 +97,12 @@ namespace UVtools.Core.FileFormats [FieldLength(nameof(DataSize))] public byte[] EncodedRle { get; set; } - public byte[] Encode(Mat mat) + public unsafe byte[] Encode(Mat mat) { List rawData = new List(); List chunk = new List(); - var spanMat = mat.GetPixelSpan(); + var spanMat = mat.GetBytePointer(); + var imageLength = mat.GetLength(); uint span = 0; byte lc = 0; @@ -114,7 +115,7 @@ namespace UVtools.Core.FileFormats rawData.AddRange(chunk.ToArray()); } - for (int i = 0; i < spanMat.Length; i++) + for (int i = 0; i < imageLength; i++) { byte c = (byte) (spanMat[i] & 0xf0); @@ -137,10 +138,11 @@ namespace UVtools.Core.FileFormats return EncodedRle; } - public Mat Decode(bool consumeRle = true) + public unsafe Mat Decode(bool consumeRle = true) { - Mat mat = new Mat(new Size((int)Parent.HeaderSettings.ResolutionX, (int) Parent.HeaderSettings.ResolutionY), DepthType.Cv8U, 1); - var matSpan = mat.GetPixelSpan(); + var mat = EmguExtensions.InitMat(Parent.Resolution); + var matSpan = mat.GetBytePointer(); + var imageLength = mat.GetLength(); byte last = 0; int span = 0; @@ -158,7 +160,7 @@ namespace UVtools.Core.FileFormats { for(; span > 0; span--) { - if (index >= matSpan.Length) + if (index >= imageLength) { throw new FileLoadException($"'{span}' bytes to many"); } @@ -175,7 +177,7 @@ namespace UVtools.Core.FileFormats for (; span > 0; span--) { - if (index >= matSpan.Length) + if (index >= imageLength) { throw new FileLoadException($"'{span}' bytes to many"); } @@ -183,9 +185,9 @@ namespace UVtools.Core.FileFormats matSpan[index++] = last; } - if (index != matSpan.Length) + if (index != imageLength) { - throw new FileLoadException($"Incomplete buffer, expected: {matSpan.Length}, got: {index}"); + throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {index}"); } @@ -332,13 +334,14 @@ namespace UVtools.Core.FileFormats #region Methods - public byte[] PreviewEncode(Mat mat) + public unsafe byte[] PreviewEncode(Mat mat) { byte[] bytes = new byte[mat.Width * mat.Height * 2]; - var span = mat.GetPixelSpan(); + var span = mat.GetBytePointer(); + var imageLength = mat.GetLength(); int index = 0; - for (int i = 0; i < span.Length; i+=3) + for (int i = 0; i < imageLength; i+=3) { byte b = span[i]; byte g = span[i+1]; @@ -407,10 +410,10 @@ namespace UVtools.Core.FileFormats Debug.WriteLine("-End-"); } - public Mat PreviewDecode(byte []data) + public unsafe Mat PreviewDecode(byte []data) { Mat mat = new Mat((int) HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3); - var span = mat.GetPixelSpan(); + var span = mat.GetBytePointer(); int spanIndex = 0; for (int i = 0; i < data.Length; i+=2) { diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 0debddd..0eaab9c 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -306,11 +306,10 @@ namespace UVtools.Core.FileFormats [FieldOrder(6)] public uint Unknown3 { get; set; } [FieldOrder(7)] public uint Unknown4 { get; set; } - public Mat Decode(byte[] rawImageData) + public unsafe Mat Decode(byte[] rawImageData) { var image = new Mat(new Size((int)ResolutionX, (int)ResolutionY), DepthType.Cv8U, 3); - var span = image.GetPixelSpan(); - + var span = image.GetBytePointer(); int pixel = 0; for (uint n = 0; n < ImageLength; n++) @@ -339,10 +338,12 @@ namespace UVtools.Core.FileFormats return image; } - public static byte[] Encode(Mat image) + public static unsafe byte[] Encode(Mat image) { List rawData = new List(); - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); + ushort color15 = 0; uint rep = 0; @@ -373,7 +374,7 @@ namespace UVtools.Core.FileFormats } } - for (int pixel = 0; pixel < span.Length; pixel += image.NumberOfChannels) + for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels) { var ncolor15 = (span[pixel] >> 3) @@ -461,10 +462,10 @@ namespace UVtools.Core.FileFormats parent.HeaderSettings.LayerOffTime); } - public Mat Decode(uint layerIndex, bool consumeData = true) + public unsafe Mat Decode(uint layerIndex, bool consumeData = true) { - var image = new Mat(new Size((int)Parent.ResolutionX, (int)Parent.ResolutionY), DepthType.Cv8U, 1); - var span = image.GetPixelSpan(); + var image = EmguExtensions.InitMat(Parent.Resolution); + var span = image.GetBytePointer(); if (Parent.HeaderSettings.EncryptionKey > 0) { diff --git a/UVtools.Core/FileFormats/PWSFile.cs b/UVtools.Core/FileFormats/PWSFile.cs index 883892e..a05dc98 100644 --- a/UVtools.Core/FileFormats/PWSFile.cs +++ b/UVtools.Core/FileFormats/PWSFile.cs @@ -284,10 +284,10 @@ namespace UVtools.Core.FileFormats Section = new Section(SectionMark, this); } - public Mat Decode(bool consumeData = true) + public unsafe Mat Decode(bool consumeData = true) { Mat image = new Mat(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); int pixel = 0; for (uint i = 0; i < Data.Length; i += 2) @@ -313,9 +313,10 @@ namespace UVtools.Core.FileFormats return image; } - public static Preview Encode(Mat image) + public static unsafe Preview Encode(Mat image) { - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); Preview preview = new Preview { @@ -326,7 +327,7 @@ namespace UVtools.Core.FileFormats }; int i = 0; - for (int pixel = 0; pixel < span.Length; pixel += image.NumberOfChannels) + for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels) { // BGR int b = span[pixel] >> 3; @@ -424,10 +425,11 @@ namespace UVtools.Core.FileFormats return EncodedRle; } - private Mat DecodePWS() + private unsafe Mat DecodePWS() { - var image = new Mat(new Size((int) Parent.ResolutionX, (int) Parent.ResolutionY), DepthType.Cv8U, 1); - var span = image.GetPixelSpan(); + var image = EmguExtensions.InitMat(Parent.Resolution); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); int index = 0; for (byte bit = 0; bit < Parent.AntiAliasing; bit++) @@ -450,13 +452,13 @@ namespace UVtools.Core.FileFormats pixel += reps; - if (pixel == span.Length) + if (pixel == imageLength) { index++; break; } - if (pixel > span.Length) + if (pixel > imageLength) { image.Dispose(); throw new FileLoadException("Error image ran off the end"); @@ -464,7 +466,7 @@ namespace UVtools.Core.FileFormats } } - for (int i = 0; i < span.Length; i++) + for (int i = 0; i < imageLength; i++) { int newC = span[i] * (256 / Parent.AntiAliasing); @@ -479,10 +481,11 @@ namespace UVtools.Core.FileFormats return image; } - public byte[] EncodePWS(Mat image) + public unsafe byte[] EncodePWS(Mat image) { List rawData = new List(); - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); bool obit; int rep; @@ -516,7 +519,7 @@ namespace UVtools.Core.FileFormats byte threshold = (byte)(256 / Parent.AntiAliasing * aalevel - 1); - for (int pixel = 0; pixel < span.Length; pixel++) + for (int pixel = 0; pixel < imageLength; pixel++) { var nbit = span[pixel] >= threshold; @@ -547,10 +550,11 @@ namespace UVtools.Core.FileFormats return rawData.ToArray(); } - private Mat DecodePW0() + private unsafe Mat DecodePW0() { - var image = new Mat(new Size((int)Parent.ResolutionX, (int)Parent.ResolutionY), DepthType.Cv8U, 1); - var span = image.GetPixelSpan(); + var image = EmguExtensions.InitMat(Parent.Resolution); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); uint n = 0; for (int index = 0; index < EncodedRle.Length; index++) @@ -590,32 +594,33 @@ namespace UVtools.Core.FileFormats n += reps; - if (n == span.Length) + if (n == imageLength) { //index++; break; } - if (n > span.Length) + if (n > imageLength) { image.Dispose(); - throw new FileLoadException($"Error image ran off the end: {n - reps}({reps}) of {span.Length}"); + throw new FileLoadException($"Error image ran off the end: {n - reps}({reps}) of {imageLength}"); } } - if (n != span.Length) + if (n != imageLength) { image.Dispose(); - throw new FileLoadException($"Error image ended short: {n} of {span.Length}"); + throw new FileLoadException($"Error image ended short: {n} of {imageLength}"); } return image; } - public byte[] EncodePW0(Mat image) + public unsafe byte[] EncodePW0(Mat image) { List rawData = new List(); - var span = image.GetPixelSpan(); + var span = image.GetBytePointer(); + var imageLength = image.GetLength(); int lastColor = -1; int reps = 0; @@ -654,7 +659,7 @@ namespace UVtools.Core.FileFormats } } - for (int i = 0; i < span.Length; i++) + for (int i = 0; i < imageLength; i++) { int color = span[i] >> 4; diff --git a/UVtools.GUI/FrmMain.cs b/UVtools.GUI/FrmMain.cs index bc56723..0f6cd03 100644 --- a/UVtools.GUI/FrmMain.cs +++ b/UVtools.GUI/FrmMain.cs @@ -877,11 +877,22 @@ namespace UVtools.GUI { var type = config.GetType(); tw.WriteLine($"[{type.Name}]"); - foreach (var property in type.GetProperties()) + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - tw.WriteLine($"{property.Name} = {property.GetValue(config)}"); + if (property.Name.Equals("Item")) continue; + var value = property.GetValue(config); + switch (value) + { + case null: + continue; + case IList list: + tw.WriteLine($"{property.Name} = {list.Count}"); + break; + default: + tw.WriteLine($"{property.Name} = {value}"); + break; + } } - tw.WriteLine(); } @@ -910,9 +921,21 @@ namespace UVtools.GUI { var type = config.GetType(); sb.AppendLine($"[{type.Name}]"); - foreach (var property in type.GetProperties()) + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - sb.AppendLine($"{property.Name} = {property.GetValue(config)}"); + if (property.Name.Equals("Item")) continue; + var value = property.GetValue(config); + switch (value) + { + case null: + continue; + case IList list: + sb.AppendLine($"{property.Name} = {list.Count}"); + break; + default: + sb.AppendLine($"{property.Name} = {value}"); + break; + } } sb.AppendLine(); diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs index 5d7a38c..92437c3 100644 --- a/UVtools.WPF/MainWindow.Information.cs +++ b/UVtools.WPF/MainWindow.Information.cs @@ -6,9 +6,11 @@ * of this license document, but changing it is not allowed. */ using System; +using System.Collections; using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using Avalonia; using Avalonia.Controls; @@ -178,14 +180,24 @@ namespace UVtools.WPF { var type = config.GetType(); tw.WriteLine($"[{type.Name}]"); - foreach (var property in type.GetProperties()) + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - tw.WriteLine($"{property.Name} = {property.GetValue(config)}"); + if (property.Name.Equals("Item")) continue; + var value = property.GetValue(config); + switch (value) + { + case null: + continue; + case IList list: + tw.WriteLine($"{property.Name} = {list.Count}"); + break; + default: + tw.WriteLine($"{property.Name} = {value}"); + break; + } } - tw.WriteLine(); } - tw.Close(); } } @@ -211,9 +223,21 @@ namespace UVtools.WPF { var type = config.GetType(); sb.AppendLine($"[{type.Name}]"); - foreach (var property in type.GetProperties()) + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - sb.AppendLine($"{property.Name} = {property.GetValue(config)}"); + if (property.Name.Equals("Item")) continue; + var value = property.GetValue(config); + switch (value) + { + case null: + continue; + case IList list: + sb.AppendLine($"{property.Name} = {list.Count}"); + break; + default: + sb.AppendLine($"{property.Name} = {value}"); + break; + } } sb.AppendLine(); @@ -225,14 +249,26 @@ namespace UVtools.WPF public void RefreshProperties() { SlicerProperties.Clear(); - if (!(SlicerFile.Configs is null)) + if (SlicerFile.Configs is null) return; + foreach (var config in SlicerFile.Configs) { - foreach (var config in SlicerFile.Configs) + var type = config.GetType(); + foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { - var type = config.GetType(); - foreach (var property in type.GetProperties()) + if (property.Name.Equals("Item")) continue; + var value = property.GetValue(config); + switch (value) { - SlicerProperties.Add(new SlicerProperty(property.Name, property.GetValue(config, null)?.ToString(), type.Name)); + case null: + continue; + case IList list: + SlicerProperties.Add(new SlicerProperty(property.Name, list.Count.ToString(), + config.GetType().Name)); + break; + default: + SlicerProperties.Add( + new SlicerProperty(property.Name, value.ToString(), config.GetType().Name)); + break; } } }