From c71ff0dd923b8e9b2f40178621008cbb3f5ae8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Tue, 23 Nov 2021 02:28:31 +0000 Subject: 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 --- CHANGELOG.md | 14 +++- UVtools.Core/Extensions/EmguExtensions.cs | 86 +++++++++++++++---- UVtools.Core/Extensions/SizeExtensions.cs | 25 ++++-- UVtools.Core/FileFormats/ChituboxFile.cs | 21 +++-- UVtools.Core/FileFormats/FileFormat.cs | 3 + UVtools.Core/Layers/LayerManager.cs | 96 ++++++++++++++-------- UVtools.Core/Managers/ClipboardManager.cs | 22 ++++- .../Operations/OperationChangeResolution.cs | 82 +++++++++++++----- UVtools.Core/Operations/OperationLayerImport.cs | 16 ++-- UVtools.Core/UVtools.Core.csproj | 4 +- UVtools.WPF/Assets/Styles/Styles.xaml | 6 ++ .../Tools/ToolChangeResolutionControl.axaml | 94 +++++++++++++++------ .../Controls/Tools/ToolLayerImportControl.axaml.cs | 2 +- UVtools.WPF/MainWindow.LayerPreview.cs | 2 +- UVtools.WPF/MainWindow.axaml | 4 +- UVtools.WPF/MainWindow.axaml.cs | 11 +++ UVtools.WPF/UVtools.WPF.csproj | 2 +- UVtools.WPF/Windows/AboutWindow.axaml | 3 +- 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 @@ -472,6 +472,30 @@ namespace UVtools.Core.Extensions #endregion #region Copy methods + /// + /// Copy a region from to center of other + /// + /// Source to be copied to + /// Size of the center offset + /// Target to paste the + public static void CopyCenterToCenter(this Mat src, Size size, Mat dst) + { + var srcRoi = src.RoiFromCenter(size); + CopyToCenter(srcRoi, dst); + } + + /// + /// Copy a region from to center of other + /// + /// Source to be copied to + /// Region to copy + /// Target to paste the + public static void CopyRegionToCenter(this Mat src, Rectangle region, Mat dst) + { + var srcRoi = src.Roi(region); + CopyToCenter(srcRoi, dst); + } + /// /// Copy a to center of other /// @@ -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) @@ -563,6 +597,26 @@ namespace UVtools.Core.Extensions return roi.IsEmpty || roi.Size == mat.Size ? mat : new Mat(mat, roi); } + /// + /// Gets a Roi from center, but return source when have same size as source + /// + /// + /// + /// + 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; + } + /// /// Gets a new mat obtained from it center at a target size and roi /// @@ -570,23 +624,21 @@ namespace UVtools.Core.Extensions /// /// /// - 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)); /// /// 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(); 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; + }); } /// @@ -986,34 +1005,41 @@ namespace UVtools.Core /// 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; + }); } /// @@ -1021,18 +1047,22 @@ namespace UVtools.Core /// /// /// - 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; + }); } /// 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,8 +30,16 @@ namespace UVtools.Core.Managers /// public uint LayerCount { get; } + /// + /// Gets the LayerHeight for this clip + /// public float LayerHeight { get; } + /// + /// Gets the Resolution for this clip + /// + public Size Resolution { get; } + /// /// Gets the description of this operation /// @@ -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 /// /// Collect differences and create a clip /// - 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 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 2.25.0 + 2.25.1 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -49,7 +49,7 @@ - + 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 @@ + +