Matrix
Encapsulates a 3-by-3 affine matrix that represents a geometric transform. This class cannot be inherited.
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Graphics.GdiPlus;
namespace System.Drawing.Drawing2D
{
public sealed class Matrix : MarshalByRefObject, IDisposable
{
internal unsafe global::Windows.Win32.Graphics.GdiPlus.Matrix* NativeMatrix { get; set; }
[Nullable(1)]
public float[] Elements {
[NullableContext(1)]
get {
float[] array = new float[6];
GetElements(array);
return array;
}
}
public unsafe Matrix3x2 MatrixElements {
get {
Matrix3x2 result = default(Matrix3x2);
PInvoke.GdipGetMatrixElements(NativeMatrix, (float*)(&result)).ThrowIfFailed();
GC.KeepAlive(this);
return result;
}
set {
PInvoke.GdipSetMatrixElements(NativeMatrix, value.M11, value.M12, value.M21, value.M22, value.M31, value.M32).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public float OffsetX => Offset.X;
public float OffsetY => Offset.Y;
internal unsafe PointF Offset {
get {
Span<float> elements = new Span<float>(stackalloc byte[24], 6);
GetElements(elements);
return new PointF(elements[4], elements[5]);
}
}
public unsafe bool IsInvertible {
get {
BOOL value = default(BOOL);
PInvoke.GdipIsMatrixInvertible(NativeMatrix, &value).ThrowIfFailed();
GC.KeepAlive(this);
return value;
}
}
public unsafe bool IsIdentity {
get {
BOOL value = default(BOOL);
PInvoke.GdipIsMatrixIdentity(NativeMatrix, &value).ThrowIfFailed();
GC.KeepAlive(this);
return value;
}
}
public unsafe Matrix()
{
global::Windows.Win32.Graphics.GdiPlus.Matrix* nativeMatrix = default(global::Windows.Win32.Graphics.GdiPlus.Matrix*);
PInvoke.GdipCreateMatrix(&nativeMatrix).ThrowIfFailed();
NativeMatrix = nativeMatrix;
}
public unsafe Matrix(float m11, float m12, float m21, float m22, float dx, float dy)
{
global::Windows.Win32.Graphics.GdiPlus.Matrix* nativeMatrix = default(global::Windows.Win32.Graphics.GdiPlus.Matrix*);
PInvoke.GdipCreateMatrix2(m11, m12, m21, m22, dx, dy, &nativeMatrix).ThrowIfFailed();
NativeMatrix = nativeMatrix;
}
public unsafe Matrix(Matrix3x2 matrix)
: this(CreateNativeHandle(matrix))
{
}
private unsafe Matrix(global::Windows.Win32.Graphics.GdiPlus.Matrix* nativeMatrix)
{
NativeMatrix = nativeMatrix;
}
internal unsafe static global::Windows.Win32.Graphics.GdiPlus.Matrix* CreateNativeHandle(Matrix3x2 matrix)
{
global::Windows.Win32.Graphics.GdiPlus.Matrix* result = default(global::Windows.Win32.Graphics.GdiPlus.Matrix*);
PInvoke.GdipCreateMatrix2(matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32, &result).ThrowIfFailed();
return result;
}
[NullableContext(1)]
public unsafe Matrix(RectangleF rect, params PointF[] plgpts)
{
ArgumentNullException.ThrowIfNull(plgpts, "plgpts");
if (plgpts.Length != 3)
throw Status.InvalidParameter.GetException();
fixed (PointF* dstplg = plgpts) {
global::Windows.Win32.Graphics.GdiPlus.Matrix* nativeMatrix = default(global::Windows.Win32.Graphics.GdiPlus.Matrix*);
PInvoke.GdipCreateMatrix3((RectF*)(&rect), (global::Windows.Win32.Graphics.GdiPlus.PointF*)dstplg, &nativeMatrix).ThrowIfFailed();
NativeMatrix = nativeMatrix;
}
}
[NullableContext(1)]
public unsafe Matrix(Rectangle rect, params Point[] plgpts)
{
ArgumentNullException.ThrowIfNull(plgpts, "plgpts");
if (plgpts.Length != 3)
throw Status.InvalidParameter.GetException();
fixed (Point* dstplg = plgpts) {
global::Windows.Win32.Graphics.GdiPlus.Matrix* nativeMatrix = default(global::Windows.Win32.Graphics.GdiPlus.Matrix*);
PInvoke.GdipCreateMatrix3I((Rect*)(&rect), (global::Windows.Win32.Graphics.GdiPlus.Point*)dstplg, &nativeMatrix).ThrowIfFailed();
NativeMatrix = nativeMatrix;
}
}
public void Dispose()
{
DisposeInternal();
GC.SuppressFinalize(this);
}
private unsafe void DisposeInternal()
{
if (NativeMatrix != null) {
if (Gdip.Initialized)
PInvoke.GdipDeleteMatrix(NativeMatrix);
NativeMatrix = null;
}
}
~Matrix()
{
DisposeInternal();
}
[NullableContext(1)]
public unsafe Matrix Clone()
{
global::Windows.Win32.Graphics.GdiPlus.Matrix* nativeMatrix = default(global::Windows.Win32.Graphics.GdiPlus.Matrix*);
PInvoke.GdipCloneMatrix(NativeMatrix, &nativeMatrix).ThrowIfFailed();
GC.KeepAlive(this);
return new Matrix(nativeMatrix);
}
internal unsafe void GetElements(Span<float> elements)
{
fixed (float* matrixOut = &elements.GetPinnableReference()) {
PInvoke.GdipGetMatrixElements(NativeMatrix, matrixOut).ThrowIfFailed();
GC.KeepAlive(this);
}
}
public unsafe void Reset()
{
PInvoke.GdipSetMatrixElements(NativeMatrix, 1, 0, 0, 1, 0, 0).ThrowIfFailed();
GC.KeepAlive(this);
}
[NullableContext(1)]
public void Multiply(Matrix matrix)
{
Multiply(matrix, MatrixOrder.Prepend);
}
[NullableContext(1)]
public unsafe void Multiply(Matrix matrix, MatrixOrder order)
{
ArgumentNullException.ThrowIfNull(matrix, "matrix");
if (matrix.NativeMatrix == NativeMatrix)
throw new InvalidOperationException(System.SR.GdiplusObjectBusy);
PInvoke.GdipMultiplyMatrix(NativeMatrix, matrix.NativeMatrix, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
GC.KeepAlive(matrix);
}
public void Translate(float offsetX, float offsetY)
{
Translate(offsetX, offsetY, MatrixOrder.Prepend);
}
public unsafe void Translate(float offsetX, float offsetY, MatrixOrder order)
{
PInvoke.GdipTranslateMatrix(NativeMatrix, offsetX, offsetY, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
public void Scale(float scaleX, float scaleY)
{
Scale(scaleX, scaleY, MatrixOrder.Prepend);
}
public unsafe void Scale(float scaleX, float scaleY, MatrixOrder order)
{
PInvoke.GdipScaleMatrix(NativeMatrix, scaleX, scaleY, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
public void Rotate(float angle)
{
Rotate(angle, MatrixOrder.Prepend);
}
public unsafe void Rotate(float angle, MatrixOrder order)
{
PInvoke.GdipRotateMatrix(NativeMatrix, angle, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
public void RotateAt(float angle, PointF point)
{
RotateAt(angle, point, MatrixOrder.Prepend);
}
public unsafe void RotateAt(float angle, PointF point, MatrixOrder order)
{
Status status;
if (order == MatrixOrder.Prepend) {
status = PInvoke.GdipTranslateMatrix(NativeMatrix, point.X, point.Y, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order);
status |= PInvoke.GdipRotateMatrix(NativeMatrix, angle, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order);
status |= PInvoke.GdipTranslateMatrix(NativeMatrix, 0 - point.X, 0 - point.Y, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order);
} else {
status = PInvoke.GdipTranslateMatrix(NativeMatrix, 0 - point.X, 0 - point.Y, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order);
status |= PInvoke.GdipRotateMatrix(NativeMatrix, angle, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order);
status |= PInvoke.GdipTranslateMatrix(NativeMatrix, point.X, point.Y, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order);
}
status.ThrowIfFailed();
GC.KeepAlive(this);
}
public unsafe void Shear(float shearX, float shearY)
{
PInvoke.GdipShearMatrix(NativeMatrix, shearX, shearY, global::Windows.Win32.Graphics.GdiPlus.MatrixOrder.MatrixOrderPrepend).ThrowIfFailed();
GC.KeepAlive(this);
}
public unsafe void Shear(float shearX, float shearY, MatrixOrder order)
{
PInvoke.GdipShearMatrix(NativeMatrix, shearX, shearY, (global::Windows.Win32.Graphics.GdiPlus.MatrixOrder)order).ThrowIfFailed();
GC.KeepAlive(this);
}
public unsafe void Invert()
{
PInvoke.GdipInvertMatrix(NativeMatrix).ThrowIfFailed();
GC.KeepAlive(this);
}
[NullableContext(1)]
public void TransformPoints(params PointF[] pts)
{
ArgumentNullException.ThrowIfNull(pts, "pts");
TransformPoints(pts.AsSpan());
}
public unsafe void TransformPoints([ParamCollection] [ScopedRef] ReadOnlySpan<PointF> pts)
{
fixed (PointF* pts2 = &pts.GetPinnableReference()) {
PInvoke.GdipTransformMatrixPoints(NativeMatrix, (global::Windows.Win32.Graphics.GdiPlus.PointF*)pts2, pts.Length).ThrowIfFailed();
}
GC.KeepAlive(this);
}
[NullableContext(1)]
public void TransformPoints(params Point[] pts)
{
ArgumentNullException.ThrowIfNull(pts, "pts");
TransformPoints(pts.AsSpan());
}
public unsafe void TransformPoints([ParamCollection] [ScopedRef] ReadOnlySpan<Point> pts)
{
fixed (Point* pts2 = &pts.GetPinnableReference()) {
PInvoke.GdipTransformMatrixPointsI(NativeMatrix, (global::Windows.Win32.Graphics.GdiPlus.Point*)pts2, pts.Length).ThrowIfFailed();
}
GC.KeepAlive(this);
}
[NullableContext(1)]
public void TransformVectors(params PointF[] pts)
{
ArgumentNullException.ThrowIfNull(pts, "pts");
TransformVectors(pts.AsSpan());
}
public unsafe void TransformVectors([ParamCollection] [ScopedRef] ReadOnlySpan<PointF> pts)
{
fixed (PointF* pts2 = &pts.GetPinnableReference()) {
PInvoke.GdipVectorTransformMatrixPoints(NativeMatrix, (global::Windows.Win32.Graphics.GdiPlus.PointF*)pts2, pts.Length).ThrowIfFailed();
}
GC.KeepAlive(this);
}
[NullableContext(1)]
public void VectorTransformPoints(params Point[] pts)
{
TransformVectors(pts);
}
public void VectorTransformPoints([ParamCollection] [ScopedRef] ReadOnlySpan<Point> pts)
{
TransformVectors(pts);
}
[NullableContext(1)]
public void TransformVectors(params Point[] pts)
{
ArgumentNullException.ThrowIfNull(pts, "pts");
TransformVectors(pts.AsSpan());
}
public unsafe void TransformVectors([ParamCollection] [ScopedRef] ReadOnlySpan<Point> pts)
{
fixed (Point* pts2 = &pts.GetPinnableReference()) {
PInvoke.GdipVectorTransformMatrixPointsI(NativeMatrix, (global::Windows.Win32.Graphics.GdiPlus.Point*)pts2, pts.Length).ThrowIfFailed();
}
GC.KeepAlive(this);
}
[NullableContext(2)]
public unsafe override bool Equals([NotNullWhen(true)] object obj)
{
Matrix matrix = obj as Matrix;
if (matrix == null)
return false;
BOOL value = default(BOOL);
PInvoke.GdipIsMatrixEqual(NativeMatrix, matrix.NativeMatrix, &value).ThrowIfFailed();
GC.KeepAlive(this);
GC.KeepAlive(matrix);
return value;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}