Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Matrix3x2 transform methods #48195

Merged
merged 6 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
JeremyKuhne marked this conversation as resolved.
Show resolved Hide resolved
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,53 @@ 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));
Gdip.CheckStatus(Gdip.GdipGetWorldTransform(
new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix)));
JeremyKuhne marked this conversation as resolved.
Show resolved Hide resolved

try
{
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
Expand Down Expand Up @@ -94,7 +94,7 @@
Link="Common\System\IO\TempFile.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common.TestData" Version="$(SystemDrawingCommonTestDataVersion)" GeneratePathProperty="true" />
<PackageReference Include="System.Drawing.Common.TestData" Version="$(SystemDrawingCommonTestDataVersion)" GeneratePathProperty="true" />
<PackageReference Include="System.ComponentModel.TypeConverter.TestData" Version="$(SystemComponentModelTypeConverterTestDataVersion)" />
<PackageReference Include="System.Windows.Extensions.TestData" Version="$(SystemWindowsExtensionsTestDataVersion)" />
<EmbeddedResource Include="$(PkgSystem_Drawing_Common_TestData)\contentFiles\any\any\bitmaps\48x48_multiple_entries_4bit.ico">
Expand All @@ -117,6 +117,8 @@
<ProjectReference Include="$(LibrariesProjectRoot)Microsoft.Win32.SystemEvents\src\Microsoft.Win32.SystemEvents.csproj" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('$(NetCoreAppCurrent)'))">
<Compile Include="GraphicsTests.Core.cs" />
<Compile Include="Drawing2D\MatrixTests.Core.cs" />
<ProjectReference Include="..\src\System.Drawing.Common.csproj" />
</ItemGroup>
</Project>