From 25770464298414fc7839385c8001a0f2ab42fb81 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Nov 2025 18:03:13 +1000 Subject: [PATCH 01/32] Fix #3000 --- .../Metadata/Profiles/Exif/ExifProfile.cs | 12 +- src/ImageSharp/Primitives/Point.cs | 12 +- src/ImageSharp/Primitives/PointF.cs | 12 +- src/ImageSharp/Primitives/SizeF.cs | 12 +- .../Processing/AffineTransformBuilder.cs | 41 +- .../Transforms/TransformExtensions.cs | 8 +- .../AffineTransformProcessor{TPixel}.cs | 4 +- .../ProjectiveTransformProcessor{TPixel}.cs | 4 +- .../Transforms/Linear/RotateProcessor.cs | 4 +- .../Transforms/Linear/SkewProcessor.cs | 4 +- .../Processors/Transforms/TransformUtils.cs | 374 ++++++++++-------- .../Processing/ProjectiveTransformBuilder.cs | 56 +-- src/ImageSharp/Processing/TransformSpace.cs | 26 -- .../Transforms/AffineTransformTests.cs | 40 +- .../Transforms/ProjectiveTransformTests.cs | 8 +- .../Processors/Transforms/ResizeTests.cs | 2 +- .../Processors/Transforms/RotateTests.cs | 8 +- .../Transforms/TransformBuilderTestBase.cs | 4 +- tests/ImageSharp.Tests/TestImages.cs | 3 + .../Issue3000_Rgba32_Issue_3000_p-3-3.png | 3 + .../Issue3000_Rgba32_Issue_3000_p-4-4.png | 3 + ...urceRectangle1_Rgba32_TestPattern96x48.png | 4 +- ...gle1_Rgba32_TestPattern96x48__original.png | 3 + ...urceRectangle2_Rgba32_TestPattern96x48.png | 4 +- ...2_TestPattern100x50_R(0)_S(1,2)_T(0,0).png | 4 +- ...2_TestPattern100x50_R(0)_S(2,1)_T(0,0).png | 4 +- ...tPattern100x50_R(50)_S(1,1)_T(-20,-10).png | 4 +- ..._TestPattern100x50_R(50)_S(1,1)_T(0,0).png | 4 +- ...estPattern100x50_R(50)_S(1,1)_T(20,10).png | 4 +- ...ttern100x50_R(50)_S(1.1,1.3)_T(30,-20).png | 4 +- ...tPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png | 4 +- ...d_Rgba32_TestPattern96x96_R(50)_S(0.8).png | 4 +- ...pler_Rgba32_TestPattern150x150_Bicubic.png | 4 +- ...hSampler_Rgba32_TestPattern150x150_Box.png | 4 +- ...r_Rgba32_TestPattern150x150_CatmullRom.png | 4 +- ...pler_Rgba32_TestPattern150x150_Hermite.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos2.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos3.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos5.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos8.png | 4 +- ...2_TestPattern150x150_MitchellNetravali.png | 4 +- ...a32_TestPattern150x150_NearestNeighbor.png | 4 +- ...ler_Rgba32_TestPattern150x150_Robidoux.png | 4 +- ...gba32_TestPattern150x150_RobidouxSharp.png | 4 +- ...mpler_Rgba32_TestPattern150x150_Spline.png | 4 +- ...ler_Rgba32_TestPattern150x150_Triangle.png | 4 +- ...ampler_Rgba32_TestPattern150x150_Welch.png | 4 +- ...sions_Rgba32_TestPattern100x100_0.0001.png | 4 +- ...imensions_Rgba32_TestPattern100x100_57.png | 4 +- .../DrawImageTests/DrawTransformed.png | 4 +- ...X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png | 4 +- ...[ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png | 4 +- ...ntF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png | 4 +- ... X=140, Y=210 ]-PointF [ X=15, Y=125 ].png | 4 +- ...pler_Rgba32_TestPattern150x150_Bicubic.png | 4 +- ...hSampler_Rgba32_TestPattern150x150_Box.png | 4 +- ...r_Rgba32_TestPattern150x150_CatmullRom.png | 4 +- ...pler_Rgba32_TestPattern150x150_Hermite.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos2.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos3.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos5.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos8.png | 4 +- ...2_TestPattern150x150_MitchellNetravali.png | 4 +- ...a32_TestPattern150x150_NearestNeighbor.png | 4 +- ...ler_Rgba32_TestPattern150x150_Robidoux.png | 4 +- ...gba32_TestPattern150x150_RobidouxSharp.png | 4 +- ...mpler_Rgba32_TestPattern150x150_Spline.png | 4 +- ...ler_Rgba32_TestPattern150x150_Triangle.png | 4 +- ...ampler_Rgba32_TestPattern150x150_Welch.png | 4 +- ...2_Solid30x30_(255,0,0,255)_Bottom-Both.png | 4 +- ...id30x30_(255,0,0,255)_Bottom-LeftOrTop.png | 4 +- ...x30_(255,0,0,255)_Bottom-RightOrBottom.png | 4 +- ...a32_Solid30x30_(255,0,0,255)_Left-Both.png | 4 +- ...olid30x30_(255,0,0,255)_Left-LeftOrTop.png | 4 +- ...30x30_(255,0,0,255)_Left-RightOrBottom.png | 4 +- ...32_Solid30x30_(255,0,0,255)_Right-Both.png | 4 +- ...lid30x30_(255,0,0,255)_Right-LeftOrTop.png | 4 +- ...0x30_(255,0,0,255)_Right-RightOrBottom.png | 4 +- ...ba32_Solid30x30_(255,0,0,255)_Top-Both.png | 4 +- ...Solid30x30_(255,0,0,255)_Top-LeftOrTop.png | 4 +- ...d30x30_(255,0,0,255)_Top-RightOrBottom.png | 4 +- ...imensions_Rgba32_TestPattern100x100_57.png | 4 +- ...otate_WithAngle_TestPattern100x50_-170.png | 4 +- ...Rotate_WithAngle_TestPattern100x50_-50.png | 4 +- ...Rotate_WithAngle_TestPattern100x50_170.png | 4 +- .../Rotate_WithAngle_TestPattern100x50_50.png | 4 +- ...otate_WithAngle_TestPattern50x100_-170.png | 4 +- ...Rotate_WithAngle_TestPattern50x100_-50.png | 4 +- ...Rotate_WithAngle_TestPattern50x100_170.png | 4 +- .../Rotate_WithAngle_TestPattern50x100_50.png | 4 +- ...lType_Bgra32_TestPattern100x50_-20_-10.png | 4 +- ...xelType_Bgra32_TestPattern100x50_20_10.png | 4 +- ...elType_Rgb24_TestPattern100x50_-20_-10.png | 4 +- ...ixelType_Rgb24_TestPattern100x50_20_10.png | 4 +- ...w_WorksWithAllResamplers_ducky_Bicubic.png | 4 +- .../Skew_WorksWithAllResamplers_ducky_Box.png | 4 +- ...orksWithAllResamplers_ducky_CatmullRom.png | 4 +- ...w_WorksWithAllResamplers_ducky_Hermite.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos2.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos3.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos5.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos8.png | 4 +- ...hAllResamplers_ducky_MitchellNetravali.png | 4 +- ...ithAllResamplers_ducky_NearestNeighbor.png | 4 +- ..._WorksWithAllResamplers_ducky_Robidoux.png | 4 +- ...sWithAllResamplers_ducky_RobidouxSharp.png | 4 +- ...ew_WorksWithAllResamplers_ducky_Spline.png | 4 +- ..._WorksWithAllResamplers_ducky_Triangle.png | 4 +- ...kew_WorksWithAllResamplers_ducky_Welch.png | 4 +- tests/Images/Input/Png/issues/issue_3000.png | 3 + 110 files changed, 537 insertions(+), 457 deletions(-) delete mode 100644 src/ImageSharp/Processing/TransformSpace.cs create mode 100644 tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-3-3.png create mode 100644 tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-4-4.png create mode 100644 tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png create mode 100644 tests/Images/Input/Png/issues/issue_3000.png diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index d7932f90b..de4a89813 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -340,18 +340,18 @@ public sealed class ExifProfile : IDeepCloneable if (area.Value?.Length == 4) { RectangleF rectangle = new(area.Value[0], area.Value[1], area.Value[2], area.Value[3]); - if (!TransformUtils.TryGetTransformedRectangle(rectangle, matrix, out Rectangle bounds)) + if (!TransformUtils.TryGetTransformedRectangle(rectangle, matrix, out RectangleF bounds)) { return; } // Ensure the bounds are within the image dimensions. - bounds = Rectangle.Intersect(bounds, new Rectangle(0, 0, width, height)); + bounds = RectangleF.Intersect(bounds, new Rectangle(0, 0, width, height)); - area.Value[0] = (ushort)bounds.X; - area.Value[1] = (ushort)bounds.Y; - area.Value[2] = (ushort)bounds.Width; - area.Value[3] = (ushort)bounds.Height; + area.Value[0] = (ushort)MathF.Floor(bounds.X); + area.Value[1] = (ushort)MathF.Floor(bounds.Y); + area.Value[2] = (ushort)MathF.Ceiling(bounds.Width); + area.Value[3] = (ushort)MathF.Ceiling(bounds.Height); this.SetValue(ExifTag.SubjectArea, area.Value); } else diff --git a/src/ImageSharp/Primitives/Point.cs b/src/ImageSharp/Primitives/Point.cs index 8ace7ffac..8627fe980 100644 --- a/src/ImageSharp/Primitives/Point.cs +++ b/src/ImageSharp/Primitives/Point.cs @@ -69,7 +69,7 @@ public struct Point : IEquatable /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public readonly bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . @@ -239,7 +239,7 @@ public struct Point : IEquatable /// /// The out value for X. /// The out value for Y. - public void Deconstruct(out int x, out int y) + public readonly void Deconstruct(out int x, out int y) { x = this.X; y = this.Y; @@ -268,17 +268,17 @@ public struct Point : IEquatable public void Offset(Point point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y); /// - public override string ToString() => $"Point [ X={this.X}, Y={this.Y} ]"; + public override readonly string ToString() => $"Point [ X={this.X}, Y={this.Y} ]"; /// - public override bool Equals(object? obj) => obj is Point other && this.Equals(other); + public override readonly bool Equals(object? obj) => obj is Point other && this.Equals(other); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Point other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + public readonly bool Equals(Point other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); diff --git a/src/ImageSharp/Primitives/PointF.cs b/src/ImageSharp/Primitives/PointF.cs index de363e2bd..35a506bb4 100644 --- a/src/ImageSharp/Primitives/PointF.cs +++ b/src/ImageSharp/Primitives/PointF.cs @@ -58,7 +58,7 @@ public struct PointF : IEquatable /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public readonly bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . @@ -251,7 +251,7 @@ public struct PointF : IEquatable /// /// The out value for X. /// The out value for Y. - public void Deconstruct(out float x, out float y) + public readonly void Deconstruct(out float x, out float y) { x = this.X; y = this.Y; @@ -277,15 +277,15 @@ public struct PointF : IEquatable public void Offset(PointF point) => this.Offset(point.X, point.Y); /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y); /// - public override string ToString() => $"PointF [ X={this.X}, Y={this.Y} ]"; + public override readonly string ToString() => $"PointF [ X={this.X}, Y={this.Y} ]"; /// - public override bool Equals(object? obj) => obj is PointF pointF && this.Equals(pointF); + public override readonly bool Equals(object? obj) => obj is PointF pointF && this.Equals(pointF); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + public readonly bool Equals(PointF other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); } diff --git a/src/ImageSharp/Primitives/SizeF.cs b/src/ImageSharp/Primitives/SizeF.cs index 81c749875..108ea1eed 100644 --- a/src/ImageSharp/Primitives/SizeF.cs +++ b/src/ImageSharp/Primitives/SizeF.cs @@ -67,7 +67,7 @@ public struct SizeF : IEquatable /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] - public bool IsEmpty => this.Equals(Empty); + public readonly bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . @@ -201,24 +201,24 @@ public struct SizeF : IEquatable /// /// The out value for the width. /// The out value for the height. - public void Deconstruct(out float width, out float height) + public readonly void Deconstruct(out float width, out float height) { width = this.Width; height = this.Height; } /// - public override int GetHashCode() => HashCode.Combine(this.Width, this.Height); + public override readonly int GetHashCode() => HashCode.Combine(this.Width, this.Height); /// - public override string ToString() => $"SizeF [ Width={this.Width}, Height={this.Height} ]"; + public override readonly string ToString() => $"SizeF [ Width={this.Width}, Height={this.Height} ]"; /// - public override bool Equals(object? obj) => obj is SizeF && this.Equals((SizeF)obj); + public override readonly bool Equals(object? obj) => obj is SizeF sizeF && this.Equals(sizeF); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); + public readonly bool Equals(SizeF other) => this.Width.Equals(other.Width) && this.Height.Equals(other.Height); /// /// Multiplies by a producing . diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index 6d1e8aaa5..e330c6c26 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -17,24 +17,9 @@ public class AffineTransformBuilder /// Initializes a new instance of the class. /// public AffineTransformBuilder() - : this(TransformSpace.Pixel) { } - /// - /// Initializes a new instance of the class. - /// - /// - /// The to use when applying the affine transform. - /// - public AffineTransformBuilder(TransformSpace transformSpace) - => this.TransformSpace = transformSpace; - - /// - /// Gets the to use when applying the affine transform. - /// - public TransformSpace TransformSpace { get; } - /// /// Prepends a rotation matrix using the given rotation angle in degrees /// and the image center point as rotation center. @@ -52,7 +37,7 @@ public class AffineTransformBuilder /// The . public AffineTransformBuilder PrependRotationRadians(float radians) => this.Prepend( - size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace)); + size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size)); /// /// Prepends a rotation matrix using the given rotation in degrees at the given origin. @@ -88,7 +73,7 @@ public class AffineTransformBuilder /// The amount of rotation, in radians. /// The . public AffineTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace)); + => this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size)); /// /// Appends a rotation matrix using the given rotation in degrees at the given origin. @@ -172,7 +157,7 @@ public class AffineTransformBuilder /// The Y angle, in radians. /// The . public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace)); + => this.Prepend(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -210,7 +195,7 @@ public class AffineTransformBuilder /// The Y angle, in radians. /// The . public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace)); + => this.Append(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -344,12 +329,26 @@ public class AffineTransformBuilder /// for linear transforms. /// /// The . - public Size GetTransformedSize(Rectangle sourceRectangle) + public SizeF GetTransformedSize(Rectangle sourceRectangle) { Matrix3x2 matrix = this.BuildMatrix(sourceRectangle); - return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace); + return GetTransformedSize(sourceRectangle, matrix); } + /// + /// Returns the size of a rectangle large enough to contain the transformed source rectangle. + /// + /// The rectangle in the source image. + /// The transformation matrix. + /// + /// The resultant matrix is degenerate containing one or more values equivalent + /// to or a zero determinant and therefore cannot be used + /// for linear transforms. + /// + /// The . + internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix3x2 matrix) + => TransformUtils.GetRawTransformedSize(matrix, sourceRectangle.Size); + private static void CheckDegenerate(Matrix3x2 matrix) { if (TransformUtils.IsDegenerate(matrix)) diff --git a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs index 60f90b10f..5857cb435 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs @@ -7,8 +7,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing; /// -/// Defines extensions that allow the application of composable transform operations on an -/// using Mutate/Clone. +/// Defines extensions that allow the application of composable transform operations +/// on an using Mutate/Clone. /// public static class TransformExtensions { @@ -51,7 +51,7 @@ public static class TransformExtensions IResampler sampler) { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = builder.GetTransformedSize(sourceRectangle); + Size targetDimensions = TransformUtils.GetTransformedCanvasSize(transform, sourceRectangle.Size); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } @@ -113,7 +113,7 @@ public static class TransformExtensions IResampler sampler) { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = builder.GetTransformedSize(sourceRectangle); + Size targetDimensions = TransformUtils.GetTransformedCanvasSize(transform, sourceRectangle.Size); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index 59f5773cf..de9daa2fc 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -77,7 +77,9 @@ internal class AffineTransformProcessor : TransformProcessor, IR return; } - // Convert from screen to world space. + // All matrices are defined in normalized coordinate space so we need to convert to pixel space. + // After normalization we need to invert the matrix for correct sampling. + matrix = TransformUtils.NormalizeToPixel(matrix); Matrix3x2.Invert(matrix, out matrix); if (sampler is NearestNeighborResampler) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index 1c30fd114..7af627a26 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -75,7 +75,9 @@ internal class ProjectiveTransformProcessor : TransformProcessor return; } - // Convert from screen to world space. + // All matrices are defined in normalized coordinate space so we need to convert to pixel space. + // After normalization we need to invert the matrix for correct sampling. + matrix = TransformUtils.NormalizeToPixel(matrix); Matrix4x4.Invert(matrix, out matrix); if (sampler is NearestNeighborResampler) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs index 0af2b268a..c745c480d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs @@ -28,14 +28,14 @@ public sealed class RotateProcessor : AffineTransformProcessor /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize, TransformSpace.Pixel), + TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize), sampler, sourceSize) => this.Degrees = degrees; // Helper constructor private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize) - : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(rotationMatrix, sourceSize, TransformSpace.Pixel)) + : base(rotationMatrix, sampler, TransformUtils.GetTransformedCanvasSize(rotationMatrix, sourceSize)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs index 0bbc8e0f6..a5621bc4c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs @@ -30,7 +30,7 @@ public sealed class SkewProcessor : AffineTransformProcessor /// The source image size public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize, TransformSpace.Pixel), + TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize), sampler, sourceSize) { @@ -40,7 +40,7 @@ public sealed class SkewProcessor : AffineTransformProcessor // Helper constructor: private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize) - : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(skewMatrix, sourceSize, TransformSpace.Pixel)) + : base(skewMatrix, sampler, TransformUtils.GetTransformedCanvasSize(skewMatrix, sourceSize)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs index d25d4f474..6badd949a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs @@ -80,79 +80,69 @@ internal static class TransformUtils } /// - /// Creates a centered rotation transform matrix using the given rotation in degrees and the source size. + /// Creates a centered rotation transform matrix using the given rotation in degrees and The original source size. /// /// The amount of rotation, in degrees. /// The source image size. - /// The to use when creating the centered matrix. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size, TransformSpace transformSpace) - => CreateRotationTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degrees), size, transformSpace); + public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size) + => CreateRotationTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degrees), size); /// - /// Creates a centered rotation transform matrix using the given rotation in radians and the source size. + /// Creates a centered rotation transform matrix using the given rotation in radians and The original source size. /// /// The amount of rotation, in radians. /// The source image size. - /// The to use when creating the centered matrix. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size, TransformSpace transformSpace) - => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size, transformSpace); + public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size) + => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size); /// - /// Creates a centered skew transform matrix from the give angles in degrees and the source size. + /// Creates a centered skew transform matrix from the give angles in degrees and The original source size. /// /// The X angle, in degrees. /// The Y angle, in degrees. /// The source image size. - /// The to use when creating the centered matrix. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size, TransformSpace transformSpace) - => CreateSkewTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), size, transformSpace); + public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size) + => CreateSkewTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), size); /// - /// Creates a centered skew transform matrix from the give angles in radians and the source size. + /// Creates a centered skew transform matrix from the give angles in radians and The original source size. /// /// The X angle, in radians. /// The Y angle, in radians. /// The source image size. - /// The to use when creating the centered matrix. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size, TransformSpace transformSpace) - => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty), size, transformSpace); + public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size) + => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty), size); /// /// Gets the centered transform matrix based upon the source rectangle. /// /// The transformation matrix. /// The source image size. - /// - /// The to use when creating the centered matrix. - /// /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix3x2 CreateCenteredTransformMatrix(Matrix3x2 matrix, Size size, TransformSpace transformSpace) + public static Matrix3x2 CreateCenteredTransformMatrix(Matrix3x2 matrix, Size size) { - Size transformSize = GetUnboundedTransformedSize(matrix, size, transformSpace); - - // We invert the matrix to handle the transformation from screen to world space. - // This ensures scaling matrices are correct. - Matrix3x2.Invert(matrix, out Matrix3x2 inverted); + // 1) Unbounded size. + SizeF ts = GetRawTransformedSize(matrix, size); - // The source size is provided using the coordinate space of the source image. - // however the transform should always be applied in the pixel space. - // To account for this we offset by the size - 1 to translate to the pixel space. - float offset = transformSpace == TransformSpace.Pixel ? 1F : 0F; + // 2) Invert the content transform for screen->world. + Matrix3x2.Invert(matrix, out Matrix3x2 inv); - Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-(transformSize.Width - offset), -(transformSize.Height - offset)) * .5F); - Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(size.Width - offset, size.Height - offset) * .5F); + // 3) Translate target (canvas) so its center is at the origin, + // translate source so its center is at the origin, then undo the content transform. + Matrix3x2 toTarget = Matrix3x2.CreateTranslation(new Vector2(-ts.Width, -ts.Height) * 0.5f); + Matrix3x2 toSource = Matrix3x2.CreateTranslation(new Vector2(size.Width, size.Height) * 0.5f); - // Translate back to world space. - Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); + // 4) World->screen. + Matrix3x2.Invert(toTarget * inv * toSource, out Matrix3x2 centered); return centered; } @@ -287,7 +277,6 @@ internal static class TransformUtils /// The top-right point of the distorted quad. /// The bottom-right point of the distorted quad. /// The bottom-left point of the distorted quad. - /// The to use when creating the matrix. /// The computed projection matrix for the quad distortion. /// /// This method is based on the algorithm described in the following article: @@ -298,8 +287,7 @@ internal static class TransformUtils PointF topLeft, PointF topRight, PointF bottomRight, - PointF bottomLeft, - TransformSpace transformSpace) + PointF bottomLeft) { PointF p1 = new(rectangle.X, rectangle.Y); PointF p2 = new(rectangle.X + rectangle.Width, rectangle.Y); @@ -345,46 +333,94 @@ internal static class TransformUtils (float)b[2], (float)b[5], 0, 1); #pragma warning restore SA1117 - // Check if the matrix involves only affine transformations by inspecting the relevant components. - // We want to use pixel space for calculations only if the transformation is purely 2D and does not include - // any perspective effects, non-standard scaling, or unusual translations that could distort the image. - if (transformSpace == TransformSpace.Pixel && IsAffineRotationOrSkew(projectionMatrix)) - { - if (projectionMatrix.M41 != 0) - { - projectionMatrix.M41--; - } - - if (projectionMatrix.M42 != 0) - { - projectionMatrix.M42--; - } - } - return projectionMatrix; } /// - /// Returns the size relative to the source for the given transformation matrix. + /// Calculates the size of a destination canvas large enough to contain + /// the fully transformed source content, including any translation offsets. /// /// The transformation matrix. - /// The source size. - /// The to use when calculating the size. - /// The . - public static Size GetTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace) - => GetTransformedSize(matrix, size, transformSpace, true); + /// The original source size. + /// + /// A representing the dimensions of the destination + /// canvas required to fully contain the transformed source, including + /// any positive or negative translation offsets. + /// + /// + /// + /// This method ensures that the transformed content remains fully visible + /// on the destination canvas by expanding its size to include translations + /// in all directions. + /// + /// + /// It behaves identically to calling + /// with + /// preserveCanvas set to . + /// + /// + /// The resulting canvas size represents the total area required to display + /// the transformed image without clipping, not merely the geometric bounds + /// of the transformed source. + /// + /// + public static Size GetTransformedCanvasSize(Matrix3x2 matrix, Size size) + => Size.Ceiling(GetTransformedSize(matrix, size, true)); /// - /// Returns the size relative to the source for the given transformation matrix. + /// Calculates the size of a destination canvas large enough to contain + /// the fully transformed source content, including any translation offsets. /// /// The transformation matrix. - /// The source size. - /// The used when generating the matrix. + /// The original source size. /// - /// The . + /// A representing the dimensions of the destination + /// canvas required to fully contain the transformed source, including + /// any positive or negative translation offsets. /// + /// + /// + /// This method ensures that the transformed content remains fully visible + /// on the destination canvas by expanding its size to include translations + /// in all directions. + /// + /// + /// It behaves identically to calling + /// with + /// preserveCanvas set to . + /// + /// + /// The resulting canvas size represents the total area required to display + /// the transformed image without clipping, not merely the geometric bounds + /// of the transformed source. + /// + /// + public static Size GetTransformedCanvasSize(Matrix4x4 matrix, Size size) + => Size.Ceiling(GetTransformedSize(matrix, size, true)); + + /// + /// Returns the size relative to the source for the given transformation matrix. + /// + /// The transformation matrix. + /// The original source size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF GetRawTransformedSize(Matrix4x4 matrix, Size size) + => GetTransformedSize(matrix, size, false); + + /// + /// Returns the size of the transformed source. When is true, + /// the size is expanded to include translation so the full moved content remains visible. + /// + /// The transformation matrix. + /// The original source size. + /// + /// If , expand the size to account for translation (left/up as well as right/down). + /// If , return only the transformed span without translation expansion. + /// + /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Size GetTransformedSize(Matrix4x4 matrix, Size size, TransformSpace transformSpace) + private static SizeF GetTransformedSize(Matrix4x4 matrix, Size size, bool preserveCanvas) { Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); @@ -393,27 +429,9 @@ internal static class TransformUtils return size; } - // Check if the matrix involves only affine transformations by inspecting the relevant components. - // We want to use pixel space for calculations only if the transformation is purely 2D and does not include - // any perspective effects, non-standard scaling, or unusual translations that could distort the image. - bool usePixelSpace = transformSpace == TransformSpace.Pixel && IsAffineRotationOrSkew(matrix); - - // Define an offset size to translate between pixel space and coordinate space. - // When using pixel space, apply a scaling sensitive offset to translate to discrete pixel coordinates. - // When not using pixel space, use SizeF.Empty as the offset. - - // Compute scaling factors from the matrix - float scaleX = 1F / new Vector2(matrix.M11, matrix.M21).Length(); // sqrt(M11^2 + M21^2) - float scaleY = 1F / new Vector2(matrix.M12, matrix.M22).Length(); // sqrt(M12^2 + M22^2) - - // Apply the offset relative to the scale - SizeF offsetSize = usePixelSpace ? new SizeF(scaleX, scaleY) : SizeF.Empty; - - // Subtract the offset size to translate to the appropriate space (pixel or coordinate). - if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size - offsetSize), matrix, out Rectangle bounds)) + if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size), matrix, out RectangleF bounds)) { - // Add the offset size back to translate the transformed bounds to the correct space. - return Size.Ceiling(ConstrainSize(bounds) + offsetSize); + return preserveCanvas ? GetPreserveCanvasSize(bounds) : bounds.Size; } return size; @@ -438,30 +456,31 @@ internal static class TransformUtils swizzler.Transform(new Point(sourceRectangle.Left, sourceRectangle.Top)), swizzler.Transform(new Point(sourceRectangle.Right, sourceRectangle.Top)), swizzler.Transform(new Point(sourceRectangle.Right, sourceRectangle.Bottom)), - swizzler.Transform(new Point(sourceRectangle.Left, sourceRectangle.Bottom)), - TransformSpace.Pixel); + swizzler.Transform(new Point(sourceRectangle.Left, sourceRectangle.Bottom))); /// /// Returns the size relative to the source for the given transformation matrix. /// /// The transformation matrix. - /// The source size. - /// The to use when calculating the size. + /// The original source size. /// The . - private static Size GetUnboundedTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace) - => GetTransformedSize(matrix, size, transformSpace, false); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SizeF GetRawTransformedSize(Matrix3x2 matrix, Size size) + => GetTransformedSize(matrix, size, false); /// - /// Returns the size relative to the source for the given transformation matrix. + /// Returns the size of the transformed source. When is true, + /// the size is expanded to include translation so the full moved content remains visible. /// /// The transformation matrix. - /// The source size. - /// The to use when calculating the size. - /// Whether to constrain the size to ensure that the dimensions are positive. - /// - /// The . - /// - private static Size GetTransformedSize(Matrix3x2 matrix, Size size, TransformSpace transformSpace, bool constrain) + /// The original source size. + /// + /// If , expand the size to account for translation (left/up as well as right/down). + /// If , return only the transformed span without translation expansion. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static SizeF GetTransformedSize(Matrix3x2 matrix, Size size, bool preserveCanvas) { Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); @@ -470,21 +489,9 @@ internal static class TransformUtils return size; } - // Define an offset size to translate between coordinate space and pixel space. - // Compute scaling factors from the matrix - SizeF offsetSize = SizeF.Empty; - if (transformSpace == TransformSpace.Pixel) - { - float scaleX = 1F / new Vector2(matrix.M11, matrix.M21).Length(); // sqrt(M11^2 + M21^2) - float scaleY = 1F / new Vector2(matrix.M12, matrix.M22).Length(); // sqrt(M12^2 + M22^2) - offsetSize = new SizeF(scaleX, scaleY); - } - - // Subtract the offset size to translate to the pixel space. - if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size - offsetSize), matrix, out Rectangle bounds)) + if (TryGetTransformedRectangle(new RectangleF(Point.Empty, size), matrix, out RectangleF bounds)) { - // Add the offset size back to translate the transformed bounds to the coordinate space. - return Size.Ceiling((constrain ? ConstrainSize(bounds) : bounds.Size) + offsetSize); + return preserveCanvas ? GetPreserveCanvasSize(bounds) : bounds.Size; } return size; @@ -499,7 +506,8 @@ internal static class TransformUtils /// /// if the transformation was successful; otherwise, . /// - private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix3x2 matrix, out Rectangle bounds) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix3x2 matrix, out RectangleF bounds) { if (matrix.IsIdentity || rectangle.Equals(default)) { @@ -526,7 +534,7 @@ internal static class TransformUtils /// if the transformation was successful; otherwise, . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix4x4 matrix, out Rectangle bounds) + internal static bool TryGetTransformedRectangle(RectangleF rectangle, Matrix4x4 matrix, out RectangleF bounds) { if (matrix.IsIdentity || rectangle.Equals(default)) { @@ -543,15 +551,61 @@ internal static class TransformUtils return true; } + /// + /// Calculates the size of a destination canvas large enough to contain the full + /// transformed content of a source rectangle while preserving any translation offsets. + /// + /// + /// The representing the transformed bounds of the source content + /// in destination (output) space. + /// + /// + /// A that describes the canvas dimensions required to fully + /// contain the transformed content while accounting for any positive or negative translation. + /// + /// + /// + /// This method expands the output canvas to ensure that translated content remains visible. + /// + /// + /// If the transformation produces a positive translation, the method extends the canvas + /// on the positive side (right or bottom). + /// If the transformation produces a negative translation (the content moves left or up), + /// the method extends the canvas on the negative side to include that offset. + /// + /// + /// The result is equivalent to taking the union of: + /// + /// + /// The original, untransformed rectangle at the origin [0..Width] × [0..Height]. + /// + /// + /// The translated rectangle defined by . + /// + /// + /// This ensures the entire translated image fits within the resulting canvas, + /// without trimming any portion caused by translation. + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Size ConstrainSize(Rectangle rectangle) + private static SizeF GetPreserveCanvasSize(RectangleF rectangle) { - // We want to resize the canvas here taking into account any translations. - int height = rectangle.Top < 0 ? rectangle.Bottom : Math.Max(rectangle.Height, rectangle.Bottom); - int width = rectangle.Left < 0 ? rectangle.Right : Math.Max(rectangle.Width, rectangle.Right); - - // If location in either direction is translated to a negative value equal to or exceeding the - // dimensions in either direction we need to reassign the dimension. + // Compute the required height. + // If the top is negative, expand upward by that amount (rectangle.Bottom already includes height). + // Otherwise, take the larger of the transformed height or the bottom offset. + float height = rectangle.Top < 0 + ? rectangle.Bottom + : MathF.Max(rectangle.Height, rectangle.Bottom); + + // Compute the required width. + // If the left is negative, expand leftward by that amount (rectangle.Right already includes width). + // Otherwise, take the larger of the transformed width or the right offset. + float width = rectangle.Left < 0 + ? rectangle.Right + : MathF.Max(rectangle.Width, rectangle.Right); + + // Guard: if translation exceeds or cancels dimensions, + // ensure non-zero positive size using the base rectangle dimensions. if (height <= 0) { height = rectangle.Height; @@ -562,63 +616,63 @@ internal static class TransformUtils width = rectangle.Width; } - return new Size(width, height); + // Return the final size that preserves the full visible region of the transformed content. + return new SizeF(width, height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) + private static RectangleF GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) { - // Find the minimum and maximum "corners" based on the given vectors float left = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X))); float top = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y))); float right = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X))); float bottom = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y))); - // Clamp the values to the nearest whole pixel. - return Rectangle.FromLTRB( - (int)Math.Floor(left), - (int)Math.Floor(top), - (int)Math.Ceiling(right), - (int)Math.Ceiling(bottom)); + return RectangleF.FromLTRB(left, top, right, bottom); } - private static bool IsAffineRotationOrSkew(Matrix4x4 matrix) + /// + /// Normalizes an affine 2D matrix so that it operates in pixel space. + /// Applies the row-vector conjugation T(+0.5,+0.5) * M * T(-0.5,-0.5) + /// to align the transform with pixel centers. + /// + /// The affine matrix. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 NormalizeToPixel(Matrix3x2 matrix) { - const float epsilon = 1e-6f; + const float dx = 0.5f, dy = 0.5f; - // Check if the matrix is affine (last column should be [0, 0, 0, 1]) - if (Math.Abs(matrix.M14) > epsilon || - Math.Abs(matrix.M24) > epsilon || - Math.Abs(matrix.M34) > epsilon || - Math.Abs(matrix.M44 - 1f) > epsilon) - { - return false; - } - - // Translation component (M41, m42) are allowed, others are not. - if (Math.Abs(matrix.M43) > epsilon) - { - return false; - } - - // Extract the linear (rotation and skew) part of the matrix - // Upper-left 3x3 matrix - float m11 = matrix.M11, m12 = matrix.M12, m13 = matrix.M13; - float m21 = matrix.M21, m22 = matrix.M22, m23 = matrix.M23; - float m31 = matrix.M31, m32 = matrix.M32, m33 = matrix.M33; + matrix.M31 += (-dx) + ((dx * matrix.M11) + (dy * matrix.M21)); + matrix.M32 += (-dy) + ((dx * matrix.M12) + (dy * matrix.M22)); + return matrix; + } - // Compute the determinant of the linear part - float determinant = (m11 * ((m22 * m33) - (m23 * m32))) - - (m12 * ((m21 * m33) - (m23 * m31))) + - (m13 * ((m21 * m32) - (m22 * m31))); + /// + /// Normalizes a projective 4×4 matrix so that it operates in pixel space. + /// Applies the row-vector conjugation T(+0.5,+0.5,0) * M * T(-0.5,-0.5,0) + /// to align the transform with pixel centers. + /// + /// The projective matrix. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix4x4 NormalizeToPixel(Matrix4x4 matrix) + { + const float dx = 0.5f, dy = 0.5f; - // Check if the determinant is approximately ±1 (no scaling) - if (Math.Abs(Math.Abs(determinant) - 1f) > epsilon) + // Fast path: affine (no perspective) + if (matrix.M14 == 0f && matrix.M24 == 0f && matrix.M34 == 0f && matrix.M44 == 1f) { - return false; + // t' = t + (-d + d·L) + matrix.M41 += (-dx) + ((dx * matrix.M11) + (dy * matrix.M21)); + matrix.M42 += (-dy) + ((dx * matrix.M12) + (dy * matrix.M22)); + return matrix; } - // All checks passed; the matrix represents rotation and/or skew (with possible translation) - return true; + Matrix4x4 tPos = Matrix4x4.Identity; + tPos.M41 = dx; + tPos.M42 = dy; + Matrix4x4 tNeg = Matrix4x4.Identity; + tNeg.M41 = -dx; + tNeg.M42 = -dy; + return tPos * matrix * tNeg; } } diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index 82b897ea5..dc049ef0e 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -17,24 +17,9 @@ public class ProjectiveTransformBuilder /// Initializes a new instance of the class. /// public ProjectiveTransformBuilder() - : this(TransformSpace.Pixel) { } - /// - /// Initializes a new instance of the class. - /// - /// - /// The to use when applying the projective transform. - /// - public ProjectiveTransformBuilder(TransformSpace transformSpace) - => this.TransformSpace = transformSpace; - - /// - /// Gets the to use when applying the projective transform. - /// - public TransformSpace TransformSpace { get; } - /// /// Prepends a matrix that performs a tapering projective transform. /// @@ -69,7 +54,7 @@ public class ProjectiveTransformBuilder /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder PrependRotationRadians(float radians) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace))); + => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size))); /// /// Prepends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -87,7 +72,8 @@ public class ProjectiveTransformBuilder /// The rotation origin point. /// The . internal ProjectiveTransformBuilder PrependRotationRadians(float radians, Vector2 origin) - => this.PrependMatrix(Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0))); + => this.PrependMatrix( + Matrix4x4.CreateRotationZ(radians, new Vector3(origin, 0))); /// /// Appends a centered rotation matrix using the given rotation in degrees. @@ -103,7 +89,7 @@ public class ProjectiveTransformBuilder /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size, this.TransformSpace))); + => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size))); /// /// Appends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -187,7 +173,7 @@ public class ProjectiveTransformBuilder /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace))); + => this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -225,7 +211,7 @@ public class ProjectiveTransformBuilder /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size, this.TransformSpace))); + => this.Append(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -289,7 +275,11 @@ public class ProjectiveTransformBuilder /// The . public ProjectiveTransformBuilder PrependQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) => this.Prepend(size => TransformUtils.CreateQuadDistortionMatrix( - new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft, this.TransformSpace)); + new Rectangle(Point.Empty, size), + topLeft, + topRight, + bottomRight, + bottomLeft)); /// /// Appends a quad distortion matrix using the specified corner points. @@ -301,7 +291,11 @@ public class ProjectiveTransformBuilder /// The . public ProjectiveTransformBuilder AppendQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) => this.Append(size => TransformUtils.CreateQuadDistortionMatrix( - new Rectangle(Point.Empty, size), topLeft, topRight, bottomRight, bottomLeft, this.TransformSpace)); + new Rectangle(Point.Empty, size), + topLeft, + topRight, + bottomRight, + bottomLeft)); /// /// Prepends a raw matrix. @@ -383,12 +377,26 @@ public class ProjectiveTransformBuilder /// for linear transforms. /// /// The . - public Size GetTransformedSize(Rectangle sourceRectangle) + public SizeF GetTransformedSize(Rectangle sourceRectangle) { Matrix4x4 matrix = this.BuildMatrix(sourceRectangle); - return TransformUtils.GetTransformedSize(matrix, sourceRectangle.Size, this.TransformSpace); + return GetTransformedSize(sourceRectangle, matrix); } + /// + /// Returns the size of a rectangle large enough to contain the transformed source rectangle. + /// + /// The rectangle in the source image. + /// The transformation matrix. + /// + /// The resultant matrix is degenerate containing one or more values equivalent + /// to or a zero determinant and therefore cannot be used + /// for linear transforms. + /// + /// The . + internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix4x4 matrix) + => TransformUtils.GetRawTransformedSize(matrix, sourceRectangle.Size); + private static void CheckDegenerate(Matrix4x4 matrix) { if (TransformUtils.IsDegenerate(matrix)) diff --git a/src/ImageSharp/Processing/TransformSpace.cs b/src/ImageSharp/Processing/TransformSpace.cs deleted file mode 100644 index bca676bd8..000000000 --- a/src/ImageSharp/Processing/TransformSpace.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Processing; - -/// -/// Represents the different spaces used in transformation operations. -/// -public enum TransformSpace -{ - /// - /// Coordinate space is a continuous, mathematical grid where objects and positions - /// are defined with precise, often fractional values. This space allows for fine-grained - /// transformations like scaling, rotation, and translation with high precision. - /// In coordinate space, an image can span from (0,0) to (4,4) for a 4x4 image, including the boundaries. - /// - Coordinate, - - /// - /// Pixel space is a discrete grid where each position corresponds to a specific pixel on the screen. - /// In this space, positions are defined by whole numbers, with no fractional values. - /// A 4x4 image in pixel space covers exactly 4 pixels wide and 4 pixels tall, ranging from (0,0) to (3,3). - /// Pixel space is used when rendering images to ensure that everything aligns with the actual pixels on the screen. - /// - Pixel -} diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index a7855e23a..941d78c43 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -234,7 +234,23 @@ public class AffineTransformTests image.DebugSave(provider); Assert.Equal(4, image.Width); - Assert.Equal(8, image.Height); + Assert.Equal(7, image.Height); + } + + [Theory] + [WithFile(TestImages.Png.Issue3000, PixelTypes.Rgba32, 3, 3)] + [WithFile(TestImages.Png.Issue3000, PixelTypes.Rgba32, 4, 4)] + public void Issue3000(TestImageProvider provider, float x, float y) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + image.Mutate(c => c + .Transform(new AffineTransformBuilder().AppendRotationDegrees(90, new Vector2(x, y)))); + + string details = $"p-{x}-{y}"; + image.DebugSave(provider, testOutputDetails: details); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: details); } [Theory] @@ -267,31 +283,41 @@ public class AffineTransformTests image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); } - [Fact] - public void TransformRotationDoesNotOffset() + [Theory] + [WithSolidFilledImages(100, 100, "DimGray", PixelTypes.Rgba32)] + public void TransformRotationDoesNotOffset(TestImageProvider provider) + where TPixel : unmanaged, IPixel { Rgba32 background = Color.DimGray.ToPixel(); - Rgba32 marker = Color.Aqua.ToPixel(); + TPixel marker = Color.Aqua.ToPixel(); + + using Image canvas = provider.GetImage(); - using Image img = new(100, 100, background); + using Image img = canvas.Clone(); img[0, 0] = marker; img.Mutate(c => c.Rotate(180)); Assert.Equal(marker, img[99, 99]); - using Image img2 = new(100, 100, background); + img.DebugSave(provider, "Rotate180"); + + using Image img2 = canvas.Clone(); img2[0, 0] = marker; img2.Mutate( c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - using Image img3 = new(100, 100, background); + img.DebugSave(provider, "AffineRotate180NN"); + + using Image img3 = canvas.Clone(); img3[0, 0] = marker; img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); + img3.DebugSave(provider, "AffineRotate180Bicubic"); + ImageComparer.Exact.VerifySimilarity(img, img2); ImageComparer.Exact.VerifySimilarity(img, img3); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs index 2e580ea9f..c65f136d6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ProjectiveTransformTests.cs @@ -249,17 +249,17 @@ public class ProjectiveTransformTests image.Mutate(ctx => ctx.Transform(builder)); // A 180-degree rotation inverts both axes around the image center. - // The subject location (5, 15) becomes (imageWidth - 5 - 1, imageHeight - 15 - 1) = (94, 84) + // The subject location (5, 15) becomes (imageWidth - 5, imageHeight - 15) = (95, 85) Assert.Equal( - [94, 84], + [95, 85], image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); // The subject area is also mirrored around the center. // New X = imageWidth - originalX - width // New Y = imageHeight - originalY - height - // (5, 15, 50, 50) becomes (44, 34, 50, 50) + // (5, 15, 50, 50) becomes (45, 35, 50, 50) Assert.Equal( - [44, 34, 50, 50], + [45, 35, 50, 50], image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 1e0e66965..023d47886 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -652,7 +652,7 @@ public class ResizeTests image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); Assert.Equal( - [2, 7, 11, 11], + [2, 7, 10, 10], image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs index dfa263fec..5eee7313d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs @@ -55,17 +55,17 @@ public class RotateTests image.Mutate(ctx => ctx.Rotate(180)); // A 180-degree rotation inverts both axes around the image center. - // The subject location (5, 15) becomes (imageWidth - 5 - 1, imageHeight - 15 - 1) = (94, 84) + // The subject location (5, 15) becomes (imageWidth - 5, imageHeight - 15) = (95, 85) Assert.Equal( - [94, 84], + [95, 85], image.Metadata.ExifProfile.GetValue(ExifTag.SubjectLocation).Value); // The subject area is also mirrored around the center. // New X = imageWidth - originalX - width // New Y = imageHeight - originalY - height - // (5, 15, 50, 50) becomes (44, 34, 50, 50) + // (5, 15, 50, 50) becomes (45, 35, 50, 50) Assert.Equal( - [44, 34, 50, 50], + [45, 35, 50, 50], image.Metadata.ExifProfile.GetValue(ExifTag.SubjectArea).Value); } } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index 5caf071ac..f5aa1715f 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -98,7 +98,7 @@ public abstract class TransformBuilderTestBase this.AppendRotationDegrees(builder, degrees); // TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness - Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size, TransformSpace.Pixel); + Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size); Vector2 position = new(x, y); Vector2 expected = Vector2.Transform(position, matrix); @@ -152,7 +152,7 @@ public abstract class TransformBuilderTestBase this.AppendSkewDegrees(builder, degreesX, degreesY); - Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size, TransformSpace.Pixel); + Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size); Vector2 position = new(x, y); Vector2 expected = Vector2.Transform(position, matrix); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index af6148c87..157cb379c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -163,6 +163,9 @@ public static class TestImages // Issue 2924: https://github.com/SixLabors/ImageSharp/issues/2924 public const string Issue2924 = "Png/issues/Issue_2924.png"; + // Issue 3000: htps://github.com/SixLabors/ImageSharp/issues/3000 + public const string Issue3000 = "Png/issues/Issue_3000.png"; + public static class Bad { public const string MissingDataChunk = "Png/xdtn0g01.png"; diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-3-3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-3-3.png new file mode 100644 index 000000000..385ac042c --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-3-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d75205909d532dc98da52389c804ff99cb3b796b5657afb521659fe221c2b8f0 +size 122 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-4-4.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-4-4.png new file mode 100644 index 000000000..ef4aa03b9 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-4-4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a75beaec77378de4abb09317afa56b8e99ecba0d1c8571cad31aa790afb1a687 +size 123 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png index 53ac0ff89..7fad1fcba 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbe1ffaf7b801fd92724438cc810fd0c5506e0a907b970c4f0bf5bec3627ca2a -size 551 +oid sha256:60b050406fda4ff347660e71cb28a9dfceb4b39532f62ee96cb61d2671d3cf00 +size 340 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png new file mode 100644 index 000000000..0d610ae03 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48__original.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74bf3b8655c7548f28c25b1e467992f691dc429f4b06e85cfd04a3b541825811 +size 478 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png index 2480164d6..b22cbb019 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b45933471a1af1b6d4112240e1bc6b6187065a872043ddbf917200ce9e8cc84b -size 371 +oid sha256:fbfb3143d96070c58c949e8d1e8d9ddbcf1e7863514489ea2defc65863c84e73 +size 276 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png index da8e446c0..df64eea18 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b66a5f9d8a7f3f2a78b868bec6c7d1deea927b82d81aa6d1677e0461a3920dc9 -size 3800 +oid sha256:120b661bef4adac64d362d8c987b3427cd8140ccac7404d09a16765ba1199434 +size 5191 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png index 4c45ba8c6..b1e8764c1 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5fdc46ee866e088e0ec3221145a3d2d954a0bcb6d25cbb4d538978272f34949 -size 4871 +oid sha256:0d668ebe5f8857fd21d7eb9ae86860751a6f3061f6c9f76705ff49216dc07870 +size 6215 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png index 480c07da4..6067f0ccc 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ae57ca0658b1ffa7aca9031f4ec065ab5a9813fb8a9c5acd221526df6a4f729 -size 9747 +oid sha256:2fb676b3af585e7cbe2efdb893157d5f4e152cf810d0693cafb81596e941e121 +size 9697 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png index d1ea99cf9..69f862a5a 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fced9def2b41cbbf215a49ea6ef6baf4c3c041fd180671eb209db5c6e7177e5 -size 10470 +oid sha256:afe7ddbff155b918a4eff91af31e01100355c146cb9c8a12ab2496da8b22821d +size 10446 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png index 227f54651..5b88ba508 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e4cc16c2f1b439f8780dead04db01fed95f8e20b68270ae8e7a988af999e3db -size 10561 +oid sha256:ad76301984e5b54eae374adfe130693667053fbed181847b4c68688fb74c9818 +size 10518 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png index b93742a85..7f08d0dfd 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06e3966550f1c3ae72796e5522f7829cf1f86daca469c479acf49e6fae72e3d0 -size 13227 +oid sha256:fbd57af1fa982f9090f57d820a9b927f894914e5f54774e9cd6fdcfe14e5f761 +size 13139 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png index 57c3b02ba..2d4ad649f 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ce5fefe04cc2a036fddcfcf038901a7a09b4ea5d0621a1e0d3abc8430953ae3 -size 20778 +oid sha256:c4bbc28c203550baf885cefba95c48a3f91dfb5242c09acbf3a8509b7258048e +size 20768 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png index b3bfc7ee5..08182dcfb 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b653c0fe761d351cb15b09f35da578a954d103dea7507e2c1d7c4ebf3bdac49a -size 10943 +oid sha256:566e85b1a527f48c953bcc7bc6c58ebd1fe0b14972c38edd596b025e0dd48624 +size 10940 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png index a295c016d..392d3462f 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a17bb1653cc6d6ecc292ce0670c651bfea032f61c6a0e84636205bde53a86f8 -size 13536 +oid sha256:aa5b0d5de93f26c0a7a03b57a00d4a49cda62f4a4b98b6d374261467c03a8357 +size 13500 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png index 63214687d..56f2a3127 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8970378312c0d479d618e4d5b8da54175c127db517fbe54f9057188d02cc735 -size 4165 +oid sha256:62267d8d56af3e1452f0e25144f2cfe352b88def98af28e819a3a6982040a4ca +size 4102 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png index a295c016d..5919cce39 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a17bb1653cc6d6ecc292ce0670c651bfea032f61c6a0e84636205bde53a86f8 -size 13536 +oid sha256:878d5c53b84af4d133825206a327fd4cd02a43831ecabf5c61c5d89181c5a107 +size 13499 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png index ef37b3e2d..fa01f13cc 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9bbf7ef00f98b410f309b3bf70ce87d3c6455666a26e89cd004744145a10408a -size 12559 +oid sha256:f0aa3c19852632e603ec425aeecc5243d4c6c24a1ac6e3906d29913bf7ead2df +size 12535 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png index 93a0ce6c5..98ad27d78 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f9ab86abad276d58bb029bd8e2c2aaffac5618322788cb3619577c7643e10d2 -size 14223 +oid sha256:ec648c2e8006d478ace4a78d2434a4ef7f10d4a3502468cd8b9e2b1f154620b6 +size 14278 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png index c2ca6bf57..feb217ee9 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:05c4dc9af1fef422fd5ada2fa1459c26253e0fb5e5a13226fa2e7445ece32272 -size 17927 +oid sha256:6cb06152d5a0765ad86e8005d6ddac469914ccced89d5ee37d77e7d030b97c9e +size 17281 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png index ade9a1ccd..fd1cf7e77 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82b47e1cad2eea417b99a2e4b68a5ba1a6cd6703f360e8402f3dca8b92373ecc -size 18945 +oid sha256:38ea8596a682be0075bb31ed176b1fe04b518eb887235d551a574e338d45880b +size 18869 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png index cf04e2036..619483421 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b15ce5a201ee6b946de485a58d3d8e779b6841457e096b2bd7a92968a122f9af -size 20844 +oid sha256:965f42f021c63a0f2ccc691723c4ad7f92119294aec407c7ffd46a6238c8f268 +size 20792 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png index 6be0fc0ff..7201a5f15 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1622a48b3f4790d66b229ed29acd18504cedf68d0a548832665c28d47ea663b -size 13857 +oid sha256:86f1b9e8f1e38070ce862d87c927313904ceaa9e6080f5acead90e82d164738c +size 13879 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png index 0064e973f..77c61443b 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74df7b82e2148cfc8dae7e05c96009c0d70c09bf39cdc5ef9d727063d2a8cb3f -size 4154 +oid sha256:270f9c2bf5d15fcb21796b3b9eb393e0cc00d9c337036637295ad1efb56781b1 +size 4114 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png index 5dd0c5225..22dc949c3 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc740ccd76910e384ad84a780591652ac7ee0ea30abf7fd7f5b146f8ff380f07 -size 13991 +oid sha256:d413162a83c223124a2f29f8154e4bdc08d94bd3e15569ec6cffaa13bdda72c8 +size 13953 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png index a6e120e90..3d86b73ab 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccdc54e814604d4d339f6083091abf852aae65052ceb731af998208faddb5b0b -size 13744 +oid sha256:941ea7b4d1f2c187f58920546e2f19fc275505929439cc389edcc59e652e8787 +size 13777 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png index d32c11d44..060fdc809 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd24e0a52c7743ab7d3ed255e3757c2d5495b3f56198556a157df589b1fb67ca -size 14889 +oid sha256:cc5e6a607ef2343cb74c5227dbc7861840db956951f1fc4703fe53dbccda0974 +size 14808 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png index 72782b0b9..524072160 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:878f1aab39b0b2405498c24146b8f81248b37b974e5ea7882e96174a034b645f -size 12374 +oid sha256:2ac06a9ba2b2c8bef7e0117ac52fbb790101c0f89313dc49feb1f5a1d929ab02 +size 12381 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png index 6cedab729..669aeba9a 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcc2bf4f7e0ab3d56ee71ac1e1855dababeb2e4ec167fd5dc264efdc9e727328 -size 17027 +oid sha256:ad0f483fa7fda620860858c4f330ba914480fba15d70b408fb1aa3fed52dbfc1 +size 16839 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png index 7368a3b00..438450e53 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c733878f4c0cc6075a01fbe7cb471f8b3e91c2c5eaf89309ea3c073d9cc4921 -size 854 +oid sha256:ba501a7fc32a68f8989965aa6457b3860ec42947e2bcd4526c7570ff743f38fc +size 841 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png index da66b2676..f7f2101d7 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af872886136893938aee82b1ac73e7a1820666a9a5f4bbf34159c09b3283169a -size 5520 +oid sha256:8265c5b2e8edd5eaf0aeeccf86cac486e7beec581e696d3b4f4cfee8f4be9b2b +size 5554 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png index 7e693a583..3692f1a1e 100644 --- a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ba180567e820b145a13c9b26db9c777e95126adfe8e8cacec0ffe1060dcfe8d -size 184124 +oid sha256:7f8a4db4facce1d68b363a3b59ea40c9da9fa3c989c736d97a703c84d8230660 +size 184595 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png index 38c603855..fc2f4b0c6 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=-50, Y=-50 ]-PointF [ X=200, Y=-50 ]-PointF [ X=200, Y=200 ]-PointF [ X=-50, Y=200 ].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abce6af307a81a8ebac8e502142b00b2615403b5570c8dbe7b6895cfdd1a6d60 -size 66879 +oid sha256:ac986987f25d25ab964a5bef710fe81166cb643d85511906218b4f0e72e9e840 +size 30532 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png index f7ea0d006..e0357c3f1 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=0, Y=0 ]-PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4cda265a50aa26711efafdbcd947c9a01eff872611df5298920583f9a3d4224 -size 26458 +oid sha256:e0ada2a4d32a3a757b803dbf08148f113f5d358b31af79a77e97c660ce96c302 +size 1608 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png index 78c37cc44..18d91fe29 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=150, Y=0 ]-PointF [ X=150, Y=150 ]-PointF [ X=0, Y=150 ]-PointF [ X=0, Y=0 ].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:278a488a858b8eda141493fe00c617eb1f664196853da8341d7e5b7f231ddce4 -size 24645 +oid sha256:ffc30373989ec6857797b460931f011b30baaec633b095b6fc3d8fd5d43c77ec +size 2467 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png index b4740828d..c1b6694f5 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithQuadDistortion_Rgba32_TestPattern150x150_PointF [ X=25, Y=50 ]-PointF [ X=210, Y=25 ]-PointF [ X=140, Y=210 ]-PointF [ X=15, Y=125 ].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e03e79e6fab3a9e43041e54640a04c7cc3677709e7d879f9f410cf8afc7547a7 -size 42691 +oid sha256:9828ef0faf1a6709673cfe39028ed4202920d346bcc172bda6683bb3d1d0a7a3 +size 36577 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png index 3826753d5..a515475c9 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:543dbf5376386bf518830850645d69934e2ca17ab208ce3fd5274a6a172f5206 -size 10951 +oid sha256:6bff913e6e67129325203fae91278ca17407b10d99c4e4f571e6cfe3b5b7f93c +size 10889 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png index f9aa1ffe0..779e437ff 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d0cf291ebf5d8cebab1cd76e2830e5e2d2e0d9a050f7187da72680ead39110c -size 2757 +oid sha256:54b761b76d03216e7aa6238eee92755c03f7b016bffd1400be66ecf136b29c26 +size 2747 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png index 3826753d5..e165b5f3c 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:543dbf5376386bf518830850645d69934e2ca17ab208ce3fd5274a6a172f5206 -size 10951 +oid sha256:16da371a29269dade522b3d602beee8f769723c5712a348d960805b75619376d +size 10889 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png index 2f9109ba3..7d420da8c 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57698b6666029a55edf8bd35a7ba96f68d224988cf01308a3af1c6606ae9d0b1 -size 10174 +oid sha256:b25b190603828131be8d82a27e019353c9bf80dcb38536e325abc5aa065762ed +size 10230 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png index 7dfec7898..96e71e12e 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc7c9da04142a679887c714c43f1838eba0092a869140d234fce3412673207c6 -size 13575 +oid sha256:0cc07a20532c52151388c42d7add4f9749913c4dd7629253400a51d40760df23 +size 13566 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png index 6e3b97f2d..5e04f3fe3 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8b973f41f8afa39b94c71b307b7eb393953e2d083d56e1f0e8f43d6ab1f342a -size 16821 +oid sha256:64aae32ec91233b6a139d2f515db4a3e609fa3ab6c660cb53b05d672e7f70e6f +size 16795 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png index 6986c0391..8206fbdbe 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:122c1501e09516244f0db36e1cca373ff68514a18e84f57ed3072d52d6112e36 -size 17022 +oid sha256:c6167a1fb585b49167f4c8fa1f19111f10c825ea7d41ae4e266680d22b2bb28e +size 17094 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png index 76b53fabf..76aaf324a 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:12181516bce69c9302f15bba928fd530796449599cb9744a2411cc796788ee3b -size 18066 +oid sha256:131e831cc2e2d8eb4f5860d0e685b31006ab846433e38440b6a85a445aed1a12 +size 17890 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png index ae4242a42..10301fd32 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1eb5accc5ada5b963ecef6ac15bfb1845f481e51aef63e06a522ea73bbeab945 -size 11194 +oid sha256:aedcc9342e0b37d60759330f62db446646c31da172e21d931ee8e8451ee720ae +size 11193 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png index efb6a2dee..1f736d040 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0418f0ea38ec19b407f2b5046d7ff0ed207189ad71db1e50e82c419d83620543 -size 2759 +oid sha256:34f21056cac1ec3f1bd37a6c50466210e7ca7d8263963d2c503535b40e5b31d8 +size 2752 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png index 976be43a3..b5269595c 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1233a9ab2c4b0b17b0248c3d40050c466330c22095287dfbdb8bf7dfbda4ff1f -size 11212 +oid sha256:b4e0cbe71672de45880111fb45c7b544203f67154060fa0707ba9216dfd6d883 +size 11217 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png index 04fb2e87e..81a20c24a 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2912d4e42c7b76d9ff48a49921d6472e351662597d69b88bc3708683c7933e3 -size 11221 +oid sha256:e8208cf34114bc87c6244f83a73e7c9dd4455da2fb6d25c34e32ed2fef3cfc9a +size 11214 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png index b35d68aaf..4edc410d9 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51b05c38647e0c1d88cc722e4109a882305073a065d2a27ccd3bee82f727127d -size 11775 +oid sha256:c4ee4328adcc71b1d9b3786ab2c03906aa725fefadd1d521206d5a04af421d8d +size 11711 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png index 64b9c6aba..f5877639e 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b260e816b23a43d7efb7507897ba2f5dbb6a596dd67a5abd4c9a0c005e926ee0 -size 9748 +oid sha256:fbc694ac18a702c127c588bb9184bcc39a01c1b8be5ceecadeaab4477260afec +size 9984 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png index 29b95bf52..49633ed22 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50b03d627bb53048f5e56380500f116da4c503f5bb6a1b1d3c0d67ee4256d8f6 -size 15977 +oid sha256:8e85f331d7c4304fcd8ea8788da04982d9a5e43951be642bd7dbacd8907c3151 +size 15784 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png index 54dca2639..65e75b83e 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96454548849147d7c7f0ca507c8521a7d5eaa7771f9f383cc836858870b52c57 -size 280 +oid sha256:92153056f19a20cc1d6ff65dd36ffd215eb50509cc3544e338e76c8d5665fb27 +size 278 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png index 41f94c9c7..7a9f2794c 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e94d224fdb284b6f1ba21b8caa66174edd7e6a3027f9dd03f4757e08296e6508 -size 212 +oid sha256:0db75a869ae36fbca7f57daa4495f2c16050b226474d203aba98cb8e0766d3fb +size 249 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png index 49cd1c837..4f01d8c13 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Bottom-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1162be9fa1f31bee8d3cba05c1422a1945621a412be11cce13d376efd5c679c -size 173 +oid sha256:09a80b11d888da121313d5f00ab0ec79ccf7bc49800135aa5eb411bd15fc6b86 +size 204 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png index 59f928178..8985e6476 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ed262e9b885af773a4a40a4506e678630670e208bf7f9ec10307e943b166bed -size 258 +oid sha256:1ff446c4bb62d4492fc561a9dd48c4c0d95d8f4bcd9bbadf1675b2621baf9fa1 +size 212 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png index 57ee3dc2f..1f9f0557b 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a24f2cfc225d01294b8bbc5ca7d7f1738fb0b79217046eb9edf04e4c4c01851 -size 201 +oid sha256:2d76e8055ddbdaaa8f21117ab5e06d3e7d0f5da9d21dd2a992d82e284f028606 +size 235 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png index 7e47f43ff..c8c5696b1 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Left-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:938186fb3d0f468176988a9530efd22e66241a1361fff027005ec8a8ae323ff3 -size 197 +oid sha256:c7e594fd12ea7863d297d66852d3f80d5d3645636cdd39611c7b6fae068a6dc9 +size 230 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png index 0f756e781..434457ffc 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bc4b8ea7e7f10676d8de612fe6bc5144e100b95ff3fe7a1e3d4066a7684ce4d -size 239 +oid sha256:50f7f407d040b1071f7f6fbad96d6cfb2907d87060c671e74f6122ac5d381c30 +size 208 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png index b2d420886..d3810b9d6 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:345337f7dffa48d95251503ee2ae8e91db98b5cbe06b579d73c38a018c781544 -size 182 +oid sha256:f305e35f0f4fac1c099b5d9f0b2775c1bb17f382aba13a068dedff45eda4632d +size 215 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png index 4f0ad9d04..74d204d7c 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Right-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de4e2b71dade9dfb750a2c614a684963d6962958db79145c87fd23d9f0f8c005 -size 180 +oid sha256:c41784431d5a50746f66b3c34e966cb21fa8a088de797825633349cb077b4f97 +size 212 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png index 78bdb8bbb..308fe3454 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-Both.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d8b651663366e7543211635f337c229e2f88f1142886ea3a9b69587daaada97 -size 288 +oid sha256:7a6b2c8e072993c00a96692f10c7ebe6fcc6f4cfdcb9dff2d0aa6c65db54e1c4 +size 267 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png index 7015a0557..6111bcae1 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-LeftOrTop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ab8df31f1716c05bb8687f79c7d1154f6cc6f65e3917abe60ecc42d0df173dc -size 215 +oid sha256:018fe6af5dd7ed2edd163bf3da5e22f8333d3e3287629a2065ebfd15b7a2a8b3 +size 256 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png index 67a765e8d..a0935a1ce 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_WithTaperMatrix_Rgba32_Solid30x30_(255,0,0,255)_Top-RightOrBottom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a1671da9ea7702a37a866fabfb3ca0d746233ee108594198f23cb563af43ae6 -size 180 +oid sha256:530501bed8301799b68ba3dc8d50c877ed3f58073ab1a8a283e8f776e761cd28 +size 200 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png index 4b2bb99d9..81863d3ef 100644 --- a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e7dedec16ccd66543a1d44052a104957ba62099ba2f2ccc72285c233c2ae3fa -size 4411 +oid sha256:73a18d217d0db5e6ed55cbf52801b7f083e083ca2f0a6ac20d320fd29961e6e0 +size 4399 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png index 65bb77977..c8b2ee0ce 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3594547265b23603b1a76ff6bc6f0eab4af55d6e0070e53356123dfc7ae256f8 -size 9034 +oid sha256:881daeef5b8db99af8b89e7d4e968fb4c43e13c904e936aefa1d0156b767803e +size 9051 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png index 7c54b1b07..ccd3d8fa5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ae9ef073f3338b71d2a40fcf2e89d9b6ab62204d6de9b6a1f75f4705ee197f0 -size 10704 +oid sha256:86032d5b7e49574655c1cd54886ac57d6385714481ba6bd72176d858f884cd1a +size 10720 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png index b6e930224..30a81a5b9 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:994dda7da034595aa77d107652bea06c86077d24ef8a6883b18f1f509bb19928 -size 8906 +oid sha256:cb823498eacf5bab12f7607d230523a3488e26fcc10fe1b3ab51de900a9dff97 +size 8835 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png index d1ea99cf9..69f862a5a 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fced9def2b41cbbf215a49ea6ef6baf4c3c041fd180671eb209db5c6e7177e5 -size 10470 +oid sha256:afe7ddbff155b918a4eff91af31e01100355c146cb9c8a12ab2496da8b22821d +size 10446 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png index 2f3f0f17f..b2f18f855 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29c5f48f1ece0b12854b4c44fba84fdfc9ac5751cdf564a32478dcdaed43b2a4 -size 9798 +oid sha256:be885eca2d9771be335a29d3da533a17376efa4c563996ac4cb37138fd1899eb +size 9826 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png index 5242a9d98..39c4304be 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7de58474c3f386c4ec31a9088d561a513f82c08d1157132d735169b847b9680 -size 11579 +oid sha256:afea3d7ec03c945d695b3cd85e9ea1d79cb35745256efe1e32273eb08789e5ce +size 11476 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png index 2af9d2fc2..e98d3115f 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ef9b7051d7a5733dfe2534fddefdc28dfbc49d087355f46c4d945b04f0e3936 -size 9672 +oid sha256:096eba716663179933fe6eb526822ac9dedf83c3633d43df328ed33fb16900f6 +size 9687 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png index 83c02764f..380b4b7d5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:825770c9b2e9f265d834eab6b40604df5508bf9bc5b4f82f5d3effd6d5a26935 -size 11434 +oid sha256:c7367063704e10827bb81f46ebb56b6fe87a816eb8ec258ca95d8e26d9276f0f +size 11408 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png index d6dba3f88..e8822425d 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e283463b0f450dd72cf303acccf3dd1ff7a31fe401ff0f288d67c4baefca240 -size 8742 +oid sha256:37acdbbcebe56ab98e17b025312e5860c471d807603c2ce6f4a50fd5f219c0f7 +size 8732 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png index 76bb244d5..0a798ce06 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:485d9d9ef955a04af43d17e6bc3952e9bf65a9752b6cf8ba9cbbe8f772f05a18 -size 8995 +oid sha256:722d191e930331e296a548641a80ac163430bdb4017e3d68e4638fbb1d6ed45c +size 9021 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png index c1c1d814f..8d4d2fd9b 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3d749ac365764051ea16bc39d1aff84c06faf282359805b58bb97c9eed7f0bb -size 6400 +oid sha256:ce2bbe927b718a1b4de05b2baad7016b69490d1b5dfb085420192b7ac6c0ec5d +size 6367 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png index 27608881e..2d5fd9e9f 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8d82f2a15502b0a29aa4df1077ec90c88f9211f283fdc0edd7b059ed9b387441 -size 6334 +oid sha256:d79a7044e95a1ca032124366e4705bd93a866609547ebb489ff7d2228547cea5 +size 6330 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png index 340455428..a9de3011d 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e8afa56c5abb0e4b5895f35415db1178d041120d9f8306902f554cfaaada88d -size 26540 +oid sha256:677e4419a1cac8692da2d852f6e169c4a66ef8f164fffa65550fccb71bb54563 +size 26606 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png index 9ef786692..2ae230ac5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2c174ef54b68f025352e25800f655fe3b94a0d3f75cb48bd2ac0e8d6931faf8 -size 24827 +oid sha256:0f700e854d2d4ee9c12150ef300e0041fa4706ae595eeea6869894a1e2947eaf +size 24963 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png index 14f774853..63f5f9959 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b56ceae2f350a1402beecc5b5e2930be1011a95fbf224cccf73b96f3931b646 -size 26531 +oid sha256:965248b773ea4b3f8e870ff598ccf88784dd700d9aa063b70b0146a00fc806bc +size 26613 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png index c8204eacf..bf1e06332 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:049ee7fc2bb758609a64149c338bfae2eab44755f53e6b7c25a5e8b8725ed8ac -size 24416 +oid sha256:cd8e29141de12f1c1a2b3fb057a58b5a017b6db3498023a8f77dc8ac02d7845c +size 24396 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png index 2bc57092a..c903716e7 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72c487a2fa3d608021b26a4d6b4517f8548fdcfc62fbafdd8649015dbec8ff87 -size 26504 +oid sha256:a28ac3e9bd2edc2aa7163275006f701672cd7f0b92454a0453a6e73387c70138 +size 26611 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png index fee364e21..5749ec1ac 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:099733c9d4490c86cfbb10a016e2dd073539a95f9d5ff9018cf9b5be5404fa13 -size 33435 +oid sha256:2a57780c4145cf1eef6028d7e58ef94ab6d47f80a024f43fe98297f2d398a7fc +size 33409 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png index 30325ccc6..5f98b4184 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27f2a2b21f8ae878e15120ea5a4a983bde7984b3468dc8426055885efc278fe6 -size 35547 +oid sha256:129f1770502c71fcbe67da35a6966a4c72e4d17ee70a8e951a5229db99d03089 +size 35607 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png index ff81256a7..979b0366a 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b5cbe60e26e123e5a5cdf5a4e88305340f76d32a9c64a220c1fa7512f84e786 -size 39442 +oid sha256:0b7a58c5219ce2b6c91f98b7a75a9c89292e69d816451425b9b829d6a2b1711c +size 39420 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png index 263dd7426..6cfbcee61 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:102cceb79acb1dfd7ec8887b4906e33456c774d48320d1624b3c73975d26f145 -size 25981 +oid sha256:b80944705b204ab37b1a1a0b7d0705fba4f4eae4ea9453ae8ed0e39104765ee9 +size 25836 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png index 9ef786692..2ae230ac5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2c174ef54b68f025352e25800f655fe3b94a0d3f75cb48bd2ac0e8d6931faf8 -size 24827 +oid sha256:0f700e854d2d4ee9c12150ef300e0041fa4706ae595eeea6869894a1e2947eaf +size 24963 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png index 85bbd5ec3..8aeed9c8d 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e61629aeefac7e0a1a6b46b44ad86ed4a5ba0908bb3febc18bb5f9f3ded1c08d -size 25751 +oid sha256:314e15b8d690b17aa67ee856bcaf55afc62ce86c1b1d86b0f17dffe8739a2d17 +size 25739 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png index f200a5f95..e7e2c554b 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:45b1b48e1df393f4c435189c71a6bd3bccfe7a055d76d414a8d0c009b59fa0a0 -size 26145 +oid sha256:abf063ddd974a8eef6b24da9e58a10143d9fefa29369b11f7b27685312a0c74b +size 26084 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png index 434bb32a8..0d11b5565 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6d186f9e547f658b719bc033e3b110d64cf2a02caecc510d4e2f88359e69746 -size 24176 +oid sha256:f417e1771b2a367f857888ae90bd75f9922d9e5fc8826009ded592d9da010a44 +size 24155 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png index e3be1ffe5..0f305ddd3 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:339b3299984f1450f1a8200e487964c0338b511b82e459d67a3583d0bd46b805 -size 24013 +oid sha256:6aebe33dad1ea3a709f54f234cb779956f5b561a9a5ecf23a95e7ec42d91c535 +size 23905 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png index 7dbeeaf35..835d72907 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5335c6184829fdc405475bd34d2fae60cf6d5ae050b4d671ac5dd25242ff1368 -size 31888 +oid sha256:bb7684478edd471057076a408c6b32102838db466476929ee58e248f5c20a2b2 +size 31872 diff --git a/tests/Images/Input/Png/issues/issue_3000.png b/tests/Images/Input/Png/issues/issue_3000.png new file mode 100644 index 000000000..d7d7ad4c6 --- /dev/null +++ b/tests/Images/Input/Png/issues/issue_3000.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f33e2f343140b01d9c5c913f4ea695748e3a93df145bc0bf2f73763f2d24cadc +size 385 From 24a1972c4d3c1a5f939da5c5d6e7cfbb8251ee8e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Nov 2025 18:22:19 +1000 Subject: [PATCH 02/32] Fix casing --- tests/ImageSharp.Tests/TestImages.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 157cb379c..76475031c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -164,7 +164,7 @@ public static class TestImages public const string Issue2924 = "Png/issues/Issue_2924.png"; // Issue 3000: htps://github.com/SixLabors/ImageSharp/issues/3000 - public const string Issue3000 = "Png/issues/Issue_3000.png"; + public const string Issue3000 = "Png/issues/issue_3000.png"; public static class Bad { From d8bf8ee810a15f058396026369645c9a6c0b1566 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Nov 2025 18:38:26 +1000 Subject: [PATCH 03/32] Rename files to fix casing --- ...Issue_3000_p-3-3.png => issue3000_Rgba32_issue_3000_p-3-3.png} | 0 ...Issue_3000_p-4-4.png => issue3000_Rgba32_issue_3000_p-4-4.png} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/Images/External/ReferenceOutput/AffineTransformTests/{Issue3000_Rgba32_Issue_3000_p-3-3.png => issue3000_Rgba32_issue_3000_p-3-3.png} (100%) rename tests/Images/External/ReferenceOutput/AffineTransformTests/{Issue3000_Rgba32_Issue_3000_p-4-4.png => issue3000_Rgba32_issue_3000_p-4-4.png} (100%) diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-3-3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-3-3.png similarity index 100% rename from tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-3-3.png rename to tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-3-3.png diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-4-4.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-4-4.png similarity index 100% rename from tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_Issue_3000_p-4-4.png rename to tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-4-4.png From 6dea233103c50fd6d4464fcfb8b0945dbabdf068 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 13 Nov 2025 18:48:35 +1000 Subject: [PATCH 04/32] Fix the correct pathname part --- ...issue_3000_p-3-3.png => Issue3000_Rgba32_issue_3000_p-3-3.png} | 0 ...issue_3000_p-4-4.png => Issue3000_Rgba32_issue_3000_p-4-4.png} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/Images/External/ReferenceOutput/AffineTransformTests/{issue3000_Rgba32_issue_3000_p-3-3.png => Issue3000_Rgba32_issue_3000_p-3-3.png} (100%) rename tests/Images/External/ReferenceOutput/AffineTransformTests/{issue3000_Rgba32_issue_3000_p-4-4.png => Issue3000_Rgba32_issue_3000_p-4-4.png} (100%) diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-3-3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-3-3.png similarity index 100% rename from tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-3-3.png rename to tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-3-3.png diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-4-4.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-4-4.png similarity index 100% rename from tests/Images/External/ReferenceOutput/AffineTransformTests/issue3000_Rgba32_issue_3000_p-4-4.png rename to tests/Images/External/ReferenceOutput/AffineTransformTests/Issue3000_Rgba32_issue_3000_p-4-4.png From e8eb6ba391fa554d21cda809872561a733ca7f3f Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 14 Nov 2025 16:55:43 +0100 Subject: [PATCH 05/32] Add .NET10.0 as TFM --- .github/workflows/build-and-test.yml | 29 +++++++++++++++++-- src/ImageSharp/ImageSharp.csproj | 2 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e00757cb7..7737b8daa 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -65,6 +65,31 @@ jobs: isARM: - ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }} options: + - os: ubuntu-latest + framework: net10.0 + sdk: 10.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable + framework: net10.0 + sdk: 10.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: windows-latest + framework: net10.0 + sdk: 10.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: buildjet-4vcpu-ubuntu-2204-arm + framework: net10.0 + sdk: 10.0.x + sdk-preview: true + runtime: -x64 + codecov: false + - os: ubuntu-latest framework: net9.0 sdk: 9.0.x @@ -163,14 +188,14 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 8.0.x + ${{matrix.options.sdk}} - name: DotNet Setup Preview if: ${{ matrix.options.sdk-preview == true }} uses: actions/setup-dotnet@v4 with: dotnet-version: | - 9.0.x + ${{matrix.options.sdk}} - name: DotNet Build if: ${{ matrix.options.sdk-preview != true }} diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index b7629044a..a9a7df50d 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -30,7 +30,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index ce391fad2..627ebea0f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -12,7 +12,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 From 053aca4eff88a434b48d4be78437832d851f15ae Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 14 Nov 2025 17:13:51 +0100 Subject: [PATCH 06/32] Use 10.0 in build --- .github/workflows/build-and-test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 7737b8daa..e5522ee1d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -188,15 +188,16 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - ${{matrix.options.sdk}} + 8.0.x - name: DotNet Setup Preview if: ${{ matrix.options.sdk-preview == true }} uses: actions/setup-dotnet@v4 with: dotnet-version: | - ${{matrix.options.sdk}} - + 9.0.x + 10.0.x + - name: DotNet Build if: ${{ matrix.options.sdk-preview != true }} shell: pwsh From 8bd918aa9632c157e40295f26330e69a342cd4a4 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 14 Nov 2025 17:23:40 +0100 Subject: [PATCH 07/32] Add .NET10.0 in all projects --- tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj | 4 ++-- .../ImageSharp.Tests.ProfilingSandbox.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index c92bb6a6b..004065ee2 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -19,7 +19,7 @@ BenchmarkDotNet requires a certain structure to the code, as such, some of these rules cannot be implemented. --> - + @@ -39,7 +39,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index 832f3d171..d3187694d 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -19,7 +19,7 @@ - net8.0;net9.0 + net8.0;net9.0;net10.0 From 6db71cdf4124156ea80fa98fa9e751c6bcdc4995 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 14 Nov 2025 17:31:41 +0100 Subject: [PATCH 08/32] Enable C#14 --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 755cbe3b3..c2b11624c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,7 +22,7 @@ - 12.0 + 14.0 - + + 12.0 + + + 14.0 From b6937708e5c49aa8f9ac0e5bcac10d1ae54c622b Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 14 Nov 2025 23:47:27 +0100 Subject: [PATCH 10/32] Try fix build error Can not reproduce it locally.. --- .../Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs index 0a9b888ed..c8f46d3aa 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/ICC/DataWriter/IccDataWriterPrimitivesTests.cs @@ -42,7 +42,7 @@ public class IccDataWriterPrimitivesTests byte[] output = writer.GetData(); Assert.Equal(0, count); - Assert.Equal([], output); + Assert.Equal(Array.Empty(), output); } [Fact] @@ -62,7 +62,7 @@ public class IccDataWriterPrimitivesTests byte[] output = writer.GetData(); Assert.Equal(0, count); - Assert.Equal([], output); + Assert.Equal(Array.Empty(), output); } [Theory] From c560e321dec10522e2adb81c7f583e03febdcca3 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 15 Nov 2025 10:08:16 +0100 Subject: [PATCH 11/32] Drop .NET9 --- .github/workflows/build-and-test.yml | 28 +------------------ src/ImageSharp/ImageSharp.csproj | 2 +- .../ImageSharp.Benchmarks.csproj | 2 +- .../ImageSharp.Tests.ProfilingSandbox.csproj | 2 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- 5 files changed, 5 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e5522ee1d..818c4566c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -90,31 +90,6 @@ jobs: runtime: -x64 codecov: false - - os: ubuntu-latest - framework: net9.0 - sdk: 9.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable - framework: net9.0 - sdk: 9.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: windows-latest - framework: net9.0 - sdk: 9.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: buildjet-4vcpu-ubuntu-2204-arm - framework: net9.0 - sdk: 9.0.x - sdk-preview: true - runtime: -x64 - codecov: false - - os: ubuntu-latest framework: net8.0 sdk: 8.0.x @@ -195,9 +170,8 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 9.0.x 10.0.x - + - name: DotNet Build if: ${{ matrix.options.sdk-preview != true }} shell: pwsh diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a9a7df50d..2c7172387 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -30,7 +30,7 @@ - net8.0;net9.0;net10.0 + net8.0;net10.0 diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 004065ee2..2628623b4 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -39,7 +39,7 @@ - net8.0;net9.0;net10.0 + net8.0;net10.0 diff --git a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj index d3187694d..bc52610d2 100644 --- a/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj +++ b/tests/ImageSharp.Tests.ProfilingSandbox/ImageSharp.Tests.ProfilingSandbox.csproj @@ -19,7 +19,7 @@ - net8.0;net9.0;net10.0 + net8.0;net10.0 diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 627ebea0f..a1d5b0ee2 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -12,7 +12,7 @@ - net8.0;net9.0;net10.0 + net8.0;net10.0 From 54d2dc2b889678951ffc9f9e329c380a06ecba45 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sat, 15 Nov 2025 10:36:36 +0100 Subject: [PATCH 12/32] Update remoteexecutor --- tests/Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index e20717cdd..8c88ff647 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -25,7 +25,7 @@ See https://github.com/ImageMagick/ImageMagick/commit/27a0a9c37f18af9c8d823a3ea076f600843b553c --> - + From ad4b3e90b24c084a841835dbfefc22353e05fade Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 21:36:15 +0100 Subject: [PATCH 13/32] Update ISA Groups https://github.com/dotnet/runtime/issues/121685#issuecomment-3539194032 --- .../Common/SimdUtilsTests.Shuffle.cs | 10 +-- .../ImageSharp.Tests/Common/SimdUtilsTests.cs | 4 +- .../Formats/Jpg/Block8x8FTests.cs | 4 +- .../ImageSharp.Tests/Formats/Jpg/DCTTests.cs | 4 +- .../Formats/Jpg/JpegColorConverterTests.cs | 12 ++-- .../Formats/Png/PngDecoderFilterTests.cs | 2 +- .../Formats/Png/PngEncoderFilterTests.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 2 +- .../WebP/ColorSpaceTransformUtilsTests.cs | 4 +- .../Formats/WebP/LosslessUtilsTests.cs | 14 ++-- .../Formats/WebP/PredictorEncoderTests.cs | 4 +- .../Formats/WebP/QuantEncTests.cs | 2 +- .../Formats/WebP/Vp8ResidualTests.cs | 2 +- .../Formats/WebP/WebpCommonUtilsTests.cs | 4 +- .../Formats/WebP/YuvConversionTests.cs | 2 +- .../Processors/Convolution/BokehBlurTest.cs | 2 +- .../Processing/Transforms/ResizeTests.cs | 2 +- .../FeatureTesting/FeatureTestRunner.cs | 65 ++++++++--------- .../Tests/FeatureTestRunnerTests.cs | 69 +++---------------- 19 files changed, 80 insertions(+), 130 deletions(-) diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs index ba37ee166..2a7616570 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs @@ -292,7 +292,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); } [Theory] @@ -352,7 +352,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); } [Theory] @@ -394,7 +394,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); } [Theory] @@ -436,7 +436,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); } [Theory] @@ -478,7 +478,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); } private static void TestShuffleFloat4Channel( diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 36b301264..eeaf93645 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -133,7 +133,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE41); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); } [Theory] @@ -171,7 +171,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512BW | HwIntrinsics.DisableAVX2); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index ab205c8a3..e1d4feaa7 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -267,7 +267,7 @@ public partial class Block8x8FTests : JpegFixture RunTest, srcSeed, qtSeed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); } [Fact] @@ -462,7 +462,7 @@ public partial class Block8x8FTests : JpegFixture // 3. DisableAvx2 - call fallback code of float implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 062a8a5ee..7d7b05a28 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -152,7 +152,7 @@ public static class DCTTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -360,7 +360,7 @@ public static class DCTTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index d58ff9823..5ea9e4d78 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -19,7 +19,7 @@ public class JpegColorConverterTests private const int TestBufferLength = 40; - private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2; + private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2; private static readonly ApproximateColorProfileComparer ColorSpaceComparer = new(epsilon: Precision); @@ -73,7 +73,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -106,7 +106,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -139,7 +139,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -172,7 +172,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -205,7 +205,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs index 00db13d4b..99406be2f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs @@ -171,7 +171,7 @@ public class PngDecoderFilterTests public void PaethFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.AllowAll); [Fact] - public void PaethFilter_WithoutSsse3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableSSSE3); + public void PaethFilter_WithoutSsse3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableSSE42); [Fact] public void PaethFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs index a930426b6..736c8b4c0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs @@ -51,7 +51,7 @@ public class PngEncoderFilterTests : MeasureFixture FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSSE3); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); } [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 0d666d8c8..ca01655a0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -618,7 +618,7 @@ public partial class PngEncoderTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.DisableSSSE3, + HwIntrinsics.DisableSSE42, provider); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs index 66f320143..06249090d 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs @@ -71,7 +71,7 @@ public class ColorSpaceTransformUtilsTests public void CollectColorBlueTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.AllowAll); [Fact] - public void CollectColorBlueTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableSSE41); + public void CollectColorBlueTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableSSE42); [Fact] public void CollectColorBlueTransforms_WithoutVector256_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableAVX2); @@ -80,7 +80,7 @@ public class ColorSpaceTransformUtilsTests public void CollectColorRedTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.AllowAll); [Fact] - public void CollectColorRedTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableSSE41); + public void CollectColorRedTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableSSE42); [Fact] public void CollectColorRedTransforms_WithoutVector256_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs index 982f0a5d5..d2429e71f 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs @@ -304,19 +304,19 @@ public class LosslessUtilsTests public void Predictor11_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableSSE2); + public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableSSE42); [Fact] public void Predictor12_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableSSE2); + public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableSSE42); [Fact] public void Predictor13_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableSSE2); + public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableSSE42); [Fact] public void SubtractGreen_Works() => RunSubtractGreenTest(); @@ -331,7 +331,7 @@ public class LosslessUtilsTests public void SubtractGreen_Scalar_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSSE3); + public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); [Fact] public void AddGreenToBlueAndRed_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.AllowAll); @@ -340,13 +340,13 @@ public class LosslessUtilsTests public void AddGreenToBlueAndRed_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2); [Fact] - public void AddGreenToBlueAndRed_WithoutAVX2OrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE2 | HwIntrinsics.DisableSSSE3); + public void AddGreenToBlueAndRed_WithoutAVX2OrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); [Fact] public void TransformColor_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.AllowAll); [Fact] - public void TransformColor_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableSSE2); + public void TransformColor_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableSSE42); [Fact] public void TransformColor_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableAVX2); @@ -355,7 +355,7 @@ public class LosslessUtilsTests public void TransformColorInverse_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.AllowAll); [Fact] - public void TransformColorInverse_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableSSE2); + public void TransformColorInverse_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableSSE42); [Fact] public void TransformColorInverse_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index 23ef8e7b1..4ab1d019b 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -24,7 +24,7 @@ public class PredictorEncoderTests [Fact] public void ColorSpaceTransform_WithPeakImage_WithoutSSE41_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.DisableSSE41); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.DisableSSE42); [Fact] public void ColorSpaceTransform_WithBikeImage_WithHardwareIntrinsics_Works() @@ -32,7 +32,7 @@ public class PredictorEncoderTests [Fact] public void ColorSpaceTransform_WithBikeImage_WithoutSSE41_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableSSE41); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableSSE42); [Fact] public void ColorSpaceTransform_WithBikeImage_WithoutAvx2_Works() diff --git a/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs b/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs index fc7da8cf6..54a80c29f 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs @@ -45,7 +45,7 @@ public class QuantEncTests public void QuantizeBlock_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.AllowAll); [Fact] - public void QuantizeBlock_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableSSE2); + public void QuantizeBlock_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableSSE42); [Fact] public void QuantizeBlock_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs index 62c67c2e0..c00460471 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs @@ -252,5 +252,5 @@ public class Vp8ResidualTests public void SetCoeffsTest_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); [Fact] - public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableSSE2); + public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableSSE42); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs index ca1859e54..13a204bce 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs @@ -23,7 +23,7 @@ public class WebpCommonUtilsTests [Fact] public void CheckNonOpaque_WithOpaquePixels_WithoutSse2_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.DisableSSE2); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.DisableSSE42); [Fact] public void CheckNonOpaque_WithOpaquePixels_WithoutAvx2_Works() @@ -35,7 +35,7 @@ public class WebpCommonUtilsTests [Fact] public void CheckNonOpaque_WithNoneOpaquePixels_WithoutSse2_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableSSE2); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableSSE42); [Fact] public void CheckNonOpaque_WithNoneOpaquePixels_WithoutAvx2_Works() diff --git a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs index e86642fea..5e34f1221 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs @@ -32,7 +32,7 @@ public class YuvConversionTests public void UpSampleYuvToRgb_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.AllowAll); [Fact] - public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE2); + public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE42); [Theory] [WithFile(TestImages.Webp.Yuv, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index f045c981e..bed2cdbd6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -148,7 +148,7 @@ public class BokehBlurTest [Theory] [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.AllowAll)] - [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableSSE41)] + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableSSE42)] public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value, HwIntrinsics intrinsicsFilter) { static void RunTest(string arg1, string arg2) diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index 1d445b980..3f2d8e059 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -102,6 +102,6 @@ public class ResizeTests : BaseImageOperationsExtensionTest FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableFMA); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs index 63126dcbc..d3671abd4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs +++ b/tests/ImageSharp.Tests/TestUtilities/FeatureTesting/FeatureTestRunner.cs @@ -436,37 +436,38 @@ public enum HwIntrinsics : long // Use flags so we can pass multiple values without using params. // Don't base on 0 or use inverse for All as that doesn't translate to string values. DisableHWIntrinsic = 1L << 0, - DisableSSE = 1L << 1, - DisableSSE2 = 1L << 2, - DisableAES = 1L << 3, - DisablePCLMULQDQ = 1L << 4, - DisableSSE3 = 1L << 5, - DisableSSSE3 = 1L << 6, - DisableSSE41 = 1L << 7, - DisableSSE42 = 1L << 8, - DisablePOPCNT = 1L << 9, - DisableAVX = 1L << 10, - DisableFMA = 1L << 11, - DisableAVX2 = 1L << 12, + DisableSSE42 = 1L << 1, + DisableAVX = 1L << 2, + DisableAVX2 = 1L << 3, + DisableAVX512 = 1L << 4, + DisableAVX512v2 = 1L << 5, + DisableAVX512v3 = 1L << 6, + DisableAVX10v1 = 1L << 7, + DisableAVX10v2 = 1L << 8, + DisableAPX = 1L << 9, + DisableAES = 1L << 10, + DisableAVX512VP2INTERSECT = 1L << 11, + DisableAVXIFMA = 1L << 12, DisableAVXVNNI = 1L << 13, - DisableAVX512BW = 1L << 14, - DisableAVX512BW_VL = 1L << 15, - DisableAVX512CD = 1L << 16, - DisableAVX512CD_VL = 1L << 17, - DisableAVX512DQ = 1L << 18, - DisableAVX512DQ_VL = 1L << 19, - DisableAVX512F = 1L << 20, - DisableAVX512F_VL = 1L << 21, - DisableAVX512VBMI = 1L << 22, - DisableAVX512VBMI_VL = 1L << 23, - DisableBMI1 = 1L << 24, - DisableBMI2 = 1L << 25, - DisableLZCNT = 1L << 26, - DisableArm64AdvSimd = 1L << 27, - DisableArm64Crc32 = 1L << 28, - DisableArm64Dp = 1L << 29, - DisableArm64Aes = 1L << 30, - DisableArm64Sha1 = 1L << 31, - DisableArm64Sha256 = 1L << 32, - AllowAll = 1L << 33 + DisableAVXVNNIINT = 1L << 14, + DisableGFNI = 1L << 15, + DisableSHA = 1L << 16, + DisableVAES = 1L << 17, + DisableWAITPKG = 1L << 18, + DisableX86Serialize = 1 << 19, + // Arm64 + DisableArm64Aes = 1L << 20, + DisableArm64Atomics = 1L << 21, + DisableArm64Crc32 = 1L << 22, + DisableArm64Dczva = 1L << 23, + DisableArm64Dp = 1L << 24, + DisableArm64Rdm = 1L << 25, + DisableArm64Sha1 = 1L << 26, + DisableArm64Sha256 = 1L << 27, + DisableArm64Sve = 1L << 28, + DisableArm64Sve2 = 1L << 29, + // RISC-V64 + DisableRiscV64Zba = 1L << 30, + DisableRiscV64Zbb = 1L << 31, + AllowAll = 1L << 32, } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index 82b247d9d..363428d04 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -110,54 +110,29 @@ public class FeatureTestRunnerTests Assert.False(Sha1.IsSupported); Assert.False(Sha256.IsSupported); break; - case HwIntrinsics.DisableSSE: - Assert.False(Sse.IsSupported); - break; - case HwIntrinsics.DisableSSE2: - Assert.False(Sse2.IsSupported); - break; case HwIntrinsics.DisableAES: Assert.False(Aes.IsSupported); - break; - case HwIntrinsics.DisablePCLMULQDQ: Assert.False(Pclmulqdq.IsSupported); break; - case HwIntrinsics.DisableSSE3: + case HwIntrinsics.DisableSSE42: + Assert.False(Sse.IsSupported); + Assert.False(Sse2.IsSupported); Assert.False(Sse3.IsSupported); - break; - case HwIntrinsics.DisableSSSE3: Assert.False(Ssse3.IsSupported); - break; - case HwIntrinsics.DisableSSE41: Assert.False(Sse41.IsSupported); - break; - case HwIntrinsics.DisableSSE42: Assert.False(Sse42.IsSupported); - break; - case HwIntrinsics.DisablePOPCNT: Assert.False(Popcnt.IsSupported); break; case HwIntrinsics.DisableAVX: Assert.False(Avx.IsSupported); break; - case HwIntrinsics.DisableFMA: - Assert.False(Fma.IsSupported); - break; case HwIntrinsics.DisableAVX2: Assert.False(Avx2.IsSupported); - break; - case HwIntrinsics.DisableBMI1: + Assert.False(Fma.IsSupported); Assert.False(Bmi1.IsSupported); - break; - case HwIntrinsics.DisableBMI2: Assert.False(Bmi2.IsSupported); - break; - case HwIntrinsics.DisableLZCNT: Assert.False(Lzcnt.IsSupported); break; - case HwIntrinsics.DisableArm64AdvSimd: - Assert.False(AdvSimd.IsSupported); - break; case HwIntrinsics.DisableArm64Aes: Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); break; @@ -194,7 +169,7 @@ public class FeatureTestRunnerTests FeatureTestRunner.RunWithHwIntrinsicsFeature( AssertHwIntrinsicsFeatureDisabled, - HwIntrinsics.DisableSSE, + HwIntrinsics.DisableSSE42, new FakeSerializable()); } @@ -231,54 +206,28 @@ public class FeatureTestRunnerTests Assert.False(Sha1.IsSupported); Assert.False(Sha256.IsSupported); break; - case HwIntrinsics.DisableSSE: - Assert.False(Sse.IsSupported); - break; - case HwIntrinsics.DisableSSE2: - Assert.False(Sse2.IsSupported); - break; case HwIntrinsics.DisableAES: Assert.False(Aes.IsSupported); - break; - case HwIntrinsics.DisablePCLMULQDQ: Assert.False(Pclmulqdq.IsSupported); break; - case HwIntrinsics.DisableSSE3: - Assert.False(Sse3.IsSupported); - break; - case HwIntrinsics.DisableSSSE3: + case HwIntrinsics.DisableSSE42: + Assert.False(Sse.IsSupported); + Assert.False(Sse2.IsSupported); Assert.False(Ssse3.IsSupported); - break; - case HwIntrinsics.DisableSSE41: Assert.False(Sse41.IsSupported); - break; - case HwIntrinsics.DisableSSE42: Assert.False(Sse42.IsSupported); - break; - case HwIntrinsics.DisablePOPCNT: Assert.False(Popcnt.IsSupported); break; case HwIntrinsics.DisableAVX: Assert.False(Avx.IsSupported); break; - case HwIntrinsics.DisableFMA: - Assert.False(Fma.IsSupported); - break; case HwIntrinsics.DisableAVX2: Assert.False(Avx2.IsSupported); - break; - case HwIntrinsics.DisableBMI1: + Assert.False(Fma.IsSupported); Assert.False(Bmi1.IsSupported); - break; - case HwIntrinsics.DisableBMI2: Assert.False(Bmi2.IsSupported); - break; - case HwIntrinsics.DisableLZCNT: Assert.False(Lzcnt.IsSupported); break; - case HwIntrinsics.DisableArm64AdvSimd: - Assert.False(AdvSimd.IsSupported); - break; case HwIntrinsics.DisableArm64Aes: Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); break; From 1d1aa0ae4f1d5a775ab1b49b8ea9f1f0c209f9e8 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 21:48:06 +0100 Subject: [PATCH 14/32] Add usermessages to asserts --- .../TestUtilities/Tests/FeatureTestRunnerTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index 363428d04..c6539bbdf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -115,13 +115,13 @@ public class FeatureTestRunnerTests Assert.False(Pclmulqdq.IsSupported); break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); + Assert.False(Sse.IsSupported, "Sse should be disabled."); + Assert.False(Sse2.IsSupported, "Sse2 should be disabled."); + Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); + Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); + Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); + Assert.False(Sse42.IsSupported, "Sse42 should be disabled."); + Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); break; case HwIntrinsics.DisableAVX: Assert.False(Avx.IsSupported); From 994c37b35341b2e5c84c0c12f3492e979bf6db5d Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 21:56:25 +0100 Subject: [PATCH 15/32] SSE is not disabled by SSE42 --- .../TestUtilities/Tests/FeatureTestRunnerTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index c6539bbdf..faf723040 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -115,7 +115,6 @@ public class FeatureTestRunnerTests Assert.False(Pclmulqdq.IsSupported); break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse.IsSupported, "Sse should be disabled."); Assert.False(Sse2.IsSupported, "Sse2 should be disabled."); Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); @@ -211,7 +210,6 @@ public class FeatureTestRunnerTests Assert.False(Pclmulqdq.IsSupported); break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse.IsSupported); Assert.False(Sse2.IsSupported); Assert.False(Ssse3.IsSupported); Assert.False(Sse41.IsSupported); From 330ca36b39d1be162e260ec00be9c255c468f321 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 22:03:46 +0100 Subject: [PATCH 16/32] SSE2 ist not disabled by SSE42 --- .../TestUtilities/Tests/FeatureTestRunnerTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index faf723040..566099664 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -115,7 +115,6 @@ public class FeatureTestRunnerTests Assert.False(Pclmulqdq.IsSupported); break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse2.IsSupported, "Sse2 should be disabled."); Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); @@ -210,7 +209,6 @@ public class FeatureTestRunnerTests Assert.False(Pclmulqdq.IsSupported); break; case HwIntrinsics.DisableSSE42: - Assert.False(Sse2.IsSupported); Assert.False(Ssse3.IsSupported); Assert.False(Sse41.IsSupported); Assert.False(Sse42.IsSupported); From 6aad0e733eee9403804b5850759318ffa9457246 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 22:14:27 +0100 Subject: [PATCH 17/32] In .net8 there is no grouping --- .../Tests/FeatureTestRunnerTests.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index 566099664..1b220ce1b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -115,21 +115,26 @@ public class FeatureTestRunnerTests Assert.False(Pclmulqdq.IsSupported); break; case HwIntrinsics.DisableSSE42: +#if NET10_0_OR_GREATER Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); - Assert.False(Sse42.IsSupported, "Sse42 should be disabled."); Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); +#else + Assert.False(Sse42.IsSupported, "Sse42 should be disabled."); +#endif break; case HwIntrinsics.DisableAVX: Assert.False(Avx.IsSupported); break; case HwIntrinsics.DisableAVX2: Assert.False(Avx2.IsSupported); +#if NET10_0_OR_GREATER Assert.False(Fma.IsSupported); Assert.False(Bmi1.IsSupported); Assert.False(Bmi2.IsSupported); Assert.False(Lzcnt.IsSupported); +#endif break; case HwIntrinsics.DisableArm64Aes: Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); @@ -206,23 +211,30 @@ public class FeatureTestRunnerTests break; case HwIntrinsics.DisableAES: Assert.False(Aes.IsSupported); +#if NET10_0_OR_GREATER Assert.False(Pclmulqdq.IsSupported); +#endif break; case HwIntrinsics.DisableSSE42: - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); +#if NET10_0_OR_GREATER + Assert.False(Sse3.IsSupported, "Sse3 should be disabled."); + Assert.False(Ssse3.IsSupported, "Ssse3 should be disabled."); + Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); + Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); +#endif + Assert.False(Sse42.IsSupported, "Sse42 should be disabled."); break; case HwIntrinsics.DisableAVX: Assert.False(Avx.IsSupported); break; case HwIntrinsics.DisableAVX2: Assert.False(Avx2.IsSupported); +#if NET10_0_OR_GREATER Assert.False(Fma.IsSupported); Assert.False(Bmi1.IsSupported); Assert.False(Bmi2.IsSupported); Assert.False(Lzcnt.IsSupported); +#endif break; case HwIntrinsics.DisableArm64Aes: Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); From eca4734ad173ee0beb7338257b5c424e2cbaa2f8 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 22:34:45 +0100 Subject: [PATCH 18/32] Add more usermessages --- .../Tests/FeatureTestRunnerTests.cs | 176 +++++++++--------- 1 file changed, 89 insertions(+), 87 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index 1b220ce1b..c9ea4cf39 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -47,7 +47,7 @@ public class FeatureTestRunnerTests } FeatureTestRunner.RunWithHwIntrinsicsFeature( - () => Assert.True(Vector.IsHardwareAccelerated), + () => Assert.True(Vector.IsHardwareAccelerated, "Vector hardware acceleration should be enabled when AllowAll is specified."), HwIntrinsics.AllowAll); } @@ -56,21 +56,21 @@ public class FeatureTestRunnerTests { static void AssertDisabled() { - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); - Assert.False(Avx.IsSupported); - Assert.False(Fma.IsSupported); - Assert.False(Avx2.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse2.IsSupported, "SSE2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse3.IsSupported, "SSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Ssse3.IsSupported, "SSSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse41.IsSupported, "SSE4.1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse42.IsSupported, "SSE4.2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Popcnt.IsSupported, "POPCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableHWIntrinsic is set."); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableHWIntrinsic is set."); } FeatureTestRunner.RunWithHwIntrinsicsFeature( @@ -88,31 +88,33 @@ public class FeatureTestRunnerTests switch (Enum.Parse(intrinsic)) { case HwIntrinsics.DisableHWIntrinsic: - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); - Assert.False(Avx.IsSupported); - Assert.False(Fma.IsSupported); - Assert.False(Avx2.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); - Assert.False(AdvSimd.IsSupported); - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); - Assert.False(Crc32.IsSupported); - Assert.False(Dp.IsSupported); - Assert.False(Sha1.IsSupported); - Assert.False(Sha256.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse2.IsSupported, "SSE2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse3.IsSupported, "SSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Ssse3.IsSupported, "SSSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse41.IsSupported, "SSE4.1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse42.IsSupported, "SSE4.2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Popcnt.IsSupported, "POPCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableHWIntrinsic is set."); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(AdvSimd.IsSupported, "Arm64 AdvSimd should be disabled when DisableHWIntrinsic is set."); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableHWIntrinsic is set."); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableHWIntrinsic is set."); break; case HwIntrinsics.DisableAES: - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableAES is set."); +#if NET10_0_OR_GREATER + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableAES is set (paired disable)."); +#endif break; case HwIntrinsics.DisableSSE42: #if NET10_0_OR_GREATER @@ -121,35 +123,35 @@ public class FeatureTestRunnerTests Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); #else - Assert.False(Sse42.IsSupported, "Sse42 should be disabled."); + Assert.False(Sse42.IsSupported, "Sse42 should be disabled when DisableSSE42 is set."); #endif break; case HwIntrinsics.DisableAVX: - Assert.False(Avx.IsSupported); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set."); break; case HwIntrinsics.DisableAVX2: - Assert.False(Avx2.IsSupported); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableAVX2 is set."); #if NET10_0_OR_GREATER - Assert.False(Fma.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableAVX2 is set (paired disable)."); #endif break; case HwIntrinsics.DisableArm64Aes: - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableArm64Aes is set."); break; case HwIntrinsics.DisableArm64Crc32: - Assert.False(Crc32.IsSupported); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableArm64Crc32 is set."); break; case HwIntrinsics.DisableArm64Dp: - Assert.False(Dp.IsSupported); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableArm64Dp is set."); break; case HwIntrinsics.DisableArm64Sha1: - Assert.False(Sha1.IsSupported); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableArm64Sha1 is set."); break; case HwIntrinsics.DisableArm64Sha256: - Assert.False(Sha256.IsSupported); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableArm64Sha256 is set."); break; } } @@ -167,7 +169,7 @@ public class FeatureTestRunnerTests { Assert.NotNull(serializable); Assert.NotNull(FeatureTestRunner.DeserializeForXunit(serializable)); - Assert.False(Sse.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableSSE42 is set (sanity check using serializable param overload)."); } FeatureTestRunner.RunWithHwIntrinsicsFeature( @@ -187,32 +189,32 @@ public class FeatureTestRunnerTests switch (Enum.Parse(intrinsic)) { case HwIntrinsics.DisableHWIntrinsic: - Assert.False(Sse.IsSupported); - Assert.False(Sse2.IsSupported); - Assert.False(Aes.IsSupported); - Assert.False(Pclmulqdq.IsSupported); - Assert.False(Sse3.IsSupported); - Assert.False(Ssse3.IsSupported); - Assert.False(Sse41.IsSupported); - Assert.False(Sse42.IsSupported); - Assert.False(Popcnt.IsSupported); - Assert.False(Avx.IsSupported); - Assert.False(Fma.IsSupported); - Assert.False(Avx2.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); - Assert.False(AdvSimd.IsSupported); - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); - Assert.False(Crc32.IsSupported); - Assert.False(Dp.IsSupported); - Assert.False(Sha1.IsSupported); - Assert.False(Sha256.IsSupported); + Assert.False(Sse.IsSupported, "SSE should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse2.IsSupported, "SSE2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse3.IsSupported, "SSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Ssse3.IsSupported, "SSSE3 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse41.IsSupported, "SSE4.1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sse42.IsSupported, "SSE4.2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Popcnt.IsSupported, "POPCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableHWIntrinsic is set."); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableHWIntrinsic is set."); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableHWIntrinsic is set."); + Assert.False(AdvSimd.IsSupported, "Arm64 AdvSimd should be disabled when DisableHWIntrinsic is set."); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableHWIntrinsic is set."); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableHWIntrinsic is set."); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableHWIntrinsic is set."); break; case HwIntrinsics.DisableAES: - Assert.False(Aes.IsSupported); + Assert.False(Aes.IsSupported, "AES (x86) should be disabled when DisableAES is set."); #if NET10_0_OR_GREATER - Assert.False(Pclmulqdq.IsSupported); + Assert.False(Pclmulqdq.IsSupported, "PCLMULQDQ should be disabled when DisableAES is set (paired disable)."); #endif break; case HwIntrinsics.DisableSSE42: @@ -222,34 +224,34 @@ public class FeatureTestRunnerTests Assert.False(Sse41.IsSupported, "Sse41 should be disabled."); Assert.False(Popcnt.IsSupported, "Popcnt should be disabled."); #endif - Assert.False(Sse42.IsSupported, "Sse42 should be disabled."); + Assert.False(Sse42.IsSupported, "Sse42 should be disabled when DisableSSE42 is set."); break; case HwIntrinsics.DisableAVX: - Assert.False(Avx.IsSupported); + Assert.False(Avx.IsSupported, "AVX should be disabled when DisableAVX is set."); break; case HwIntrinsics.DisableAVX2: - Assert.False(Avx2.IsSupported); + Assert.False(Avx2.IsSupported, "AVX2 should be disabled when DisableAVX2 is set."); #if NET10_0_OR_GREATER - Assert.False(Fma.IsSupported); - Assert.False(Bmi1.IsSupported); - Assert.False(Bmi2.IsSupported); - Assert.False(Lzcnt.IsSupported); + Assert.False(Fma.IsSupported, "FMA should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi1.IsSupported, "BMI1 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Bmi2.IsSupported, "BMI2 should be disabled when DisableAVX2 is set (paired disable)."); + Assert.False(Lzcnt.IsSupported, "LZCNT should be disabled when DisableAVX2 is set (paired disable)."); #endif break; case HwIntrinsics.DisableArm64Aes: - Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported); + Assert.False(System.Runtime.Intrinsics.Arm.Aes.IsSupported, "Arm64 AES should be disabled when DisableArm64Aes is set."); break; case HwIntrinsics.DisableArm64Crc32: - Assert.False(Crc32.IsSupported); + Assert.False(Crc32.IsSupported, "Arm64 CRC32 should be disabled when DisableArm64Crc32 is set."); break; case HwIntrinsics.DisableArm64Dp: - Assert.False(Dp.IsSupported); + Assert.False(Dp.IsSupported, "Arm64 DotProd (DP) should be disabled when DisableArm64Dp is set."); break; case HwIntrinsics.DisableArm64Sha1: - Assert.False(Sha1.IsSupported); + Assert.False(Sha1.IsSupported, "Arm64 SHA1 should be disabled when DisableArm64Sha1 is set."); break; case HwIntrinsics.DisableArm64Sha256: - Assert.False(Sha256.IsSupported); + Assert.False(Sha256.IsSupported, "Arm64 SHA256 should be disabled when DisableArm64Sha256 is set."); break; } } From fd04531820748ac9d024d2004743a45ac3579f2c Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 22:43:35 +0100 Subject: [PATCH 19/32] Check for SSE42 not SSE --- .../TestUtilities/Tests/FeatureTestRunnerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs index c9ea4cf39..ee3d02670 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/FeatureTestRunnerTests.cs @@ -169,7 +169,7 @@ public class FeatureTestRunnerTests { Assert.NotNull(serializable); Assert.NotNull(FeatureTestRunner.DeserializeForXunit(serializable)); - Assert.False(Sse.IsSupported, "SSE should be disabled when DisableSSE42 is set (sanity check using serializable param overload)."); + Assert.False(Sse42.IsSupported, "SSE42 should be disabled when DisableSSE42 is set (sanity check using serializable param overload)."); } FeatureTestRunner.RunWithHwIntrinsicsFeature( From 726794a98f4dac354706d2b3b1dcb24670f31c0e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 17 Nov 2025 11:06:22 +1000 Subject: [PATCH 20/32] Rename helper, fix docs. --- .../Metadata/Profiles/Exif/ExifProfile.cs | 4 ++-- .../Processing/AffineTransformBuilder.cs | 12 +++++------ .../Transforms/TransformExtensions.cs | 4 ++-- .../Linear/AffineTransformProcessor.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 2 +- .../Linear/ProjectiveTransformProcessor.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 6 +++--- .../Transforms/Linear/RotateProcessor.cs | 4 ++-- .../Transforms/Linear/SkewProcessor.cs | 4 ++-- .../SwizzleProcessor{TSwizzler,TPixel}.cs | 2 +- ...ransformUtils.cs => TransformUtilities.cs} | 10 +++++----- .../Processing/ProjectiveTransformBuilder.cs | 20 +++++++++---------- .../Transforms/TransformBuilderTestBase.cs | 4 ++-- tests/ImageSharp.Tests/TestImages.cs | 2 +- 14 files changed, 39 insertions(+), 39 deletions(-) rename src/ImageSharp/Processing/Processors/Transforms/{TransformUtils.cs => TransformUtilities.cs} (99%) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs index de4a89813..aa2eb29e7 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs @@ -318,7 +318,7 @@ public sealed class ExifProfile : IDeepCloneable { if (location.Value?.Length == 2) { - Vector2 point = TransformUtils.ProjectiveTransform2D(location.Value[0], location.Value[1], matrix); + Vector2 point = TransformUtilities.ProjectiveTransform2D(location.Value[0], location.Value[1], matrix); // Ensure the point is within the image dimensions. point = Vector2.Clamp(point, Vector2.Zero, new Vector2(width - 1, height - 1)); @@ -340,7 +340,7 @@ public sealed class ExifProfile : IDeepCloneable if (area.Value?.Length == 4) { RectangleF rectangle = new(area.Value[0], area.Value[1], area.Value[2], area.Value[3]); - if (!TransformUtils.TryGetTransformedRectangle(rectangle, matrix, out RectangleF bounds)) + if (!TransformUtilities.TryGetTransformedRectangle(rectangle, matrix, out RectangleF bounds)) { return; } diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index e330c6c26..c1e11a1d4 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -37,7 +37,7 @@ public class AffineTransformBuilder /// The . public AffineTransformBuilder PrependRotationRadians(float radians) => this.Prepend( - size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size)); + size => TransformUtilities.CreateRotationTransformMatrixRadians(radians, size)); /// /// Prepends a rotation matrix using the given rotation in degrees at the given origin. @@ -73,7 +73,7 @@ public class AffineTransformBuilder /// The amount of rotation, in radians. /// The . public AffineTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size)); + => this.Append(size => TransformUtilities.CreateRotationTransformMatrixRadians(radians, size)); /// /// Appends a rotation matrix using the given rotation in degrees at the given origin. @@ -157,7 +157,7 @@ public class AffineTransformBuilder /// The Y angle, in radians. /// The . public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); + => this.Prepend(size => TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -195,7 +195,7 @@ public class AffineTransformBuilder /// The Y angle, in radians. /// The . public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); + => this.Append(size => TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -347,11 +347,11 @@ public class AffineTransformBuilder /// /// The . internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix3x2 matrix) - => TransformUtils.GetRawTransformedSize(matrix, sourceRectangle.Size); + => TransformUtilities.GetRawTransformedSize(matrix, sourceRectangle.Size); private static void CheckDegenerate(Matrix3x2 matrix) { - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs index 5857cb435..dd200013a 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs @@ -51,7 +51,7 @@ public static class TransformExtensions IResampler sampler) { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = TransformUtils.GetTransformedCanvasSize(transform, sourceRectangle.Size); + Size targetDimensions = TransformUtilities.GetTransformedCanvasSize(transform, sourceRectangle.Size); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } @@ -113,7 +113,7 @@ public static class TransformExtensions IResampler sampler) { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = TransformUtils.GetTransformedCanvasSize(transform, sourceRectangle.Size); + Size targetDimensions = TransformUtilities.GetTransformedCanvasSize(transform, sourceRectangle.Size); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs index 30c279e59..afd1b70b7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs @@ -21,7 +21,7 @@ public class AffineTransformProcessor : CloningImageProcessor Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeValueType(sampler); - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index de9daa2fc..260a366b9 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -79,7 +79,7 @@ internal class AffineTransformProcessor : TransformProcessor, IR // All matrices are defined in normalized coordinate space so we need to convert to pixel space. // After normalization we need to invert the matrix for correct sampling. - matrix = TransformUtils.NormalizeToPixel(matrix); + matrix = TransformUtilities.NormalizeToPixel(matrix); Matrix3x2.Invert(matrix, out matrix); if (sampler is NearestNeighborResampler) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs index 9e9507b73..1c457e84c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs @@ -21,7 +21,7 @@ public sealed class ProjectiveTransformProcessor : CloningImageProcessor Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeValueType(sampler); - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index 7af627a26..ecf587684 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -77,7 +77,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor // All matrices are defined in normalized coordinate space so we need to convert to pixel space. // After normalization we need to invert the matrix for correct sampling. - matrix = TransformUtils.NormalizeToPixel(matrix); + matrix = TransformUtilities.NormalizeToPixel(matrix); Matrix4x4.Invert(matrix, out matrix); if (sampler is NearestNeighborResampler) @@ -137,7 +137,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor for (int x = 0; x < destinationRowSpan.Length; x++) { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, this.matrix); + Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix); int px = (int)MathF.Round(point.X); int py = (int)MathF.Round(point.Y); @@ -209,7 +209,7 @@ internal class ProjectiveTransformProcessor : TransformProcessor for (int x = 0; x < span.Length; x++) { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, matrix); float pY = point.Y; float pX = point.X; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs index c745c480d..ea0f1c113 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs @@ -28,14 +28,14 @@ public sealed class RotateProcessor : AffineTransformProcessor /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize), + TransformUtilities.CreateRotationTransformMatrixDegrees(degrees, sourceSize), sampler, sourceSize) => this.Degrees = degrees; // Helper constructor private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize) - : base(rotationMatrix, sampler, TransformUtils.GetTransformedCanvasSize(rotationMatrix, sourceSize)) + : base(rotationMatrix, sampler, TransformUtilities.GetTransformedCanvasSize(rotationMatrix, sourceSize)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs index a5621bc4c..9c23a9532 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs @@ -30,7 +30,7 @@ public sealed class SkewProcessor : AffineTransformProcessor /// The source image size public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize), + TransformUtilities.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize), sampler, sourceSize) { @@ -40,7 +40,7 @@ public sealed class SkewProcessor : AffineTransformProcessor // Helper constructor: private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize) - : base(skewMatrix, sampler, TransformUtils.GetTransformedCanvasSize(skewMatrix, sourceSize)) + : base(skewMatrix, sampler, TransformUtilities.GetTransformedCanvasSize(skewMatrix, sourceSize)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs index 3bec82cce..9acf18730 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs @@ -24,7 +24,7 @@ internal class SwizzleProcessor : TransformProcessor // Calculate the transform matrix from the swizzle operation to allow us // to update any metadata that represents pixel coordinates in the source image. this.transformMatrix = new ProjectiveTransformBuilder() - .AppendMatrix(TransformUtils.GetSwizzlerMatrix(swizzler, sourceRectangle)) + .AppendMatrix(TransformUtilities.GetSwizzlerMatrix(swizzler, sourceRectangle)) .BuildMatrix(sourceRectangle); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs similarity index 99% rename from src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs rename to src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs index 6badd949a..17bdeadde 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtilities.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms; /// /// Contains utility methods for working with transforms. /// -internal static class TransformUtils +internal static class TransformUtilities { /// /// Returns a value that indicates whether the specified matrix is degenerate @@ -80,7 +80,7 @@ internal static class TransformUtils } /// - /// Creates a centered rotation transform matrix using the given rotation in degrees and The original source size. + /// Creates a centered rotation transform matrix using the given rotation in degrees and the original source size. /// /// The amount of rotation, in degrees. /// The source image size. @@ -90,7 +90,7 @@ internal static class TransformUtils => CreateRotationTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degrees), size); /// - /// Creates a centered rotation transform matrix using the given rotation in radians and The original source size. + /// Creates a centered rotation transform matrix using the given rotation in radians and the original source size. /// /// The amount of rotation, in radians. /// The source image size. @@ -100,7 +100,7 @@ internal static class TransformUtils => CreateCenteredTransformMatrix(Matrix3x2Extensions.CreateRotation(radians, PointF.Empty), size); /// - /// Creates a centered skew transform matrix from the give angles in degrees and The original source size. + /// Creates a centered skew transform matrix from the give angles in degrees and the original source size. /// /// The X angle, in degrees. /// The Y angle, in degrees. @@ -111,7 +111,7 @@ internal static class TransformUtils => CreateSkewTransformMatrixRadians(GeometryUtilities.DegreeToRadian(degreesX), GeometryUtilities.DegreeToRadian(degreesY), size); /// - /// Creates a centered skew transform matrix from the give angles in radians and The original source size. + /// Creates a centered skew transform matrix from the give angles in radians and the original source size. /// /// The X angle, in radians. /// The Y angle, in radians. diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index dc049ef0e..e40a307a3 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -28,7 +28,7 @@ public class ProjectiveTransformBuilder /// The amount to taper. /// The . public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction) - => this.Prepend(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + => this.Prepend(size => TransformUtilities.CreateTaperMatrix(size, side, corner, fraction)); /// /// Appends a matrix that performs a tapering projective transform. @@ -38,7 +38,7 @@ public class ProjectiveTransformBuilder /// The amount to taper. /// The . public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction) - => this.Append(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + => this.Append(size => TransformUtilities.CreateTaperMatrix(size, side, corner, fraction)); /// /// Prepends a centered rotation matrix using the given rotation in degrees. @@ -54,7 +54,7 @@ public class ProjectiveTransformBuilder /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder PrependRotationRadians(float radians) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size))); + => this.Prepend(size => new Matrix4x4(TransformUtilities.CreateRotationTransformMatrixRadians(radians, size))); /// /// Prepends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -89,7 +89,7 @@ public class ProjectiveTransformBuilder /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size))); + => this.Append(size => new Matrix4x4(TransformUtilities.CreateRotationTransformMatrixRadians(radians, size))); /// /// Appends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -173,7 +173,7 @@ public class ProjectiveTransformBuilder /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); + => this.Prepend(size => new Matrix4x4(TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -211,7 +211,7 @@ public class ProjectiveTransformBuilder /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); + => this.Append(size => new Matrix4x4(TransformUtilities.CreateSkewTransformMatrixRadians(radiansX, radiansY, size))); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -274,7 +274,7 @@ public class ProjectiveTransformBuilder /// The bottom-left corner point of the distorted quad. /// The . public ProjectiveTransformBuilder PrependQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) - => this.Prepend(size => TransformUtils.CreateQuadDistortionMatrix( + => this.Prepend(size => TransformUtilities.CreateQuadDistortionMatrix( new Rectangle(Point.Empty, size), topLeft, topRight, @@ -290,7 +290,7 @@ public class ProjectiveTransformBuilder /// The bottom-left corner point of the distorted quad. /// The . public ProjectiveTransformBuilder AppendQuadDistortion(PointF topLeft, PointF topRight, PointF bottomRight, PointF bottomLeft) - => this.Append(size => TransformUtils.CreateQuadDistortionMatrix( + => this.Append(size => TransformUtilities.CreateQuadDistortionMatrix( new Rectangle(Point.Empty, size), topLeft, topRight, @@ -395,11 +395,11 @@ public class ProjectiveTransformBuilder /// /// The . internal static SizeF GetTransformedSize(Rectangle sourceRectangle, Matrix4x4 matrix) - => TransformUtils.GetRawTransformedSize(matrix, sourceRectangle.Size); + => TransformUtilities.GetRawTransformedSize(matrix, sourceRectangle.Size); private static void CheckDegenerate(Matrix4x4 matrix) { - if (TransformUtils.IsDegenerate(matrix)) + if (TransformUtilities.IsDegenerate(matrix)) { throw new DegenerateTransformException("Matrix is degenerate. Check input values."); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index f5aa1715f..73215b1c6 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -98,7 +98,7 @@ public abstract class TransformBuilderTestBase this.AppendRotationDegrees(builder, degrees); // TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness - Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size); + Matrix3x2 matrix = TransformUtilities.CreateRotationTransformMatrixDegrees(degrees, size); Vector2 position = new(x, y); Vector2 expected = Vector2.Transform(position, matrix); @@ -152,7 +152,7 @@ public abstract class TransformBuilderTestBase this.AppendSkewDegrees(builder, degreesX, degreesY); - Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size); + Matrix3x2 matrix = TransformUtilities.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size); Vector2 position = new(x, y); Vector2 expected = Vector2.Transform(position, matrix); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 76475031c..94dcd2b7b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -163,7 +163,7 @@ public static class TestImages // Issue 2924: https://github.com/SixLabors/ImageSharp/issues/2924 public const string Issue2924 = "Png/issues/Issue_2924.png"; - // Issue 3000: htps://github.com/SixLabors/ImageSharp/issues/3000 + // Issue 3000: https://github.com/SixLabors/ImageSharp/issues/3000 public const string Issue3000 = "Png/issues/issue_3000.png"; public static class Bad From 7b4615f70c25dbd831f9d5ec675d5a88b45e28a9 Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Sun, 16 Nov 2025 09:11:25 +0100 Subject: [PATCH 21/32] Add ARM hosted runners Added macos-26 and ubuntu-2204 github runners which run on arm Use Tolerant ImageComparer We now use the tolerant Image Comparer for now. Disable Tests which need libgdiplus This disables all tests which need libgdiplus for macs with arm. The hosted runners do not have libgdiplus installed Install libgdiplus on mac Enable disabled tests on arm Try to create symlink Try without fallback path Skip on linux This was removed by mistake Remove whitespace --- .github/workflows/build-and-test.yml | 27 +++++++++++++++++-- .../Formats/Tiff/TiffDecoderTests.cs | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e00757cb7..3e3d6d0e2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -77,13 +77,19 @@ jobs: sdk-preview: true runtime: -x64 codecov: false + - os: macos-26 + framework: net9.0 + sdk: 9.0.x + sdk-preview: true + runtime: -x64 + codecov: false - os: windows-latest framework: net9.0 sdk: 9.0.x sdk-preview: true runtime: -x64 codecov: false - - os: buildjet-4vcpu-ubuntu-2204-arm + - os: ubuntu-22.04-arm framework: net9.0 sdk: 9.0.x sdk-preview: true @@ -100,12 +106,17 @@ jobs: sdk: 8.0.x runtime: -x64 codecov: false + - os: macos-26 + framework: net8.0 + sdk: 8.0.x + runtime: -x64 + codecov: false - os: windows-latest framework: net8.0 sdk: 8.0.x runtime: -x64 codecov: false - - os: buildjet-4vcpu-ubuntu-2204-arm + - os: ubuntu-22.04-arm framework: net8.0 sdk: 8.0.x runtime: -x64 @@ -124,6 +135,18 @@ jobs: sudo apt-get update sudo apt-get -y install libgdiplus libgif-dev libglib2.0-dev libcairo2-dev libtiff-dev libexif-dev + - name: Install libgdi+, which is required for tests running on macos + if: ${{ contains(matrix.options.os, 'macos-26') }} + run: | + brew update + brew install mono-libgdiplus + # Create symlinks to make libgdiplus discoverable + sudo mkdir -p /usr/local/lib + sudo ln -sf $(brew --prefix)/lib/libgdiplus.dylib /usr/local/lib/libgdiplus.dylib + # Verify installation + ls -la $(brew --prefix)/lib/libgdiplus* || echo "libgdiplus not found in brew prefix" + ls -la /usr/local/lib/libgdiplus* || echo "libgdiplus not found in /usr/local/lib" + - name: Git Config shell: bash run: | diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 26dc4f587..5df730cb1 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -360,7 +360,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester { using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); - image.CompareToReferenceOutput(ImageComparer.Exact, provider); + image.CompareToReferenceOutput(ImageComparer.Tolerant(), provider); } [Theory] From 04f170b3678e068f2ff4c32695512d9fb3b192eb Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 10:48:51 +1000 Subject: [PATCH 22/32] Fix #3009 --- src/ImageSharp/Image.FromBytes.cs | 28 ++++++++++++++++++- src/ImageSharp/Image.LoadPixelData.cs | 5 ++++ .../Image/ImageTests.DetectFormat.cs | 4 +++ .../Image/ImageTests.Identify.cs | 4 +++ ...s.Load_FromBytes_PassLocalConfiguration.cs | 11 ++++++++ ...s.Load_FromBytes_UseGlobalConfiguration.cs | 4 +++ ...ts.Load_FromStream_ThrowsRightException.cs | 13 ++++++++- 7 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 96ef84510..d48065d01 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -34,7 +34,12 @@ public abstract partial class Image /// The encoded image format is unknown. public static unsafe IImageFormat DetectFormat(DecoderOptions options, ReadOnlySpan buffer) { - Guard.NotNull(options, nameof(options.Configuration)); + Guard.NotNull(options, nameof(options)); + + if (buffer.IsEmpty) + { + throw new UnknownImageFormatException("Cannot detect image format from empty data."); + } fixed (byte* ptr = buffer) { @@ -66,6 +71,13 @@ public abstract partial class Image /// The encoded image format is unknown. public static unsafe ImageInfo Identify(DecoderOptions options, ReadOnlySpan buffer) { + Guard.NotNull(options, nameof(options)); + + if (buffer.IsEmpty) + { + throw new UnknownImageFormatException("Cannot identify image format from empty data."); + } + fixed (byte* ptr = buffer) { using UnmanagedMemoryStream stream = new(ptr, buffer.Length); @@ -99,6 +111,13 @@ public abstract partial class Image /// The encoded image format is unknown. public static unsafe Image Load(DecoderOptions options, ReadOnlySpan buffer) { + Guard.NotNull(options, nameof(options)); + + if (buffer.IsEmpty) + { + throw new UnknownImageFormatException("Cannot load image from empty data."); + } + fixed (byte* ptr = buffer) { using UnmanagedMemoryStream stream = new(ptr, buffer.Length); @@ -133,6 +152,13 @@ public abstract partial class Image public static unsafe Image Load(DecoderOptions options, ReadOnlySpan data) where TPixel : unmanaged, IPixel { + Guard.NotNull(options, nameof(options)); + + if (data.IsEmpty) + { + throw new UnknownImageFormatException("Cannot load image from empty data."); + } + fixed (byte* ptr = data) { using UnmanagedMemoryStream stream = new(ptr, data.Length); diff --git a/src/ImageSharp/Image.LoadPixelData.cs b/src/ImageSharp/Image.LoadPixelData.cs index 53b672b7d..efe1b6e2f 100644 --- a/src/ImageSharp/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image.LoadPixelData.cs @@ -69,6 +69,11 @@ public abstract partial class Image { Guard.NotNull(configuration, nameof(configuration)); + if (data.IsEmpty) + { + throw new ArgumentException("Pixel data cannot be empty.", nameof(data)); + } + int count = width * height; Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data)); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs index a1966e2bb..178f5375f 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.DetectFormat.cs @@ -50,6 +50,10 @@ public partial class ImageTests Assert.Equal(this.LocalImageFormat, format); } + [Fact] + public void FromBytes_EmptySpan_Throws() + => Assert.Throws(() => Image.DetectFormat([])); + [Fact] public void FromFileSystemPath_GlobalConfiguration() { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs index 490fa3b0b..433a1e101 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Identify.cs @@ -38,6 +38,10 @@ public partial class ImageTests Assert.Equal(ExpectedGlobalFormat, info.Metadata.DecodedImageFormat); } + [Fact] + public void FromBytes_EmptySpan_Throws() + => Assert.Throws(() => Image.Identify([])); + [Fact] public void FromBytes_CustomConfiguration() { diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs index 3a47a0ea7..600bed010 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs @@ -79,5 +79,16 @@ public partial class ImageTests this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); } + + [Fact] + public void FromBytes_EmptySpan_Throws() + { + DecoderOptions options = new() + { + Configuration = this.TopLevelConfiguration + }; + + Assert.Throws(() => Image.Load(options, [])); + } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs index 00ec985ac..a0ce1e5d8 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -45,5 +45,9 @@ public partial class ImageTests VerifyDecodedImage(img); Assert.IsType(img.Metadata.DecodedImageFormat); } + + [Fact] + public void FromBytes_EmptySpan_Throws() + => Assert.ThrowsAny(() => Image.Load([])); } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs index a064b6472..c2609545d 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_ThrowsRightException.cs @@ -32,6 +32,17 @@ public partial class ImageTests } }); - public void Dispose() => this.Stream?.Dispose(); + [Fact] + public void FromStream_Empty_Throws() + { + using MemoryStream ms = new(); + Assert.Throws(() => Image.Load(DecoderOptions.Default, ms)); + } + + public void Dispose() + { + this.Stream?.Dispose(); + GC.SuppressFinalize(this); + } } } From 47ac160a454fe96180de0134924c5e6c8a7cf328 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 11:38:15 +1000 Subject: [PATCH 23/32] Normalize WebP Chunk parsing. Fixes #3010 --- .../Formats/Webp/WebpChunkParsingUtils.cs | 41 ++++++++--- .../Formats/Webp/WebpDecoderCore.cs | 70 +++++-------------- 2 files changed, 46 insertions(+), 65 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs index dc95ca044..0f97c404b 100644 --- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs @@ -258,6 +258,9 @@ internal static class WebpChunkParsingUtils /// The stream to read from. /// The buffer to store the read data into. /// A unsigned 24 bit integer. + /// + /// Thrown if the input stream is not valid. + /// public static uint ReadUInt24LittleEndian(Stream stream, Span buffer) { if (stream.Read(buffer, 0, 3) == 3) @@ -274,6 +277,9 @@ internal static class WebpChunkParsingUtils /// /// The stream to read from. /// The uint24 data to write. + /// + /// Thrown if the data is not a valid unsigned 24 bit integer. + /// public static unsafe void WriteUInt24LittleEndian(Stream stream, uint data) { if (data >= 1 << 24) @@ -296,18 +302,24 @@ internal static class WebpChunkParsingUtils /// /// The stream to read the data from. /// Buffer to store the data read from the stream. + /// If true, the chunk size is required to be read, otherwise it can be skipped. /// The chunk size in bytes. - public static uint ReadChunkSize(Stream stream, Span buffer) + /// Thrown if the input stream is not valid. + public static uint ReadChunkSize(Stream stream, Span buffer, bool required = true) { - DebugGuard.IsTrue(buffer.Length is 4, "buffer has wrong length"); - if (stream.Read(buffer) is 4) { uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer); return chunkSize % 2 is 0 ? chunkSize : chunkSize + 1; } - throw new ImageFormatException("Invalid Webp data, could not read chunk size."); + if (required) + { + throw new ImageFormatException("Invalid Webp data, could not read chunk size."); + } + + // Return the size of the remaining data in the stream. + return (uint)(stream.Length - stream.Position); } /// @@ -320,14 +332,12 @@ internal static class WebpChunkParsingUtils /// public static WebpChunkType ReadChunkType(BufferedReadStream stream, Span buffer) { - DebugGuard.IsTrue(buffer.Length == 4, "buffer has wrong length"); - if (stream.Read(buffer) == 4) { - WebpChunkType chunkType = (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); - return chunkType; + return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); } + // We should ignore unknown chunks but still be able to read the type. throw new ImageFormatException("Invalid Webp data, could not read chunk type."); } @@ -336,6 +346,12 @@ internal static class WebpChunkParsingUtils /// If there are more such chunks, readers MAY ignore all except the first one. /// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks. /// + /// The stream to read the data from. + /// The chunk type to parse. + /// The image metadata to write to. + /// If true, metadata will be ignored. + /// Indicates how to handle segment integrity issues. + /// Buffer to store the data read from the stream. public static void ParseOptionalChunks( BufferedReadStream stream, WebpChunkType chunkType, @@ -344,10 +360,13 @@ internal static class WebpChunkParsingUtils SegmentIntegrityHandling segmentIntegrityHandling, Span buffer) { + bool ignoreNone = segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone; long streamLength = stream.Length; while (stream.Position < streamLength) { - uint chunkLength = ReadChunkSize(stream, buffer); + // Ignore unknown chunk types or when metadata is to be ignored. + // If handling should ignore none, we still need to validate the chunk length. + uint chunkLength = ReadChunkSize(stream, buffer, ignoreNone && (chunkType is WebpChunkType.Exif or WebpChunkType.Xmp) && !ignoreMetaData); if (ignoreMetaData) { @@ -362,7 +381,7 @@ internal static class WebpChunkParsingUtils bytesRead = stream.Read(exifData, 0, (int)chunkLength); if (bytesRead != chunkLength) { - if (segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone) + if (ignoreNone) { WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile"); } @@ -394,7 +413,7 @@ internal static class WebpChunkParsingUtils bytesRead = stream.Read(xmpData, 0, (int)chunkLength); if (bytesRead != chunkLength) { - if (segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone) + if (ignoreNone) { WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile"); } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 0e9888adb..2d06d0e49 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -248,7 +248,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable else { // Ignore unknown chunks. - uint chunkSize = ReadChunkSize(stream, buffer, false); + uint chunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, false); stream.Skip((int)chunkSize); } } @@ -328,7 +328,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable while (stream.Position < streamLength) { // Read chunk header. - WebpChunkType chunkType = ReadChunkType(stream, buffer); + WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); if (chunkType == WebpChunkType.Exif && metadata.ExifProfile == null) { this.ReadExifProfile(stream, metadata, buffer); @@ -340,7 +340,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable else { // Skip duplicate XMP or EXIF chunk. - uint chunkLength = ReadChunkSize(stream, buffer); + uint chunkLength = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, false); stream.Skip((int)chunkLength); } } @@ -354,8 +354,11 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable /// Temporary buffer. private void ReadExifProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) { - uint exifChunkSize = ReadChunkSize(stream, buffer); - if (this.skipMetadata) + bool ignoreMetadata = this.skipMetadata; + bool ignoreNone = this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone && !ignoreMetadata; + + uint exifChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, ignoreNone); + if (ignoreMetadata) { stream.Skip((int)exifChunkSize); } @@ -365,7 +368,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable int bytesRead = stream.Read(exifData, 0, (int)exifChunkSize); if (bytesRead != exifChunkSize) { - if (this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone) + if (ignoreNone) { WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile"); } @@ -408,8 +411,11 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable /// Temporary buffer. private void ReadXmpProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) { - uint xmpChunkSize = ReadChunkSize(stream, buffer); - if (this.skipMetadata) + bool ignoreMetadata = this.skipMetadata; + bool ignoreNone = this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone && !ignoreMetadata; + + uint xmpChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, ignoreNone); + if (ignoreMetadata) { stream.Skip((int)xmpChunkSize); } @@ -419,7 +425,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable int bytesRead = stream.Read(xmpData, 0, (int)xmpChunkSize); if (bytesRead != xmpChunkSize) { - if (this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone) + if (ignoreNone) { WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile"); } @@ -439,7 +445,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable /// Temporary buffer. private void ReadIccProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) { - uint iccpChunkSize = ReadChunkSize(stream, buffer); + uint iccpChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer); if (this.skipMetadata) { stream.Skip((int)iccpChunkSize); @@ -512,50 +518,6 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable } } - /// - /// Identifies the chunk type from the chunk. - /// - /// The stream to decode from. - /// Temporary buffer. - /// - /// Thrown if the input stream is not valid. - /// - private static WebpChunkType ReadChunkType(BufferedReadStream stream, Span buffer) - { - if (stream.Read(buffer, 0, 4) == 4) - { - return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); - } - - throw new ImageFormatException("Invalid Webp data."); - } - - /// - /// Reads the chunk size. If Chunk Size is odd, a single padding byte will be added to the payload, - /// so the chunk size will be increased by 1 in those cases. - /// - /// The stream to decode from. - /// Temporary buffer. - /// If true, the chunk size is required to be read, otherwise it can be skipped. - /// The chunk size in bytes. - /// Invalid data. - private static uint ReadChunkSize(BufferedReadStream stream, Span buffer, bool required = true) - { - if (stream.Read(buffer, 0, 4) == 4) - { - uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer); - return (chunkSize % 2 == 0) ? chunkSize : chunkSize + 1; - } - - if (required) - { - throw new ImageFormatException("Invalid Webp data."); - } - - // Return the size of the remaining data in the stream. - return (uint)(stream.Length - stream.Position); - } - /// public void Dispose() => this.alphaData?.Dispose(); } From 4979e995faf05831e8b7d3d24936174fbc8d08cf Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 13:11:44 +1000 Subject: [PATCH 24/32] Normalize EXIF XMP and ICC reading. --- .../Formats/Webp/WebpAnimationDecoder.cs | 21 +- .../Formats/Webp/WebpChunkParsingUtils.cs | 211 +++++++++++++----- .../Formats/Webp/WebpDecoderCore.cs | 143 +----------- 3 files changed, 180 insertions(+), 195 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 86489cd36..a23705413 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -112,12 +112,12 @@ internal class WebpAnimationDecoder : IDisposable this.webpMetadata = this.metadata.GetWebpMetadata(); this.webpMetadata.RepeatCount = features.AnimationLoopCount; - Color backgroundColor = this.backgroundColorHandling == BackgroundColorHandling.Ignore + this.webpMetadata.BackgroundColor = this.backgroundColorHandling == BackgroundColorHandling.Ignore ? Color.Transparent : features.AnimationBackgroundColor!.Value; - this.webpMetadata.BackgroundColor = backgroundColor; - + bool ignoreMetadata = this.skipMetadata; + SegmentIntegrityHandling segmentIntegrityHandling = this.segmentIntegrityHandling; Span buffer = stackalloc byte[4]; uint frameCount = 0; int remainingBytes = (int)completeDataSize; @@ -135,9 +135,16 @@ internal class WebpAnimationDecoder : IDisposable remainingBytes -= (int)dataSize; break; + case WebpChunkType.Iccp: case WebpChunkType.Xmp: case WebpChunkType.Exif: - WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, this.metadata, this.skipMetadata, this.segmentIntegrityHandling, buffer); + WebpChunkParsingUtils.ParseOptionalChunks( + stream, + chunkType, + this.metadata, + ignoreMetadata, + segmentIntegrityHandling, + buffer); break; default: @@ -187,9 +194,12 @@ internal class WebpAnimationDecoder : IDisposable this.webpMetadata.BackgroundColor = backgroundColor; TPixel backgroundPixel = backgroundColor.ToPixel(); + bool ignoreMetadata = this.skipMetadata; + SegmentIntegrityHandling segmentIntegrityHandling = this.segmentIntegrityHandling; Span buffer = stackalloc byte[4]; uint frameCount = 0; int remainingBytes = (int)completeDataSize; + while (remainingBytes > 0) { WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); @@ -209,9 +219,10 @@ internal class WebpAnimationDecoder : IDisposable remainingBytes -= (int)dataSize; break; + case WebpChunkType.Iccp: case WebpChunkType.Xmp: case WebpChunkType.Exif: - WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, image!.Metadata, this.skipMetadata, this.segmentIntegrityHandling, buffer); + WebpChunkParsingUtils.ParseOptionalChunks(stream, chunkType, image!.Metadata, ignoreMetadata, segmentIntegrityHandling, buffer); break; default: diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs index 0f97c404b..26ae28fd4 100644 --- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Xmp; namespace SixLabors.ImageSharp.Formats.Webp; @@ -275,7 +276,7 @@ internal static class WebpChunkParsingUtils /// /// Writes a unsigned 24 bit integer. /// - /// The stream to read from. + /// The stream to write to. /// The uint24 data to write. /// /// Thrown if the data is not a valid unsigned 24 bit integer. @@ -337,7 +338,8 @@ internal static class WebpChunkParsingUtils return (WebpChunkType)BinaryPrimitives.ReadUInt32BigEndian(buffer); } - // We should ignore unknown chunks but still be able to read the type. + // While we ignore unknown chunks we still need a to be a ble to read a chunk type + // known or otherwise from the stream. throw new ImageFormatException("Invalid Webp data, could not read chunk type."); } @@ -349,88 +351,179 @@ internal static class WebpChunkParsingUtils /// The stream to read the data from. /// The chunk type to parse. /// The image metadata to write to. - /// If true, metadata will be ignored. + /// If true, metadata will be ignored. /// Indicates how to handle segment integrity issues. /// Buffer to store the data read from the stream. public static void ParseOptionalChunks( BufferedReadStream stream, WebpChunkType chunkType, ImageMetadata metadata, - bool ignoreMetaData, + bool ignoreMetadata, SegmentIntegrityHandling segmentIntegrityHandling, Span buffer) { - bool ignoreNone = segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone; long streamLength = stream.Length; while (stream.Position < streamLength) { - // Ignore unknown chunk types or when metadata is to be ignored. - // If handling should ignore none, we still need to validate the chunk length. - uint chunkLength = ReadChunkSize(stream, buffer, ignoreNone && (chunkType is WebpChunkType.Exif or WebpChunkType.Xmp) && !ignoreMetaData); - - if (ignoreMetaData) - { - stream.Skip((int)chunkLength); - } - - int bytesRead; switch (chunkType) { - case WebpChunkType.Exif: - byte[] exifData = new byte[chunkLength]; - bytesRead = stream.Read(exifData, 0, (int)chunkLength); - if (bytesRead != chunkLength) - { - if (ignoreNone) - { - WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile"); - } - - return; - } - - if (metadata.ExifProfile == null) - { - ExifProfile exifProfile = new(exifData); - - // Set the resolution from the metadata. - double horizontalValue = GetExifResolutionValue(exifProfile, ExifTag.XResolution); - double verticalValue = GetExifResolutionValue(exifProfile, ExifTag.YResolution); - - if (horizontalValue > 0 && verticalValue > 0) - { - metadata.HorizontalResolution = horizontalValue; - metadata.VerticalResolution = verticalValue; - metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(exifProfile); - } - - metadata.ExifProfile = exifProfile; - } + case WebpChunkType.Iccp: + ReadIccProfile(stream, metadata, ignoreMetadata, segmentIntegrityHandling, buffer); + break; + case WebpChunkType.Exif: + ReadExifProfile(stream, metadata, ignoreMetadata, segmentIntegrityHandling, buffer); break; case WebpChunkType.Xmp: - byte[] xmpData = new byte[chunkLength]; - bytesRead = stream.Read(xmpData, 0, (int)chunkLength); - if (bytesRead != chunkLength) - { - if (ignoreNone) - { - WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile"); - } - - return; - } - - metadata.XmpProfile ??= new XmpProfile(xmpData); - + ReadXmpProfile(stream, metadata, ignoreMetadata, segmentIntegrityHandling, buffer); break; default: + + // Ignore unknown chunks. + // These must always fall after the image data so we are safe to always skip them. + uint chunkLength = ReadChunkSize(stream, buffer, false); stream.Skip((int)chunkLength); break; } } } + /// + /// Reads the ICCP chunk from the stream. + /// + /// The stream to decode from. + /// The image metadata. + /// If true, metadata will be ignored. + /// Indicates how to handle segment integrity issues. + /// Temporary buffer. + public static void ReadIccProfile( + BufferedReadStream stream, + ImageMetadata metadata, + bool ignoreMetadata, + SegmentIntegrityHandling segmentIntegrityHandling, + Span buffer) + { + // While ICC profiles are optional, an invalid ICC profile cannot be ignored as it must precede the image data + // and since we canot determine its size to allow skipping without reading the chunk size, we have to throw if it's invalid. + // Hence we do not consider segment integrity handling here. + uint iccpChunkSize = ReadChunkSize(stream, buffer); + if (ignoreMetadata || metadata.IccProfile != null) + { + stream.Skip((int)iccpChunkSize); + } + else + { + bool ignoreNone = segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone; + byte[] iccpData = new byte[iccpChunkSize]; + int bytesRead = stream.Read(iccpData, 0, (int)iccpChunkSize); + + // We have the size but the profile is invalid if we cannot read enough data. + // Use the segment integrity handling to determine if we throw. + if (bytesRead != iccpChunkSize && ignoreNone) + { + WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the iccp chunk"); + } + + IccProfile profile = new(iccpData); + if (profile.CheckIsValid()) + { + metadata.IccProfile = profile; + } + } + } + + /// + /// Reads the EXIF profile from the stream. + /// + /// The stream to decode from. + /// The image metadata. + /// If true, metadata will be ignored. + /// Indicates how to handle segment integrity issues. + /// Temporary buffer. + public static void ReadExifProfile( + BufferedReadStream stream, + ImageMetadata metadata, + bool ignoreMetadata, + SegmentIntegrityHandling segmentIntegrityHandling, + Span buffer) + { + bool ignoreNone = segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone; + uint exifChunkSize = ReadChunkSize(stream, buffer, ignoreNone); + if (ignoreMetadata || metadata.ExifProfile != null) + { + stream.Skip((int)exifChunkSize); + } + else + { + byte[] exifData = new byte[exifChunkSize]; + int bytesRead = stream.Read(exifData, 0, (int)exifChunkSize); + if (bytesRead != exifChunkSize) + { + if (ignoreNone) + { + WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile"); + } + + return; + } + + ExifProfile exifProfile = new(exifData); + + // Set the resolution from the metadata. + double horizontalValue = GetExifResolutionValue(exifProfile, ExifTag.XResolution); + double verticalValue = GetExifResolutionValue(exifProfile, ExifTag.YResolution); + + if (horizontalValue > 0 && verticalValue > 0) + { + metadata.HorizontalResolution = horizontalValue; + metadata.VerticalResolution = verticalValue; + metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(exifProfile); + } + + metadata.ExifProfile = exifProfile; + } + } + + /// + /// Reads the XMP profile the stream. + /// + /// The stream to decode from. + /// The image metadata. + /// If true, metadata will be ignored. + /// Indicates how to handle segment integrity issues. + /// Temporary buffer. + public static void ReadXmpProfile( + BufferedReadStream stream, + ImageMetadata metadata, + bool ignoreMetadata, + SegmentIntegrityHandling segmentIntegrityHandling, + Span buffer) + { + bool ignoreNone = segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone; + + uint xmpChunkSize = ReadChunkSize(stream, buffer, ignoreNone); + if (ignoreMetadata || metadata.XmpProfile != null) + { + stream.Skip((int)xmpChunkSize); + } + else + { + byte[] xmpData = new byte[xmpChunkSize]; + int bytesRead = stream.Read(xmpData, 0, (int)xmpChunkSize); + if (bytesRead != xmpChunkSize) + { + if (ignoreNone) + { + WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile"); + } + + return; + } + + metadata.XmpProfile = new XmpProfile(xmpData); + } + } + private static double GetExifResolutionValue(ExifProfile exifProfile, ExifTag tag) { if (exifProfile.TryGetValue(tag, out IExifValue? resolution)) diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 2d06d0e49..fd31a7fad 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -3,15 +3,11 @@ using System.Buffers; using System.Buffers.Binary; -using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.Metadata.Profiles.Icc; -using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; @@ -248,6 +244,7 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable else { // Ignore unknown chunks. + // These must always fall after the image data so we are safe to always skip them. uint chunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, false); stream.Skip((int)chunkSize); } @@ -279,18 +276,20 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable bool ignoreAlpha, Span buffer) { + bool ignoreMetadata = this.skipMetadata; + SegmentIntegrityHandling integrityHandling = this.segmentIntegrityHandling; switch (chunkType) { case WebpChunkType.Iccp: - this.ReadIccProfile(stream, metadata, buffer); + WebpChunkParsingUtils.ReadIccProfile(stream, metadata, ignoreMetadata, integrityHandling, buffer); break; case WebpChunkType.Exif: - this.ReadExifProfile(stream, metadata, buffer); + WebpChunkParsingUtils.ReadExifProfile(stream, metadata, ignoreMetadata, integrityHandling, buffer); break; case WebpChunkType.Xmp: - this.ReadXmpProfile(stream, metadata, buffer); + WebpChunkParsingUtils.ReadXmpProfile(stream, metadata, ignoreMetadata, integrityHandling, buffer); break; case WebpChunkType.AnimationParameter: @@ -319,7 +318,10 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable /// Temporary buffer. private void ParseOptionalChunks(BufferedReadStream stream, ImageMetadata metadata, WebpFeatures features, Span buffer) { - if (this.skipMetadata || (!features.ExifProfile && !features.XmpMetaData)) + bool ignoreMetadata = this.skipMetadata; + SegmentIntegrityHandling integrityHandling = this.segmentIntegrityHandling; + + if (ignoreMetadata || (!features.ExifProfile && !features.XmpMetaData)) { return; } @@ -331,11 +333,11 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable WebpChunkType chunkType = WebpChunkParsingUtils.ReadChunkType(stream, buffer); if (chunkType == WebpChunkType.Exif && metadata.ExifProfile == null) { - this.ReadExifProfile(stream, metadata, buffer); + WebpChunkParsingUtils.ReadExifProfile(stream, metadata, ignoreMetadata, integrityHandling, buffer); } else if (chunkType == WebpChunkType.Xmp && metadata.XmpProfile == null) { - this.ReadXmpProfile(stream, metadata, buffer); + WebpChunkParsingUtils.ReadXmpProfile(stream, metadata, ignoreMetadata, integrityHandling, buffer); } else { @@ -346,127 +348,6 @@ internal sealed class WebpDecoderCore : ImageDecoderCore, IDisposable } } - /// - /// Reads the EXIF profile from the stream. - /// - /// The stream to decode from. - /// The image metadata. - /// Temporary buffer. - private void ReadExifProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) - { - bool ignoreMetadata = this.skipMetadata; - bool ignoreNone = this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone && !ignoreMetadata; - - uint exifChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, ignoreNone); - if (ignoreMetadata) - { - stream.Skip((int)exifChunkSize); - } - else - { - byte[] exifData = new byte[exifChunkSize]; - int bytesRead = stream.Read(exifData, 0, (int)exifChunkSize); - if (bytesRead != exifChunkSize) - { - if (ignoreNone) - { - WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the EXIF profile"); - } - - return; - } - - ExifProfile exifProfile = new(exifData); - - // Set the resolution from the metadata. - double horizontalValue = GetExifResolutionValue(exifProfile, ExifTag.XResolution); - double verticalValue = GetExifResolutionValue(exifProfile, ExifTag.YResolution); - - if (horizontalValue > 0 && verticalValue > 0) - { - metadata.HorizontalResolution = horizontalValue; - metadata.VerticalResolution = verticalValue; - metadata.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(exifProfile); - } - - metadata.ExifProfile = exifProfile; - } - } - - private static double GetExifResolutionValue(ExifProfile exifProfile, ExifTag tag) - { - if (exifProfile.TryGetValue(tag, out IExifValue? resolution)) - { - return resolution.Value.ToDouble(); - } - - return 0; - } - - /// - /// Reads the XMP profile the stream. - /// - /// The stream to decode from. - /// The image metadata. - /// Temporary buffer. - private void ReadXmpProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) - { - bool ignoreMetadata = this.skipMetadata; - bool ignoreNone = this.segmentIntegrityHandling == SegmentIntegrityHandling.IgnoreNone && !ignoreMetadata; - - uint xmpChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer, ignoreNone); - if (ignoreMetadata) - { - stream.Skip((int)xmpChunkSize); - } - else - { - byte[] xmpData = new byte[xmpChunkSize]; - int bytesRead = stream.Read(xmpData, 0, (int)xmpChunkSize); - if (bytesRead != xmpChunkSize) - { - if (ignoreNone) - { - WebpThrowHelper.ThrowImageFormatException("Could not read enough data for the XMP profile"); - } - - return; - } - - metadata.XmpProfile = new XmpProfile(xmpData); - } - } - - /// - /// Reads the ICCP chunk from the stream. - /// - /// The stream to decode from. - /// The image metadata. - /// Temporary buffer. - private void ReadIccProfile(BufferedReadStream stream, ImageMetadata metadata, Span buffer) - { - uint iccpChunkSize = WebpChunkParsingUtils.ReadChunkSize(stream, buffer); - if (this.skipMetadata) - { - stream.Skip((int)iccpChunkSize); - } - else - { - byte[] iccpData = new byte[iccpChunkSize]; - int bytesRead = stream.Read(iccpData, 0, (int)iccpChunkSize); - if (bytesRead != iccpChunkSize) - { - WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the iccp chunk"); - } - - IccProfile profile = new(iccpData); - if (profile.CheckIsValid()) - { - metadata.IccProfile = profile; - } - } - } - /// /// Reads the animation parameters chunk from the stream. /// From 958aea050d747f1b6d5235e80eaabc425f49521c Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 21 Nov 2025 07:21:56 +0100 Subject: [PATCH 25/32] Use a more tolerant comparer --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 5df730cb1..13e739e4f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -360,7 +360,9 @@ public class TiffDecoderTests : TiffDecoderBaseTester { using Image image = provider.GetImage(TiffDecoder.Instance); image.DebugSave(provider); - image.CompareToReferenceOutput(ImageComparer.Tolerant(), provider); + + // ARM reports a 0.0000% difference, so we use a tolerant comparer here. + image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.0001F), provider); } [Theory] From c82b1f7908889195f616e118a732be66600c8d5e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 16:45:01 +1000 Subject: [PATCH 26/32] Explicitly handle missing SOS marker. Fix #2948 --- .../Formats/Jpeg/JpegDecoderCore.cs | 19 ++++++++++++++++++ .../Formats/Jpg/JpegDecoderTests.cs | 20 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Jpg/issues/issue-2948-sos.jpg | 3 +++ 4 files changed, 43 insertions(+) create mode 100644 tests/Images/Input/Jpg/issues/issue-2948-sos.jpg diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 0ad78b903..7825955e7 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -71,6 +71,11 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData /// private bool hasAdobeMarker; + /// + /// Whether the image has a SOS marker. + /// + private bool hasSOSMarker; + /// /// Contains information about the JFIF marker. /// @@ -197,6 +202,12 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData { using SpectralConverter spectralConverter = new(this.configuration, this.resizeMode == JpegDecoderResizeMode.ScaleOnly ? null : this.Options.TargetSize); this.ParseStream(stream, spectralConverter, cancellationToken); + + if (!this.hasSOSMarker) + { + JpegThrowHelper.ThrowInvalidImageContentException("Missing SOS marker."); + } + this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); @@ -215,6 +226,12 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { this.ParseStream(stream, spectralConverter: null, cancellationToken); + + if (!this.hasSOSMarker) + { + JpegThrowHelper.ThrowInvalidImageContentException("Missing SOS marker."); + } + this.InitExifProfile(); this.InitIccProfile(); this.InitIptcProfile(); @@ -403,6 +420,8 @@ internal sealed class JpegDecoderCore : ImageDecoderCore, IRawJpegData break; case JpegConstants.Markers.SOS: + + this.hasSOSMarker = true; if (!metadataOnly) { this.ProcessStartOfScanMarker(stream, markerContentByteSize); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 71753bf9c..6dd26cdcb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -417,4 +417,24 @@ public partial class JpegDecoderTests image.DebugSave(provider); image.CompareToReferenceOutput(provider); } + + // https://github.com/SixLabors/ImageSharp/issues/2948 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2948, PixelTypes.Rgb24)] + public void Issue2948_No_SOS_Decode_Throws_InvalidImageContentException(TestImageProvider provider) + where TPixel : unmanaged, IPixel + => Assert.Throws(() => + { + using Image image = provider.GetImage(JpegDecoder.Instance); + }); + + // https://github.com/SixLabors/ImageSharp/issues/2948 + [Theory] + [InlineData(TestImages.Jpeg.Issues.Issue2948)] + public void Issue2948_No_SOS_Identify_Throws_InvalidImageContentException(string imagePath) + => Assert.Throws(() => + { + TestFile testFile = TestFile.Create(imagePath); + ImageInfo imageInfo = Image.Identify(testFile.Bytes); + }); } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 94dcd2b7b..bc699da88 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -348,6 +348,7 @@ public static class TestImages public const string Issue2638 = "Jpg/issues/Issue2638.jpg"; public const string Issue2758 = "Jpg/issues/issue-2758.jpg"; public const string Issue2857 = "Jpg/issues/issue-2857-subsub-ifds.jpg"; + public const string Issue2948 = "Jpg/issues/issue-2948-sos.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg b/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg new file mode 100644 index 000000000..d210e87e5 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2948-sos.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ad9c3a6babb41c5aa2a46fbfe74ed5482028c73f48531cf144e1e324ca7988b3 +size 103789 From 143c3bb4f2f9a66e130a40f998dd757bc51d18b9 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 21 Nov 2025 17:15:53 +1000 Subject: [PATCH 27/32] Cleanup --- .github/workflows/build-and-test.yml | 6 ------ tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs | 6 +----- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3e3d6d0e2..41136bfd8 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -62,8 +62,6 @@ jobs: needs: WarmLFS strategy: matrix: - isARM: - - ${{ contains(github.event.pull_request.labels.*.name, 'arch:arm32') || contains(github.event.pull_request.labels.*.name, 'arch:arm64') }} options: - os: ubuntu-latest framework: net9.0 @@ -121,10 +119,6 @@ jobs: sdk: 8.0.x runtime: -x64 codecov: false - exclude: - - isARM: false - options: - os: buildjet-4vcpu-ubuntu-2204-arm runs-on: ${{ matrix.options.os }} diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 6dd26cdcb..3fd55eb91 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -432,9 +432,5 @@ public partial class JpegDecoderTests [Theory] [InlineData(TestImages.Jpeg.Issues.Issue2948)] public void Issue2948_No_SOS_Identify_Throws_InvalidImageContentException(string imagePath) - => Assert.Throws(() => - { - TestFile testFile = TestFile.Create(imagePath); - ImageInfo imageInfo = Image.Identify(testFile.Bytes); - }); + => Assert.Throws(() => _ = Image.Identify(TestFile.Create(imagePath).Bytes)); } From fc211a7aa7e4360112290c1a52b3addc445a86ee Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 21 Nov 2025 14:22:16 +0100 Subject: [PATCH 28/32] Update Intrinsic usage --- .../Common/SimdUtilsTests.Shuffle.cs | 10 +++++----- tests/ImageSharp.Tests/Common/SimdUtilsTests.cs | 2 +- .../ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs | 4 ++-- tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs | 13 ++++++------- .../Formats/Jpg/JpegColorConverterTests.cs | 12 ++++-------- .../Formats/Png/PngDecoderFilterTests.cs | 2 +- .../Formats/Png/PngEncoderFilterTests.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 2 +- .../Formats/WebP/ColorSpaceTransformUtilsTests.cs | 4 ++-- .../Formats/WebP/LosslessUtilsTests.cs | 14 +++++++------- .../Formats/WebP/PredictorEncoderTests.cs | 4 ++-- .../ImageSharp.Tests/Formats/WebP/QuantEncTests.cs | 2 +- .../Formats/WebP/Vp8ResidualTests.cs | 2 +- .../Formats/WebP/WebpCommonUtilsTests.cs | 4 ++-- .../Formats/WebP/YuvConversionTests.cs | 2 +- .../Processors/Convolution/BokehBlurTest.cs | 12 ++++++------ 16 files changed, 43 insertions(+), 48 deletions(-) diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs index 2a7616570..81daac3e0 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.Shuffle.cs @@ -292,7 +292,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -352,7 +352,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -394,7 +394,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -436,7 +436,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -478,7 +478,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } private static void TestShuffleFloat4Channel( diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index eeaf93645..0a9bb063e 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -133,7 +133,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index e1d4feaa7..368a7b369 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -267,7 +267,7 @@ public partial class Block8x8FTests : JpegFixture RunTest, srcSeed, qtSeed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Fact] @@ -462,7 +462,7 @@ public partial class Block8x8FTests : JpegFixture // 3. DisableAvx2 - call fallback code of float implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 7d7b05a28..50eada4c7 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -152,7 +152,7 @@ public static class DCTTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } [Theory] @@ -352,15 +352,14 @@ public static class DCTTests Assert.Equal(expectedDest, actualDest, new ApproximateFloatComparer(1f)); } - // 4 paths: - // 1. AllowAll - call avx/fma implementation - // 2. DisableFMA - call avx without fma implementation - // 3. DisableAvx - call Vector4 implementation - // 4. DisableHWIntrinsic - call scalar fallback implementation + // 3 paths: + // 1. AllowAll - call avx implementation + // 2. DisableAvx - call Vector4 implementation + // 3. DisableHWIntrinsic - call scalar fallback implementation FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, seed, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); } } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 5ea9e4d78..0137709d8 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -19,12 +19,8 @@ public class JpegColorConverterTests private const int TestBufferLength = 40; - private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2; - private static readonly ApproximateColorProfileComparer ColorSpaceComparer = new(epsilon: Precision); - private static readonly ColorProfileConverter ColorSpaceConverter = new(); - public static readonly TheoryData Seeds = new() { 1, 2, 3 }; public JpegColorConverterTests(ITestOutputHelper output) @@ -73,7 +69,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -106,7 +102,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -139,7 +135,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -172,7 +168,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs index 99406be2f..6616b0477 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderFilterTests.cs @@ -171,7 +171,7 @@ public class PngDecoderFilterTests public void PaethFilter_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.AllowAll); [Fact] - public void PaethFilter_WithoutSsse3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableSSE42); + public void PaethFilter_WithoutSsse3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void PaethFilter_WithoutHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPaethFilterTest, HwIntrinsics.DisableHWIntrinsic); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs index 736c8b4c0..3a31b395f 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderFilterTests.cs @@ -51,7 +51,7 @@ public class PngEncoderFilterTests : MeasureFixture FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); } [Fact] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index ca01655a0..4ebcbc13b 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -618,7 +618,7 @@ public partial class PngEncoderTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.DisableSSE42, + HwIntrinsics.DisableHWIntrinsic, provider); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs index 06249090d..9657859e0 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/ColorSpaceTransformUtilsTests.cs @@ -71,7 +71,7 @@ public class ColorSpaceTransformUtilsTests public void CollectColorBlueTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.AllowAll); [Fact] - public void CollectColorBlueTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableSSE42); + public void CollectColorBlueTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void CollectColorBlueTransforms_WithoutVector256_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorBlueTransformsTest, HwIntrinsics.DisableAVX2); @@ -80,7 +80,7 @@ public class ColorSpaceTransformUtilsTests public void CollectColorRedTransforms_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.AllowAll); [Fact] - public void CollectColorRedTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableSSE42); + public void CollectColorRedTransforms_WithoutVector128_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void CollectColorRedTransforms_WithoutVector256_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCollectColorRedTransformsTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs index d2429e71f..4dcccb1a3 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/LosslessUtilsTests.cs @@ -304,19 +304,19 @@ public class LosslessUtilsTests public void Predictor11_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableSSE42); + public void Predictor11_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor11Test, HwIntrinsics.DisableHWIntrinsic); [Fact] public void Predictor12_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableSSE42); + public void Predictor12_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor12Test, HwIntrinsics.DisableHWIntrinsic); [Fact] public void Predictor13_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.AllowAll); [Fact] - public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableSSE42); + public void Predictor13_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunPredictor13Test, HwIntrinsics.DisableHWIntrinsic); [Fact] public void SubtractGreen_Works() => RunSubtractGreenTest(); @@ -331,7 +331,7 @@ public class LosslessUtilsTests public void SubtractGreen_Scalar_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableHWIntrinsic); [Fact] - public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + public void SubtractGreen_WithoutAvxOrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSubtractGreenTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); [Fact] public void AddGreenToBlueAndRed_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.AllowAll); @@ -340,13 +340,13 @@ public class LosslessUtilsTests public void AddGreenToBlueAndRed_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2); [Fact] - public void AddGreenToBlueAndRed_WithoutAVX2OrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + public void AddGreenToBlueAndRed_WithoutAVX2OrSSSE3_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunAddGreenToBlueAndRedTest, HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); [Fact] public void TransformColor_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.AllowAll); [Fact] - public void TransformColor_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableSSE42); + public void TransformColor_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void TransformColor_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorTest, HwIntrinsics.DisableAVX2); @@ -355,7 +355,7 @@ public class LosslessUtilsTests public void TransformColorInverse_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.AllowAll); [Fact] - public void TransformColorInverse_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableSSE42); + public void TransformColorInverse_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void TransformColorInverse_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunTransformColorInverseTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index 4ab1d019b..16990c357 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -24,7 +24,7 @@ public class PredictorEncoderTests [Fact] public void ColorSpaceTransform_WithPeakImage_WithoutSSE41_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.DisableSSE42); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithPeakImage_ProducesExpectedData, HwIntrinsics.DisableHWIntrinsic); [Fact] public void ColorSpaceTransform_WithBikeImage_WithHardwareIntrinsics_Works() @@ -32,7 +32,7 @@ public class PredictorEncoderTests [Fact] public void ColorSpaceTransform_WithBikeImage_WithoutSSE41_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableSSE42); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(ColorSpaceTransform_WithBikeImage_ProducesExpectedData, HwIntrinsics.DisableHWIntrinsic); [Fact] public void ColorSpaceTransform_WithBikeImage_WithoutAvx2_Works() diff --git a/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs b/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs index 54a80c29f..3808ba6d0 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/QuantEncTests.cs @@ -45,7 +45,7 @@ public class QuantEncTests public void QuantizeBlock_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.AllowAll); [Fact] - public void QuantizeBlock_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableSSE42); + public void QuantizeBlock_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void QuantizeBlock_WithoutAVX2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunQuantizeBlockTest, HwIntrinsics.DisableAVX2); diff --git a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs index c00460471..2a0821108 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/Vp8ResidualTests.cs @@ -252,5 +252,5 @@ public class Vp8ResidualTests public void SetCoeffsTest_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.AllowAll); [Fact] - public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableSSE42); + public void SetCoeffsTest_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunSetCoeffsTest, HwIntrinsics.DisableHWIntrinsic); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs index 13a204bce..3962d4f37 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpCommonUtilsTests.cs @@ -23,7 +23,7 @@ public class WebpCommonUtilsTests [Fact] public void CheckNonOpaque_WithOpaquePixels_WithoutSse2_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.DisableSSE42); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithOpaquePixelsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void CheckNonOpaque_WithOpaquePixels_WithoutAvx2_Works() @@ -35,7 +35,7 @@ public class WebpCommonUtilsTests [Fact] public void CheckNonOpaque_WithNoneOpaquePixels_WithoutSse2_Works() - => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableSSE42); + => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunCheckNoneOpaqueWithNoneOpaquePixelsTest, HwIntrinsics.DisableHWIntrinsic); [Fact] public void CheckNonOpaque_WithNoneOpaquePixels_WithoutAvx2_Works() diff --git a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs index 5e34f1221..482c41b25 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/YuvConversionTests.cs @@ -32,7 +32,7 @@ public class YuvConversionTests public void UpSampleYuvToRgb_WithHardwareIntrinsics_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.AllowAll); [Fact] - public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableSSE42); + public void UpSampleYuvToRgb_WithoutSSE2_Works() => FeatureTestRunner.RunWithHwIntrinsicsFeature(RunUpSampleYuvToRgbTest, HwIntrinsics.DisableHWIntrinsic); [Theory] [WithFile(TestImages.Webp.Yuv, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs index bed2cdbd6..3d4c239c9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BokehBlurTest.cs @@ -50,7 +50,7 @@ public class BokehBlurTest List components = new(); foreach (Match match in Regex.Matches(Components10x2, @"\[\[(.*?)\]\]", RegexOptions.Singleline)) { - string[] values = match.Groups[1].Value.Trim().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + string[] values = match.Groups[1].Value.Trim().Split([' '], StringSplitOptions.RemoveEmptyEntries); Complex64[] component = values.Select( value => { @@ -114,12 +114,12 @@ public class BokehBlurTest }; public static readonly string[] TestFiles = - { - TestImages.Png.CalliphoraPartial, + [ + TestImages.Png.CalliphoraPartial, TestImages.Png.Bike, TestImages.Png.BikeGrayscale, - TestImages.Png.Cross, - }; + TestImages.Png.Cross + ]; [Theory] [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32)] @@ -148,7 +148,7 @@ public class BokehBlurTest [Theory] [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.AllowAll)] - [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableSSE42)] + [WithFileCollection(nameof(TestFiles), nameof(BokehBlurValues), PixelTypes.Rgba32, HwIntrinsics.DisableHWIntrinsic)] public void BokehBlurFilterProcessor_Bounded(TestImageProvider provider, BokehBlurInfo value, HwIntrinsics intrinsicsFilter) { static void RunTest(string arg1, string arg2) From a4765d1ff9bdc635f1ad3e301f4e42b93447c4ab Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 21 Nov 2025 14:26:56 +0100 Subject: [PATCH 29/32] Use .NET10 for build and not .net9 --- .github/workflows/build-and-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 64a05db83..99ca63fc3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -76,8 +76,8 @@ jobs: runtime: -x64 codecov: false - os: macos-26 - framework: net9.0 - sdk: 9.0.x + framework: net10.0 + sdk: 10.0.x sdk-preview: true runtime: -x64 codecov: false From 284e40e3dbd305d02ee7a8f03289b152ec250e5c Mon Sep 17 00:00:00 2001 From: Stefan Nikolei Date: Fri, 21 Nov 2025 22:36:18 +0100 Subject: [PATCH 30/32] Disable SSE42 --- tests/ImageSharp.Tests/Common/SimdUtilsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index 0a9bb063e..eeaf93645 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -133,7 +133,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); } [Theory] From 766175ff06371286a75f132568ddbcaad29a3bf3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 22 Nov 2025 12:17:21 +1000 Subject: [PATCH 31/32] Fix tests --- tests/ImageSharp.Tests/Common/SimdUtilsTests.cs | 2 +- .../Formats/Jpg/JpegColorConverterTests.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs index eeaf93645..e39f9456f 100644 --- a/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs +++ b/tests/ImageSharp.Tests/Common/SimdUtilsTests.cs @@ -133,7 +133,7 @@ public partial class SimdUtilsTests FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, count, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2); } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index 0137709d8..8d94fb5cf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -102,7 +102,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -135,7 +135,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -168,7 +168,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { @@ -201,7 +201,7 @@ public class JpegColorConverterTests { FeatureTestRunner.RunWithHwIntrinsicsFeature( RunTest, - HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableSSE42 | HwIntrinsics.DisableHWIntrinsic); + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512 | HwIntrinsics.DisableAVX2 | HwIntrinsics.DisableHWIntrinsic); static void RunTest(string arg) { From ea4ba7a7daab9be4731588e54a0f7ddd178cd391 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 22 Nov 2025 12:29:14 +1000 Subject: [PATCH 32/32] Update TiffDecoderTests.cs --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 13e739e4f..d850c67a5 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -785,7 +785,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester // ImageMagick cannot decode this image. image.DebugSave(provider); image.CompareToReferenceOutput( - ImageComparer.TolerantPercentage(0.0018F), // NET 9+ Uses zlib-ng to decompress, which manages to decode 2 extra pixels. + ImageComparer.TolerantPercentage(0.0034F), // NET 10 Uses zlib-ng to decompress, which manages to decode 3 extra pixels. provider, appendPixelTypeToFileName: false); }