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-07-15 01:24:39 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-07-15 01:24:39 +0300
commit43c1a6a944c643bff7293de2975237d347c31ef4 (patch)
tree5ef7e65a20e8bbf5422cb2d957c6f021d14a16aa
parent54d0cc1776282cf951861d71c7b35caed15870e2 (diff)
v3.5.4v3.5.4
- **PCB Exposure:** - (Add) Parse of deprecated commands (G70, G71, G90, G91) - (Fix) Able to have parameterless macro apertures (#503) - **UI:** - (Add) Menu -> File -> Free unused RAM: Force the garbage collection of all unused objects within the program to free unused memory (RAM). It's never required for the end user run this. The program will automatically take care of it when required. This function is for debug purposes. - (Improvement) Window title bar: Show elapsed minutes and seconds instead of total seconds minutes and second - (Fix) Tool - Mask: Loaded image resolution shows as (unloaded) - (Fix) Applying a large set of modifications in layer depth with pixel editor cause huge memory spike due layer aggregation without disposing, leading to program crash on most cases where RAM is insufficient (#506) - (Upgrade) AvaloniaUI from 0.10.15 to 0.10.16
-rw-r--r--CHANGELOG.md15
-rw-r--r--RELEASE_NOTES.md16
-rw-r--r--UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj2
-rw-r--r--UVtools.Core/Extensions/UnitExtensions.cs7
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs203
-rw-r--r--UVtools.Core/Gerber/Apertures/Aperture.cs22
-rw-r--r--UVtools.Core/Gerber/GerberDocument.cs34
-rw-r--r--UVtools.Core/Operations/OperationMask.cs23
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.InstallerMM/UVtools.InstallerMM.wxs6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs28
-rw-r--r--UVtools.WPF/MainWindow.axaml18
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs8
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj10
14 files changed, 321 insertions, 73 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 770a007..fe32973 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## 14/07/2022 - v3.5.4
+
+- **PCB Exposure:**
+ - (Add) Parse of deprecated commands (G70, G71, G90, G91)
+ - (Fix) Able to have parameterless macro apertures (#503)
+- **UI:**
+ - (Add) Menu -> File -> Free unused RAM: Force the garbage collection of all unused objects within the program to free unused memory (RAM).
+ It's never required for the end user run this. The program will automatically take care of it when required.
+ This function is for debug purposes.
+ - (Improvement) Window title bar: Show elapsed minutes and seconds instead of total seconds minutes and second
+- (Fix) Tool - Mask: Loaded image resolution shows as (unloaded)
+- (Fix) Applying a large set of modifications in layer depth with pixel editor cause huge memory spike due layer aggregation without disposing, leading to program crash on most cases where RAM is insufficient (#506)
+- (Upgrade) AvaloniaUI from 0.10.15 to 0.10.16
+- (Upgrade) .NET from 6.0.6 to 6.0.7
+
## 07/07/2022 - v3.5.3
- (Add) Layer action - Export layers to HTML: Export file information and layers to an html file
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index e79a7ea..7292e31 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,4 +1,12 @@
-- (Add) Layer action - Export layers to HTML: Export file information and layers to an html file
-- (Change) CXDLP: Validate header and footer value must start with "CXSW3D" instead of force equal to "CXSW3DV2" (#501)
-- (Upgrade) .NET from 6.0.5 to 6.0.6
-
+- **PCB Exposure:**
+ - (Add) Parse of deprecated commands (G70, G71, G90, G91)
+ - (Fix) Able to have parameterless macro apertures (#503)
+- **UI:**
+ - (Add) Menu -> File -> Free unused RAM: Force the garbage collection of all unused objects within the program to free unused memory (RAM).
+ It's never required for the end user run this. The program will automatically take care of it when required.
+ This function is for debug purposes.
+ - (Improvement) Window title bar: Show elapsed minutes and seconds instead of total seconds minutes and second
+- (Fix) Tool - Mask: Loaded image resolution shows as (unloaded)
+- (Fix) Applying a large set of modifications in layer depth with pixel editor cause huge memory spike due layer aggregation without disposing, leading to program crash on most cases where RAM is insufficient (#506)
+- (Upgrade) AvaloniaUI from 0.10.15 to 0.10.16
+- (Upgrade) .NET from 6.0.6 to 6.0.7
diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
index c41bc1d..b177036 100644
--- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
+++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj
@@ -38,7 +38,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.15" />
+ <PackageReference Include="Avalonia" Version="0.10.16" />
</ItemGroup>
<ItemGroup>
diff --git a/UVtools.Core/Extensions/UnitExtensions.cs b/UVtools.Core/Extensions/UnitExtensions.cs
index 9e4f355..ddc3295 100644
--- a/UVtools.Core/Extensions/UnitExtensions.cs
+++ b/UVtools.Core/Extensions/UnitExtensions.cs
@@ -11,7 +11,12 @@ namespace UVtools.Core.Extensions;
public static class UnitExtensions
{
/// <summary>
+ /// 1 inch to 1 mm
+ /// </summary>
+ public const double InchToMillimeter = 25.4;
+
+ /// <summary>
/// 1 mm to 1 inch
/// </summary>
- public const double MillimeterInInch = 0.0393700787;
+ public const double MillimeterToInch = 0.0393700787;
} \ No newline at end of file
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 6609773..e62310d 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -11,7 +11,6 @@ using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
using System.Collections;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
@@ -1470,7 +1469,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
}
/// <summary>
- /// Gets the image width resolution
+ /// Gets the image width and height resolution
/// </summary>
public Size Resolution
{
@@ -1533,7 +1532,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
/// <summary>
/// Gets the display diagonal in inch's
/// </summary>
- public float DisplayDiagonalInches => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)) * UnitExtensions.MillimeterInInch, 2);
+ public float DisplayDiagonalInches => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)) * UnitExtensions.MillimeterToInch, 2);
/// <summary>
/// Gets the display ratio
@@ -5534,6 +5533,178 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
progress ??= new OperationProgress();
progress.Reset("Drawings", (uint)drawings.Count);
+ var group1 = drawings
+ .Where(operation => operation.OperationType
+ is PixelOperation.PixelOperationType.Drawing
+ or PixelOperation.PixelOperationType.Text
+ or PixelOperation.PixelOperationType.Eraser)
+ .GroupBy(operation => operation.LayerIndex);
+
+ Parallel.ForEach(group1, CoreSettings.GetParallelOptions(progress), layerOperationGroup =>
+ {
+ var layer = this[layerOperationGroup.Key];
+ using var mat = layer.LayerMat;
+
+ foreach (var operation in layerOperationGroup)
+ {
+ if (operation.OperationType == PixelOperation.PixelOperationType.Drawing)
+ {
+ var operationDrawing = (PixelDrawing)operation;
+
+ if (operationDrawing.BrushSize == 1)
+ {
+ mat.SetByte(operation.Location.X, operation.Location.Y, operationDrawing.Brightness);
+ continue;
+ }
+
+ mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location,
+ new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
+ /*switch (operationDrawing.BrushShape)
+ {
+ case PixelDrawing.BrushShapeType.Square:
+ CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
+ break;
+ case PixelDrawing.BrushShapeType.Circle:
+ CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2,
+ new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }*/
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
+ {
+ var operationText = (PixelText)operation;
+ mat.PutTextRotated(operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.LineAlignment, operationText.Angle);
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser)
+ {
+ using var layerContours = mat.FindContours(out var hierarchy, RetrType.Tree);
+
+ if (mat.GetByte(operation.Location) >= 10)
+ {
+ using var vec = EmguContours.GetContoursInside(layerContours, hierarchy, operation.Location);
+
+ if (vec.Size > 0)
+ {
+ CvInvoke.DrawContours(mat, vec, -1, new MCvScalar(operation.PixelBrightness), -1);
+ }
+ }
+ }
+ }
+
+ layer.LayerMat = mat;
+ progress.LockAndIncrement();
+ });
+
+ var group2 = drawings
+ .Where(operation => operation.OperationType
+ is PixelOperation.PixelOperationType.Supports
+ or PixelOperation.PixelOperationType.DrainHole)
+ .GroupBy(operation => operation.LayerIndex)
+ .OrderByDescending(group => group.Key);
+
+ if (group2.Any())
+ {
+ using var matCache = new MatCacheManager(this, 0, group2.First().Key)
+ {
+ AutoDispose = true,
+ Direction = false
+ };
+ foreach (var layerOperationGroup in group2)
+ {
+ var toProcess = layerOperationGroup.ToList();
+ var drawnSupportLayers = 0;
+ var drawnDrainHoleLayers = 0;
+ for (int operationLayer = (int)layerOperationGroup.Key - 1; operationLayer >= 0 && toProcess.Count > 0; operationLayer--)
+ {
+ progress.ThrowIfCancellationRequested();
+ var layer = this[operationLayer];
+ var mat = matCache.Get1((uint) operationLayer);
+ var isMatModified = false;
+
+ for (var i = toProcess.Count-1; i >= 0; i--)
+ {
+ var operation = toProcess[i];
+ if (operation.OperationType == PixelOperation.PixelOperationType.Supports)
+ {
+ var operationSupport = (PixelSupport) operation;
+
+ int radius = (operationLayer > 10
+ ? Math.Min(operationSupport.TipDiameter + drawnSupportLayers, operationSupport.PillarDiameter)
+ : operationSupport.BaseDiameter) / 2;
+ uint whitePixels;
+
+ int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2);
+ int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2);
+
+ using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
+ {
+ using var matCircleMask = matCircleRoi.NewBlank();
+ CvInvoke.Circle(matCircleMask,
+ new Point(operationSupport.TipDiameter / 2, operationSupport.TipDiameter / 2),
+ operationSupport.TipDiameter / 2, new MCvScalar(operation.PixelBrightness), -1);
+ CvInvoke.BitwiseAnd(matCircleRoi, matCircleMask, matCircleMask);
+ whitePixels = (uint) CvInvoke.CountNonZero(matCircleMask);
+ }
+
+ if (whitePixels >= Math.Pow(operationSupport.TipDiameter, 2) / 3)
+ {
+ //CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(255), -1);
+ if (drawnSupportLayers == 0) continue; // Supports nonexistent, keep digging
+ toProcess.RemoveAt(i);
+ continue; // White area end supporting
+ }
+
+ CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(operation.PixelBrightness), -1, operationSupport.LineType);
+ isMatModified = true;
+ drawnSupportLayers++;
+ }
+ else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole)
+ {
+ var operationDrainHole = (PixelDrainHole) operation;
+
+ int radius = operationDrainHole.Diameter / 2;
+ uint blackPixels;
+
+ int yStart = Math.Max(0, operation.Location.Y - radius);
+ int xStart = Math.Max(0, operation.Location.X - radius);
+
+ using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter)))
+ {
+ using var matCircleRoiInv = new Mat();
+ CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv);
+ using var matCircleMask = matCircleRoi.NewBlank();
+ CvInvoke.Circle(matCircleMask, new Point(radius, radius), radius, EmguExtensions.WhiteColor, -1);
+ CvInvoke.BitwiseAnd(matCircleRoiInv, matCircleMask, matCircleMask);
+ blackPixels = (uint) CvInvoke.CountNonZero(matCircleMask);
+ }
+
+ if (blackPixels >= Math.Pow(operationDrainHole.Diameter, 2) / 3) // Enough area to drain?
+ {
+ if (drawnDrainHoleLayers == 0) continue; // Drill not found a target yet, keep digging
+ toProcess.RemoveAt(i);
+ continue; // Stop drill drain found!
+ }
+
+ CvInvoke.Circle(mat, operation.Location, radius, EmguExtensions.BlackColor, -1, operationDrainHole.LineType);
+ isMatModified = true;
+ drawnDrainHoleLayers++;
+ }
+ }
+
+ if (isMatModified)
+ {
+ layer.LayerMat = mat;
+ }
+ }
+
+ progress += (uint)layerOperationGroup.Count();
+ }
+ }
+
+ /*
+ // Old and memory hunger code
ConcurrentDictionary<uint, Mat> modifiedLayers = new();
for (var i = 0; i < drawings.Count; i++)
{
@@ -5552,18 +5723,18 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location,
new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType);
- /*switch (operationDrawing.BrushShape)
- {
- case PixelDrawing.BrushShapeType.Square:
- CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
- break;
- case PixelDrawing.BrushShapeType.Circle:
- CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2,
- new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }*/
+ //switch (operationDrawing.BrushShape)
+ //{
+ // case PixelDrawing.BrushShapeType.Square:
+ // CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
+ // break;
+ // case PixelDrawing.BrushShapeType.Circle:
+ // CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2,
+ // new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType);
+ // break;
+ // default:
+ // throw new ArgumentOutOfRangeException();
+ //}
}
else if (operation.OperationType == PixelOperation.PixelOperationType.Text)
{
@@ -5666,7 +5837,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
progress.LockAndIncrement();
});
-
+ */
}
#endregion
} \ No newline at end of file
diff --git a/UVtools.Core/Gerber/Apertures/Aperture.cs b/UVtools.Core/Gerber/Apertures/Aperture.cs
index 39bff36..071ef7f 100644
--- a/UVtools.Core/Gerber/Apertures/Aperture.cs
+++ b/UVtools.Core/Gerber/Apertures/Aperture.cs
@@ -53,8 +53,8 @@ public abstract class Aperture
public static Aperture? Parse(string line, GerberDocument document)
{
- var match = Regex.Match(line, @"\%ADD(\d+)(\S+),(\S+)\*\%");
- if (!match.Success || match.Groups.Count < 4) return null;
+ var match = Regex.Match(line, @"\%ADD(\d+)(\w+),?(\S+)?\*\%");
+ if (!match.Success || match.Groups.Count < 3) return null;
if (!int.TryParse(match.Groups[1].Value, out var index)) return null;
//if (!char.TryParse(match.Groups[2].Value, out var type)) return null;
@@ -63,11 +63,13 @@ public abstract class Aperture
{
case "C":
{
+ if (match.Groups.Count < 4) return null;
if (!double.TryParse(match.Groups[3].Value, out var diameter)) return null;
return new CircleAperture(document, index, diameter);
}
case "O":
{
+ if (match.Groups.Count < 4) return null;
var split = match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length < 2) return null;
if (!float.TryParse(split[0], out var width)) return null;
@@ -77,6 +79,7 @@ public abstract class Aperture
}
case "R":
{
+ if (match.Groups.Count < 4) return null;
var split = match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length < 2) return null;
if (!float.TryParse(split[0], out var width)) return null;
@@ -86,6 +89,7 @@ public abstract class Aperture
}
case "P":
{
+ if (match.Groups.Count < 4) return null;
var split = match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (split.Length < 2) return null;
if (!double.TryParse(split[0], out var diameter)) return null;
@@ -97,11 +101,15 @@ public abstract class Aperture
{
if (!document.Macros.TryGetValue(match.Groups[2].Value, out var macro)) return null;
macro = macro.Clone();
- var parseLine = line.TrimEnd('%', '*');
- var commaIndex = parseLine.IndexOf(',')+1;
- if (commaIndex == 0) return null;
- parseLine = parseLine[commaIndex..];
- var args = new[] {"0"}.Concat(parseLine.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).ToArray();
+ //var parseLine = line.TrimEnd('%', '*');
+ //var commaIndex = parseLine.IndexOf(',')+1;
+ //parseLine = parseLine[commaIndex..];
+ string[] args = { "0" };
+ if (match.Groups.Count >= 4)
+ {
+ args = args.Concat(match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).ToArray();
+ }
+
foreach (var primitive in macro)
{
primitive.ParseExpressions(document, args);
diff --git a/UVtools.Core/Gerber/GerberDocument.cs b/UVtools.Core/Gerber/GerberDocument.cs
index 5bde5bf..2e20db4 100644
--- a/UVtools.Core/Gerber/GerberDocument.cs
+++ b/UVtools.Core/Gerber/GerberDocument.cs
@@ -102,6 +102,32 @@ public class GerberDocument
continue;
}
+ if (line.StartsWith("G70"))
+ {
+ document.UnitType = GerberUnitType.Inch;
+ continue;
+ }
+
+
+ if (line.StartsWith("G71"))
+ {
+ document.UnitType = GerberUnitType.Millimeter;
+ continue;
+ }
+
+ if (line.StartsWith("G90"))
+ {
+ document.PositionType = GerberPositionType.Absolute;
+ continue;
+ }
+
+
+ if (line.StartsWith("G91"))
+ {
+ document.PositionType = GerberPositionType.Relative;
+ continue;
+ }
+
if (line.StartsWith("%FS") && line.Length >= FSlength)
{
// %FSLAX34Y34*%
@@ -379,25 +405,25 @@ public class GerberDocument
public float GetMillimeters(float size)
{
if (UnitType == GerberUnitType.Millimeter) return size;
- return size * 25.4f;
+ return size * (float)UnitExtensions.InchToMillimeter;
}
public double GetMillimeters(double size)
{
if (UnitType == GerberUnitType.Millimeter) return size;
- return size * 25.4;
+ return size * UnitExtensions.InchToMillimeter;
}
public SizeF GetMillimeters(SizeF size)
{
if (UnitType == GerberUnitType.Millimeter) return size;
- return new SizeF(size.Width * 25.4f, size.Height * 25.4f);
+ return new SizeF(size.Width * (float)UnitExtensions.InchToMillimeter, size.Height * (float)UnitExtensions.InchToMillimeter);
}
public PointF GetMillimeters(PointF point)
{
if (UnitType == GerberUnitType.Millimeter) return point;
- return new PointF(point.X * 25.4f, point.Y * 25.4f);
+ return new PointF(point.X * (float)UnitExtensions.InchToMillimeter, point.Y * (float)UnitExtensions.InchToMillimeter);
}
public static Point PositionMmToPx(PointF atMm, SizeF xyPpmm)
diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs
index c22a104..348f3de 100644
--- a/UVtools.Core/Operations/OperationMask.cs
+++ b/UVtools.Core/Operations/OperationMask.cs
@@ -8,9 +8,12 @@
using Emgu.CV;
using System;
+using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
+using Emgu.CV.CvEnum;
+using Emgu.CV.DepthAI;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
@@ -66,6 +69,26 @@ public class OperationMask : Operation
#region Methods
+ /// <summary>
+ /// Loads mask from a image file
+ /// </summary>
+ /// <param name="filePath"></param>
+ /// <param name="invertMask"></param>
+ /// <param name="maskSize"></param>
+ public void LoadFromFile(string filePath, bool invertMask = false, Size maskSize = default)
+ {
+ Mask = CvInvoke.Imread(filePath, ImreadModes.Grayscale);
+ if (maskSize.Width > 0 && maskSize.Height > 0 && Mask.Size != maskSize)
+ {
+ CvInvoke.Resize(Mask, Mask, maskSize);
+ }
+
+ if (invertMask)
+ {
+ InvertMask();
+ }
+ }
+
public void InvertMask()
{
if (!HaveInputMask) return;
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 19602bf..11a0667 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>3.5.3</Version>
+ <Version>3.5.4</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
index b211267..f227341 100644
--- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs
+++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
@@ -2,7 +2,7 @@
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ComponentRules="OneToOne"?>
<!-- SourceDir instructs IsWiX the location of the directory that contains files for this merge module -->
- <?define SourceDir="..\publish\UVtools_win-x64_v3.5.3"?>
+ <?define SourceDir="..\publish\UVtools_win-x64_v3.5.4"?>
<Module Id="UVtools" Language="1033" Version="1.0.0.0">
<Package Id="12aaa1cf-ff06-4a02-abd5-2ac01ac4f83b" Manufacturer="PTRTECH" InstallerVersion="200" Keywords="MSLA, DLP" Description="MSLA/DLP, file analysis, repair, conversion and manipulation" InstallScope="perMachine" Platform="x64" />
<Directory Id="TARGETDIR" Name="SourceDir">
@@ -1557,8 +1557,8 @@
<Component Id="owc3110926C9437495884D1A256F611E7C5" Guid="3110926C-9437-4958-84D1-A256F611E7C5">
<File Id="owf3110926C9437495884D1A256F611E7C5" Source="$(var.SourceDir)\Microsoft.Toolkit.HighPerformance.dll" KeyPath="yes" />
</Component>
- <Component Id="owcCD68D9AAF66447D9A67F431CEFC402E2" Guid="CD68D9AA-F664-47D9-A67F-431CEFC402E2">
- <File Id="owfCD68D9AAF66447D9A67F431CEFC402E2" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.622.26707.dll" KeyPath="yes" />
+ <Component Id="owc6FEB071413A045D9BB5F9C092F36EA8A" Guid="6FEB0714-13A0-45D9-BB5F-9C092F36EA8A">
+ <File Id="owf6FEB071413A045D9BB5F9C092F36EA8A" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.722.32202.dll" KeyPath="yes" />
</Component>
</Directory>
<Directory Id="ProgramMenuFolder">
diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
index 16d5911..40b7923 100644
--- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
@@ -53,7 +53,7 @@ public class ToolMaskControl : ToolControl
}
public string InfoPrinterResolutionStr => $"Printer resolution: {App.SlicerFile.Resolution}";
- public string InfoMaskResolutionStr => $"Mask resolution: "+ (Operation.HaveMask ? Operation.Mask.Size.ToString() : "(Unloaded)");
+ public string InfoMaskResolutionStr => $"Mask resolution: "+ (Operation.HaveInputMask ? Operation.Mask.Size.ToString() : "(Unloaded)");
public Bitmap MaskImage
{
@@ -106,28 +106,7 @@ public class ToolMaskControl : ToolControl
try
{
- Operation.Mask = CvInvoke.Imread(result[0], ImreadModes.Grayscale);
- var roi = App.MainWindow.ROI;
- if (roi.IsEmpty)
- {
- if (Operation.Mask.Size != App.SlicerFile.Resolution)
- {
- CvInvoke.Resize(Operation.Mask, Operation.Mask, App.SlicerFile.Resolution);
- }
- }
- else
- {
- if (Operation.Mask.Size != roi.Size)
- {
- CvInvoke.Resize(Operation.Mask, Operation.Mask, roi.Size);
- }
- }
-
- if (_isMaskInverted)
- {
- Operation.InvertMask();
- }
-
+ Operation.LoadFromFile(result[0], _isMaskInverted, App.MainWindow.ROI.Size.IsEmpty ? SlicerFile.Resolution : App.MainWindow.ROI.Size);
MaskImage = Operation.Mask.ToBitmap();
}
catch (Exception e)
@@ -164,8 +143,7 @@ public class ToolMaskControl : ToolControl
CvInvoke.Circle(Operation.Mask, center, (int)i, new MCvScalar(color), 2);
}
- if (_isMaskInverted)
- Operation.InvertMask();
+ if (_isMaskInverted) Operation.InvertMask();
MaskImage = Operation.Mask.ToBitmap();
}
} \ No newline at end of file
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index ed04fb6..9aeb018 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -115,13 +115,21 @@
Items="{Binding MenuFileConvertItems}"
i:MenuItem.Icon="fa-solid fa-exchange-alt"/>
+ <MenuItem Name="MainMenu.File.FreeUnusedRAM"
+ Header="Free unused RAM"
+ ToolTip.Tip="Force the garbage collection of all unused objects within the program to free unused memory (RAM).
+&#x0a;It's never required for the end user run this. The program will automatically take care of it when required.
+&#x0a;This function is for debug purposes."
+ Command="{Binding OnMenuFileFreeUnusedRAM}"
+ i:MenuItem.Icon="fa-solid fa-memory"/>
+
<Separator/>
- <MenuItem Name="MainMenu.File.Fullscreen"
- Header="_Fullscreen"
- InputGesture="F11" HotKey="F11"
- Command="{Binding OnMenuFileFullscreen}"
- i:MenuItem.Icon="fa-solid fa-window-maximize"/>
+ <MenuItem Name="MainMenu.File.Fullscreen"
+ Header="_Fullscreen"
+ InputGesture="F11" HotKey="F11"
+ Command="{Binding OnMenuFileFullscreen}"
+ i:MenuItem.Icon="fa-solid fa-window-maximize"/>
<MenuItem Name="MainMenu.File.Settings"
Header="_Settings"
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index db22d89..e5c7d0c 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -1069,6 +1069,12 @@ public partial class MainWindow : WindowEx
ResetDataContext();
}
+ public void OnMenuFileFreeUnusedRAM()
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+
public void OnMenuFileFullscreen()
{
WindowState = WindowState == WindowState.FullScreen ? WindowState.Maximized : WindowState.FullScreen;
@@ -1233,7 +1239,7 @@ public partial class MainWindow : WindowEx
if (IsFileLoaded)
{
- title += $"File: {SlicerFile.Filename} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) ";
+ title += $"File: {SlicerFile.Filename} ({LastStopWatch.Elapsed.Minutes}m {LastStopWatch.Elapsed.Seconds}s) ";
}
title += $"Version: {About.VersionStr} RAM: {SizeExtensions.SizeSuffix(Environment.WorkingSet)}";
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index fe54b0d..e7f2cf2 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>3.5.3</Version>
+ <Version>3.5.4</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -39,10 +39,10 @@
<NoWarn>1701;1702;</NoWarn>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.15" />
- <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.15" />
- <PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
- <PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
+ <PackageReference Include="Avalonia" Version="0.10.16" />
+ <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.16" />
+ <PackageReference Include="Avalonia.Desktop" Version="0.10.16" />
+ <PackageReference Include="Avalonia.Diagnostics" Version="0.10.16" />
<PackageReference Include="MessageBox.Avalonia" Version="2.0.1" />
<PackageReference Include="Projektanker.Icons.Avalonia" Version="5.1.0" />
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="5.1.0" />