Skip to content

Commit

Permalink
Add Matrix3x2 transform methods (#48195)
Browse files Browse the repository at this point in the history
* Add Matrix3x2 transform methods

This implements #47940 for System.Drawing.Common.

* Move GdipGetWorldTransform into try block.

* Fix build issues

* Add workaround to nuget issue

Co-authored-by: Santiago Fernandez Madero <safern@microsoft.com>
  • Loading branch information
JeremyKuhne and safern committed Mar 4, 2021
1 parent 1e1b5cf commit 667b4e7
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or
'$(TargetFramework)' == 'net461'">
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" />
<!-- PrivateAssets=all is a workaround to issue: https://github.com/NuGet/Home/issues/10617 -->
<PackageReference Include="System.Memory" Version="$(SystemMemoryVersion)" PrivateAssets="all" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ internal Graphics() { }
public int TextContrast { get { throw null; } set { } }
public System.Drawing.Text.TextRenderingHint TextRenderingHint { get { throw null; } set { } }
public System.Drawing.Drawing2D.Matrix Transform { get { throw null; } set { } }
public System.Numerics.Matrix3x2 TransformElements { get { throw null; } set { } }
public System.Drawing.RectangleF VisibleClipBounds { get { throw null; } }
public void AddMetafileComment(byte[] data) { }
public System.Drawing.Drawing2D.GraphicsContainer BeginContainer() { throw null; }
Expand Down Expand Up @@ -1653,7 +1654,9 @@ public Matrix() { }
public Matrix(System.Drawing.Rectangle rect, System.Drawing.Point[] plgpts) { }
public Matrix(System.Drawing.RectangleF rect, System.Drawing.PointF[] plgpts) { }
public Matrix(float m11, float m12, float m21, float m22, float dx, float dy) { }
public Matrix(System.Numerics.Matrix3x2 matrix) { }
public float[] Elements { get { throw null; } }
public System.Numerics.Matrix3x2 MatrixElements { get { throw null; } set { } }
public bool IsIdentity { get { throw null; } }
public bool IsInvertible { get { throw null; } }
public float OffsetX { get { throw null; } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ProjectReference Include="..\..\System.ComponentModel.Primitives\ref\System.ComponentModel.Primitives.csproj" />
<ProjectReference Include="..\..\System.ComponentModel.TypeConverter\ref\System.ComponentModel.TypeConverter.csproj" />
<ProjectReference Include="..\..\System.Drawing.Primitives\ref\System.Drawing.Primitives.csproj" />
<ProjectReference Include="..\..\System.Numerics.Vectors\ref\System.Numerics.Vectors.csproj" />
<ProjectReference Include="..\..\System.ObjectModel\ref\System.ObjectModel.csproj" />
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
<ProjectReference Include="..\..\System.Runtime.Extensions\ref\System.Runtime.Extensions.csproj" />
Expand All @@ -26,6 +27,7 @@
<Reference Include="System.ComponentModel.TypeConverter" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Drawing.Primitives" />
<Reference Include="System.Numerics.Vectors" />
<Reference Include="System.ObjectModel" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
<Reference Include="System.Drawing.Primitives" />
<Reference Include="System.IO.FileSystem" />
<Reference Include="System.Memory" />
<Reference Include="System.Numerics.Vectors" />
<Reference Include="System.ObjectModel" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.InteropServices;
using Gdip = System.Drawing.SafeNativeMethods.Gdip;

Expand All @@ -24,11 +25,33 @@ public Matrix(float m11, float m12, float m21, float m22, float dx, float dy)
NativeMatrix = nativeMatrix;
}

/// <summary>
/// Construct a <see cref="Matrix"/> utilizing the given <paramref name="matrix"/>.
/// </summary>
/// <param name="matrix">Matrix data to construct from.</param>
public Matrix(Matrix3x2 matrix) : this(CreateNativeHandle(matrix))
{
}

private Matrix(IntPtr nativeMatrix)
{
NativeMatrix = nativeMatrix;
}

internal static IntPtr CreateNativeHandle(Matrix3x2 matrix)
{
Gdip.CheckStatus(Gdip.GdipCreateMatrix2(
matrix.M11,
matrix.M12,
matrix.M21,
matrix.M22,
matrix.M31,
matrix.M32,
out IntPtr nativeMatrix));

return nativeMatrix;
}

public unsafe Matrix(RectangleF rect, PointF[] plgpts)
{
if (plgpts == null)
Expand Down Expand Up @@ -93,6 +116,30 @@ public float[] Elements
}
}

/// <summary>
/// Gets/sets the elements for the matrix.
/// </summary>
public unsafe Matrix3x2 MatrixElements
{
get
{
Matrix3x2 matrix = default;
Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(this, NativeMatrix), (float*)&matrix));
return matrix;
}
set
{
Gdip.CheckStatus(Gdip.GdipSetMatrixElements(
new HandleRef(this, NativeMatrix),
value.M11,
value.M12,
value.M21,
value.M22,
value.M31,
value.M32));
}
}

internal unsafe void GetElements(Span<float> elements)
{
Debug.Assert(elements.Length >= 6);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Numerics;
using System.Runtime.InteropServices;
using Gdip = System.Drawing.SafeNativeMethods.Gdip;

Expand Down Expand Up @@ -332,6 +332,54 @@ public Matrix Transform
}
}

/// <summary>
/// Gets or sets the world transform elements for this <see cref="Graphics"/>.
/// </summary>
/// <remarks>
/// This is a more performant alternative to <see cref="Transform"/> that does not need disposal.
/// </remarks>
public unsafe Matrix3x2 TransformElements
{
get
{
Gdip.CheckStatus(Gdip.GdipCreateMatrix(out IntPtr nativeMatrix));

try
{
Gdip.CheckStatus(Gdip.GdipGetWorldTransform(
new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix)));

Matrix3x2 matrix = default;
Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(null, nativeMatrix), (float*)&matrix));
return matrix;
}
finally
{
if (nativeMatrix != IntPtr.Zero)
{
Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix));
}
}
}
set
{
IntPtr nativeMatrix = Matrix.CreateNativeHandle(value);

try
{
Gdip.CheckStatus(Gdip.GdipSetWorldTransform(
new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix)));
}
finally
{
if (nativeMatrix != IntPtr.Zero)
{
Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix));
}
}
}
}

public IntPtr GetHdc()
{
IntPtr hdc = IntPtr.Zero;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Numerics;
using Xunit;

namespace System.Drawing.Drawing2D.Tests
{
public partial class MatrixTests
{
[ConditionalTheory(Helpers.IsDrawingSupported)]
[MemberData(nameof(MatrixElements_TestData))]
public void Ctor_Matrix3x2(float m11, float m12, float m21, float m22, float dx, float dy, bool isIdentity, bool isInvertible)
{
Matrix3x2 matrix3X2 = new Matrix3x2(m11, m12, m21, m22, dx, dy);
using (var matrix = new Matrix(matrix3X2))
{
Assert.Equal(new float[] { m11, m12, m21, m22, dx, dy }, matrix.Elements);
Assert.Equal(matrix3X2, matrix.MatrixElements);
Assert.Equal(isIdentity, matrix.IsIdentity);
Assert.Equal(isInvertible, matrix.IsInvertible);
Assert.Equal(dx, matrix.OffsetX);
Assert.Equal(dy, matrix.OffsetY);
}
}

[ConditionalTheory(Helpers.IsDrawingSupported)]
[MemberData(nameof(MatrixElements_TestData))]
public void MatrixElements_RoundTrip(float m11, float m12, float m21, float m22, float dx, float dy, bool isIdentity, bool isInvertible)
{
Matrix3x2 matrix3X2 = new Matrix3x2(m11, m12, m21, m22, dx, dy);
using (var matrix = new Matrix())
{
matrix.MatrixElements = matrix3X2;
Assert.Equal(new float[] { m11, m12, m21, m22, dx, dy }, matrix.Elements);
Assert.Equal(matrix3X2, matrix.MatrixElements);
Assert.Equal(isIdentity, matrix.IsIdentity);
Assert.Equal(isInvertible, matrix.IsInvertible);
Assert.Equal(dx, matrix.OffsetX);
Assert.Equal(dy, matrix.OffsetY);
}
}
}
}
36 changes: 21 additions & 15 deletions src/libraries/System.Drawing.Common/tests/Drawing2D/MatrixTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

namespace System.Drawing.Drawing2D.Tests
{
public class MatrixTests
public partial class MatrixTests
{
private static Matrix CreateDisposedMatrix()
{
Expand Down Expand Up @@ -67,20 +67,7 @@ public void Ctor_FloatingPointBoundsInElements(float f)
}

[ConditionalTheory(Helpers.IsDrawingSupported)]
[InlineData(1, 0, 0, 1, 0, 0, true, true)]
[InlineData(0, 1, 2, 1, 3, 4, false, true)]
[InlineData(0, 0, 0, 0, 0, 0, false, false)]
[InlineData(1, 2, 3, 4, 5, 6, false, true)]
[InlineData(-1, -2, -3, -4, -5, -6, false, true)]
[InlineData(123, 24, 82, 16, 47, 30, false, false)]
[InlineData(156, 46, 0, 0, 106, 19, false, false)]
[InlineData(146, 66, 158, 104, 42, 150, false, true)]
[InlineData(119, 140, 145, 74, 102, 58, false, true)]
[InlineData(1.1f, 0.1f, -0.1f, 0.9f, 0, 0, false, true)]
[InlineData(1.01f, 0.01f, -0.01f, 0.99f, 0, 0, false, true)]
[InlineData(1.001f, 0.001f, -0.001f, 0.999f, 0, 0, false, true)]
[InlineData(1.0001f, 0.0001f, -0.0001f, 0.9999f, 0, 0, true, true)]
[InlineData(1.0009f, 0.0009f, -0.0009f, 0.99995f, 0, 0, false, true)]
[MemberData(nameof(MatrixElements_TestData))]
public void Ctor_Elements(float m11, float m12, float m21, float m22, float dx, float dy, bool isIdentity, bool isInvertible)
{
using (var matrix = new Matrix(m11, m12, m21, m22, dx, dy))
Expand All @@ -93,6 +80,25 @@ public void Ctor_Elements(float m11, float m12, float m21, float m22, float dx,
}
}

public static TheoryData<float, float, float, float, float, float, bool, bool> MatrixElements_TestData
=> new TheoryData<float, float, float, float, float, float, bool, bool>
{
{ 1, 0, 0, 1, 0, 0, true, true },
{ 0, 1, 2, 1, 3, 4, false, true },
{ 0, 0, 0, 0, 0, 0, false, false },
{ 1, 2, 3, 4, 5, 6, false, true },
{ -1, -2, -3, -4, -5, -6, false, true },
{ 123, 24, 82, 16, 47, 30, false, false },
{ 156, 46, 0, 0, 106, 19, false, false },
{ 146, 66, 158, 104, 42, 150, false, true },
{ 119, 140, 145, 74, 102, 58, false, true },
{ 1.1f, 0.1f, -0.1f, 0.9f, 0, 0, false, true },
{ 1.01f, 0.01f, -0.01f, 0.99f, 0, 0, false, true },
{ 1.001f, 0.001f, -0.001f, 0.999f, 0, 0, false, true },
{ 1.0001f, 0.0001f, -0.0001f, 0.9999f, 0, 0, true, true },
{ 1.0009f, 0.0009f, -0.0009f, 0.99995f, 0, 0, false, true }
};

public static IEnumerable<object[]> Ctor_Rectangle_Points_TestData()
{
yield return new object[] { new Rectangle(1, 4, 8, 16), new Point[] { new Point(32, 64), new Point(128, 256), new Point(512, 1024) }, new float[] { 12, 24, 30, 60, -100, -200 }, false, false };
Expand Down
81 changes: 81 additions & 0 deletions src/libraries/System.Drawing.Common/tests/GraphicsTests.Core.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Drawing.Drawing2D;
using System.Numerics;
using Xunit;

namespace System.Drawing.Tests
{
public partial class GraphicsTests
{
private static Matrix3x2 s_testMatrix = Matrix3x2.CreateRotation(45) * Matrix3x2.CreateScale(2) * Matrix3x2.CreateTranslation(new Vector2(10, 20));


[ConditionalFact(Helpers.IsDrawingSupported)]
public void TransformElements_SetNonInvertibleMatrix_ThrowsArgumentException()
{
using (var image = new Bitmap(5, 5))
using (Graphics graphics = Graphics.FromImage(image))
{
Matrix3x2 matrix = new Matrix3x2(123, 24, 82, 16, 47, 30);
AssertExtensions.Throws<ArgumentException>(null, () => graphics.TransformElements = matrix);
}
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)]
[ConditionalFact(Helpers.IsDrawingSupported)]
public void TransformElements_GetSetWhenBusy_ThrowsInvalidOperationException()
{
using (var image = new Bitmap(10, 10))
using (Graphics graphics = Graphics.FromImage(image))
{
graphics.GetHdc();
try
{
Assert.Throws<InvalidOperationException>(() => graphics.TransformElements);
Assert.Throws<InvalidOperationException>(() => graphics.TransformElements = Matrix3x2.Identity);
}
finally
{
graphics.ReleaseHdc();
}
}
}

[ConditionalFact(Helpers.IsDrawingSupported)]
public void TransformElements_GetSetWhenDisposed_ThrowsArgumentException()
{
using (var image = new Bitmap(10, 10))
{
Graphics graphics = Graphics.FromImage(image);
graphics.Dispose();

AssertExtensions.Throws<ArgumentException>(null, () => graphics.TransformElements);
AssertExtensions.Throws<ArgumentException>(null, () => graphics.TransformElements = Matrix3x2.Identity);
}
}

[ConditionalFact(Helpers.IsDrawingSupported)]
public void TransformElements_RoundTrip()
{
using (var image = new Bitmap(10, 10))
using (Graphics graphics = Graphics.FromImage(image))
{
graphics.TransformElements = s_testMatrix;
Assert.Equal(s_testMatrix, graphics.TransformElements);

using (Matrix matrix = graphics.Transform)
{
Assert.Equal(s_testMatrix, matrix.MatrixElements);
}

using (Matrix matrix = new Matrix())
{
graphics.Transform = matrix;
Assert.True(graphics.TransformElements.IsIdentity);
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/libraries/System.Drawing.Common/tests/GraphicsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace System.Drawing.Tests
{
public class GraphicsTests
public partial class GraphicsTests
{
[ActiveIssue("https://github.com/dotnet/runtime/issues/22221", TestPlatforms.AnyUnix)]
[ConditionalFact(Helpers.IsDrawingSupported)]
Expand Down
Loading

0 comments on commit 667b4e7

Please sign in to comment.