Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2022-01-05 04:10:46 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-01-05 04:10:46 +0300
commit702b73d5e205a20933d2c2f8d89e9496f8873492 (patch)
treeaad3d486a8d2e10de8aa72f3a00a8ec5328d5cac
parent1a1d40b736159e241db8864fdcef9d332e07fdf6 (diff)
v2.27.5v2.27.5
- **Pixel Arithmetic:** - (Add) Corode: Noise pixel area, defaulting to 3px2 - (Change) Corode: Cryptonumeric random to normal random to speed up calculation - (Change) Fuzzy skin preset: Set a ignore threshold area of 5000px2 - (Improvement) Masking performance and auto crop the layer to speed up the processing when using an "Apply to" other than "All" - (Fix) Some "Apply to" methods was creating a wrong mask with some operators - **CXDLP V3:** - (Fix) Checksum (CRC32) (#389) - (Fix) Software name and material name serialization
-rw-r--r--CHANGELOG.md12
-rw-r--r--Scripts/010 Editor/cxdlp.bt (renamed from Scripts/010 Editor/cxdlp_v2.bt)47
-rw-r--r--UVtools.Core/Converters/NullTerminatedConverter.cs24
-rw-r--r--UVtools.Core/Converters/NullTerminatedLengthConverter.cs31
-rw-r--r--UVtools.Core/Extensions/CryptExtensions.cs4
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs30
-rw-r--r--UVtools.Core/FileFormats/CXDLPFile.cs145
-rw-r--r--UVtools.Core/FileFormats/PhotonWorkshopFile.cs2
-rw-r--r--UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs61
-rw-r--r--UVtools.Core/Objects/ValueDescription.cs1
-rw-r--r--UVtools.Core/Operations/Operation.cs33
-rw-r--r--UVtools.Core/Operations/OperationPixelArithmetic.cs258
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml90
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
15 files changed, 470 insertions, 272 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da5862e..0279b91 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## 05/01/2022 - v2.27.5
+
+- **Pixel Arithmetic:**
+ - (Add) Corode: Noise pixel area, defaulting to 3px2
+ - (Change) Corode: Cryptonumeric random to normal random to speed up calculation
+ - (Change) Fuzzy skin preset: Set a ignore threshold area of 5000px2
+ - (Improvement) Masking performance and auto crop the layer to speed up the processing when using an "Apply to" other than "All"
+ - (Fix) Some "Apply to" methods was creating a wrong mask with some operators
+- **CXDLP V3:**
+ - (Fix) Checksum (CRC32) (#389)
+ - (Fix) Software name and material name serialization
+
## 26/12/2021 - v2.27.4
- **UI:**
diff --git a/Scripts/010 Editor/cxdlp_v2.bt b/Scripts/010 Editor/cxdlp.bt
index 02f6754..2512246 100644
--- a/Scripts/010 Editor/cxdlp_v2.bt
+++ b/Scripts/010 Editor/cxdlp.bt
@@ -5,6 +5,7 @@
// Authors: Julien Delnatte
//------------------------------------------------
// CHANGELOG:
+// Add version 3
// Add uint16 unknown after header (Set to 2)
// Add uint32 modelSize and ubyte model[modelSize] after unknown
// Add ubyte offset[64] after resolutionY (Zeros)
@@ -47,7 +48,10 @@ struct HEADER {
uint16 ResolutionY <fgcolor=cBlack, bgcolor=cWhite>;
ubyte Offset[64];
+} header;
+
+struct PREVIEWS {
RgbPreviewImageRawData preview(116*116*2);
ubyte rn0[2] <fgcolor=cBlack, bgcolor=cRed>;
@@ -56,7 +60,9 @@ struct HEADER {
RgbPreviewImageRawData preview2(290*290*2);
ubyte rn2[2] <fgcolor=cBlack, bgcolor=cRed>;
+} previews;
+struct SLICER_INFO {
uint32 PlateformXLength <fgcolor=cBlack, bgcolor=cWhite>;
wchar_t plateformX[PlateformXLength/2];
@@ -78,7 +84,7 @@ struct HEADER {
uint16 BottomLightPWM <fgcolor=cBlack, bgcolor=cWhite>;
uint16 LightPWM <fgcolor=cBlack, bgcolor=cWhite>;
-} header;
+} slicerInfo;
struct LAYER_DEF {
local int i;
@@ -88,25 +94,23 @@ struct LAYER_DEF {
ubyte rn3[2] <fgcolor=cBlack, bgcolor=cRed>;
} layerDefs;
-local uint versionIdentifier = ReadUInt();
-//Printf( "%d\n", layerDefs.ID[0].layerArea );
-if(header.Version >= 2 && versionIdentifier != layerDefs.ID[0].layerArea){
- struct SLICER_INFO {
+if(header.Version >= 3){
+ struct SLICER_INFO_V3 {
uint32 SoftwareNameSize;
char SoftwareName[SoftwareNameSize];
uint32 MaterialNameSize;
char MaterialName[MaterialNameSize];
//ubyte offset[67-SoftwareNameSize-MaterialNameSize];
//ubyte offset[20];
- uint Padding;
- uint Padding;
- uint Padding;
- uint Padding;
- ubyte Padding;
+ uint Unknown;
+ uint Unknown;
+ uint Unknown;
+ uint Unknown;
+ ubyte Unknown;
ubyte LightPWM;
- ushort Padding;
+ ushort Unknown;
ubyte rn0[2] <fgcolor=cBlack, bgcolor=cRed>;
- } slicerInfo;
+ } slicerInfoV3;
}
@@ -120,6 +124,21 @@ struct LAYERS {
struct FOOTER {
uint32 FooterSize <fgcolor=cBlack, bgcolor=cWhite>;
char Marker[FooterSize] <fgcolor=cBlack, bgcolor=cRed>;
+} footer;
+
+
+//ubyte CheckSum <fgcolor=cBlack, bgcolor=cWhite>;
+//ubyte CheckSum <fgcolor=cBlack, bgcolor=cWhite>;
+//ubyte CheckSum <fgcolor=cBlack, bgcolor=cWhite>;
+//ubyte CheckSum <fgcolor=cBlack, bgcolor=cWhite>;
+uint CheckSum <fgcolor=cBlack, bgcolor=cWhite>;
+
+/*
+local ubyte calculatedChecksum = 0;
+local ulong i = 0;
+for(i = 0; i < FileSize()-4; i++ ){
+ calculatedChecksum ^= ReadByte(i);
+}
- uint CheckSum <fgcolor=cBlack, bgcolor=cWhite>;
-} footer; \ No newline at end of file
+ubyte CalculatedChecksum = calculatedChecksum;
+*/ \ No newline at end of file
diff --git a/UVtools.Core/Converters/NullTerminatedConverter.cs b/UVtools.Core/Converters/NullTerminatedConverter.cs
new file mode 100644
index 0000000..077e87f
--- /dev/null
+++ b/UVtools.Core/Converters/NullTerminatedConverter.cs
@@ -0,0 +1,24 @@
+/*
+ * 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 BinarySerialization;
+
+namespace UVtools.Core.Converters
+{
+ public class NullTerminatedConverter : IValueConverter
+ {
+ public object Convert(object value, object converterParameter, BinarySerializationContext context)
+ {
+ return value.ToString()?.TrimEnd(char.MinValue);
+ }
+
+ public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
+ {
+ return value is null ? null : $"{value}{char.MinValue}";
+ }
+ }
+}
diff --git a/UVtools.Core/Converters/NullTerminatedLengthConverter.cs b/UVtools.Core/Converters/NullTerminatedLengthConverter.cs
new file mode 100644
index 0000000..d097e00
--- /dev/null
+++ b/UVtools.Core/Converters/NullTerminatedLengthConverter.cs
@@ -0,0 +1,31 @@
+/*
+ * 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 BinarySerialization;
+
+namespace UVtools.Core.Converters
+{
+ public class NullTerminatedLengthConverter : IValueConverter
+ {
+ // Read
+ public object Convert(object value, object converterParameter, BinarySerializationContext context)
+ {
+ //var uintValue = System.Convert.ToUInt32(value);
+ //if (uintValue == 0) return 0;
+ //return uintValue - 1;
+ return value;
+ }
+
+ // Write
+ public object ConvertBack(object value, object converterParameter, BinarySerializationContext context)
+ {
+ var uintValue = System.Convert.ToUInt32(value);
+ if (uintValue == 0) return 0;
+ return uintValue + 1;
+ }
+ }
+}
diff --git a/UVtools.Core/Extensions/CryptExtensions.cs b/UVtools.Core/Extensions/CryptExtensions.cs
index 4d9c027..268d8e1 100644
--- a/UVtools.Core/Extensions/CryptExtensions.cs
+++ b/UVtools.Core/Extensions/CryptExtensions.cs
@@ -14,13 +14,13 @@ namespace UVtools.Core.Extensions
{
public static class CryptExtensions
{
- public static SHA1CryptoServiceProvider SHA1 { get; } = new();
+ public static readonly SHA1CryptoServiceProvider SHA1 = new();
public static string ComputeSHA1Hash(byte[] input)
{
return Convert.ToBase64String(SHA1.ComputeHash(input));
}
- public static SHA256 SHA256 { get; } = SHA256.Create();
+ public static readonly SHA256 SHA256 = SHA256.Create();
public static byte[] ComputeSHA256Hash(byte[] input)
{
return SHA256.ComputeHash(input);
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index dc1ec2e..a56da53 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -600,6 +600,25 @@ namespace UVtools.Core.Extensions
}
/// <summary>
+ /// 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
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static Mat Roi(this Mat mat, Size size)
+ {
+ return size.IsEmpty || size == mat.Size ? mat : new Mat(mat, new(Point.Empty, size));
+ }
+
+ /// <summary>
+ /// Gets a Roi from a mat size, but return source when roi is empty or have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="fromMat"></param>
+ /// <returns></returns>
+ public static Mat Roi(this Mat mat, Mat fromMat) => mat.Roi(fromMat.Size);
+
+ /// <summary>
/// Gets a Roi from center, but return source when have same size as source
/// </summary>
/// <param name="mat"></param>
@@ -1304,5 +1323,16 @@ namespace UVtools.Core.Extensions
return CvInvoke.GetStructuringElement(elementShape, new Size(size, size), new Point(-1, -1));
}
#endregion
+
+ #region Disposes
+ /// <summary>
+ /// Dispose this <see cref="Mat"/> if it's a sub matrix / roi
+ /// </summary>
+ /// <param name="mat">Mat to dispose</param>
+ public static void DisposeIfSubMatrix(this Mat mat)
+ {
+ if(mat.IsSubmatrix) mat.Dispose();
+ }
+ #endregion
}
}
diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs
index d766379..469a556 100644
--- a/UVtools.Core/FileFormats/CXDLPFile.cs
+++ b/UVtools.Core/FileFormats/CXDLPFile.cs
@@ -20,6 +20,7 @@ using Emgu.CV;
using Emgu.CV.Structure;
using UVtools.Core.Extensions;
using UVtools.Core.Layers;
+using UVtools.Core.Objects;
using UVtools.Core.Operations;
namespace UVtools.Core.FileFormats
@@ -238,22 +239,27 @@ namespace UVtools.Core.FileFormats
public sealed class SlicerInfoV3
{
- [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint SoftwareNameSize { get; set; } = (uint)About.SoftwareWithVersion.Length;
+ //[FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint SoftwareNameSize { get; set; } = (uint)About.SoftwareWithVersion.Length + 1;
- [FieldOrder(1)] [FieldLength(nameof(SoftwareNameSize))] public string SoftwareName { get; set; } = About.SoftwareWithVersion;
+ [FieldOrder(0)] public NullTerminatedUintStringBigEndian SoftwareName { get; set; } = new(About.SoftwareWithVersion);
- [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint MaterialNameSize { get; set; }
+ //[FieldOrder(2)] [FieldEndianness(Endianness.Big)] public uint MaterialNameSize { get; set; }
- [FieldOrder(3)] [FieldLength(nameof(MaterialNameSize))] public string MaterialName { get; set; }
+ [FieldOrder(1)] public NullTerminatedUintStringBigEndian MaterialName { get; set; } = new();
- [FieldOrder(4)] public uint Padding1 { get; set; }
- [FieldOrder(5)] public uint Padding2 { get; set; }
- [FieldOrder(6)] public uint Padding3 { get; set; }
- [FieldOrder(7)] public uint Padding4 { get; set; }
- [FieldOrder(8)] public byte Padding5 { get; set; }
- [FieldOrder(9)] public byte LightPWM { get; set; } = byte.MaxValue;
- [FieldOrder(10)] public ushort MyControl { get; set; }
- [FieldOrder(11)] public PageBreak PageBreak { get; set; } = new();
+ [FieldOrder(2)] public uint Unknown1 { get; set; }
+ [FieldOrder(3)] public uint Unknown2 { get; set; }
+ [FieldOrder(4)] public uint Unknown3 { get; set; }
+ [FieldOrder(5)] public uint Unknown4 { get; set; }
+ [FieldOrder(6)] public byte Unknown5 { get; set; } = 1;
+ [FieldOrder(7)] public byte LightPWM { get; set; } = byte.MaxValue;
+ [FieldOrder(8)] public ushort Unknown6 { get; set; } = 2;
+ [FieldOrder(9)] public PageBreak PageBreak { get; set; } = new();
+
+ public override string ToString()
+ {
+ return $"{nameof(SoftwareName)}: {SoftwareName}, {nameof(MaterialName)}: {MaterialName}, {nameof(Unknown1)}: {Unknown1}, {nameof(Unknown2)}: {Unknown2}, {nameof(Unknown3)}: {Unknown3}, {nameof(Unknown4)}: {Unknown4}, {nameof(Unknown5)}: {Unknown5}, {nameof(LightPWM)}: {LightPWM}, {nameof(Unknown6)}: {Unknown6}, {nameof(PageBreak)}: {PageBreak}";
+ }
}
#endregion
@@ -426,16 +432,7 @@ namespace UVtools.Core.FileFormats
get => HeaderSettings.Version;
set
{
- if (base.Version == 3)
- {
- base.Version = 2;
- SlicerInfoV3Settings.MyControl = 1;
- }
- else
- {
- base.Version = value;
- }
-
+ base.Version = value;
HeaderSettings.Version = (ushort)base.Version;
}
}
@@ -609,8 +606,8 @@ namespace UVtools.Core.FileFormats
public override string MaterialName
{
- get => SlicerInfoV3Settings.MaterialName;
- set => base.MaterialName = SlicerInfoV3Settings.MaterialName = value;
+ get => SlicerInfoV3Settings.MaterialName.Value;
+ set => base.MaterialName = SlicerInfoV3Settings.MaterialName.Value = value;
}
public override object[] Configs => new object[] { HeaderSettings, SlicerInfoSettings, SlicerInfoV3Settings, FooterSettings };
@@ -702,7 +699,7 @@ namespace UVtools.Core.FileFormats
//Helpers.SerializeWriteFileStream(outputFile, pageBreak);
outputFile.WriteBytes(pageBreak);
- if (HeaderSettings.Version >= 2 && SlicerInfoV3Settings.MyControl > 0)
+ if (HeaderSettings.Version >= 3)
{
Helpers.SerializeWriteFileStream(outputFile, SlicerInfoV3Settings);
}
@@ -845,6 +842,7 @@ namespace UVtools.Core.FileFormats
Helpers.SerializeWriteFileStream(outputFile, FooterSettings);
+ progress.Reset("Calculating checksum");
uint checkSum = CalculateCheckSum(outputFile, false);
outputFile.Write(BitExtensions.ToBytesBigEndian(checkSum));
@@ -866,19 +864,26 @@ namespace UVtools.Core.FileFormats
var position = inputFile.Position;
+ progress.Reset("Validating checksum");
var expectedCheckSum = CalculateCheckSum(inputFile, false, -4);
- inputFile.Seek(3, SeekOrigin.Current);
- byte checkSum = (byte) inputFile.ReadByte();
-
- inputFile.Seek(position, SeekOrigin.Begin);
-
+ uint checkSum;
+ if (HeaderSettings.Version <= 2)
+ {
+ inputFile.Seek(3, SeekOrigin.Current);
+ checkSum = (uint) inputFile.ReadByte();
+ }
+ else
+ {
+ checkSum = inputFile.ReadUIntBigEndian();
+ }
if (expectedCheckSum != checkSum)
{
throw new FileLoadException($"Checksum fails, expecting: {expectedCheckSum} but got: {checkSum}.\n" +
$"Try to reslice the file.", FileFullPath);
}
-
+
+ inputFile.Seek(position, SeekOrigin.Begin);
var previews = new byte[ThumbnailsOriginalSize.Length][];
for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
{
@@ -898,15 +903,12 @@ namespace UVtools.Core.FileFormats
Debug.WriteLine(SlicerInfoSettings);
LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
- uint firstPreLayerArea = inputFile.ReadUIntBigEndian();
- inputFile.Seek(LayerCount * 4 + 2 - 4, SeekOrigin.Current); // Skip pre layers
- uint afterPreLayersUint = inputFile.ReadUIntBigEndian();
- inputFile.Seek(-4, SeekOrigin.Current);
+ inputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
- if (HeaderSettings.Version >= 2 && firstPreLayerArea != afterPreLayersUint) // New informative header v3
+ if (HeaderSettings.Version >= 3) // New informative header v3
{
SlicerInfoV3Settings = Helpers.Deserialize<SlicerInfoV3>(inputFile);
- SlicerInfoV3Settings.MyControl = 1; // To know v3 is present
+ Debug.WriteLine(SlicerInfoV3Settings);
}
@@ -993,34 +995,85 @@ namespace UVtools.Core.FileFormats
outputFile.Seek(offset, SeekOrigin.Begin);
Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ if (HeaderSettings.Version >= 3)
+ {
+ outputFile.Seek(LayerCount * 4 + 2, SeekOrigin.Current); // Skip pre layers
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoV3Settings);
+ }
+
uint checkSum = CalculateCheckSum(outputFile, false, -4);
outputFile.WriteBytes(BitExtensions.ToBytesBigEndian(checkSum));
}
- private byte CalculateCheckSum(FileStream fs, bool restorePosition = true, int offsetSize = 0)
+ private uint CalculateCheckSum(FileStream fs, bool restorePosition = true, int offsetSize = 0)
{
- byte checkSum = 0;
+ uint checkSum = 0;
var position = fs.Position;
var dataSize = fs.Length + offsetSize;
const int bufferSize = 50 * 1024 * 1024;
fs.Seek(0, SeekOrigin.Begin);
- for (
- int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
- chunkSize > 0;
- chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
+ if (HeaderSettings.Version >= 3)
{
- var bytes = fs.ReadBytes(chunkSize);
- for (int i = 0; i < bytes.Length; i++)
+ // https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.Table.cs
+ var table = new uint[256];
+
+ for (uint i = 0; i < 256; i++)
{
- checkSum ^= bytes[i];
+ uint val = i;
+
+ for (int j = 0; j < 8; j++)
+ {
+ if ((val & 0b0000_0001) == 0)
+ {
+ val >>= 1;
+ }
+ else
+ {
+ val = (val >> 1) ^ 0xEDB88320u;
+ }
+ }
+
+ table[i] = val;
+ }
+
+ for (
+ int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
+ chunkSize > 0;
+ chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
+ {
+ var bytes = fs.ReadBytes(chunkSize);
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ // https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Hashing/src/System/IO/Hashing/Crc32.cs
+ byte idx = (byte)checkSum;
+ idx ^= bytes[i];
+ checkSum = table[idx] ^ (checkSum >> 8);
+ }
}
}
+ else
+ {
+ for (
+ int chunkSize = (int)Math.Min(bufferSize, dataSize - fs.Position);
+ chunkSize > 0;
+ chunkSize = (int)Math.Min(chunkSize, dataSize - fs.Position))
+ {
+ var bytes = fs.ReadBytes(chunkSize);
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ checkSum ^= bytes[i];
+ }
+ }
+ }
+
if (restorePosition) fs.Seek(position, SeekOrigin.Begin);
return checkSum;
+
+
}
#endregion
diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
index 322907f..afcb3f3 100644
--- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
+++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs
@@ -43,7 +43,7 @@ namespace UVtools.Core.FileFormats
0
};
- // CRC-16-ANSI (aka CRC-16-IMB) Polynomial: x^16 + x^15 + x^2 + 1
+ // CRC-16-ANSI (aka CRC-16-IBM) Polynomial: x^16 + x^15 + x^2 + 1
public static readonly int[] CRC16Table = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
diff --git a/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs b/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs
new file mode 100644
index 0000000..ff71690
--- /dev/null
+++ b/UVtools.Core/Objects/NullTerminatedUintStringBigEndian.cs
@@ -0,0 +1,61 @@
+/*
+ * 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 BinarySerialization;
+
+namespace UVtools.Core.Objects
+{
+ /// <summary>
+ /// A string that always end with 0x00 if not null
+ /// It contains the string length as uint
+ /// </summary>
+ public sealed class NullTerminatedUintStringBigEndian
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)]
+ public uint SerializedLength { get; set; }
+
+ [FieldOrder(1)] [FieldLength(nameof(SerializedLength))]
+ public string SerializedValue { get; set; }
+
+ [Ignore]
+ public string Value
+ {
+ get => SerializedValue?.TrimEnd(char.MinValue);
+ set => SerializedValue = value is null ? null : $"{value}{char.MinValue}";
+ }
+
+ public NullTerminatedUintStringBigEndian() { }
+
+ public NullTerminatedUintStringBigEndian(string value)
+ {
+ Value = value;
+ }
+
+ public override string ToString() => Value;
+
+ private bool Equals(NullTerminatedUintStringBigEndian other)
+ {
+ return SerializedValue == other.SerializedValue;
+ }
+
+ public bool Equals(string value)
+ {
+ return Value == value;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || obj is NullTerminatedUintStringBigEndian other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return (SerializedValue != null ? SerializedValue.GetHashCode() : 0);
+ }
+ }
+}
diff --git a/UVtools.Core/Objects/ValueDescription.cs b/UVtools.Core/Objects/ValueDescription.cs
index 3f7372c..84bfe3d 100644
--- a/UVtools.Core/Objects/ValueDescription.cs
+++ b/UVtools.Core/Objects/ValueDescription.cs
@@ -7,7 +7,6 @@
*/
using System;
-using System.Collections.Generic;
namespace UVtools.Core.Objects
{
diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs
index 8bc2db6..cee3c13 100644
--- a/UVtools.Core/Operations/Operation.cs
+++ b/UVtools.Core/Operations/Operation.cs
@@ -41,6 +41,7 @@ namespace UVtools.Core.Operations
#region Members
private FileFormat _slicerFile;
+ private Rectangle _originalBoundingRectangle;
private OperationImportFrom _importedFrom = OperationImportFrom.None;
private Rectangle _roi = Rectangle.Empty;
private Point[][] _maskPoints;
@@ -71,11 +72,19 @@ namespace UVtools.Core.Operations
set
{
if(!RaiseAndSetIfChanged(ref _slicerFile, value)) return;
+ OriginalBoundingRectangle = _slicerFile.BoundingRectangle;
InitWithSlicerFile();
}
}
[XmlIgnore]
+ public Rectangle OriginalBoundingRectangle
+ {
+ get => _originalBoundingRectangle;
+ private set => RaiseAndSetIfChanged(ref _originalBoundingRectangle, value);
+ }
+
+ [XmlIgnore]
public object Tag { get; set; }
/// <summary>
@@ -287,6 +296,7 @@ namespace UVtools.Core.Operations
protected Operation(FileFormat slicerFile) : this()
{
_slicerFile = slicerFile;
+ OriginalBoundingRectangle = _slicerFile.BoundingRectangle;
SelectAllLayers();
InitWithSlicerFile();
}
@@ -431,16 +441,29 @@ namespace UVtools.Core.Operations
}
public Size GetRoiSizeOrDefault() => GetRoiSizeOrDefault(SlicerFile.Resolution);
- public Size GetRoiSizeOrDefault(Mat defaultMat) => GetRoiSizeOrDefault(defaultMat.Size);
-
- public Size GetRoiSizeOrDefault(Size defaultSize)
+ public Size GetRoiSizeOrDefault(Mat defaultMat) => defaultMat is null ? GetRoiSizeOrDefault() : GetRoiSizeOrDefault(defaultMat.Size);
+ public Size GetRoiSizeOrDefault(Rectangle fallbackRectangle) => GetRoiSizeOrDefault(fallbackRectangle.Size);
+ public Size GetRoiSizeOrDefault(Size fallbackSize)
{
- return HaveROI && defaultSize != _roi.Size ? _roi.Size : defaultSize;
+ return HaveROI ? _roi.Size : fallbackSize;
}
+
public Mat GetRoiOrDefault(Mat defaultMat)
{
- return HaveROI && defaultMat.Size != _roi.Size ? new Mat(defaultMat, _roi) : defaultMat;
+ return HaveROI && defaultMat.Size != _roi.Size ? defaultMat.Roi(_roi) : defaultMat;
+ }
+
+ public Mat GetRoiOrDefault(Mat defaultMat, Rectangle fallbackRoi)
+ {
+ if (HaveROI && defaultMat.Size != _roi.Size) return defaultMat.Roi(_roi);
+ if (fallbackRoi.IsEmpty) return defaultMat;
+ return defaultMat.Size != fallbackRoi.Size ? defaultMat.Roi(fallbackRoi) : defaultMat;
+ }
+
+ public Mat GetRoiOrVolumeBounds(Mat defaultMat)
+ {
+ return GetRoiOrDefault(defaultMat, _originalBoundingRectangle);
}
public void ClearMasks()
diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs
index 536fe36..a351d01 100644
--- a/UVtools.Core/Operations/OperationPixelArithmetic.cs
+++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs
@@ -10,7 +10,6 @@ using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
-using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
@@ -76,6 +75,7 @@ namespace UVtools.Core.Operations
private short _noiseMinOffset = -128;
private short _noiseMaxOffset = 128;
private byte _noiseThreshold;
+ private ushort _noisePixelArea = 3;
#endregion
@@ -363,6 +363,12 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _noiseThreshold, value);
}
+ public ushort NoisePixelArea
+ {
+ get => _noisePixelArea;
+ set => RaiseAndSetIfChanged(ref _noisePixelArea, value);
+ }
+
public byte Value
{
get => _value;
@@ -505,6 +511,16 @@ namespace UVtools.Core.Operations
#region Methods
+ private Size GetMatSizeCropped(Mat mat = null)
+ {
+ return _applyMethod == PixelArithmeticApplyMethod.All ? GetRoiSizeOrDefault(mat) : GetRoiSizeOrDefault(OriginalBoundingRectangle);
+ }
+
+ private Mat GetMatRoiCropped(Mat mat)
+ {
+ return _applyMethod == PixelArithmeticApplyMethod.All ? GetRoiOrDefault(mat) : GetRoiOrVolumeBounds(mat);
+ }
+
protected override bool ExecuteInternally(OperationProgress progress)
{
Mat patternMat = null;
@@ -536,16 +552,15 @@ namespace UVtools.Core.Operations
_patternAlternate ??= _pattern;
- using var blankMat = new Mat(SlicerFile.Resolution, DepthType.Cv8U, 1);
- patternMat = blankMat.NewBlank();
- patternAlternateMat = blankMat.NewBlank();
- var target = GetRoiOrDefault(blankMat);
+ var target = new Mat(GetMatSizeCropped(), DepthType.Cv8U, 1);
+ patternMat = target.NewBlank();
+ patternAlternateMat = target.NewBlank();
- CvInvoke.Repeat(_pattern, target.Rows / _pattern.Rows + 1, target.Cols / _pattern.Cols + 1, patternMat);
- CvInvoke.Repeat(_patternAlternate, target.Rows / _patternAlternate.Rows + 1, target.Cols / _patternAlternate.Cols + 1, patternAlternateMat);
+ CvInvoke.Repeat(_pattern, (int)Math.Ceiling((double)target.Rows / _pattern.Rows), (int)Math.Ceiling((double)target.Cols / _pattern.Cols), patternMat);
+ CvInvoke.Repeat(_patternAlternate, (int)Math.Ceiling((double)target.Rows / _patternAlternate.Rows), (int)Math.Ceiling((double)target.Cols / _patternAlternate.Cols), patternAlternateMat);
- patternMatMask = new Mat(patternMat, new Rectangle(0, 0, target.Width, target.Height));
- patternAlternateMatMask = new Mat(patternAlternateMat, new Rectangle(0, 0, target.Width, target.Height));
+ patternMatMask = patternMat.Roi(target);
+ patternAlternateMatMask = patternAlternateMat.Roi(target);
/*if (_patternInvert)
{
@@ -553,24 +568,21 @@ namespace UVtools.Core.Operations
CvInvoke.BitwiseNot(patternAlternateMatMask, patternAlternateMatMask);
}*/
}
- else if (_operator is not PixelArithmeticOperators.BitwiseNot
- and not PixelArithmeticOperators.KeepRegion
- and not PixelArithmeticOperators.DiscardRegion)
+ else if (IsUsePatternVisible)
{
- patternMatMask = EmguExtensions.InitMat(HaveROI ? ROI.Size : SlicerFile.Resolution, new MCvScalar(_value));
+ patternMatMask = EmguExtensions.InitMat(GetMatSizeCropped(), new MCvScalar(_value));
}
Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.ParallelOptions, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
- using (var mat = SlicerFile[layerIndex].LayerMat)
+ var layer = SlicerFile[layerIndex];
+ using (var mat = layer.LayerMat)
{
- //Execute(mat, tempMat);
-
using var original = mat.Clone();
- var originalRoi = GetRoiOrDefault(original);
- var target = GetRoiOrDefault(mat);
+ var originalRoi = GetMatRoiCropped(original);
+ var target = GetMatRoiCropped(mat);
Mat tempMat;
if (_usePattern && IsUsePatternVisible)
@@ -599,13 +611,13 @@ namespace UVtools.Core.Operations
applyMask = null;
break;
case PixelArithmeticApplyMethod.Model:
- applyMask = target;
+ applyMask = target.Clone();
break;
case PixelArithmeticApplyMethod.ModelSurface:
case PixelArithmeticApplyMethod.ModelSurfaceAndInset:
if (layerIndex == SlicerFile.LastLayerIndex)
{
- applyMask = target;
+ applyMask = target.Clone();
}
else
{
@@ -613,7 +625,7 @@ namespace UVtools.Core.Operations
// Difference
using var nextMat = SlicerFile[layerIndex + 1].LayerMat;
- var nextMatRoi = GetRoiOrDefault(nextMat);
+ var nextMatRoi = GetMatRoiCropped(nextMat);
CvInvoke.Subtract(target, nextMatRoi, applyMask);
// 1px walls
@@ -638,10 +650,16 @@ namespace UVtools.Core.Operations
break;
case PixelArithmeticApplyMethod.ModelInner:
{
- applyMask = wallThickness <= 0 ? target : new Mat();
+ if (wallThickness <= 0)
+ {
+ applyMask = target.Clone();
+ break;
+ }
+
+ applyMask = new Mat();
int iterations = wallThickness;
- var kernel = Kernel.GetKernel(ref iterations);
- CvInvoke.Erode(target, applyMask, kernel, anchor, iterations, BorderType.Reflect101, default);
+ var kernel = Kernel.GetKernel(ref iterations);
+ CvInvoke.Erode(target, applyMask, kernel, anchor, iterations, BorderType.Reflect101, default);
break;
}
case PixelArithmeticApplyMethod.ModelWalls:
@@ -739,13 +757,49 @@ namespace UVtools.Core.Operations
break;
case PixelArithmeticOperators.Threshold:
var tempThreshold = _thresholdType;
- if (_thresholdType is ThresholdType.Otsu or ThresholdType.Triangle) tempThreshold |= ThresholdType.Binary;
+ if (_thresholdType is ThresholdType.Otsu or ThresholdType.Triangle) tempThreshold = ThresholdType.Binary | tempThreshold;
CvInvoke.Threshold(target, target, _value, _thresholdMaxValue, tempThreshold);
if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask);
break;
case PixelArithmeticOperators.Corrode:
var span = mat.GetDataByteSpan();
- if (HaveROI)
+ var random = new Random();
+
+ var bounds = HaveROI ? ROI : layer.BoundingRectangle;
+
+ for (var y = bounds.Y; y < bounds.Bottom; y += _noisePixelArea)
+ for (var x = bounds.X; x < bounds.Right; x += _noisePixelArea)
+ {
+ byte zoneBrightness = 0;
+ for (var y1 = y; y1 < y + _noisePixelArea && y1 < bounds.Bottom && zoneBrightness < byte.MaxValue; y1++)
+ {
+ var pixelPos = mat.GetPixelPos(x, y1);
+ for (var x1 = x; x1 < x + _noisePixelArea && x1 < bounds.Right && zoneBrightness < byte.MaxValue; x1++)
+ {
+ zoneBrightness = Math.Max(zoneBrightness, span[pixelPos++]);
+ }
+ }
+
+ if (zoneBrightness <= _noiseThreshold) continue;
+ byte brightness = (byte)Math.Clamp(random.Next(_noiseMinOffset, _noiseMaxOffset + 1) + zoneBrightness, byte.MinValue, byte.MaxValue);
+ //byte brightness = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + zoneBrightness, byte.MinValue, byte.MaxValue);
+ for (var y1 = y; y1 < y + _noisePixelArea && y1 < bounds.Bottom; y1++)
+ {
+ var pixelPos = mat.GetPixelPos(x, y1);
+ for (var x1 = x; x1 < x + _noisePixelArea && x1 < bounds.Right; x1++)
+ {
+
+ if (span[pixelPos] <= _noiseThreshold) continue;
+ span[pixelPos++] = brightness;
+ }
+ }
+ }
+
+ if (_applyMethod is not PixelArithmeticApplyMethod.All and not PixelArithmeticApplyMethod.Model) ApplyMask(originalRoi, target, applyMask);
+
+
+ // old method
+ /*if (HaveROI)
{
for (var y = ROI.Y; y < ROI.Bottom; y++)
for (var x = ROI.X; x < ROI.Right; x++)
@@ -766,11 +820,12 @@ namespace UVtools.Core.Operations
for (var i = 0; i < span.Length; i++)
{
- if (span[i] <= _noiseThreshold || spanMask[i] == 0) continue;
- span[i] = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
+ //if (span[i] <= _noiseThreshold || spanMask[i] == 0) continue;
+ //span[i] = (byte)Math.Clamp(RandomNumberGenerator.GetInt32(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
+ span[i] = (byte)Math.Clamp(random.Next(_noiseMinOffset, _noiseMaxOffset + 1) + span[i], byte.MinValue, byte.MaxValue);
}
- }
-
+ }*/
+
break;
case PixelArithmeticOperators.KeepRegion:
{
@@ -818,132 +873,6 @@ namespace UVtools.Core.Operations
public bool IsAlternatePattern(uint layerIndex) => !IsNormalPattern(layerIndex);
- /*public override bool Execute(Mat mat, params object[] arguments)
- {
- using var original = mat.Clone();
- var target = GetRoiOrDefault(mat);
-
- Mat tempMat;
- bool needDispose = false;
- if (arguments is not null && arguments.Length > 0)
- {
- tempMat = arguments[0] as Mat;
- }
- else
- {
- tempMat = GetTempMat();
- needDispose = true;
- }
-
- Mat applyMask;
- var anchor = new Point(-1, -1);
- var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);
- int wallThickness = LayerManager.MutateGetIterationChamfer(
- layerIndex,
- LayerIndexStart,
- LayerIndexEnd,
- (int)_wallThicknessStart,
- (int)_wallThicknessEnd,
- _wallChamfer
- );
-
- switch (_applyMethod)
- {
- case PixelArithmeticApplyMethod.All:
- applyMask = null;
- break;
- case PixelArithmeticApplyMethod.Model:
- applyMask = target.Clone();
- break;
- case PixelArithmeticApplyMethod.ModelInner:
- CvInvoke.Erode(target, applyMask, kernel, anchor, wallThickness, BorderType.Reflect101, default);
- applyMask = target;
- break;
- case PixelArithmeticApplyMethod.ModelWalls:
- applyMask = target;
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- switch (_operator)
- {
- case PixelArithmeticOperators.Set:
- tempMat.CopyTo(target, applyMask);
- break;
- case PixelArithmeticOperators.Add:
- CvInvoke.Add(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.Subtract:
- CvInvoke.Subtract(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.Multiply:
- CvInvoke.Multiply(target, tempMat, target);
- break;
- case PixelArithmeticOperators.Divide:
- CvInvoke.Divide(target, tempMat, target);
- break;
- //case PixelArithmeticOperators.Exponential:
- // CvInvoke.Pow(target, _value, tempMat);
- // if(!_affectBackPixels) ApplyMask(original, mat, original);
- // break;
- case PixelArithmeticOperators.Minimum:
- CvInvoke.Min(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(original, target, original);
- break;
- case PixelArithmeticOperators.Maximum:
- CvInvoke.Max(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(original, target, original);
- break;
- case PixelArithmeticOperators.BitwiseNot:
- CvInvoke.BitwiseNot(target, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseAnd:
- CvInvoke.BitwiseAnd(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseOr:
- CvInvoke.BitwiseOr(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.BitwiseXor:
- CvInvoke.BitwiseXor(target, tempMat, target, applyMask);
- break;
- case PixelArithmeticOperators.Threshold:
- CvInvoke.Threshold(target, target, _value, _thresholdMaxValue, _thresholdType);
- break;
- case PixelArithmeticOperators.AbsDiff:
- CvInvoke.AbsDiff(target, tempMat, target);
- if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(original, target, original);
- break;
- case PixelArithmeticOperators.KeepRegion:
- {
- using var targetClone = target.Clone();
- original.SetTo(EmguExtensions.BlackColor);
- mat.SetTo(EmguExtensions.BlackColor);
- targetClone.CopyTo(target);
- break;
- }
- case PixelArithmeticOperators.DiscardRegion:
- target.SetTo(EmguExtensions.BlackColor);
- break;
- default:
- throw new NotImplementedException();
- }
-
- ApplyMask(original, target);
-
- if (needDispose)
- {
- tempMat?.Dispose();
- }
-
- return true;
- }*/
-
- public Mat GetTempMat() => _operator
- is not PixelArithmeticOperators.BitwiseNot
- and not PixelArithmeticOperators.KeepRegion
- and not PixelArithmeticOperators.DiscardRegion ? EmguExtensions.InitMat(HaveROI ? ROI.Size : SlicerFile.Resolution, new MCvScalar(_value)) : null;
-
public void PresetElephantFootCompensation()
{
SelectBottomLayers();
@@ -977,6 +906,8 @@ namespace UVtools.Core.Operations
NoiseMinOffset = -200;
NoiseMaxOffset = 127;
WallThickness = 6;
+ IgnoreAreaOperator = PixelArithmeticIgnoreAreaOperator.SmallerThan;
+ IgnoreAreaThreshold = 5000;
}
public void PresetStripAntiAliasing()
@@ -1326,7 +1257,7 @@ namespace UVtools.Core.Operations
protected bool Equals(OperationPixelArithmetic other)
{
- return _operator == other._operator && _applyMethod == other._applyMethod && _wallThicknessStart == other._wallThicknessStart && _wallThicknessEnd == other._wallThicknessEnd && _wallChamfer == other._wallChamfer && _ignoreAreaOperator == other._ignoreAreaOperator && _ignoreAreaThreshold == other._ignoreAreaThreshold && _value == other._value && _usePattern == other._usePattern && _thresholdType == other._thresholdType && _thresholdMaxValue == other._thresholdMaxValue && _patternAlternatePerLayersNumber == other._patternAlternatePerLayersNumber && _patternInvert == other._patternInvert && _patternText == other._patternText && _patternTextAlternate == other._patternTextAlternate && Equals(_pattern, other._pattern) && Equals(_patternAlternate, other._patternAlternate) && _patternGenMinBrightness == other._patternGenMinBrightness && _patternGenBrightness == other._patternGenBrightness && _patternGenInfillThickness == other._patternGenInfillThickness && _patternGenInfillSpacing == other._patternGenInfillSpacing && _noiseMinOffset == other._noiseMinOffset && _noiseMaxOffset == other._noiseMaxOffset && _noiseThreshold == other._noiseThreshold;
+ return _operator == other._operator && _applyMethod == other._applyMethod && _wallThicknessStart == other._wallThicknessStart && _wallThicknessEnd == other._wallThicknessEnd && _wallChamfer == other._wallChamfer && _ignoreAreaOperator == other._ignoreAreaOperator && _ignoreAreaThreshold == other._ignoreAreaThreshold && _value == other._value && _usePattern == other._usePattern && _thresholdType == other._thresholdType && _thresholdMaxValue == other._thresholdMaxValue && _patternAlternatePerLayersNumber == other._patternAlternatePerLayersNumber && _patternInvert == other._patternInvert && _patternText == other._patternText && _patternTextAlternate == other._patternTextAlternate && _patternGenMinBrightness == other._patternGenMinBrightness && _patternGenBrightness == other._patternGenBrightness && _patternGenInfillThickness == other._patternGenInfillThickness && _patternGenInfillSpacing == other._patternGenInfillSpacing && _noiseMinOffset == other._noiseMinOffset && _noiseMaxOffset == other._noiseMaxOffset && _noiseThreshold == other._noiseThreshold && _noisePixelArea == other._noisePixelArea;
}
public override bool Equals(object obj)
@@ -1334,29 +1265,27 @@ namespace UVtools.Core.Operations
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
- return Equals((OperationPixelArithmetic)obj);
+ return Equals((OperationPixelArithmetic) obj);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
- hashCode.Add((int)_operator);
- hashCode.Add((int)_applyMethod);
+ hashCode.Add((int) _operator);
+ hashCode.Add((int) _applyMethod);
hashCode.Add(_wallThicknessStart);
hashCode.Add(_wallThicknessEnd);
hashCode.Add(_wallChamfer);
- hashCode.Add((int)_ignoreAreaOperator);
+ hashCode.Add((int) _ignoreAreaOperator);
hashCode.Add(_ignoreAreaThreshold);
hashCode.Add(_value);
hashCode.Add(_usePattern);
- hashCode.Add((int)_thresholdType);
+ hashCode.Add((int) _thresholdType);
hashCode.Add(_thresholdMaxValue);
hashCode.Add(_patternAlternatePerLayersNumber);
hashCode.Add(_patternInvert);
hashCode.Add(_patternText);
hashCode.Add(_patternTextAlternate);
- hashCode.Add(_pattern);
- hashCode.Add(_patternAlternate);
hashCode.Add(_patternGenMinBrightness);
hashCode.Add(_patternGenBrightness);
hashCode.Add(_patternGenInfillThickness);
@@ -1364,6 +1293,7 @@ namespace UVtools.Core.Operations
hashCode.Add(_noiseMinOffset);
hashCode.Add(_noiseMaxOffset);
hashCode.Add(_noiseThreshold);
+ hashCode.Add(_noisePixelArea);
return hashCode.ToHashCode();
}
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 5b360be..8ce455b 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.27.4</Version>
+ <Version>2.27.5</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml
index 27a4bb6..193a74b 100644
--- a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml
@@ -102,47 +102,63 @@
<StackPanel Grid.Row="8" Grid.Column="2" Grid.ColumnSpan="9"
IsVisible="{Binding Operation.IsCorrodeVisible}"
- Spacing="10" Orientation="Horizontal">
+ Spacing="10" Orientation="Vertical">
- <TextBlock
- VerticalAlignment="Center"
- Text="Noise range:"/>
-
- <TextBlock
- VerticalAlignment="Center"
- Text="Min:"/>
+ <StackPanel Spacing="10" Orientation="Horizontal">
- <NumericUpDown
- Minimum="-1000"
- Maximum="1000"
- Width="80"
- Value="{Binding Operation.NoiseMinOffset}"
- ToolTip.Tip="Minimum value of random noise offset"/>
-
- <TextBlock
+ <TextBlock
+ VerticalAlignment="Center"
+ Text="Noise range:"/>
+
+ <TextBlock
VerticalAlignment="Center"
- Text="Max:"/>
-
- <NumericUpDown
- Minimum="-1000"
- Maximum="1000"
- Width="80"
- Value="{Binding Operation.NoiseMaxOffset}"
- ToolTip.Tip="Maximum value of random noise offset"/>
-
- <TextBlock
- VerticalAlignment="Center"
- Text="Threshold:"/>
-
- <NumericUpDown
- Classes="ValueLabel ValueLabel_sun"
- Minimum="0"
- Maximum="255"
- Width="80"
- Value="{Binding Operation.NoiseThreshold}"
- ToolTip.Tip="Only the pixels with brightness above this threshold are processed"/>
+ Text="Min:"/>
- </StackPanel>
+ <NumericUpDown
+ Minimum="-1000"
+ Maximum="1000"
+ Width="80"
+ Value="{Binding Operation.NoiseMinOffset}"
+ ToolTip.Tip="Minimum value of random noise offset"/>
+
+ <TextBlock
+ VerticalAlignment="Center"
+ Text="Max:"/>
+
+ <NumericUpDown
+ Minimum="-1000"
+ Maximum="1000"
+ Width="80"
+ Value="{Binding Operation.NoiseMaxOffset}"
+ ToolTip.Tip="Maximum value of random noise offset"/>
+
+ <TextBlock
+ VerticalAlignment="Center"
+ Text="Threshold:"/>
+
+ <NumericUpDown
+ Classes="ValueLabel ValueLabel_sun"
+ Minimum="0"
+ Maximum="255"
+ Width="80"
+ Value="{Binding Operation.NoiseThreshold}"
+ ToolTip.Tip="Only the pixels with brightness above this threshold are processed"/>
+ </StackPanel>
+
+ <StackPanel Spacing="10" Orientation="Horizontal">
+ <TextBlock
+ VerticalAlignment="Center"
+ Text="Noise pixel area: "/>
+
+ <NumericUpDown
+ Classes="ValueLabel ValueLabel_px2"
+ Minimum="1"
+ Maximum="65535"
+ Width="80"
+ Value="{Binding Operation.NoisePixelArea}"/>
+ </StackPanel>
+
+ </StackPanel>
<TextBlock Grid.Row="8" Grid.Column="0"
VerticalAlignment="Center"
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 717d6a9..e59a5d2 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>2.27.4</Version>
+ <Version>2.27.5</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>