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>2021-11-23 05:28:31 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2021-11-23 05:28:31 +0300
commitc71ff0dd923b8e9b2f40178621008cbb3f5ae8dc (patch)
treeb814abe6d9fce37f2414f74d9951fd0a9ab46f6e
parent6f6b7c07e58a385efac7c2c025d915f60eb923d6 (diff)
v2.25.1v2.25.1
- **Change resolution:** - (Add) Presets: 5K UHD, 6K and 8K UHD - (Add) Resulting pixel ratio information - (Add) Fix the pixel ratio by resize the layers images with the proposed ratio to match the new resolution - (Fix) New images could have noise when processed on linux and macos - (Add) Layer slider debounce time to render the image [Configurable] (#343) - (Fix) CTB v1: Incorrect getter for the LightOffDelay - (Fix) Reallocating layers were not notifying nor updating the layer collection about the changes, leading to wrong layer count - (Fix) Undo and redo now also reverts the file resolution when changed
-rw-r--r--CHANGELOG.md14
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs86
-rw-r--r--UVtools.Core/Extensions/SizeExtensions.cs25
-rw-r--r--UVtools.Core/FileFormats/ChituboxFile.cs21
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs3
-rw-r--r--UVtools.Core/Layers/LayerManager.cs96
-rw-r--r--UVtools.Core/Managers/ClipboardManager.cs22
-rw-r--r--UVtools.Core/Operations/OperationChangeResolution.cs82
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs16
-rw-r--r--UVtools.Core/UVtools.Core.csproj4
-rw-r--r--UVtools.WPF/Assets/Styles/Styles.xaml6
-rw-r--r--UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml94
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs2
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs2
-rw-r--r--UVtools.WPF/MainWindow.axaml4
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs11
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj2
-rw-r--r--UVtools.WPF/Windows/AboutWindow.axaml3
18 files changed, 357 insertions, 136 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f682326..c25a270 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# Changelog
+## 23/11/2021 - v2.25.1
+
+- **Change resolution:**
+ - (Add) Presets: 5K UHD, 6K and 8K UHD
+ - (Add) Resulting pixel ratio information
+ - (Add) Fix the pixel ratio by resize the layers images with the proposed ratio to match the new resolution
+ - (Fix) New images could have noise when processed on linux and macos
+- (Add) Layer slider debounce time to render the image [Configurable] (#343)
+- (Fix) CTB v1: Incorrect getter for the LightOffDelay
+- (Fix) Reallocating layers were not notifying nor updating the layer collection about the changes, leading to wrong layer count
+- (Fix) Undo and redo now also reverts the file resolution when changed
+
## 18/11/2021 - v2.25.0
- **File formats:**
@@ -7,7 +19,7 @@
- (Add) More abstraction on partial save
- **Scripting:**
- (Add) ScriptOpenFolderDialogInput - Selects a folder path
- - (Add) ScriptOpenFileDialogInput - Selectes a file to open
+ - (Add) ScriptOpenFileDialogInput - Selects a file to open
- (Add) ScriptSaveFileDialogInput - Selects a file to save
- (Add) [UNSAVED] tag to the title bar when there are unsaved changes on the current session
- (Improvement) Better handling of empty images on the UI
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index e41eb34..60d6e90 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -473,6 +473,30 @@ namespace UVtools.Core.Extensions
#region Copy methods
/// <summary>
+ /// Copy a region from <see cref="Mat"/> to center of other <see cref="Mat"/>
+ /// </summary>
+ /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
+ /// <param name="size">Size of the center offset</param>
+ /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
+ public static void CopyCenterToCenter(this Mat src, Size size, Mat dst)
+ {
+ var srcRoi = src.RoiFromCenter(size);
+ CopyToCenter(srcRoi, dst);
+ }
+
+ /// <summary>
+ /// Copy a region from <see cref="Mat"/> to center of other <see cref="Mat"/>
+ /// </summary>
+ /// <param name="src">Source <see cref="Mat"/> to be copied to</param>
+ /// <param name="region">Region to copy</param>
+ /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
+ public static void CopyRegionToCenter(this Mat src, Rectangle region, Mat dst)
+ {
+ var srcRoi = src.Roi(region);
+ CopyToCenter(srcRoi, dst);
+ }
+
+ /// <summary>
/// Copy a <see cref="Mat"/> to center of other <see cref="Mat"/>
/// </summary>
/// <param name="src">Source <see cref="Mat"/> to be copied to</param>
@@ -481,20 +505,30 @@ namespace UVtools.Core.Extensions
{
var srcStep = src.GetRealStep();
var dstStep = dst.GetRealStep();
+ var dx = Math.Abs(dstStep - srcStep) / 2;
+ var dy = Math.Abs(dst.Height - src.Height) / 2;
+
+ if (src.Size == dst.Size)
+ {
+ src.CopyTo(dst);
+ return;
+ }
+
if (dstStep > srcStep && dst.Height > src.Height)
{
- var dx = Math.Abs(dstStep - srcStep) / 2;
- var dy = Math.Abs(dst.Height - src.Height) / 2;
- Mat m = new(dst, new Rectangle(dx, dy, src.Width, src.Height));
- src.CopyTo(m);
+ using var dstRoi = dst.Roi(new Rectangle(dx, dy, src.Width, src.Height));
+ src.CopyTo(dstRoi);
+ return;
}
- else if (dstStep < srcStep && dst.Height < src.Height)
+
+ if (dstStep < srcStep && dst.Height < src.Height)
{
- var dx = Math.Abs(dstStep - srcStep) / 2;
- var dy = Math.Abs(dst.Height - src.Height) / 2;
- Mat m = new(src, new Rectangle(dx, dy, dst.Width, dst.Height));
- m.CopyTo(dst);
+ using var srcRoi = src.Roi(new Rectangle(dx, dy, dst.Width, dst.Height));
+ srcRoi.CopyTo(dst);
+ return;
}
+
+ throw new InvalidOperationException("Unable to copy, out of bounds");
}
public static void CopyAreasSmallerThan(this Mat src, double threshold, Mat dst)
@@ -564,29 +598,47 @@ namespace UVtools.Core.Extensions
}
/// <summary>
+ /// Gets a Roi from center, but return source when have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="size"></param>
+ /// <returns></returns>
+ public static Mat RoiFromCenter(this Mat mat, Size size)
+ {
+ if(mat.Size == size) return mat;
+
+ var newRoi = mat.Roi(new Rectangle(
+ mat.Width / 2 - size.Width / 2,
+ mat.Height / 2 - size.Height / 2,
+ size.Width,
+ size.Height
+ ));
+
+ return newRoi;
+ }
+
+ /// <summary>
/// Gets a new mat obtained from it center at a target size and roi
/// </summary>
/// <param name="mat"></param>
/// <param name="targetSize"></param>
/// <param name="roi"></param>
/// <returns></returns>
- public static Mat NewRoiFromCenter(this Mat mat, Size targetSize, Rectangle roi)
+ public static Mat NewMatFromCenterRoi(this Mat mat, Size targetSize, Rectangle roi)
{
if (targetSize == mat.Size) return mat.Clone();
var newMat = InitMat(targetSize);
-
- var roiMat = new Mat(mat, roi);
-
-
+ var roiMat = mat.Roi(roi);
+
//int xStart = mat.Width / 2 - targetSize.Width / 2;
//int yStart = mat.Height / 2 - targetSize.Height / 2;
-
- var newMatRoi = new Mat(newMat, new Rectangle(
+ var newMatRoi = newMat.RoiFromCenter(roi.Size);
+ /*var newMatRoi = new Mat(newMat, new Rectangle(
targetSize.Width / 2 - roi.Width / 2,
targetSize.Height / 2 - roi.Height / 2,
roi.Width,
roi.Height
- ));
+ ));*/
roiMat.CopyTo(newMatRoi);
return newMat;
}
diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs
index 4c320bd..bf600ff 100644
--- a/UVtools.Core/Extensions/SizeExtensions.cs
+++ b/UVtools.Core/Extensions/SizeExtensions.cs
@@ -6,9 +6,7 @@
* of this license document, but changing it is not allowed.
*/
using System;
-using System.Collections.Generic;
using System.Drawing;
-using System.Text;
namespace UVtools.Core.Extensions
{
@@ -43,10 +41,25 @@ namespace UVtools.Core.Extensions
SizeSuffixes[mag]);
}
- public static Size Inflate(this Size size, Size otherSize) => new (size.Width + otherSize.Width, size.Height + otherSize.Height);
- public static Size Inflate(this Size size) => size.Inflate(size);
- public static Size Inflate(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels);
- public static Size Inflate(this Size size, int width, int height) => new (size.Width + width, size.Height + height);
+ public static Size Add(this Size size, Size otherSize) => new (size.Width + otherSize.Width, size.Height + otherSize.Height);
+ public static Size Add(this Size size) => size.Add(size);
+ public static Size Add(this Size size, int pixels) => new (size.Width + pixels, size.Height + pixels);
+ public static Size Add(this Size size, int width, int height) => new (size.Width + width, size.Height + height);
+
+ public static Size Subtract(this Size size, Size otherSize) => new(size.Width - otherSize.Width, size.Height - otherSize.Height);
+ public static Size Subtract(this Size size) => size.Subtract(size);
+ public static Size Subtract(this Size size, int pixels) => new(size.Width - pixels, size.Height - pixels);
+ public static Size Subtract(this Size size, int width, int height) => new(size.Width - width, size.Height - height);
+
+ public static Size Multiply(this Size size, SizeF otherSize) => new((int)(size.Width * otherSize.Width), (int)(size.Height * otherSize.Height));
+ public static Size Multiply(this Size size) => size.Multiply(size);
+ public static Size Multiply(this Size size, double dxy) => new((int)(size.Width * dxy), (int)(size.Height * dxy));
+ public static Size Multiply(this Size size, double dx, double dy) => new((int)(size.Width * dx), (int)(size.Height * dy));
+
+ public static Size Divide(this Size size, SizeF otherSize) => new((int)(size.Width / otherSize.Width), (int)(size.Height / otherSize.Height));
+ public static Size Divide(this Size size) => size.Divide(size);
+ public static Size Divide(this Size size, double dxy) => new((int)(size.Width / dxy), (int)(size.Height / dxy));
+ public static Size Divide(this Size size, double dx, double dy) => new((int)(size.Width / dx), (int)(size.Height / dy));
/// <summary>
/// Gets if this size have a zero value on width or height
diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs
index 2137b8f..f032d6d 100644
--- a/UVtools.Core/FileFormats/ChituboxFile.cs
+++ b/UVtools.Core/FileFormats/ChituboxFile.cs
@@ -1799,7 +1799,7 @@ namespace UVtools.Core.FileFormats
}
//uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings);
- LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount];
+ LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, LayerCount];
using var outputFile = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write);
outputFile.Seek(Helpers.Serializer.SizeOf(HeaderSettings), SeekOrigin.Begin);
@@ -1869,7 +1869,7 @@ namespace UVtools.Core.FileFormats
HeaderSettings.LayersDefinitionOffsetAddress = (uint)outputFile.Position;
uint layerDefSize = (uint)Helpers.Serializer.SizeOf(new LayerDef());
//uint layerDefCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + layerDefSize * HeaderSettings.LayerCount * HeaderSettings.AntiAliasLevel;
+ uint layerDataCurrentOffset = HeaderSettings.LayersDefinitionOffsetAddress + layerDefSize * LayerCount * HeaderSettings.AntiAliasLevel;
var layersHash = new Dictionary<string, LayerDef>();
progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
@@ -1932,7 +1932,7 @@ namespace UVtools.Core.FileFormats
}
outputFile.Seek(HeaderSettings.LayersDefinitionOffsetAddress +
- aaIndex * HeaderSettings.LayerCount * layerDefSize +
+ aaIndex * LayerCount * layerDefSize +
layerDefSize * layerIndex
, SeekOrigin.Begin);
Helpers.SerializeWriteFileStream(outputFile, layerDef);
@@ -2047,17 +2047,18 @@ namespace UVtools.Core.FileFormats
}
}
- LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, HeaderSettings.LayerCount];
- var layerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDefEx[HeaderSettings.LayerCount] : null;
+ LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
+ LayerDefinitions = new LayerDef[HeaderSettings.AntiAliasLevel, LayerCount];
+ var layerDefinitionsEx = HeaderSettings.Version >= 3 ? new LayerDefEx[LayerCount] : null;
uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
- progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.AntiAliasLevel * HeaderSettings.LayerCount);
+ progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.AntiAliasLevel * LayerCount);
for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
{
Debug.WriteLine($"-Image GROUP {aaIndex}-");
- for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
progress.Token.ThrowIfCancellationRequested();
inputFile.Seek(layerOffset, SeekOrigin.Begin);
@@ -2087,8 +2088,6 @@ namespace UVtools.Core.FileFormats
}
}
- LayerManager.Init(HeaderSettings.LayerCount, DecodeType == FileDecodeType.Partial);
-
if (DecodeType == FileDecodeType.Full)
{
progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
@@ -2148,7 +2147,7 @@ namespace UVtools.Core.FileFormats
uint layerOffset = HeaderSettings.LayersDefinitionOffsetAddress;
for (byte aaIndex = 0; aaIndex < HeaderSettings.AntiAliasLevel; aaIndex++)
{
- for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
var layer = this[layerIndex];
LayerDefinitions[aaIndex, layerIndex].SetFrom(layer);
@@ -2160,7 +2159,7 @@ namespace UVtools.Core.FileFormats
if (HeaderSettings.Version >= 3)
{
- for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++)
+ for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
outputFile.Seek(LayerDefinitions[0, layerIndex].DataAddress - 84, SeekOrigin.Begin);
Helpers.SerializeWriteFileStream(outputFile, new LayerDefEx(LayerDefinitions[0, layerIndex], this[layerIndex]));
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index c39e592..f624301 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -993,6 +993,9 @@ namespace UVtools.Core.FileFormats
ResolutionX = (uint) value.Width;
ResolutionY = (uint) value.Height;
RaisePropertyChanged();
+ RaisePropertyChanged(nameof(Xppmm));
+ RaisePropertyChanged(nameof(Yppmm));
+ RaisePropertyChanged(nameof(Ppmm));
}
}
diff --git a/UVtools.Core/Layers/LayerManager.cs b/UVtools.Core/Layers/LayerManager.cs
index 2b1a1bc..020e4a0 100644
--- a/UVtools.Core/Layers/LayerManager.cs
+++ b/UVtools.Core/Layers/LayerManager.cs
@@ -176,19 +176,28 @@ namespace UVtools.Core
}
}
- public void Init(uint layerCount, bool initializeLayers = false)
+ public void Init(Layer[] layers)
{
- _layers = new Layer[layerCount];
- if (!initializeLayers) return;
- for (uint layerIndex = 0; layerIndex < layerCount; layerIndex++)
+ var oldLayerCount = LayerCount;
+ _layers = layers;
+ if (LayerCount != oldLayerCount)
{
- this[layerIndex] = new Layer(layerIndex, this);
+ SlicerFile.LayerCount = LayerCount;
}
}
- public void Init(Layer[] layers)
+ public void Init(uint layerCount, bool initializeLayers = false)
{
- _layers = layers;
+ var layers = new Layer[layerCount];
+ if (initializeLayers)
+ {
+ for (uint layerIndex = 0; layerIndex < layerCount; layerIndex++)
+ {
+ layers[layerIndex] = new Layer(layerIndex, this);
+ }
+ }
+
+ Init(layers);
}
public void Add(Layer layer)
@@ -348,7 +357,7 @@ namespace UVtools.Core
public LayerManager(uint layerCount, FileFormat slicerFile) : this(slicerFile)
{
SlicerFile = slicerFile;
- _layers = new Layer[layerCount];
+ Init(layerCount);
}
#endregion
@@ -970,14 +979,24 @@ namespace UVtools.Core
var oldLayerCount = LayerCount;
int differenceLayerCount = (int)newLayerCount - Count;
if (differenceLayerCount == 0) return;
- Array.Resize(ref _layers, (int) newLayerCount);
+ var newLayers = new Layer[newLayerCount];
+
+ Array.Copy(_layers, 0, newLayers, 0, Math.Min(newLayerCount, newLayers.Length));
+
if (differenceLayerCount > 0 && initBlack)
{
- Parallel.For(oldLayerCount, newLayerCount, CoreSettings.ParallelOptions, layerIndex =>
+ using var blackMat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ var pngBytes = blackMat.GetPngByes();
+ for (var layerIndex = oldLayerCount; layerIndex < newLayerCount; layerIndex++)
{
- this[layerIndex] = new Layer((uint)layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this);
- });
+ newLayers[layerIndex] = new Layer(layerIndex, pngBytes.ToArray(), this);
+ }
}
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ Layers = newLayers;
+ });
}
/// <summary>
@@ -986,34 +1005,41 @@ namespace UVtools.Core
/// <returns></returns>
public void ReallocateInsert(uint insertAtLayerIndex, uint layerCount, bool initBlack = false)
{
+ if (layerCount == 0) return;
+ insertAtLayerIndex = Math.Min(insertAtLayerIndex, LayerCount);
var newLayers = new Layer[LayerCount + layerCount];
- // Rearrange
- for (uint layerIndex = 0; layerIndex < insertAtLayerIndex; layerIndex++)
- {
- newLayers[layerIndex] = _layers[layerIndex];
- }
-
- // Rearrange
- for (uint layerIndex = insertAtLayerIndex; layerIndex < _layers.Length; layerIndex++)
+ // Copy from start to insert index
+ if(insertAtLayerIndex > 0)
+ Array.Copy(_layers, 0, newLayers, 0, insertAtLayerIndex);
+
+ // Rearrange from last insert to end
+ if(insertAtLayerIndex < LayerCount)
+ Array.Copy(
+ _layers, insertAtLayerIndex,
+ newLayers, insertAtLayerIndex + layerCount,
+ LayerCount - insertAtLayerIndex);
+ /*for (uint layerIndex = insertAtLayerIndex; layerIndex < _layers.Length; layerIndex++)
{
newLayers[layerCount + layerIndex] = _layers[layerIndex];
newLayers[layerCount + layerIndex].Index = layerCount + layerIndex;
- }
+ }*/
- // Allocate new layers
+ // Allocate new layers in between
if (initBlack)
{
- Parallel.For(insertAtLayerIndex, insertAtLayerIndex + layerCount, CoreSettings.ParallelOptions, layerIndex =>
+ using var blackMat = EmguExtensions.InitMat(SlicerFile.Resolution);
+ var pngBytes = blackMat.GetPngByes();
+ for (var layerIndex = insertAtLayerIndex; layerIndex < insertAtLayerIndex + layerCount; layerIndex++)
{
- newLayers[layerIndex] = new Layer((uint) layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this);
- });
+ newLayers[layerIndex] = new Layer(layerIndex, pngBytes.ToArray(), this);
+ }
}
- /*for (uint layerIndex = insertAtLayerIndex; layerIndex < insertAtLayerIndex + layerCount; layerIndex++)
+
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
{
- Layers[layerIndex] = initBlack ? new Layer(layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this) : null;
- }*/
- _layers = newLayers;
+ Layers = newLayers;
+ });
}
/// <summary>
@@ -1021,18 +1047,22 @@ namespace UVtools.Core
/// </summary>
/// <param name="startLayerIndex"></param>
/// <param name="endLayerIndex"></param>
- public void ReallocateRange(uint startLayerIndex, uint endLayerIndex)
+ public void ReallocateKeepRange(uint startLayerIndex, uint endLayerIndex)
{
if ((int)(endLayerIndex - startLayerIndex) < 0) return;
var newLayers = new Layer[1 + endLayerIndex - startLayerIndex];
- uint currentLayerIndex = 0;
+ Array.Copy(_layers, startLayerIndex, newLayers, 0, newLayers.Length);
+ /*uint currentLayerIndex = 0;
for (uint layerIndex = startLayerIndex; layerIndex <= endLayerIndex; layerIndex++)
{
newLayers[currentLayerIndex++] = _layers[layerIndex];
- }
+ }*/
- _layers = newLayers;
+ SlicerFile.SuppressRebuildPropertiesWork(() =>
+ {
+ Layers = newLayers;
+ });
}
/// <summary>
diff --git a/UVtools.Core/Managers/ClipboardManager.cs b/UVtools.Core/Managers/ClipboardManager.cs
index 7c5de4b..0ab372f 100644
--- a/UVtools.Core/Managers/ClipboardManager.cs
+++ b/UVtools.Core/Managers/ClipboardManager.cs
@@ -10,6 +10,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+using System.Drawing;
using System.Linq;
using UVtools.Core.FileFormats;
using UVtools.Core.Layers;
@@ -29,9 +30,17 @@ namespace UVtools.Core.Managers
/// </summary>
public uint LayerCount { get; }
+ /// <summary>
+ /// Gets the LayerHeight for this clip
+ /// </summary>
public float LayerHeight { get; }
/// <summary>
+ /// Gets the Resolution for this clip
+ /// </summary>
+ public Size Resolution { get; }
+
+ /// <summary>
/// Gets the description of this operation
/// </summary>
public string Description { get; set; }
@@ -39,7 +48,7 @@ namespace UVtools.Core.Managers
public bool IsFullBackup { get; set; }
- public Operations.Operation Operation
+ public Operation Operation
{
get => _operation;
set
@@ -52,19 +61,19 @@ namespace UVtools.Core.Managers
#endregion
#region Constructor
- public ClipboardItem(FileFormat slicerFile, Operations.Operation operation, bool isFullBackup = false) : this(slicerFile)
+ public ClipboardItem(FileFormat slicerFile, Operation operation, bool isFullBackup = false) : this(slicerFile, string.Empty, isFullBackup)
{
Operation = operation;
string description = operation.ToString();
if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}";
Description = description;
- IsFullBackup = isFullBackup;
}
public ClipboardItem(FileFormat slicerFile, string description = null, bool isFullBackup = false)
{
LayerCount = slicerFile.LayerCount;
LayerHeight = slicerFile.LayerHeight;
+ Resolution = slicerFile.Resolution;
Description = description;
IsFullBackup = isFullBackup;
}
@@ -142,6 +151,11 @@ namespace UVtools.Core.Managers
SlicerFile.LayerHeight = clip.LayerHeight;
}
+ if (SlicerFile.Resolution != clip.Resolution)
+ {
+ SlicerFile.Resolution = clip.Resolution;
+ }
+
SlicerFile.SuppressRebuildPropertiesWork(() =>
{
SlicerFile.LayerManager.Layers = Layer.CloneLayers(layers);
@@ -394,7 +408,7 @@ namespace UVtools.Core.Managers
/// <summary>
/// Collect differences and create a clip
/// </summary>
- public ClipboardItem Clip(Operations.Operation operation, Layer[] layersSnapshot = null, bool doFullBackup = false)
+ public ClipboardItem Clip(Operation operation, Layer[] layersSnapshot = null, bool doFullBackup = false)
{
string description = operation.ToString();
if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}";
diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs
index 505978b..e2b88c9 100644
--- a/UVtools.Core/Operations/OperationChangeResolution.cs
+++ b/UVtools.Core/Operations/OperationChangeResolution.cs
@@ -11,6 +11,7 @@ using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
+using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations
@@ -21,6 +22,8 @@ namespace UVtools.Core.Operations
#region Members
private uint _newResolutionX;
private uint _newResolutionY;
+ private bool _fixRatio;
+
#endregion
#region Subclasses
@@ -81,9 +84,11 @@ namespace UVtools.Core.Operations
sb.AppendLine($"The new resolution must be different from current resolution ({SlicerFile.ResolutionX} x {SlicerFile.ResolutionY}).");
}
- if (NewResolutionX < SlicerFile.BoundingRectangle.Width || NewResolutionY < SlicerFile.BoundingRectangle.Height)
+ var finalBoundsWidth = FinalBoundsWidth;
+ var finalBoundsHeight = FinalBoundsHeight;
+ if (NewResolutionX < finalBoundsWidth || NewResolutionY < finalBoundsHeight)
{
- sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({SlicerFile.BoundingRectangle.Width} x {SlicerFile.BoundingRectangle.Height}), continuing operation would clip the model");
+ sb.AppendLine($"The new resolution ({NewResolutionX} x {NewResolutionY}) is not large enough to hold the model volume ({finalBoundsWidth} x {finalBoundsHeight}), continuing operation would clip the model.");
sb.AppendLine("To fix this, try to rotate the object and/or resize to fit on this new resolution.");
}
@@ -92,7 +97,7 @@ namespace UVtools.Core.Operations
public override string ToString()
{
- var result = $"{_newResolutionX} x {_newResolutionY}";
+ var result = $"{_newResolutionX} x {_newResolutionY} [Fix ratio: {_fixRatio}]";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
@@ -103,16 +108,46 @@ namespace UVtools.Core.Operations
public uint NewResolutionX
{
get => _newResolutionX;
- set => RaiseAndSetIfChanged(ref _newResolutionX, value);
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _newResolutionX, value)) return;
+ RaisePropertyChanged(nameof(NewRatioX));
+ RaisePropertyChanged(nameof(NewFixedRatioX));
+ RaisePropertyChanged(nameof(FinalBoundsWidth));
+ }
}
public uint NewResolutionY
{
get => _newResolutionY;
- set => RaiseAndSetIfChanged(ref _newResolutionY, value);
+ set
+ {
+ RaiseAndSetIfChanged(ref _newResolutionY, value);
+ RaisePropertyChanged(nameof(NewRatioY));
+ RaisePropertyChanged(nameof(NewFixedRatioY));
+ RaisePropertyChanged(nameof(FinalBoundsHeight));
+ }
}
- public Size VolumeBondsSize => SlicerFile.BoundingRectangle.Size;
+ public double NewRatioX => Math.Round((double)SlicerFile.ResolutionX / _newResolutionX, 2);
+ public double NewRatioY => Math.Round((double)SlicerFile.ResolutionY / _newResolutionY, 2);
+
+ public double NewFixedRatioX => Math.Round((double)_newResolutionX / SlicerFile.ResolutionX, 2);
+ public double NewFixedRatioY => Math.Round((double)_newResolutionY / SlicerFile.ResolutionY, 2);
+
+ public bool FixRatio
+ {
+ get => _fixRatio;
+ set
+ {
+ if(!RaiseAndSetIfChanged(ref _fixRatio, value)) return;
+ RaisePropertyChanged(nameof(FinalBoundsWidth));
+ RaisePropertyChanged(nameof(FinalBoundsHeight));
+ }
+ }
+
+ public uint FinalBoundsWidth => (uint)(_fixRatio ? SlicerFile.BoundingRectangle.Width * NewFixedRatioX : SlicerFile.BoundingRectangle.Width);
+ public uint FinalBoundsHeight => (uint)(_fixRatio ? SlicerFile.BoundingRectangle.Height * NewFixedRatioY : SlicerFile.BoundingRectangle.Height);
#endregion
@@ -151,6 +186,9 @@ namespace UVtools.Core.Operations
new Resolution(2560, 1620, "WQXGA"),
new Resolution(3840, 2160, "4K UHD"),
new Resolution(3840, 2400, "WQUXGA"),
+ new Resolution(4920, 2880, "5K UHD"),
+ new Resolution(5448, 3064, "6K"),
+ new Resolution(7680, 4320, "8K UHD"),
};
}
@@ -160,8 +198,12 @@ namespace UVtools.Core.Operations
protected override bool ExecuteInternally(OperationProgress progress)
{
progress.ItemCount = SlicerFile.LayerCount;
- var boundingRectangle = SlicerFile.BoundingRectangle;
var newSize = new Size((int) NewResolutionX, (int) NewResolutionY);
+ var finalBoundsWidth = FinalBoundsWidth;
+ var finalBoundsHeight = FinalBoundsHeight;
+
+ var finalBounds = new Size((int)finalBoundsWidth, (int)finalBoundsHeight);
+
Parallel.For(0, SlicerFile.LayerCount, CoreSettings.ParallelOptions, layerIndex =>
{
if (progress.Token.IsCancellationRequested) return;
@@ -170,14 +212,17 @@ namespace UVtools.Core.Operations
if (mat.Size != newSize)
{
- using var matRoi = new Mat(mat, boundingRectangle);
- using var matDst = new Mat(newSize, mat.Depth, mat.NumberOfChannels);
- using var matDstRoi = new Mat(matDst,
- new Rectangle((int) (NewResolutionX / 2 - boundingRectangle.Width / 2),
- (int) NewResolutionY / 2 - boundingRectangle.Height / 2,
- boundingRectangle.Width, boundingRectangle.Height));
- matRoi.CopyTo(matDstRoi);
- //Execute(mat);
+ using var matDst = EmguExtensions.InitMat(newSize);
+
+ var newFixedRatioX = NewFixedRatioX;
+ var newFixedRatioY = NewFixedRatioY;
+ if (_fixRatio && (newFixedRatioX != 1.0 || newFixedRatioY != 1.0))
+ {
+ CvInvoke.Resize(mat, mat, SlicerFile.Resolution.Multiply(newFixedRatioX, newFixedRatioY));
+ }
+
+ mat.CopyCenterToCenter(finalBounds, matDst);
+
SlicerFile[layerIndex].LayerMat = matDst;
}
@@ -212,7 +257,7 @@ namespace UVtools.Core.Operations
private bool Equals(OperationChangeResolution other)
{
- return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY;
+ return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY && _fixRatio == other._fixRatio;
}
public override bool Equals(object obj)
@@ -222,10 +267,7 @@ namespace UVtools.Core.Operations
public override int GetHashCode()
{
- unchecked
- {
- return ((int) _newResolutionX * 397) ^ (int) _newResolutionY;
- }
+ return HashCode.Combine(_newResolutionX, _newResolutionY, _fixRatio);
}
#endregion
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index fe5e77d..4b1bf6d 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -35,8 +35,8 @@ namespace UVtools.Core.Operations
Replace,
[Description("Stack: Stacks and combine imported layers in the current layers")]
Stack,
- [Description("Merge: Merges current layers with the imported content by summing value of layer pixels")]
- Merge,
+ [Description("MergeSum: Merges current layers with the imported content by summing value of layer pixels")]
+ MergeSum,
[Description("MergeMax: Merges current layers with the imported content by using the maximum value of layer pixels")]
MergeMax,
[Description("Subtract: Subtracts current layers with the imported content")]
@@ -158,7 +158,7 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _extendBeyondLayerCount, value);
}
- public bool IsExtendBeyondLayerCountVisible => _importType is ImportTypes.Replace or ImportTypes.Stack or ImportTypes.Merge or ImportTypes.MergeMax;
+ public bool IsExtendBeyondLayerCountVisible => _importType is ImportTypes.Replace or ImportTypes.Stack or ImportTypes.MergeSum or ImportTypes.MergeMax;
public bool DiscardUnmodifiedLayers
{
@@ -354,7 +354,7 @@ namespace UVtools.Core.Operations
}
break;
- case ImportTypes.Merge:
+ case ImportTypes.MergeSum:
case ImportTypes.MergeMax:
if (SlicerFile.Resolution != fileFormat.Resolution) continue;
if (_extendBeyondLayerCount)
@@ -393,7 +393,7 @@ namespace UVtools.Core.Operations
}
using var layer = fileFormat[i].LayerMat;
- using var layerRoi = layer.NewRoiFromCenter(SlicerFile.Resolution, fileFormatBoundingRectangle);
+ using var layerRoi = layer.NewMatFromCenterRoi(SlicerFile.Resolution, fileFormatBoundingRectangle);
SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
break;
@@ -408,7 +408,7 @@ namespace UVtools.Core.Operations
}
using var layer = fileFormat[i].LayerMat;
- using var layerRoi = layer.NewRoiFromCenter(SlicerFile.Resolution, fileFormatBoundingRectangle);
+ using var layerRoi = layer.NewMatFromCenterRoi(SlicerFile.Resolution, fileFormatBoundingRectangle);
SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
break;
}
@@ -424,7 +424,7 @@ namespace UVtools.Core.Operations
break;
}
- case ImportTypes.Merge:
+ case ImportTypes.MergeSum:
{
if (layerIndex >= SlicerFile.LayerCount) return;
using var originalMat = SlicerFile[layerIndex].LayerMat;
@@ -512,7 +512,7 @@ namespace UVtools.Core.Operations
if (lastProcessedLayerIndex + 1 < SlicerFile.LayerCount && _discardUnmodifiedLayers)
{
- SlicerFile.LayerManager.ReallocateRange(0, (uint)lastProcessedLayerIndex);
+ SlicerFile.LayerManager.Reallocate((uint)lastProcessedLayerIndex + 1);
}
return true;
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 3d59718..7550887 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.25.0</Version>
+ <Version>2.25.1</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
@@ -49,7 +49,7 @@
<ItemGroup>
<PackageReference Include="AnimatedGif" Version="1.0.5" />
- <PackageReference Include="BinarySerializer" Version="8.6.1.1" />
+ <PackageReference Include="BinarySerializer" Version="8.6.1.2" />
<PackageReference Include="Emgu.CV" Version="4.5.4.4788" />
<PackageReference Include="KdTree" Version="1.4.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.0.1" />
diff --git a/UVtools.WPF/Assets/Styles/Styles.xaml b/UVtools.WPF/Assets/Styles/Styles.xaml
index 798c16f..96ccc9c 100644
--- a/UVtools.WPF/Assets/Styles/Styles.xaml
+++ b/UVtools.WPF/Assets/Styles/Styles.xaml
@@ -29,6 +29,12 @@
<Setter Property="AcceptsTab" Value="True" />
</Style>
+ <Style Selector="NumericUpDown.ReadOnly">
+ <Setter Property="IsReadOnly" Value="True" />
+ <Setter Property="AllowSpin" Value="False" />
+ <Setter Property="ShowButtonSpinner" Value="False" />
+ </Style>
+
<Style Selector="TextBox.NumericUpDownValueLabel">
<Setter Property="IsEnabled" Value="False" />
diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml
index 54153f9..5cc041e 100644
--- a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml
+++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml
@@ -7,33 +7,73 @@
<StackPanel Orientation="Vertical" Spacing="10">
<TextBlock Text="{Binding Operation.SlicerFile.Resolution, StringFormat=Current resolution (X/Y): {0}}"/>
<TextBlock Text="{Binding Operation.SlicerFile.PixelSizeMicrons, StringFormat=Current pixel pitch (X/Y): {0}µm}"/>
- <TextBlock Text="{Binding Operation.VolumeBondsSize, StringFormat=Object volume (X/Y): {0}}"/>
-
- <StackPanel Orientation="Horizontal" Spacing="10">
- <TextBlock VerticalAlignment="Center" Text="New X/Y:"/>
- <NumericUpDown
- MinWidth="120"
- Minimum="1"
- Maximum="50000"
- Width="150"
- Value="{Binding Operation.NewResolutionX}"/>
-
- <TextBlock VerticalAlignment="Center" Text="x"/>
-
- <NumericUpDown
- MinWidth="120"
- Minimum="1"
- Maximum="50000"
- Width="150"
- Value="{Binding Operation.NewResolutionY}"
- />
-
- <ComboBox
- MinWidth="250"
- SelectedItem="{Binding SelectedPresetItem}"
- Items="{Binding Operation.Presets}"
- PlaceholderText="Resolution presets"/>
- </StackPanel>
+ <TextBlock Text="{Binding SlicerFile.BoundingRectangle.Size, StringFormat=Object volume (X/Y): {0}}"/>
+ <TextBlock FontWeight="Bold">
+ <TextBlock.Text>
+ <MultiBinding StringFormat="Resulting pixel ratio: {{X={0}x, Y={1}x}}">
+ <Binding Path="Operation.NewRatioX"/>
+ <Binding Path="Operation.NewRatioY"/>
+ </MultiBinding>
+ </TextBlock.Text>
+ </TextBlock>
+
+ <Grid RowDefinitions="Auto,10,Auto"
+ ColumnDefinitions="Auto,10,Auto,5,Auto,5,Auto,10,Auto">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center" Text="New X/Y:"/>
+
+ <NumericUpDown Grid.Row="0" Grid.Column="2"
+ MinWidth="120"
+ Minimum="1"
+ Maximum="50000"
+ Width="150"
+ Value="{Binding Operation.NewResolutionX}"/>
+
+ <TextBlock Grid.Row="0" Grid.Column="4" VerticalAlignment="Center" Text="x"/>
+
+ <NumericUpDown Grid.Row="0" Grid.Column="6"
+ MinWidth="120"
+ Minimum="1"
+ Maximum="50000"
+ Width="150"
+ Value="{Binding Operation.NewResolutionY}"/>
+
+ <ComboBox Grid.Row="0" Grid.Column="8"
+ MinWidth="250"
+ SelectedItem="{Binding SelectedPresetItem}"
+ Items="{Binding Operation.Presets}"
+ PlaceholderText="Resolution presets"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center" Text="Fix pixel ratio:"/>
+
+ <NumericUpDown Grid.Row="2" Grid.Column="2"
+ Classes="ValueLabel ValueLabel_times ReadOnly"
+ FormatString="F2"
+ MinWidth="120"
+ Width="150"
+ IsEnabled="{Binding Operation.FixRatio}"
+ Value="{Binding Operation.NewFixedRatioX}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="4" VerticalAlignment="Center" Text="x"/>
+
+ <NumericUpDown Grid.Row="2" Grid.Column="6"
+ Classes="ValueLabel ValueLabel_times ReadOnly"
+ FormatString="F2"
+ MinWidth="120"
+ Width="150"
+ IsEnabled="{Binding Operation.FixRatio}"
+ Value="{Binding Operation.NewFixedRatioY}"/>
+
+ <CheckBox Grid.Row="2" Grid.Column="8"
+ Content="Resize layers with proposed ratio"
+ IsChecked="{Binding Operation.FixRatio}"
+ ToolTip.Tip="Fix the pixel ratio by resize the layers images with the proposed ratio to match the new resolution.
+&#x0a;Only use this option when both source and target display have the same dimensions / build volume.
+Otherwise, the new display size must be taken into account and you need to manually resize after this."/>
+ </Grid>
+
</StackPanel>
</UserControl>
diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
index f2bfefa..0c20c4b 100644
--- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs
@@ -159,7 +159,7 @@ namespace UVtools.WPF.Controls.Tools
{
RaisePropertyChanged(nameof(InfoLayerHeightStr));
RaisePropertyChanged(nameof(InfoImportResult));
- ParentWindow.ButtonOkEnabled = Operation.Files.Count > 0;
+ if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.Files.Count > 0;
}
public async void AddFiles()
diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs
index d5cfa96..fd40cfd 100644
--- a/UVtools.WPF/MainWindow.LayerPreview.cs
+++ b/UVtools.WPF/MainWindow.LayerPreview.cs
@@ -2160,7 +2160,7 @@ namespace UVtools.WPF
var size = EmguExtensions.GetTextSizeExtended(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine, DrawingPixelText.LineAlignment);
//var rotatedSize = size.Rotate(DrawingPixelText.Angle);
//Point point = (rotatedSize.Inflate(rotatedSize)).Rotate(DrawingPixelText.Angle, rotatedSize.ToPoint());
- cursor = EmguExtensions.InitMat(size.Inflate(), 4);
+ cursor = EmguExtensions.InitMat(size.Add(), 4);
//CvInvoke.Rectangle(cursor, new Rectangle(Point.Empty, size), _pixelEditorCursorColor, -1, DrawingPixelText.LineType);
//_pixelEditorCursorColor.V3 = 255;
//CvInvoke.Rectangle(cursor, new Rectangle(new Point(size.Width, 0), size), _pixelEditorCursorColor, 1, DrawingPixelText.LineType);
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index d2b2ac3..4034c2f 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -416,7 +416,7 @@
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
- <MultiBinding StringFormat="\{0\}/\{1\}">
+ <MultiBinding StringFormat="{}{0}/{1}">
<Binding Path="VisibleThumbnailIndex"/>
<Binding Path="SlicerFile.CreatedThumbnailsCount"/>
</MultiBinding>
@@ -1521,7 +1521,7 @@
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
- <MultiBinding StringFormat="\{0\}/\{1\}">
+ <MultiBinding StringFormat="{}{0}/{1}">
<Binding Path="Clipboard.CurrentIndexCountStr"/>
<Binding Path="Clipboard.Items.Count"/>
</MultiBinding>
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index b5f54ea..6531bf2 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -1807,6 +1807,17 @@ namespace UVtools.WPF
if (e.PropertyName == nameof(SlicerFile.Thumbnails))
{
RefreshThumbnail();
+ return;
+ }
+ if (e.PropertyName == nameof(SlicerFile.Resolution))
+ {
+ RaisePropertyChanged(nameof(LayerResolutionStr));
+ return;
+ }
+ if (e.PropertyName == nameof(SlicerFile.Ppmm))
+ {
+ RaisePropertyChanged(nameof(LayerZoomStr));
+ return;
}
}
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index f231c37..11e2b57 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.25.0</Version>
+ <Version>2.25.1</Version>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
diff --git a/UVtools.WPF/Windows/AboutWindow.axaml b/UVtools.WPF/Windows/AboutWindow.axaml
index d9cc68b..342a733 100644
--- a/UVtools.WPF/Windows/AboutWindow.axaml
+++ b/UVtools.WPF/Windows/AboutWindow.axaml
@@ -90,8 +90,7 @@
</Border>
<Grid
RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,*"
- Margin="20"
- >
+ Margin="20">
<TextBlock Grid.Row="0" Text="{Binding Software}" FontWeight="Bold"/>
<TextBlock Grid.Row="2" Text="{Binding Version}"/>