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-06-06 03:26:59 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2021-06-06 03:26:59 +0300
commit98b7c01df8447fae2fa738c74af52de4e6af6f47 (patch)
tree5f64c900f9f46123d5f4e28b42021face18deb95
parent0321ec746db70950aa2e253f0a58a7d73e40aeac (diff)
v2.13.2v2.13.2
- (Upgrade) AvaloniaUI from 0.10.5 to 0.10.6 - (Add) Pixel editor - Text: Allow multi-line text and line alignment modes
-rw-r--r--CHANGELOG.md5
-rw-r--r--UVtools.Core/Extensions/DrawingExtensions.cs2
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs642
-rw-r--r--UVtools.Core/FileFormats/CWSFile.cs8
-rw-r--r--UVtools.Core/FileFormats/CXDLPFile.cs6
-rw-r--r--UVtools.Core/FileFormats/FDGFile.cs2
-rw-r--r--UVtools.Core/FileFormats/GR1File.cs6
-rw-r--r--UVtools.Core/FileFormats/MDLPFile.cs6
-rw-r--r--UVtools.Core/FileFormats/PHZFile.cs2
-rw-r--r--UVtools.Core/Layer/Layer.cs2
-rw-r--r--UVtools.Core/Layer/LayerManager.cs24
-rw-r--r--UVtools.Core/Objects/StaticObjects.cs6
-rw-r--r--UVtools.Core/Operations/OperationCalibrateElephantFoot.cs2
-rw-r--r--UVtools.Core/Operations/OperationCalibrateExposureFinder.cs4
-rw-r--r--UVtools.Core/Operations/OperationCalibrateStressTower.cs2
-rw-r--r--UVtools.Core/Operations/OperationInfill.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs4
-rw-r--r--UVtools.Core/Operations/OperationPattern.cs2
-rw-r--r--UVtools.Core/Operations/OperationPixelDimming.cs4
-rw-r--r--UVtools.Core/Operations/OperationRedrawModel.cs6
-rw-r--r--UVtools.Core/Operations/OperationRepairLayers.cs4
-rw-r--r--UVtools.Core/PixelEditor/PixelText.cs13
-rw-r--r--UVtools.Core/Slicer.cs59
-rw-r--r--UVtools.Core/Slicer/LinAlgUtils.cs101
-rw-r--r--UVtools.Core/Slicer/RangeUtils.cs77
-rw-r--r--UVtools.Core/Slicer/Slice.cs42
-rw-r--r--UVtools.Core/Slicer/SliceLine.cs61
-rw-r--r--UVtools.Core/Slicer/Slicer.cs186
-rw-r--r--UVtools.Core/UVtools.Core.csproj3
-rw-r--r--UVtools.InstallerMM/UVtools.InstallerMM.wxs3
-rw-r--r--UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs2
-rw-r--r--UVtools.WPF/Controls/KernelControl.axaml.cs2
-rw-r--r--UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs2
-rw-r--r--UVtools.WPF/MainWindow.Issues.cs2
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs18
-rw-r--r--UVtools.WPF/MainWindow.PixelEditor.cs2
-rw-r--r--UVtools.WPF/MainWindow.axaml45
-rw-r--r--UVtools.WPF/Program.cs6
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj10
-rw-r--r--UVtools.WPF/Windows/BenchmarkWindow.axaml.cs6
40 files changed, 998 insertions, 383 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bf3cbb..1a5be9d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## 06/06/2021 - v2.13.2
+
+- (Upgrade) AvaloniaUI from 0.10.5 to 0.10.6
+- (Add) Pixel editor - Text: Allow multi-line text and line alignment modes
+
## 29/05/2021 - v2.13.1
- (Add) Layer preview - Outline: Skeletonize
diff --git a/UVtools.Core/Extensions/DrawingExtensions.cs b/UVtools.Core/Extensions/DrawingExtensions.cs
index f98529c..b1d8050 100644
--- a/UVtools.Core/Extensions/DrawingExtensions.cs
+++ b/UVtools.Core/Extensions/DrawingExtensions.cs
@@ -15,7 +15,7 @@ namespace UVtools.Core.Extensions
byte g = (byte)(color.G == 0 ? 0 :
Math.Min(Math.Max(min, color.G * factor), max));
-
+
byte b = (byte)(color.B == 0 ? 0 :
Math.Min(Math.Max(min, color.B * factor), max));
return Color.FromArgb(r, g, b);
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 4240372..f4c3e9b 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -13,16 +13,113 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
+using UVtools.Core.Objects;
namespace UVtools.Core.Extensions
{
public static class EmguExtensions
{
+ #region Constants
public static readonly MCvScalar WhiteColor = new(255, 255, 255, 255);
public static readonly MCvScalar BlackColor = new(0, 0, 0, 255);
//public static readonly MCvScalar TransparentColor = new();
+ #endregion
+ #region Initializers methods
+ /// <summary>
+ /// Create a byte array of size of this <see cref="Mat"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Blank byte array</returns>
+ public static byte[] CreateBlankByteArray(this Mat mat)
+ {
+ return new byte[mat.GetLength()];
+ }
+
+ /// <summary>
+ /// Creates a new empty <see cref="Mat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns></returns>
+ public static Mat New(this Mat mat)
+ {
+ return new(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels);
+ }
+
+ /// <summary>
+ /// Creates a new empty <see cref="Mat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="color"></param>
+ /// <returns></returns>
+ public static Mat New(this Mat src, MCvScalar color)
+ {
+ Mat mat = new(src.Rows, src.Cols, src.Depth, src.NumberOfChannels);
+ mat.SetTo(color);
+ return mat;
+ }
+
+ /// <summary>
+ /// Creates a new blanked (All zeros) <see cref="Mat"/> with same size and type of the source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Blanked <see cref="Mat"/></returns>
+ public static Mat NewBlank(this Mat mat)
+ {
+ return InitMat(mat.Size, mat.NumberOfChannels, mat.Depth);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Mat"/> with same size and type of the source and set it to a color
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="color"></param>
+ /// <returns></returns>
+ public static Mat NewSetTo(this Mat mat, MCvScalar color)
+ {
+ return InitMat(mat.Size, color, mat.NumberOfChannels, mat.Depth);
+ }
+
+
+ public static Mat InitMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U)
+ {
+ return size.IsEmpty ? new() : Mat.Zeros(size.Height, size.Width, depthType, channels);
+ }
+
+ public static Mat InitMat(Size size, MCvScalar color, int channels = 1, DepthType depthType = DepthType.Cv8U)
+ {
+ if (size.IsEmpty) return new();
+ var mat = new Mat(size, depthType, channels);
+ mat.SetTo(color);
+ return mat;
+ }
+
+ /// <summary>
+ /// Allocates a new array of mat's
+ /// </summary>
+ /// <param name="count">Array size</param>
+ /// <returns></returns>
+ public static Mat[] InitMats(uint count) => InitMats(count, Size.Empty);
+
+ /// <summary>
+ /// Allocates a new array of mat 's
+ /// </summary>
+ /// <param name="count">Array size</param>
+ /// <param name="size">Image size to create, use <see cref="Size.Empty"/> to create a empty Mat</param>
+ /// <returns>New mat array</returns>
+ public static Mat[] InitMats(uint count, Size size)
+ {
+ var layers = new Mat[count];
+ for (var i = 0; i < layers.Length; i++)
+ {
+ layers[i] = InitMat(size);
+ }
+
+ return layers;
+ }
+ #endregion
+
+ #region Memory accessors
public static unsafe byte* GetBytePointer(this Mat mat)
{
return (byte*)mat.DataPointer.ToPointer();
@@ -34,46 +131,194 @@ namespace UVtools.Core.Extensions
/// <typeparam name="T">Pixel type</typeparam>
/// <param name="mat"><see cref="Mat"/> Input</param>
/// <returns>A <see cref="Span{T}"/> containing all pixels in data memory</returns>
- public static unsafe Span<T> GetPixelSpan<T>(this Mat mat)
+ public static unsafe Span<T> GetDataSpan<T>(this Mat mat)
{
return new(mat.DataPointer.ToPointer(), mat.GetLength());
}
- public static unsafe Span<byte> GetPixelSpanByte(this Mat mat)
+ public static unsafe Span<byte> GetDataByteSpan(this Mat mat)
{
return new(mat.DataPointer.ToPointer(), mat.GetLength());
}
- public static unsafe Span<T> GetPixelSpan<T>(this Mat mat, int length, int offset = 0)
+ public static unsafe Span<T> GetDataSpan<T>(this Mat mat, int length, int offset = 0)
{
return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length);
}
- public static Span<T> GetSinglePixelSpan<T>(this Mat mat, int x, int y)
+ public static Span<T> GetPixelSpan<T>(this Mat mat, int x, int y)
{
- return mat.GetPixelSpan<T>(mat.NumberOfChannels, mat.GetPixelPos(x, y));
+ return mat.GetDataSpan<T>(mat.NumberOfChannels, mat.GetPixelPos(x, y));
}
- public static Span<T> GetSinglePixelSpan<T>(this Mat mat, int pos)
+ public static Span<T> GetPixelSpan<T>(this Mat mat, int pos)
{
- return mat.GetPixelSpan<T>(mat.NumberOfChannels, pos);
+ return mat.GetDataSpan<T>(mat.NumberOfChannels, pos);
}
- public static unsafe Span<T> GetPixelRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0)
+ public static unsafe Span<T> GetRowSpan<T>(this Mat mat, int y, int length = 0, int offset = 0)
{
return new(IntPtr.Add(mat.DataPointer, y * mat.Step + offset).ToPointer(), length <= 0 ? mat.Step : length);
- //return mat.GetPixelSpan<T>().Slice(offset, mat.Step);
}
- public static unsafe Span<T> GetPixelColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0)
+ public static unsafe Span<T> GetColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0)
{
var colMat = mat.Col(x);
return new(IntPtr.Add(colMat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length);
}
+ #endregion
+
+ #region Get/Set methods
+ /// <summary>
+ /// Gets the total length of this <see cref="Mat"/></param>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>The total length of this <see cref="Mat"/></returns>
+ public static int GetLength(this Mat mat)
+ {
+ return mat.Step * mat.Height;
+ }
+
+ /// <summary>
+ /// Gets a pixel index position on a span given X and Y
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="x">X coordinate</param>
+ /// <param name="y">Y coordinate</param>
+ /// <returns>The pixel index position</returns>
+ public static int GetPixelPos(this Mat mat, int x, int y)
+ {
+ return y * mat.Step + x * mat.NumberOfChannels;
+ }
+
+ /// <summary>
+ /// Gets a pixel index position on a span given X and Y
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="point">X and Y Location</param>
+ /// <returns>The pixel index position</returns>
+ public static int GetPixelPos(this Mat mat, Point point)
+ {
+ return point.Y * mat.Step + point.X * mat.NumberOfChannels;
+ }
/// <summary>
- /// Gets if a <see cref="Mat"/> is all zeroed
+ /// Gets a byte array copy of this <see cref="Mat"/>
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <returns>Byte array </returns>
+ public static byte[] GetBytes(this Mat mat)
+ {
+ var data = new byte[mat.GetLength()];
+ Marshal.Copy(mat.DataPointer, data, 0, data.Length);
+ return data;
+ }
+
+ public static byte GetByte(this Mat mat, int pos)
+ {
+ //return new Span<byte>(IntPtr.Add(mat.DataPointer, pos).ToPointer(), mat.Step)[0];
+ var value = new byte[1];
+ Marshal.Copy(mat.DataPointer + pos, value, 0, value.Length);
+ return value[0];
+ }
+
+ public static byte GetByte(this Mat mat, int x, int y) => GetByte(mat, mat.GetPixelPos(x, y));
+ public static byte GetByte(this Mat mat, Point pos) => GetByte(mat, mat.GetPixelPos(pos.X, pos.Y));
+
+
+ public static void SetByte(this Mat mat, int pixel, byte value) => SetByte(mat, pixel, new[] { value });
+
+ public static void SetByte(this Mat mat, int pixel, byte[] value) =>
+ Marshal.Copy(value, 0, mat.DataPointer + pixel, value.Length);
+
+ public static void SetByte(this Mat mat, int x, int y, byte value) =>
+ SetByte(mat, x, y, new[] { value });
+
+ public static void SetByte(this Mat mat, int x, int y, byte[] value) =>
+ SetByte(mat, y * mat.Step + x * mat.NumberOfChannels, value);
+
+ public static void SetBytes(this Mat mat, byte[] value) =>
+ Marshal.Copy(value, 0, mat.DataPointer, value.Length);
+
+ public static byte[] GetPngByes(this Mat mat)
+ {
+ using var vector = new VectorOfByte();
+ CvInvoke.Imencode(".png", mat, vector);
+ return vector.ToArray();
+ }
+ #endregion
+
+ #region Copy methods
+ /// <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>
+ /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
+ public static void CopyToCenter(this Mat src, Mat dst)
+ {
+ if (dst.Step > src.Step && dst.Height > src.Height)
+ {
+ var dx = Math.Abs(dst.Step - src.Step) / 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);
+ }
+ else if (dst.Step < src.Step && dst.Height < src.Height)
+ {
+ var dx = Math.Abs(dst.Step - src.Step) / 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);
+ }
+ }
+ #endregion
+
+ #region Roi methods
+
+ /// <summary>
+ /// Gets a Roi, but return source when roi is empty or have same size as source
+ /// </summary>
+ /// <param name="mat"></param>
+ /// <param name="roi"></param>
+ /// <returns></returns>
+ public static Mat Roi(this Mat mat, Rectangle roi)
+ {
+ return roi.IsEmpty || roi.Size == mat.Size ? mat : new Mat(mat, roi);
+ }
+
+ /// <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)
+ {
+ if (targetSize == mat.Size) return mat.Clone();
+ var newMat = InitMat(targetSize);
+
+ var roiMat = new Mat(mat, roi);
+
+
+ //int xStart = mat.Width / 2 - targetSize.Width / 2;
+ //int yStart = mat.Height / 2 - targetSize.Height / 2;
+
+ 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;
+ }
+ #endregion
+
+ #region Is methods
+ /// <summary>
+ /// Gets if a <see cref="Mat"/> is all zeroed by a threshold
/// </summary>
/// <param name="mat"></param>
/// <param name="threshold">Pixel brightness threshold</param>
@@ -81,14 +326,16 @@ namespace UVtools.Core.Extensions
public static unsafe bool IsZeroed(this Mat mat, byte threshold = 0)
{
var ptr = mat.GetBytePointer();
- for (int i = 0; i < mat.GetLength(); i++)
+ var length = mat.GetLength();
+ for (var i = 0; i < length; i++)
{
if (ptr[i] > threshold) return false;
}
return true;
}
+ #endregion
-
+ #region Transform methods
public static void Transform(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear)
{
//var dst = new Mat(src.Size, src.Depth, src.NumberOfChannels);
@@ -106,7 +353,8 @@ namespace UVtools.Core.Extensions
/// Rotates a Mat by an angle while keeping the image size
/// </summary>
/// <param name="src"></param>
- /// <param name="angle"></param>
+ /// <param name="angle">Angle in degrees to rotate</param>
+ /// <param name="newSize"></param>
/// <param name="scale"></param>
public static void Rotate(this Mat src, double angle, Size newSize = default, double scale = 1.0) => Rotate(src, src, angle, newSize, scale);
@@ -194,13 +442,145 @@ namespace UVtools.Core.Extensions
xTrans + (src.Width - src.Width * xScale) / 2.0,
yTrans + (src.Height - src.Height * yScale) / 2.0, dstSize, interpolation);
}
+ #endregion
+
+ #region Text methods
+ public enum PutTextLineAlignment : byte
+ {
+ /// <summary>
+ /// Left aligned without trimming, openCV default call
+ /// </summary>
+ None,
+
+ /// <summary>
+ /// Left aligned and trimmed
+ /// </summary>
+ Left,
+
+ /// <summary>
+ /// Center aligned and trimmed
+ /// </summary>
+ Center,
+
+ /// <summary>
+ /// Right aligned and trimmed
+ /// </summary>
+ Right
+ }
+
+ public static string PutTextLineAlignmentTrim(string line, PutTextLineAlignment lineAlignment)
+ {
+ switch (lineAlignment)
+ {
+ case PutTextLineAlignment.None:
+ return line.TrimEnd();
+ case PutTextLineAlignment.Left:
+ case PutTextLineAlignment.Center:
+ case PutTextLineAlignment.Right:
+ return line.Trim();
+ default:
+ throw new ArgumentOutOfRangeException(nameof(lineAlignment), lineAlignment, null);
+ }
+ }
+
+ public static Size GetTextSizeExtended(string text, FontFace fontFace, double fontScale, int thickness, ref int baseLine, PutTextLineAlignment lineAlignment = default)
+ {
+ text = text.TrimEnd('\n', '\r', ' ');
+ var lines = text.Split(StaticObjects.LineBreakCharacters, StringSplitOptions.None);
+ var textSize = CvInvoke.GetTextSize(text, fontFace, fontScale, thickness, ref baseLine);
+
+ if (lines.Length is 0 or 1) return textSize;
+
+ var lineGap = textSize.Height / 3;
+ var width = 0;
+ var height = lines.Length * (lineGap + textSize.Height) - lineGap;
+
+ for (var i = 0; i < lines.Length; i++)
+ {
+ lines[i] = PutTextLineAlignmentTrim(lines[i], lineAlignment);
+
+ if (string.IsNullOrWhiteSpace(lines[i])) continue;
+ int baseLineRef = 0;
+ var lineSize = CvInvoke.GetTextSize(lines[i], fontFace, fontScale, thickness, ref baseLineRef);
+ width = Math.Max(width, lineSize.Width);
+ }
+
+
+ return new(width, height);
+ }
+
+ /// <summary>
+ /// Extended OpenCV PutText to accepting line breaks and line alignment
+ /// </summary>
+ public static void PutTextExtended(this Mat src, string text, Point org, FontFace fontFace, double fontScale,
+ MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default)
+ {
+ text = text.TrimEnd('\n', '\r', ' ');
+ var lines = text.Split(StaticObjects.LineBreakCharacters, StringSplitOptions.None);
+
+ switch (lines.Length)
+ {
+ case 0:
+ return;
+ case 1:
+ CvInvoke.PutText(src, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
+ return;
+ }
+
+ // Get height of text lines in pixels (height of all lines is the same)
+ int baseLine = 0;
+ var textSize = CvInvoke.GetTextSize(text, fontFace, fontScale, thickness, ref baseLine);
+ var lineGap = textSize.Height / 3;
+ var linesSize = new Size[lines.Length];
+ int width = 0;
+
+ // Sanitize lines
+ for (var i = 0; i < lines.Length; i++)
+ {
+ lines[i] = PutTextLineAlignmentTrim(lines[i], lineAlignment);
+ }
+
+ // If line needs alignment, calculate the size for each line
+ if (lineAlignment is not PutTextLineAlignment.Left and not PutTextLineAlignment.None)
+ {
+ for (var i = 0; i < lines.Length; i++)
+ {
+ if (string.IsNullOrWhiteSpace(lines[i])) continue;
+ int baseLineRef = 0;
+ linesSize[i] = CvInvoke.GetTextSize(lines[i], fontFace, fontScale, thickness, ref baseLineRef);
+ width = Math.Max(width, linesSize[i].Width);
+ }
+ }
+
+ for (var i = 0; i < lines.Length; i++)
+ {
+ if(string.IsNullOrWhiteSpace(lines[i])) continue;
+
+ int x = lineAlignment switch
+ {
+ PutTextLineAlignment.None or PutTextLineAlignment.Left => org.X,
+ PutTextLineAlignment.Center => org.X + (width - linesSize[i].Width) / 2,
+ PutTextLineAlignment.Right => org.X + width - linesSize[i].Width,
+ _ => throw new ArgumentOutOfRangeException(nameof(lineAlignment), lineAlignment, null)
+ };
+
+ // Find total size of text block before this line
+ var lineYAdjustment = i * (lineGap + textSize.Height);
+ // Move text down from original line based on line number
+ int lineY = !bottomLeftOrigin ? org.Y + lineYAdjustment : org.Y - lineYAdjustment;
+ CvInvoke.PutText(src, lines[i], new Point(x, lineY), fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
+ }
+ }
+ /// <summary>
+ /// Extended OpenCV PutText to accepting line breaks, line alignment and rotation
+ /// </summary>
public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale, MCvScalar color,
- int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, double angle = 0)
+ int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, PutTextLineAlignment lineAlignment = default, double angle = 0)
{
if (angle % 360 == 0) // No rotation needed, cheaper cycle
{
- CvInvoke.PutText(src, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
+ src.PutTextExtended(text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin, lineAlignment);
return;
}
@@ -209,17 +589,19 @@ namespace UVtools.Core.Extensions
var sizeDifference = (rotatedSrc.Size - src.Size).Half();
org.Offset(sizeDifference.ToPoint());
org = org.Rotate(-angle, new Point(rotatedSrc.Size.Width / 2, rotatedSrc.Size.Height / 2));
- CvInvoke.PutText(rotatedSrc, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
+ rotatedSrc.PutTextExtended(text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin, lineAlignment);
- using var mask = rotatedSrc.CloneBlank();
- CvInvoke.PutText(mask, text, org, fontFace, fontScale, WhiteColor, thickness, lineType, bottomLeftOrigin);
+ using var mask = rotatedSrc.NewBlank();
+ mask.PutTextExtended(text, org, fontFace, fontScale, WhiteColor, thickness, lineType, bottomLeftOrigin, lineAlignment);
rotatedSrc.Rotate(angle, src.Size);
mask.Rotate(angle, src.Size);
rotatedSrc.CopyTo(src, mask);
}
+ #endregion
+ #region Utilities methods
/// <summary>
/// Determine the area (i.e. total number of pixels in the image),
/// initialize the output skeletonized image, and construct the
@@ -233,7 +615,7 @@ namespace UVtools.Core.Extensions
{
if (ksize.IsEmpty) ksize = new Size(3, 3);
Point anchor = new(-1, -1);
- var skeleton = src.CloneBlank();
+ var skeleton = src.NewBlank();
using var kernel = CvInvoke.GetStructuringElement(elementShape, ksize, anchor);
var image = src;
@@ -262,7 +644,7 @@ namespace UVtools.Core.Extensions
return skeleton;
}
-
+
/// <summary>
/// Determine the area (i.e. total number of pixels in the image),
/// initialize the output skeletonized image, and construct the
@@ -273,220 +655,6 @@ namespace UVtools.Core.Extensions
/// <param name="elementShape"></param>
public static Mat Skeletonize(this Mat src, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
=> src.Skeletonize(out _, ksize, elementShape);
-
- /// <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>
- /// <param name="dst">Target <see cref="Mat"/> to paste the <param name="src"></param></param>
- public static void CopyToCenter(this Mat src, Mat dst)
- {
- if (dst.Step > src.Step && dst.Height > src.Height)
- {
- var dx = Math.Abs(dst.Step - src.Step) / 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);
- }
- else if (dst.Step < src.Step && dst.Height < src.Height)
- {
- var dx = Math.Abs(dst.Step - src.Step) / 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);
- }
- }
-
- /// <summary>
- /// Gets the total length of this <see cref="Mat"/></param>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>The total length of this <see cref="Mat"/></returns>
- public static int GetLength(this Mat mat)
- {
- return mat.Step * mat.Height;
- }
-
- /// <summary>
- /// Gets a pixel index position on a span given X and Y
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="x">X coordinate</param>
- /// <param name="y">Y coordinate</param>
- /// <returns>The pixel index position</returns>
- public static int GetPixelPos(this Mat mat, int x, int y)
- {
- return y * mat.Step + x * mat.NumberOfChannels;
- }
-
- /// <summary>
- /// Gets a pixel index position on a span given X and Y
- /// </summary>
- /// <param name="mat"></param>
- /// <param name="point">X and Y Location</param>
- /// <returns>The pixel index position</returns>
- public static int GetPixelPos(this Mat mat, Point point)
- {
- return point.Y * mat.Step + point.X * mat.NumberOfChannels;
- }
-
- /// <summary>
- /// Gets a byte array copy of this <see cref="Mat"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Byte array </returns>
- public static byte[] GetBytes(this Mat mat)
- {
- byte[] data = new byte[mat.GetLength()];
- Marshal.Copy(mat.DataPointer, data, 0, data.Length);
- return data;
-
- /*GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
- using (Mat m2 = new Mat(mat.Size, mat.Depth, mat.NumberOfChannels, handle.AddrOfPinnedObject(),
- mat.Width * mat.NumberOfChannels))
- {
- mat.CopyTo(m2);
- }
- handle.Free();*/
-
- }
-
- /// <summary>
- /// Create a byte array of size of this <see cref="Mat"/>
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Blank byte array</returns>
- public static byte[] CreateByteArray(this Mat mat)
- {
- return new byte[mat.GetLength()];
- }
-
- public static Mat New(this Mat mat)
- {
- return new(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels);
- }
-
- public static Mat New(this Mat src, MCvScalar color)
- {
- Mat mat = new(src.Rows, src.Cols, src.Depth, src.NumberOfChannels);
- mat.SetTo(color);
- return mat;
- }
-
- /// <summary>
- /// Clone this <see cref="Mat"/> blanked (All zeros)
- /// </summary>
- /// <param name="mat"></param>
- /// <returns>Blanked <see cref="Mat"/></returns>
- public static Mat CloneBlank(this Mat mat)
- {
- return InitMat(mat.Size, mat.NumberOfChannels, mat.Depth);
- }
-
- public static byte GetByte(this Mat mat, int pos)
- {
- //return new Span<byte>(IntPtr.Add(mat.DataPointer, pos).ToPointer(), mat.Step)[0];
- var value = new byte[1];
- Marshal.Copy(mat.DataPointer + pos, value, 0, value.Length);
- return value[0];
- }
-
- public static byte GetByte(this Mat mat, int x, int y) => GetByte(mat, mat.GetPixelPos(x, y));
- public static byte GetByte(this Mat mat, Point pos) => GetByte(mat, mat.GetPixelPos(pos.X, pos.Y));
-
-
- public static void SetByte(this Mat mat, int pixel, byte value) => SetByte(mat, pixel, new[] {value});
-
- public static void SetByte(this Mat mat, int pixel, byte[] value) =>
- Marshal.Copy(value, 0, mat.DataPointer + pixel, value.Length);
-
- public static void SetByte(this Mat mat, int x, int y, byte value) =>
- SetByte(mat, x, y, new[] {value});
-
- public static void SetByte(this Mat mat, int x, int y, byte[] value) =>
- SetByte(mat, y * mat.Step + x * mat.NumberOfChannels, value);
-
- public static void SetBytes(this Mat mat, byte[] value) =>
- Marshal.Copy(value, 0, mat.DataPointer, value.Length);
-
- public static Mat InitMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U)
- {
- return Mat.Zeros(size.Height, size.Width, depthType, channels);
- /*var mat = new Mat(size, depthType, channels);
- switch (channels)
- {
- case 1:
- mat.SetTo(BlackByte);
- break;
- case 3:
- mat.SetTo(Black3Byte);
- break;
- case 4:
- mat.SetTo(Transparent4Byte);
- break;
- }
-
- return mat;*/
- }
-
- public static Mat InitMat(Size size, MCvScalar scalar, int channels = 1, DepthType depthType = DepthType.Cv8U)
- {
- var mat = new Mat(size, depthType, channels);
- mat.SetTo(scalar);
- return mat;
- }
-
- public static Mat RoiFromCenter(this Mat mat, Size targetSize, Rectangle roi)
- {
- if (targetSize == mat.Size) return mat;
- var newMat = InitMat(targetSize);
-
- var roiMat = new Mat(mat, roi);
-
-
- //int xStart = mat.Width / 2 - targetSize.Width / 2;
- //int yStart = mat.Height / 2 - targetSize.Height / 2;
-
- 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;
- }
-
- /// <summary>
- /// Allocates a new array of mat 's
- /// </summary>
- /// <param name="count">Array size</param>
- /// <returns></returns>
- public static Mat[] Allocate(uint count) => Allocate(count, Size.Empty);
-
- /// <summary>
- /// Allocates a new array of mat 's
- /// </summary>
- /// <param name="count">Array size</param>
- /// <param name="size">Image size to create, use <see cref="Size.Empty"/> to create a empty Mat</param>
- /// <returns>New mat array</returns>
- public static Mat[] Allocate(uint count, Size size)
- {
- var layers = new Mat[count];
- for (var i = 0; i < layers.Length; i++)
- {
- layers[i] = size == Size.Empty ? new Mat() : InitMat(size);
- }
-
- return layers;
- }
-
- public static byte[] GetPngByes(this Mat mat)
- {
- using var vector = new VectorOfByte();
- CvInvoke.Imencode(".png", mat, vector);
- return vector.ToArray();
- }
-
+ #endregion
}
}
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index 41cfe5f..2a6da89 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -617,8 +617,8 @@ namespace UVtools.Core.FileFormats
using (var mat = layer.LayerMat)
using (var matEncode = new Mat(mat.Height, mat.Step / 3, DepthType.Cv8U, 3))
{
- var span = mat.GetPixelSpan<byte>();
- var spanEncode = matEncode.GetPixelSpan<byte>();
+ var span = mat.GetDataSpan<byte>();
+ var spanEncode = matEncode.GetDataSpan<byte>();
for (int i = 0; i < span.Length; i++)
{
spanEncode[i] = span[i];
@@ -815,8 +815,8 @@ namespace UVtools.Core.FileFormats
using Mat mat = new();
CvInvoke.Imdecode(layer.CompressedBytes, ImreadModes.Color, mat);
using Mat matDecode = new(mat.Height, mat.Step, DepthType.Cv8U, 1);
- var span = mat.GetPixelSpan<byte>();
- var spanDecode = matDecode.GetPixelSpan<byte>();
+ var span = mat.GetDataSpan<byte>();
+ var spanDecode = matDecode.GetDataSpan<byte>();
for (int i = 0; i < span.Length; i++)
{
spanDecode[i] = span[i];
diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs
index 0c4129e..305cd08 100644
--- a/UVtools.Core/FileFormats/CXDLPFile.cs
+++ b/UVtools.Core/FileFormats/CXDLPFile.cs
@@ -486,7 +486,7 @@ namespace UVtools.Core.FileFormats
{
if (progress.Token.IsCancellationRequested) return;
if (Thumbnails[previewIndex] is null) return;
- var span = Thumbnails[previewIndex].GetPixelSpanByte();
+ var span = Thumbnails[previewIndex].GetDataByteSpan();
int index = 0;
for (int i = 0; i < span.Length; i += 3)
{
@@ -531,7 +531,7 @@ namespace UVtools.Core.FileFormats
List<LayerLine> layerLines = new();
var layer = this[layerIndex];
using var mat = layer.LayerMat;
- var span = mat.GetPixelSpanByte();
+ var span = mat.GetDataByteSpan();
for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
{
@@ -598,7 +598,7 @@ namespace UVtools.Core.FileFormats
Parallel.For(0, previews.Length, previewIndex =>
{
var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3);
- var span = mat.GetPixelSpanByte();
+ var span = mat.GetDataByteSpan();
int spanIndex = 0;
for (int i = 0; i < previews[previewIndex].Length; i += 2)
diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs
index 57b5ff6..82c8422 100644
--- a/UVtools.Core/FileFormats/FDGFile.cs
+++ b/UVtools.Core/FileFormats/FDGFile.cs
@@ -555,7 +555,7 @@ namespace UVtools.Core.FileFormats
//int pixel = 0;
for (int y = 0; y < image.Height; y++)
{
- var span = image.GetPixelRowSpan<byte>(y);
+ var span = image.GetRowSpan<byte>(y);
for (int x = 0; x < span.Length; x++)
{
diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs
index 7a227b1..da26561 100644
--- a/UVtools.Core/FileFormats/GR1File.cs
+++ b/UVtools.Core/FileFormats/GR1File.cs
@@ -346,7 +346,7 @@ namespace UVtools.Core.FileFormats
{
if (progress.Token.IsCancellationRequested) return;
if (Thumbnails[previewIndex] is null) return;
- var span = Thumbnails[previewIndex].GetPixelSpanByte();
+ var span = Thumbnails[previewIndex].GetDataByteSpan();
int index = 0;
for (int i = 0; i < span.Length; i += 3)
{
@@ -381,7 +381,7 @@ namespace UVtools.Core.FileFormats
List<LayerLine> layerLines = new();
var layer = this[layerIndex];
using var mat = layer.LayerMat;
- var span = mat.GetPixelSpanByte();
+ var span = mat.GetDataByteSpan();
for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
{
@@ -451,7 +451,7 @@ namespace UVtools.Core.FileFormats
Parallel.For(0, previews.Length, previewIndex =>
{
var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3);
- var span = mat.GetPixelSpanByte();
+ var span = mat.GetDataByteSpan();
int spanIndex = 0;
for (int i = 0; i < previews[previewIndex].Length; i += 2)
diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs
index 73af073..0b86e11 100644
--- a/UVtools.Core/FileFormats/MDLPFile.cs
+++ b/UVtools.Core/FileFormats/MDLPFile.cs
@@ -312,7 +312,7 @@ namespace UVtools.Core.FileFormats
{
if (progress.Token.IsCancellationRequested) return;
if (Thumbnails[previewIndex] is null) return;
- var span = Thumbnails[previewIndex].GetPixelSpanByte();
+ var span = Thumbnails[previewIndex].GetDataByteSpan();
int index = 0;
for (int i = 0; i < span.Length; i += 3)
{
@@ -347,7 +347,7 @@ namespace UVtools.Core.FileFormats
List<LayerLine> layerLines = new();
var layer = this[layerIndex];
using var mat = layer.LayerMat;
- var span = mat.GetPixelSpanByte();
+ var span = mat.GetDataByteSpan();
for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
{
@@ -416,7 +416,7 @@ namespace UVtools.Core.FileFormats
Parallel.For(0, previews.Length, previewIndex =>
{
var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3);
- var span = mat.GetPixelSpanByte();
+ var span = mat.GetDataByteSpan();
int spanIndex = 0;
for (int i = 0; i < previews[previewIndex].Length; i += 2)
diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs
index 6ede707..92cac2b 100644
--- a/UVtools.Core/FileFormats/PHZFile.cs
+++ b/UVtools.Core/FileFormats/PHZFile.cs
@@ -572,7 +572,7 @@ namespace UVtools.Core.FileFormats
//int pixel = 0;
for (int y = 0; y < image.Height; y++)
{
- var span = image.GetPixelRowSpan<byte>(y);
+ var span = image.GetRowSpan<byte>(y);
for (int x = 0; x < span.Length; x++)
{
diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs
index c1e2fc9..fb58af7 100644
--- a/UVtools.Core/Layer/Layer.cs
+++ b/UVtools.Core/Layer/Layer.cs
@@ -644,7 +644,7 @@ namespace UVtools.Core
var mat = LayerMat;
- var bytes = mat.GetPixelSpan<byte>();
+ var bytes = mat.GetDataSpan<byte>();
diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs
index 773e728..054807e 100644
--- a/UVtools.Core/Layer/LayerManager.cs
+++ b/UVtools.Core/Layer/LayerManager.cs
@@ -997,7 +997,7 @@ namespace UVtools.Core
using (var image = layer.LayerMat)
{
int step = image.Step;
- var span = image.GetPixelSpan<byte>();
+ var span = image.GetDataSpan<byte>();
if (touchBoundConfig.Enabled)
{
@@ -1138,7 +1138,7 @@ namespace UVtools.Core
//stats[i][3]: Height of Connected Component
//stats[i][4]: Total Area (in pixels) in Connected Component
- var labelSpan = labels.GetPixelSpan<int>();
+ var labelSpan = labels.GetDataSpan<int>();
for (int i = 1; i < numLabels; i++)
{
@@ -1154,7 +1154,7 @@ namespace UVtools.Core
if (previousImage is null)
{
previousImage = this[layer.Index - 1].LayerMat;
- previousSpan = previousImage.GetPixelSpan<byte>();
+ previousSpan = previousImage.GetDataSpan<byte>();
}
List<Point> points = new();
@@ -1227,7 +1227,7 @@ namespace UVtools.Core
anchor, overhangConfig.ErodeIterations, BorderType.Default,
new MCvScalar());
- var subtractedSpan = subtractedImage.GetPixelSpan<byte>();
+ var subtractedSpan = subtractedImage.GetDataSpan<byte>();
for (int y = 0; y < subtractedImage.Height; y++)
for (int x = 0; x < subtractedImage.Step; x++)
@@ -1426,8 +1426,8 @@ namespace UVtools.Core
using (var image = this[nextLayerIndex].LayerMat)
{
- var span = image.GetPixelSpan<byte>();
- using (var emguImage = image.CloneBlank())
+ var span = image.GetDataSpan<byte>();
+ using (var emguImage = image.NewBlank())
{
using (var vec =
new VectorOfVectorOfPoint(new VectorOfPoint(checkArea.Contour)))
@@ -1435,7 +1435,7 @@ namespace UVtools.Core
CvInvoke.DrawContours(emguImage, vec, -1, EmguExtensions.WhiteColor, -1);
}
- using (var intersectingAreasMat = image.CloneBlank())
+ using (var intersectingAreasMat = image.NewBlank())
{
if (haveNextAreas)
{
@@ -1455,8 +1455,8 @@ namespace UVtools.Core
bool exitPixelLoop = false;
uint blackCount = 0;
- var spanContour = emguImage.GetPixelSpan<byte>();
- var spanIntersect = intersectingAreasMat.GetPixelSpan<byte>();
+ var spanContour = emguImage.GetDataSpan<byte>();
+ var spanIntersect = intersectingAreasMat.GetDataSpan<byte>();
for (int y = checkArea.BoundingRectangle.Y;
y < checkArea.BoundingRectangle.Bottom &&
area.Type != LayerHollowArea.AreaType.Drain && !exitPixelLoop;
@@ -1638,7 +1638,7 @@ namespace UVtools.Core
var operationText = (PixelText)operation;
var mat = modifiedLayers.GetOrAdd(operation.LayerIndex, u => this[operation.LayerIndex].LayerMat);
- mat.PutTextRotated(operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.Angle);
+ 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)
{
@@ -1679,7 +1679,7 @@ namespace UVtools.Core
using (Mat matCircleRoi = new(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter)))
{
- using (Mat matCircleMask = matCircleRoi.CloneBlank())
+ using (Mat matCircleMask = matCircleRoi.NewBlank())
{
CvInvoke.Circle(matCircleMask, new Point(operationSupport.TipDiameter / 2, operationSupport.TipDiameter / 2),
operationSupport.TipDiameter / 2, new MCvScalar(operation.PixelBrightness), -1);
@@ -1717,7 +1717,7 @@ namespace UVtools.Core
using (Mat matCircleRoiInv = new())
{
CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv);
- using (Mat matCircleMask = matCircleRoi.CloneBlank())
+ using (Mat matCircleMask = matCircleRoi.NewBlank())
{
CvInvoke.Circle(matCircleMask, new Point(radius, radius), radius, new MCvScalar(255), -1);
CvInvoke.BitwiseAnd(matCircleRoiInv, matCircleMask, matCircleMask);
diff --git a/UVtools.Core/Objects/StaticObjects.cs b/UVtools.Core/Objects/StaticObjects.cs
index 5f4dbf4..4be5b4e 100644
--- a/UVtools.Core/Objects/StaticObjects.cs
+++ b/UVtools.Core/Objects/StaticObjects.cs
@@ -13,10 +13,8 @@ namespace UVtools.Core.Objects
// Compute the file's hash.
public static byte[] GetHashSha256(string filename)
{
- using (var stream = File.OpenRead(filename))
- {
- return Sha256.ComputeHash(stream);
- }
+ using var stream = File.OpenRead(filename);
+ return Sha256.ComputeHash(stream);
}
}
}
diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
index 8903780..2c3bc0a 100644
--- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
+++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs
@@ -640,7 +640,7 @@ namespace UVtools.Core.Operations
using (var erode = new Mat())
using (var diff = new Mat())
using (var target = new Mat())
- using (var mask = shape.CloneBlank())
+ using (var mask = shape.NewBlank())
{
mask.SetTo(new MCvScalar(byte.MaxValue-brightness));
CvInvoke.Erode(shape, erode, kernel, anchor, DimmingWallThickness, BorderType.Reflect101, default);
diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
index 8430fe5..2ce35f2 100644
--- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
+++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs
@@ -1237,7 +1237,7 @@ namespace UVtools.Core.Operations
layers[0] = EmguExtensions.InitMat(rect.Size);
CvInvoke.Rectangle(layers[0], rect, EmguExtensions.WhiteColor, -1, _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected);
- layers[1] = layers[0].CloneBlank();
+ layers[1] = layers[0].NewBlank();
if (holes.Length > 0)
{
CvInvoke.Rectangle(layers[1],
@@ -1666,7 +1666,7 @@ namespace UVtools.Core.Operations
{
var newLayer = layer.Clone();
newLayer.ExposureTime = (float)(newLayer.IsBottomLayer ? group.Key.BottomExposure : group.Key.Exposure);
- using var newMat = mat.CloneBlank();
+ using var newMat = mat.NewBlank();
foreach (var brightness in brightnesses)
{
ExposureItem item = new(group.Key.LayerHeight, group.Key.BottomExposure, group.Key.Exposure, brightness);
diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs
index f9f5fa2..058b7c6 100644
--- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs
+++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs
@@ -314,7 +314,7 @@ namespace UVtools.Core.Operations
{
var layers = new Mat[LayerCount];
- Slicer slicer = new(SlicerFile.Resolution, new SizeF((float) DisplayWidth, (float) DisplayHeight));
+ Slicer.Slicer slicer = new(SlicerFile.Resolution, new SizeF((float) DisplayWidth, (float) DisplayHeight));
Point center = new(SlicerFile.Resolution.Width / 2, SlicerFile.Resolution.Height / 2);
uint baseRadius = slicer.PixelsFromMillimeters(_baseDiameter) / 2;
uint baseLayers = (ushort) Math.Floor(_baseHeight / _layerHeight);
diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs
index 01b3ae9..0793da7 100644
--- a/UVtools.Core/Operations/OperationInfill.cs
+++ b/UVtools.Core/Operations/OperationInfill.cs
@@ -171,7 +171,7 @@ namespace UVtools.Core.Operations
or InfillAlgorithm.CubicInterlinked)
{
using var infillPattern = EmguExtensions.InitMat(new Size(_infillSpacing, _infillSpacing));
- using var matPattern = mat.CloneBlank();
+ using var matPattern = mat.NewBlank();
bool firstPattern = true;
uint accumulator = 0;
bool dynamicCenter = false;
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index 5f10340..035ec2e 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -381,7 +381,7 @@ namespace UVtools.Core.Operations
}
using var layer = fileFormat[i].LayerMat;
- using var layerRoi = layer.RoiFromCenter(SlicerFile.Resolution, fileFormatBoundingRectangle);
+ using var layerRoi = layer.NewRoiFromCenter(SlicerFile.Resolution, fileFormatBoundingRectangle);
SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
break;
@@ -396,7 +396,7 @@ namespace UVtools.Core.Operations
}
using var layer = fileFormat[i].LayerMat;
- using var layerRoi = layer.RoiFromCenter(SlicerFile.Resolution, fileFormatBoundingRectangle);
+ using var layerRoi = layer.NewRoiFromCenter(SlicerFile.Resolution, fileFormatBoundingRectangle);
SlicerFile[layerIndex] = new Layer(layerIndex, layerRoi, SlicerFile);
break;
}
diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs
index b5a0bc8..95dc08d 100644
--- a/UVtools.Core/Operations/OperationPattern.cs
+++ b/UVtools.Core/Operations/OperationPattern.cs
@@ -294,7 +294,7 @@ namespace UVtools.Core.Operations
using var mat = SlicerFile[layerIndex].LayerMat;
using var layerRoi = new Mat(mat, ROI);
- using var dstLayer = mat.CloneBlank();
+ using var dstLayer = mat.NewBlank();
for (ushort col = 0; col < Cols; col++)
for (ushort row = 0; row < Rows; row++)
{
diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs
index f441064..7e86554 100644
--- a/UVtools.Core/Operations/OperationPixelDimming.cs
+++ b/UVtools.Core/Operations/OperationPixelDimming.cs
@@ -622,8 +622,8 @@ namespace UVtools.Core.Operations
AlternatePattern ??= Pattern;
using var blankMat = EmguExtensions.InitMat(SlicerFile.Resolution);
- using var matPattern = blankMat.CloneBlank();
- using var matAlternatePattern = blankMat.CloneBlank();
+ using var matPattern = blankMat.NewBlank();
+ using var matAlternatePattern = blankMat.NewBlank();
var target = GetRoiOrDefault(blankMat);
CvInvoke.Repeat(Pattern, target.Rows / Pattern.Rows + 1, target.Cols / Pattern.Cols + 1, matPattern);
diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs
index 34b30e4..1e25b10 100644
--- a/UVtools.Core/Operations/OperationRedrawModel.cs
+++ b/UVtools.Core/Operations/OperationRedrawModel.cs
@@ -191,9 +191,9 @@ namespace UVtools.Core.Operations
if (contours.Size <= 0) return;
using var nextLayerMat = otherFile[layerIndex + 1].LayerMat;
using var nextLayerMatRoi = GetRoiOrDefault(nextLayerMat);
- var fullSpan = fullMatRoi.GetPixelSpan<byte>();
- var supportsSpan = supportsMat.GetPixelSpan<byte>();
- var nextSpan = nextLayerMatRoi.GetPixelSpan<byte>();
+ var fullSpan = fullMatRoi.GetDataSpan<byte>();
+ var supportsSpan = supportsMat.GetDataSpan<byte>();
+ var nextSpan = nextLayerMatRoi.GetDataSpan<byte>();
for (int i = 0; i < contours.Size; i++)
{
var foundContour = false;
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index 3e9c29d..0eb6318 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -175,7 +175,7 @@ namespace UVtools.Core.Operations
if (progress.Token.IsCancellationRequested) return;
Layer layer = SlicerFile[group.Key];
Mat image = layer.LayerMat;
- Span<byte> bytes = image.GetPixelSpan<byte>();
+ Span<byte> bytes = image.GetDataSpan<byte>();
foreach (var issue in group)
{
foreach (var issuePixel in issue.Pixels)
@@ -225,7 +225,7 @@ namespace UVtools.Core.Operations
initImage();
if (bytes == null)
- bytes = image.GetPixelSpan<byte>();
+ bytes = image.GetDataSpan<byte>();
foreach (var issuePixel in issue.Pixels)
{
diff --git a/UVtools.Core/PixelEditor/PixelText.cs b/UVtools.Core/PixelEditor/PixelText.cs
index 5a420a9..f3179dd 100644
--- a/UVtools.Core/PixelEditor/PixelText.cs
+++ b/UVtools.Core/PixelEditor/PixelText.cs
@@ -9,6 +9,7 @@ using System;
using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
+using UVtools.Core.Extensions;
namespace UVtools.Core.PixelEditor
{
@@ -19,6 +20,7 @@ namespace UVtools.Core.PixelEditor
private ushort _thickness = 1;
private string _text;
private bool _mirror;
+ private EmguExtensions.PutTextLineAlignment _lineAlignment = EmguExtensions.PutTextLineAlignment.Left;
private double _angle;
private byte _removePixelBrightness;
public override PixelOperationType OperationType => PixelOperationType.Text;
@@ -55,6 +57,12 @@ namespace UVtools.Core.PixelEditor
set => RaiseAndSetIfChanged(ref _mirror, value);
}
+ public EmguExtensions.PutTextLineAlignment LineAlignment
+ {
+ get => _lineAlignment;
+ set => RaiseAndSetIfChanged(ref _lineAlignment, value);
+ }
+
public double Angle
{
get => _angle;
@@ -81,19 +89,20 @@ namespace UVtools.Core.PixelEditor
public PixelText(){}
- public PixelText(uint layerIndex, Point location, LineType lineType, FontFace font, double fontScale, ushort thickness, string text, bool mirror, double angle, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness)
+ public PixelText(uint layerIndex, Point location, LineType lineType, FontFace font, double fontScale, ushort thickness, string text, bool mirror, EmguExtensions.PutTextLineAlignment lineAlignment, double angle, byte removePixelBrightness, byte pixelBrightness, bool isAdd) : base(layerIndex, location, lineType, pixelBrightness)
{
_font = font;
_fontScale = fontScale;
_thickness = thickness;
_text = text;
_mirror = mirror;
+ _lineAlignment = lineAlignment;
_angle = angle;
IsAdd = isAdd;
_removePixelBrightness = removePixelBrightness;
int baseLine = 0;
- Size = CvInvoke.GetTextSize(text, font, fontScale, thickness, ref baseLine);
+ Size = EmguExtensions.GetTextSizeExtended(text, font, fontScale, thickness, ref baseLine, lineAlignment);
Rectangle = new Rectangle(location, Size);
}
diff --git a/UVtools.Core/Slicer.cs b/UVtools.Core/Slicer.cs
deleted file mode 100644
index 11529d7..0000000
--- a/UVtools.Core/Slicer.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * GNU AFFERO GENERAL PUBLIC LICENSE
- * Version 3, 19 November 2007
- * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- * Everyone is permitted to copy and distribute verbatim copies
- * of this license document, but changing it is not allowed.
- */
-using System;
-using System.Drawing;
-
-namespace UVtools.Core
-{
- public class Slicer
- {
- /// <summary>
- /// Gets the size of resolution
- /// </summary>
- public Size Resolution { get; private set; }
-
- /// <summary>
- /// Gets the size of display
- /// </summary>
- public SizeF Display { get; private set; }
-
- /// <summary>
- /// Gets the pixels per millimeters
- /// </summary>
- public SizeF Ppmm { get; private set; }
-
- public Slicer(Size resolution, SizeF display)
- {
- Init(resolution, display);
- }
-
- public void Init(Size resolution, SizeF display)
- {
- Resolution = resolution;
- Display = display;
-
- Ppmm = new SizeF(resolution.Width / display.Width, resolution.Height / display.Height);
- }
-
- public decimal MillimetersFromPixelsX(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Width, 2);
- public decimal MillimetersFromPixelsY(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Height, 2);
- public decimal MillimetersFromPixels (uint pixels) => (decimal) Math.Round(pixels / Math.Max(Ppmm.Width, Ppmm.Height), 2);
-
- public static decimal MillimetersFromPixelsX(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Width / display.Width), 2);
- public static decimal MillimetersFromPixelsY(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Height / display.Height), 2);
-
-
-
- public uint PixelsFromMillimetersX(decimal millimeters) => (uint) Math.Floor(millimeters * (decimal) Ppmm.Width);
- public uint PixelsFromMillimetersY(decimal millimeters) => (uint) Math.Floor(millimeters * (decimal) Ppmm.Height);
- public uint PixelsFromMillimeters (decimal millimeters) => (uint) Math.Floor(millimeters * (decimal) Math.Max(Ppmm.Width, Ppmm.Height));
-
- public static uint PixelsFromMillimetersX(Size resolution, SizeF display, decimal millimeters) => (uint)Math.Floor(resolution.Width / display.Width * (double) millimeters);
- public static uint PixelsFromMillimetersY(Size resolution, SizeF display, decimal millimeters) => (uint)Math.Floor(resolution.Height / display.Height * (double) millimeters);
- }
-}
diff --git a/UVtools.Core/Slicer/LinAlgUtils.cs b/UVtools.Core/Slicer/LinAlgUtils.cs
new file mode 100644
index 0000000..c6efbcf
--- /dev/null
+++ b/UVtools.Core/Slicer/LinAlgUtils.cs
@@ -0,0 +1,101 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Globalization;
+using System.Linq;
+using QuantumConcepts.Formats.StereoLithography;
+
+namespace UVtools.Core.Slicer
+{
+ public static class LinAlgUtils
+ {
+ #region Find Facets
+ public static List<Facet> FindFacetsIntersectingZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.Any(v => v.Z <= z) && f.Vertices.Any(v => v.Z >= z) && Math.Abs(f.Normal.Z) != 1).ToList();
+ }
+
+ public static List<Facet> FindFlatFacetsAtZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.All(v => v.Z == z)).ToList();
+ }
+
+ public static List<Facet> FindTopFlatFacetsAtZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.Any(v => v.Z == z) && f.Normal.Z == 1).ToList();
+ }
+ public static List<Facet> FindBottomFlatFacetsAtZIndex(STLDocument stl, float z)
+ {
+ return stl.Facets.Where(f => f.Vertices.Any(v => v.Z == z) && f.Normal.Z == -1).ToList();
+ }
+ #endregion
+
+ public static SliceLine CreateLineFromFacetAtZIndex(Facet facet, float z)
+ {
+ // This won't work for flat horizontal plane vertices, so throw if that's what we've got
+ if (Math.Abs(facet.Normal.Z) == 1)
+ throw new Exception("Cannot create lines for flat horizontal planes");
+ var verts = facet.Vertices;
+ var returnLine = new SliceLine();
+ // If any vertices are ON the z-index, just return that line
+ returnLine.AddRange(verts.Where(v => v.Z == z).Select(v => new PointF(v.X, v.Y)));
+ // For uniformity, I'm representing a point as a "line" between two of the same point
+ // It's janky, I'll refactor later
+ if (returnLine.Count == 1)
+ {
+ returnLine.Add(returnLine[0]);
+ }
+ if (returnLine.Count == 2)
+ {
+ returnLine.Normal = new PointF(facet.Normal.X, facet.Normal.Y);
+ return returnLine;
+ }
+ // If no vertices are ON the z-index, find the two points where they CROSS it
+ if ((verts[0].Z - z) / (verts[1].Z - z) < 0)
+ returnLine.Add(CalculateZIntercept(verts[0], verts[1], z));
+ if ((verts[2].Z - z) / (verts[1].Z - z) < 0)
+ returnLine.Add(CalculateZIntercept(verts[2], verts[1], z));
+
+ if (returnLine.Count < 2)
+ returnLine.Add(CalculateZIntercept(verts[0], verts[2], z));
+
+ returnLine.Normal = new PointF(facet.Normal.X, facet.Normal.Y);
+
+ if (!returnLine.Validate())
+ throw new InvalidOperationException("Invalid Point Data");
+
+ return returnLine;
+ }
+
+ public static PointF CalculateZIntercept(Vertex v1, Vertex v2, float z)
+ {
+ var returnX = CalculateDimensionalValueAtIndex(new PointF(v1.Z, v1.X), new PointF(v2.Z, v2.X), z);
+ var returnY = CalculateDimensionalValueAtIndex(new PointF(v1.Z, v1.Y), new PointF(v2.Z, v2.Y), z);
+ return new PointF
+ {
+ X = returnX,
+ Y = returnY
+ };
+ }
+
+ public static float CalculateDimensionalValueAtIndex(PointF p1, PointF p2, float z, string precision = "0000")
+ {
+ var slope = (p1.Y - p2.Y) / (p1.X - p2.X);
+ var intercept = p1.Y - (slope * p1.X);
+ var rawVal = slope * z + intercept;
+ // using floats we end up with some infinitesimal rounding errors,
+ // so we need to set the precision to something reasonable. Default is 1/100th of a micron
+ // I'm sure there's a better way than converting it to a string and then back to a float,
+ // but that's what I've got right now, so that's what I'm doing.
+ var strVal = rawVal.ToString($"0.{precision}");
+ return float.Parse(strVal, CultureInfo.InvariantCulture.NumberFormat);
+ }
+ }
+}
diff --git a/UVtools.Core/Slicer/RangeUtils.cs b/UVtools.Core/Slicer/RangeUtils.cs
new file mode 100644
index 0000000..92f7f42
--- /dev/null
+++ b/UVtools.Core/Slicer/RangeUtils.cs
@@ -0,0 +1,77 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using QuantumConcepts.Formats.StereoLithography;
+
+namespace UVtools.Core.Slicer
+{
+ public static class RangeUtils
+ {
+ public static float CalculateHighX(STLDocument stl)
+ {
+ float highestX = stl.Facets[0].Vertices[0].X;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.X > highestX)
+ highestX = v.X;
+ }));
+ return highestX;
+ }
+ public static float CalculateLowX(STLDocument stl)
+ {
+ float lowestX = stl.Facets[0].Vertices[0].X;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.X < lowestX)
+ lowestX = v.X;
+ }));
+ return lowestX;
+ }
+
+ public static float CalculateHighY(STLDocument stl)
+ {
+ float highestY = stl.Facets[0].Vertices[0].Y;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.Y > highestY)
+ highestY = v.Y;
+ }));
+ return highestY;
+ }
+ public static float CalculateLowY(STLDocument stl)
+ {
+ float lowestY = stl.Facets[0].Vertices[0].Y;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.Y < lowestY)
+ lowestY = v.Y;
+ }));
+ return lowestY;
+ }
+
+ public static float CalculateHighZ(STLDocument stl)
+ {
+ float highestZ = stl.Facets[0].Vertices[0].Z;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.Z > highestZ)
+ highestZ = v.Z;
+ }));
+ return highestZ;
+ }
+ public static float CalculateLowZ(STLDocument stl)
+ {
+ float lowestZ = stl.Facets[0].Vertices[0].Z;
+ stl.Facets.ForEach(f => f.Vertices.ForEach(v =>
+ {
+ if (v.Z < lowestZ)
+ lowestZ = v.Z;
+ }));
+ return lowestZ;
+ }
+ }
+}
diff --git a/UVtools.Core/Slicer/Slice.cs b/UVtools.Core/Slicer/Slice.cs
new file mode 100644
index 0000000..de26bdc
--- /dev/null
+++ b/UVtools.Core/Slicer/Slice.cs
@@ -0,0 +1,42 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using System.Collections.Generic;
+using System.Linq;
+
+namespace UVtools.Core.Slicer
+{
+ public class Slice : List<SliceLine>
+ {
+ public bool ValidateShapeIntegrity()
+ {
+ // A shape has to have at least three lines to close
+ if (Count < 3)
+ return false;
+
+ var allPoints = this.ToList();
+ var pointFreq = new Dictionary<SliceLine, int>();
+
+ // There can't be any hanging lines. Verify every point connects to at least one other line.
+ foreach (var p in allPoints)
+ {
+ if (!pointFreq.ContainsKey(p))
+ pointFreq[p] = 1;
+ else
+ pointFreq[p]++;
+ }
+
+ foreach (var p in pointFreq.Keys)
+ {
+ if (pointFreq[p] < 2)
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/UVtools.Core/Slicer/SliceLine.cs b/UVtools.Core/Slicer/SliceLine.cs
new file mode 100644
index 0000000..1d82a7a
--- /dev/null
+++ b/UVtools.Core/Slicer/SliceLine.cs
@@ -0,0 +1,61 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace UVtools.Core.Slicer
+{
+ public class SliceLine : List<PointF>
+ {
+ private PointF _normal;
+
+ public PointF Normal
+ {
+ get => _normal;
+ set => _normal = CalculateNormal(value);
+ }
+
+ public PointF CalculateNormal(PointF normal = default)
+ {
+ if (!Validate())
+ throw new InvalidOperationException("Can't calculate Normal without points set");
+
+ if (normal == PointF.Empty)
+ normal = _normal;
+ if (normal == PointF.Empty)
+ throw new InvalidOperationException("Can't calculate Normal without a starting point");
+
+ // calculate normal slopes
+ var dX = this[0].Y - this[1].Y;
+ var dY = this[0].X - this[1].X;
+ // determine normal direction
+ var xDir = normal.X >= 0 ? 1 : -1;
+ var yDir = normal.Y >= 0 ? 1 : -1;
+ // check for delta 0 in either direction
+ if (dX == 0 && dY == 0)
+ return new PointF(0, 0);
+ if (dX == 0)
+ return new PointF(0, yDir);
+ if (dY == 0)
+ return new PointF(xDir, 0);
+
+ // if there aren't any zeroes, calculate the hypotenuse
+ var hypotenuse = (float)Math.Sqrt((dX * dX) + (dY * dY));
+ // use cross-multiplication and solve for x & y to find normal values where hypotenuse == 1
+ dX = Math.Abs(dX / hypotenuse) * xDir;
+ dY = Math.Abs(dY / hypotenuse) * yDir;
+ return new PointF(dX, dY);
+ }
+
+ public bool Validate()
+ {
+ return Count == 2;
+ }
+ }
+}
diff --git a/UVtools.Core/Slicer/Slicer.cs b/UVtools.Core/Slicer/Slicer.cs
new file mode 100644
index 0000000..a570948
--- /dev/null
+++ b/UVtools.Core/Slicer/Slicer.cs
@@ -0,0 +1,186 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using QuantumConcepts.Formats.StereoLithography;
+
+namespace UVtools.Core.Slicer
+{
+ public class Slicer
+ {
+ private float _lowX = float.NaN;
+ private float _highX = float.NaN;
+ private float _lowY = float.NaN;
+ private float _highY = float.NaN;
+ private float _lowZ = float.NaN;
+ private float _highZ = float.NaN;
+ private readonly STLDocument _stl;
+ protected Dictionary<float, Slice> _slices = new ();
+
+ private STLDocument Stl => _stl;
+
+ /// <summary>
+ /// Gets the size of resolution
+ /// </summary>
+ public Size Resolution { get; private set; }
+
+ /// <summary>
+ /// Gets the size of display
+ /// </summary>
+ public SizeF Display { get; private set; }
+
+ /// <summary>
+ /// Gets the pixels per millimeters
+ /// </summary>
+ public SizeF Ppmm { get; private set; }
+
+ public float LowX
+ {
+ get
+ {
+ if(_lowX == float.NaN)
+ _lowX = RangeUtils.CalculateLowX(_stl);
+
+ return _lowX;
+ }
+ }
+
+ public float HighX
+ {
+ get
+ {
+ if (_highX == float.NaN)
+ _highX = RangeUtils.CalculateHighX(_stl);
+
+ return _highX;
+ }
+ }
+
+ public float LowY
+ {
+ get
+ {
+ if (_lowY == float.NaN)
+ _lowY = RangeUtils.CalculateLowY(_stl);
+
+ return _lowY;
+ }
+ }
+
+ public float HighY
+ {
+ get
+ {
+ if (_highY == float.NaN)
+ _highY = RangeUtils.CalculateHighY(_stl);
+
+ return _highY;
+ }
+ }
+
+ public float LowZ
+ {
+ get
+ {
+ if (_lowZ == float.NaN)
+ _lowZ = RangeUtils.CalculateLowZ(_stl);
+
+ return _lowZ;
+ }
+ }
+
+ public float HighZ
+ {
+ get
+ {
+ if (_highZ == float.NaN)
+ _highZ = RangeUtils.CalculateHighZ(_stl);
+
+ return _highZ;
+ }
+ }
+
+ public Slicer(Size resolution, SizeF display)
+ {
+ Init(resolution, display);
+ }
+
+ public Slicer(Size resolution, SizeF display, STLDocument stl) : this(resolution, display)
+ {
+ _stl = stl;
+ }
+
+ public Slicer(Size resolution, SizeF display, string stlPath) : this(resolution, display)
+ {
+ _stl = STLDocument.Open(stlPath);
+
+ if (_stl is null)
+ throw new FileNotFoundException(null, stlPath);
+ }
+
+ public void Init(Size resolution, SizeF display)
+ {
+ Resolution = resolution;
+ Display = display;
+
+ Ppmm = new SizeF(resolution.Width / display.Width, resolution.Height / display.Height);
+ }
+
+ #region Slice Methods
+ public Slice GetSliceAtZIndex(float z)
+ {
+ // cache this in the event you have to retrieve a single value more than once,
+ // we don't want to have to do this math again
+ if (!_slices.ContainsKey(z))
+ {
+ var facets = LinAlgUtils.FindFacetsIntersectingZIndex(_stl, z);
+ _slices[z] = new Slice();
+ _slices[z].AddRange(facets.Select(f => LinAlgUtils.CreateLineFromFacetAtZIndex(f, z)));
+ }
+
+ return _slices[z];
+ }
+
+ public Dictionary<float, Slice> SliceModel(float layerHeight)
+ {
+ var newDict = new Dictionary<float, Slice>();
+ for (var z = RangeUtils.CalculateLowZ(_stl); z <= RangeUtils.CalculateHighZ(_stl); z += layerHeight)
+ {
+ if (_slices.Keys.Contains(z))
+ newDict[z] = _slices[z];
+ else
+ {
+ newDict[z] = GetSliceAtZIndex(z);
+ _slices[z] = newDict[z];
+ }
+ }
+ return newDict;
+ }
+ #endregion
+
+ public decimal MillimetersFromPixelsX(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Width, 2);
+ public decimal MillimetersFromPixelsY(uint pixels) => (decimal) Math.Round(pixels / Ppmm.Height, 2);
+ public decimal MillimetersFromPixels (uint pixels) => (decimal) Math.Round(pixels / Math.Max(Ppmm.Width, Ppmm.Height), 2);
+
+ public static decimal MillimetersFromPixelsX(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Width / display.Width), 2);
+ public static decimal MillimetersFromPixelsY(Size resolution, SizeF display, uint pixels) => (decimal)Math.Round(pixels / (resolution.Height / display.Height), 2);
+
+
+
+ public uint PixelsFromMillimetersX(decimal millimeters) => (uint) Math.Floor(millimeters * (decimal) Ppmm.Width);
+ public uint PixelsFromMillimetersY(decimal millimeters) => (uint) Math.Floor(millimeters * (decimal) Ppmm.Height);
+ public uint PixelsFromMillimeters (decimal millimeters) => (uint) Math.Floor(millimeters * (decimal) Math.Max(Ppmm.Width, Ppmm.Height));
+
+ public static uint PixelsFromMillimetersX(Size resolution, SizeF display, decimal millimeters) => (uint)Math.Floor(resolution.Width / display.Width * (double) millimeters);
+ public static uint PixelsFromMillimetersY(Size resolution, SizeF display, decimal millimeters) => (uint)Math.Floor(resolution.Height / display.Height * (double) millimeters);
+ }
+}
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index d0f18c5..f6a08d3 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.13.1</Version>
+ <Version>2.13.2</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
@@ -53,6 +53,7 @@
<PackageReference Include="morelinq" Version="3.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
+ <PackageReference Include="QuantumConcepts.Formats.STL.netcore" Version="1.3.1" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />
</ItemGroup>
diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
index 8984c44..cd32d11 100644
--- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs
+++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs
@@ -347,6 +347,9 @@
<Component Id="owc363CAFE6342F1E71AB07D13BAEEE5FF0" Guid="8e946fdc-ea52-7c4b-a8b3-69e1ecf51086">
<File Id="owf363CAFE6342F1E71AB07D13BAEEE5FF0" Source="$(var.SourceDir)\SkiaSharp.dll" KeyPath="yes" />
</Component>
+ <Component Id="owc57FFE4BD9EBED4E6F6741F389009CBAA" Guid="63b5b00e-b356-ad17-9580-4a6ac8da581a">
+ <File Id="owf57FFE4BD9EBED4E6F6741F389009CBAA" Source="$(var.SourceDir)\STL-netcore.dll" KeyPath="yes" />
+ </Component>
<Component Id="owc25FBE4153F32A5634B0ADA8A6954CC89" Guid="6c98bea6-1304-2fb8-4ce3-709ad7fb9601">
<File Id="owf25FBE4153F32A5634B0ADA8A6954CC89" Source="$(var.SourceDir)\System.AppContext.dll" KeyPath="yes" />
</Component>
diff --git a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
index 5e1dbde..028b109 100644
--- a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
+++ b/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs
@@ -80,7 +80,7 @@ namespace UVtools.ScriptSample
// Layer 3 = Right eye
// Layer 4 = Mouth
// Exercise for you: Do eyebrows
- var mats = EmguExtensions.Allocate(layerCount, SlicerFile.Resolution); // Allocate x images with file resolution
+ var mats = EmguExtensions.InitMats(layerCount, SlicerFile.Resolution); // Allocate x images with file resolution
int x, y;
int xCenter = (int) (SlicerFile.ResolutionX / 2);
diff --git a/UVtools.WPF/Controls/KernelControl.axaml.cs b/UVtools.WPF/Controls/KernelControl.axaml.cs
index 1e4535a..e524801 100644
--- a/UVtools.WPF/Controls/KernelControl.axaml.cs
+++ b/UVtools.WPF/Controls/KernelControl.axaml.cs
@@ -94,7 +94,7 @@ namespace UVtools.WPF.Controls
string text = string.Empty;
for (int y = 0; y < kernel.Height; y++)
{
- var span = kernel.GetPixelRowSpan<byte>(y);
+ var span = kernel.GetRowSpan<byte>(y);
var line = string.Empty;
for (int x = 0; x < span.Length; x++)
{
diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
index c368acf..aeff08d 100644
--- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs
@@ -138,7 +138,7 @@ namespace UVtools.WPF.Controls.Tools
public void GenerateMask()
{
var roi = App.MainWindow.ROI;
- Operation.Mask = roi.IsEmpty ? App.MainWindow.LayerCache.Image.CloneBlank() : new Mat(roi.Size, DepthType.Cv8U, 1);
+ Operation.Mask = roi.IsEmpty ? App.MainWindow.LayerCache.Image.NewBlank() : new Mat(roi.Size, DepthType.Cv8U, 1);
int radius = (int)_genDiameter;
if (radius == 0)
diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs
index 6871d75..403f17a 100644
--- a/UVtools.WPF/MainWindow.Issues.cs
+++ b/UVtools.WPF/MainWindow.Issues.cs
@@ -121,7 +121,7 @@ namespace UVtools.WPF
if (Progress.Token.IsCancellationRequested) return;
using (var image = SlicerFile[layerIssues.Key].LayerMat)
{
- var bytes = image.GetPixelSpan<byte>();
+ var bytes = image.GetDataSpan<byte>();
bool edited = false;
diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs
index f781800..c0a304a 100644
--- a/UVtools.WPF/MainWindow.LayerPreview.cs
+++ b/UVtools.WPF/MainWindow.LayerPreview.cs
@@ -24,6 +24,7 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
+using Emgu.CV.XImgproc;
using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.PixelEditor;
@@ -692,7 +693,7 @@ namespace UVtools.WPF
var watch = Stopwatch.StartNew();
LayerCache.Layer = SlicerFile[_actualLayer];
-
+
try
{
//var imageSpan = LayerCache.Image.GetPixelSpan<byte>();
@@ -1024,7 +1025,7 @@ namespace UVtools.WPF
operationText.Thickness, operationText.LineType, operationText.Mirror);*/
LayerCache.ImageBgr.PutTextRotated(operationText.Text, operationText.Location,
operationText.Font, operationText.FontScale, new MCvScalar(color.B, color.G, color.R),
- operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.Angle);
+ operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.LineAlignment, operationText.Angle);
}
else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser)
{
@@ -1087,8 +1088,7 @@ namespace UVtools.WPF
{
// Don't render crosshairs for selected issue that are not on the current layer, or for
// issue types that don't have a specific location or bounds.
- if (issue.LayerIndex != ActualLayer || issue.Type == LayerIssue.IssueType.EmptyLayer
- || issue.Type == LayerIssue.IssueType.TouchingBound)
+ if (issue.LayerIndex != ActualLayer || issue.Type is LayerIssue.IssueType.EmptyLayer or LayerIssue.IssueType.TouchingBound)
continue;
DrawCrosshair(issue.BoundingRectangle);
@@ -1116,7 +1116,7 @@ namespace UVtools.WPF
LayerImageBox.Image = LayerCache.Bitmap = LayerCache.ImageBgr.ToBitmap();
-
+
RefreshCurrentLayerData();
watch.Stop();
@@ -1897,15 +1897,17 @@ namespace UVtools.WPF
if (string.IsNullOrEmpty(text) || DrawingPixelText.FontScale < 0.2) return;
int baseLine = 0;
- var size = CvInvoke.GetTextSize(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine);
+ //var size = CvInvoke.GetTextSize(text, DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness, ref baseLine);
+ 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);
//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);
-
- CvInvoke.PutText(cursor, text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror);
+
+ cursor.PutTextExtended(text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.LineAlignment);
+ //CvInvoke.PutText(cursor, text, size.ToPoint(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror);
cursor.RotateAdjustBounds(DrawingPixelText.Angle);
//cursor.Rotate(DrawingPixelText.Angle);
//cursor.PutTextRotated(text, cursor.Size.ToPoint().Half(), DrawingPixelText.Font, DrawingPixelText.FontScale, _pixelEditorCursorColor, DrawingPixelText.Thickness, DrawingPixelText.LineType, DrawingPixelText.Mirror, DrawingPixelText.Angle);
diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs
index 80f127a..43be784 100644
--- a/UVtools.WPF/MainWindow.PixelEditor.cs
+++ b/UVtools.WPF/MainWindow.PixelEditor.cs
@@ -262,7 +262,7 @@ namespace UVtools.WPF
{
var operationText = new PixelText(layerIndex, realLocation, DrawingPixelText.LineType,
DrawingPixelText.Font, DrawingPixelText.FontScale, DrawingPixelText.Thickness,
- DrawingPixelText.Text, DrawingPixelText.Mirror, DrawingPixelText.Angle, DrawingPixelText.RemovePixelBrightness, DrawingPixelText.PixelBrightness, isAdd);
+ DrawingPixelText.Text, DrawingPixelText.Mirror, DrawingPixelText.LineAlignment, DrawingPixelText.Angle, DrawingPixelText.RemovePixelBrightness, DrawingPixelText.PixelBrightness, isAdd);
//if (PixelHistory.Contains(operation)) continue;
//PixelHistory.Add(operation);
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 89f1090..1eedf95 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -982,7 +982,7 @@
</Border>
<Grid
- RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto"
+ RowDefinitions="Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto,10,Auto"
ColumnDefinitions="Auto,10,130,5,40">
<TextBlock
@@ -1050,77 +1050,92 @@
Grid.Row="8"
Grid.Column="2"
Grid.ColumnSpan="3"
+ AcceptsReturn="True"
+ Height="90"
Text="{Binding DrawingPixelText.Text}"/>
+ <TextBlock
+ Grid.Row="10"
+ Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Line alignment:" />
+ <ComboBox
+ Grid.Row="10"
+ Grid.Column="2"
+ Grid.ColumnSpan="3"
+ Width="180"
+ Items="{Binding DrawingPixelText.LineAlignment, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding DrawingPixelText.LineAlignment, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
<CheckBox
- Grid.Row="10"
+ Grid.Row="12"
Grid.Column="2"
Grid.ColumnSpan="3"
Content="Flip text Vertically"
IsChecked="{Binding DrawingPixelText.Mirror}"/>
<TextBlock
- Grid.Row="12"
+ Grid.Row="14"
Grid.Column="0"
VerticalAlignment="Center"
Text="Rotation angle:" />
<NumericUpDown
- Grid.Row="12"
+ Grid.Row="14"
Grid.Column="2"
FormatString="F2"
Minimum="-360"
Maximum="360"
Value="{Binding DrawingPixelText.Angle}"/>
<TextBlock
- Grid.Row="12"
+ Grid.Row="14"
Grid.Column="4"
VerticalAlignment="Center"
Text="º" />
<TextBlock
- Grid.Row="14"
+ Grid.Row="16"
Grid.Column="0"
VerticalAlignment="Center"
Text="Remove pixel brightness:" />
<NumericUpDown
- Grid.Row="14"
+ Grid.Row="16"
Grid.Column="2"
Minimum="0"
Maximum="255"
Value="{Binding DrawingPixelText.RemovePixelBrightness}"/>
<TextBlock
- Grid.Row="14"
+ Grid.Row="16"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" />
<TextBlock
- Grid.Row="16"
+ Grid.Row="18"
Grid.Column="0"
VerticalAlignment="Center"
Text="Add pixel brightness:" />
<NumericUpDown
- Grid.Row="16"
+ Grid.Row="18"
Grid.Column="2"
Minimum="1"
Maximum="255"
Value="{Binding DrawingPixelText.PixelBrightness}"/>
<TextBlock
- Grid.Row="16"
+ Grid.Row="18"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0\}%}" />
<TextBlock
- Grid.Row="18"
+ Grid.Row="20"
Grid.Column="0"
VerticalAlignment="Center"
Text="Layers depth below:" />
<NumericUpDown
- Grid.Row="18"
+ Grid.Row="20"
Grid.Column="2"
Grid.ColumnSpan="3"
Minimum="0"
@@ -1129,12 +1144,12 @@
<TextBlock
- Grid.Row="20"
+ Grid.Row="22"
Grid.Column="0"
VerticalAlignment="Center"
Text="Layers depth above:" />
<NumericUpDown
- Grid.Row="20"
+ Grid.Row="22"
Grid.Column="2"
Grid.ColumnSpan="3"
Minimum="0"
diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs
index 2261565..10c884f 100644
--- a/UVtools.WPF/Program.cs
+++ b/UVtools.WPF/Program.cs
@@ -1,8 +1,11 @@
using System;
using System.Diagnostics;
+using System.Drawing;
using System.Runtime.ExceptionServices;
using Avalonia;
+using UVtools.Core.Slicer;
using UVtools.WPF.Extensions;
+using Size = System.Drawing.Size;
namespace UVtools.WPF
{
@@ -20,6 +23,9 @@ namespace UVtools.WPF
ProgramStartupTime = Stopwatch.StartNew();
Args = args;
+ //Slicer slicer = new(Size.Empty, SizeF.Empty, "D:\\Cube1x1x1.stl");
+ //var slices = slicer.SliceModel(0.05f);
+
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
//AppDomain.CurrentDomain.FirstChanceException += CurrentDomainOnFirstChanceException;
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index eeab339..85c39b9 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.13.1</Version>
+ <Version>2.13.2</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -24,11 +24,11 @@
<NoWarn>1701;1702;</NoWarn>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Avalonia" Version="0.10.5" />
+ <PackageReference Include="Avalonia" Version="0.10.6" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
- <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.5" />
- <PackageReference Include="Avalonia.Desktop" Version="0.10.5" />
- <PackageReference Include="Avalonia.Diagnostics" Version="0.10.5" />
+ <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.6" />
+ <PackageReference Include="Avalonia.Desktop" Version="0.10.6" />
+ <PackageReference Include="Avalonia.Diagnostics" Version="0.10.6" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.1.4349" />
<PackageReference Include="MessageBox.Avalonia" Version="1.3.1" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.4" />
diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs
index 7eb4229..d3cb69f 100644
--- a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs
+++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs
@@ -220,7 +220,7 @@ namespace UVtools.WPF.Windows
public byte[] EncodeCbddlpImage(Mat image, byte bit = 0)
{
List<byte> rawData = new();
- var span = image.GetPixelSpan<byte>();
+ var span = image.GetDataSpan<byte>();
bool obit = false;
int rep = 0;
@@ -282,7 +282,7 @@ namespace UVtools.WPF.Windows
List<byte> rawData = new();
byte color = byte.MaxValue >> 1;
uint stride = 0;
- var span = image.GetPixelSpan<byte>();
+ var span = image.GetDataSpan<byte>();
void AddRep()
{
@@ -360,7 +360,7 @@ namespace UVtools.WPF.Windows
public byte[] EncodePW0Image(Mat image)
{
List<byte> rawData = new();
- var span = image.GetPixelSpan<byte>();
+ var span = image.GetDataSpan<byte>();
int lastColor = -1;
int reps = 0;