Merge pull request #189 from vpenades/main

Refactored AffineTransform towards System.Numerics.Matrix3x2
This commit is contained in:
jonlipsky 2021-10-15 11:06:30 -07:00 коммит произвёл GitHub
Родитель 6f0f7af6cf e94d2a7b86
Коммит 1aeec2dd88
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
35 изменённых файлов: 538 добавлений и 514 удалений

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.Numerics;
using Microsoft.Maui.Graphics.Blazor.Canvas2D;
using Microsoft.Maui.Graphics.Text;
@ -232,10 +234,9 @@ namespace Microsoft.Maui.Graphics.Blazor
_context.Clip("evenodd");
}
protected override void NativeConcatenateTransform(AffineTransform transform)
{
transform.GetMatrix(_matrix);
_context.SetTransform(_matrix[0], _matrix[1], _matrix[2], _matrix[3], _matrix[4], _matrix[5]);
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
_context.SetTransform(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31,transform.M32);
}
protected override void NativeDrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed)

Просмотреть файл

@ -4,6 +4,7 @@ using Drawing = System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using Microsoft.Maui.Graphics.Text;
using System.Numerics;
namespace Microsoft.Maui.Graphics.GDI
{
@ -465,7 +466,7 @@ namespace Microsoft.Maui.Graphics.GDI
CurrentState.NativeTranslate(tx, ty);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
CurrentState.NativeConcatenateTransform(transform);
}

Просмотреть файл

@ -1,5 +1,7 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Numerics;
using Drawing = System.Drawing;
namespace Microsoft.Maui.Graphics.GDI
@ -373,12 +375,10 @@ namespace Microsoft.Maui.Graphics.GDI
_graphics.TranslateTransform(-x, -y);
}
public void NativeConcatenateTransform(AffineTransform transform)
{
_scale *= transform.ScaleX;
var values = new float[6];
transform.GetMatrix(values);
var transformMatrix = new Matrix(values[0], values[1], values[2], values[3], values[4], values[5]);
public void NativeConcatenateTransform(Matrix3x2 transform)
{
_scale *= GetLengthScale(transform);
var transformMatrix = new Matrix(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
_graphics.MultiplyTransform(transformMatrix);
}
}

Просмотреть файл

@ -1,3 +1,5 @@
using System.Numerics;
namespace Microsoft.Maui.Graphics.Native.Gtk {
public partial class NativeCanvas : AbstractCanvas<NativeCanvasState> {
@ -170,7 +172,7 @@ namespace Microsoft.Maui.Graphics.Native.Gtk {
}
[GtkMissingImplementation]
protected override void NativeConcatenateTransform(AffineTransform transform) { }
protected override void NativeConcatenateTransform(Matrix3x2 transform) { }
public override void SetShadow(SizeF offset, float blur, Color color) {
CurrentState.Shadow = (offset, blur, color);

Просмотреть файл

@ -16,13 +16,9 @@ namespace Microsoft.Maui.Graphics.Native.Gtk {
LineCap = Cairo.LineCap.Butt;
}
public NativeCanvasState(NativeCanvasState prototype) {
StrokeDashPattern = prototype.StrokeDashPattern;
StrokeSize = prototype.StrokeSize;
Scale = prototype.Scale;
Transform = prototype.Transform;
public NativeCanvasState(NativeCanvasState prototype)
: base(prototype)
{
Antialias = prototype.Antialias;
MiterLimit = prototype.MiterLimit;
StrokeColor = prototype.StrokeColor;

Просмотреть файл

@ -14,6 +14,8 @@ using AlphaMode = SharpDX.Direct2D1.AlphaMode;
using TextAntialiasMode = SharpDX.Direct2D1.TextAntialiasMode;
using System;
using NumericsMatrix3x2 = System.Numerics.Matrix3x2;
namespace Microsoft.Maui.Graphics.SharpDX
{
public class DXCanvas : AbstractCanvas<DXCanvasState>, IBlurrableCanvas
@ -754,7 +756,7 @@ namespace Microsoft.Maui.Graphics.SharpDX
_renderTarget.Transform = CurrentState.DxTranslate(tx, ty);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(NumericsMatrix3x2 transform)
{
_renderTarget.Transform = CurrentState.DxConcatenateTransform(transform);
}

Просмотреть файл

@ -3,6 +3,8 @@ using SharpDX.Direct2D1;
using SharpDX.DirectWrite;
using System;
using NumericsMatrix3x2 = System.Numerics.Matrix3x2;
namespace Microsoft.Maui.Graphics.SharpDX
{
public class DXCanvasState : CanvasState
@ -84,7 +86,7 @@ namespace Microsoft.Maui.Graphics.SharpDX
ShadowOffset = prototype.ShadowOffset;
ShadowBlur = prototype.ShadowBlur;
Matrix = new Matrix3x2(prototype.Matrix.ToArray());
Matrix = prototype.Matrix;
FontName = prototype.FontName;
FontSize = prototype.FontSize;
@ -613,12 +615,10 @@ namespace Microsoft.Maui.Graphics.SharpDX
return Matrix;
}
public Matrix3x2 DxConcatenateTransform(AffineTransform transform)
public Matrix3x2 DxConcatenateTransform(NumericsMatrix3x2 transform)
{
var values = new float[6];
transform.GetMatrix(values);
Matrix = Matrix3x2.Multiply(Matrix, new Matrix3x2(values));
return Matrix;
var dxTransform = new Matrix3x2(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
return Matrix = Matrix3x2.Multiply(Matrix, dxTransform);
}
public Matrix3x2 DxScale(float tx, float ty)

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.Numerics;
using SkiaSharp;
namespace Microsoft.Maui.Graphics.Skia
@ -74,16 +76,16 @@ namespace Microsoft.Maui.Graphics.Skia
return new SKPoint(target.X, target.Y);
}
public static SKMatrix AsMatrix(this AffineTransform transform)
public static SKMatrix AsMatrix(this in Matrix3x2 transform)
{
var matrix = new SKMatrix
{
ScaleX = transform.ScaleX,
SkewX = transform.ShearX,
TransX = transform.TranslateX,
SkewY = transform.ShearY,
ScaleY = transform.ScaleY,
TransY = transform.TranslateY,
ScaleX = transform.M11,
SkewX = transform.M21,
TransX = transform.M31,
SkewY = transform.M12,
ScaleY = transform.M22,
TransY = transform.M32,
Persp0 = 0,
Persp1 = 0,
Persp2 = 1

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.Numerics;
using Microsoft.Maui.Graphics.Text;
using SkiaSharp;
@ -834,7 +836,7 @@ namespace Microsoft.Maui.Graphics.Skia
_canvas.Translate(tx * CurrentState.ScaleX, ty * CurrentState.ScaleY);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
var matrix = new SKMatrix();

Просмотреть файл

@ -701,7 +701,7 @@ namespace Microsoft.Maui.Graphics.Win2D
_session.Transform = CurrentState.AppendTranslate(tx, ty);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
_session.Transform = CurrentState.AppendConcatenateTransform(transform);
}

Просмотреть файл

@ -436,12 +436,9 @@ namespace Microsoft.Maui.Graphics.Win2D
return Matrix;
}
public Matrix3x2 AppendConcatenateTransform(AffineTransform transform)
public Matrix3x2 AppendConcatenateTransform(Matrix3x2 transform)
{
var values = new float[6];
transform.GetMatrix(values);
Matrix = Matrix3x2.Multiply(Matrix, new Matrix3x2(values[0],values[1],values[2],values[3],values[4],values[5]));
return Matrix;
return Matrix = Matrix3x2.Multiply(Matrix, transform);
}
public Matrix3x2 AppendScale(float tx, float ty)

Просмотреть файл

@ -701,7 +701,7 @@ namespace Microsoft.Maui.Graphics.Win2D
_session.Transform = CurrentState.AppendTranslate(tx, ty);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
_session.Transform = CurrentState.AppendConcatenateTransform(transform);
}

Просмотреть файл

@ -437,12 +437,9 @@ namespace Microsoft.Maui.Graphics.Win2D
return Matrix;
}
public Matrix3x2 AppendConcatenateTransform(AffineTransform transform)
{
var values = new float[6];
transform.GetMatrix(values);
Matrix = Matrix3x2.Multiply(Matrix, new Matrix3x2(values[0], values[1], values[2], values[3], values[4], values[5]));
return Matrix;
public Matrix3x2 AppendConcatenateTransform(Matrix3x2 transform)
{
return Matrix = Matrix3x2.Multiply(Matrix, transform);
}
public Matrix3x2 AppendScale(float tx, float ty)

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Maui.Graphics.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@ -232,7 +234,7 @@ namespace Microsoft.Maui.Graphics.Xaml
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
CurrentState.XamlConcatenateTransform(transform);
}
@ -427,8 +429,8 @@ namespace Microsoft.Maui.Graphics.Xaml
var item = GetOrCreateItem(ItemType.DrawLine);
var element = (Line)item.Element;
var p1 = CurrentState.Transform.Transform(x1, y1);
var p2 = CurrentState.Transform.Transform(x2, y2);
var p1 = Vector2.Transform(new Vector2(x1, y1), CurrentState.Transform);
var p2 = Vector2.Transform(new Vector2(x2, y2), CurrentState.Transform);
element.X1 = p1.X;
element.Y1 = p1.Y;
element.X2 = p2.X;

Просмотреть файл

@ -1,3 +1,5 @@
using System.Numerics;
using Windows.UI.Text;
using Windows.UI.Xaml.Media;
@ -401,7 +403,7 @@ namespace Microsoft.Maui.Graphics.Xaml
_transformGroup.Children.Add(transform);
}
public void XamlConcatenateTransform(AffineTransform transform)
public void XamlConcatenateTransform(Matrix3x2 transform)
{
InitGroup();
var nativeTransform = transform.AsTransform();

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using Windows.UI.Input;
using Windows.UI.Xaml.Media;
@ -146,15 +148,14 @@ namespace Microsoft.Maui.Graphics.Xaml
return geometry;
}
public static Transform AsTransform(this AffineTransform transform)
public static Transform AsTransform(this in Matrix3x2 transform)
{
if (transform.IsIdentity)
{
return new MatrixTransform();
}
var values = new float[6];
transform.GetMatrix(values);
return new MatrixTransform() { Matrix = new Matrix(values[0], values[1], values[2], values[3],values[4], values[5])};
var xamlTransform = new MatrixTransform();
xamlTransform.Matrix = transform.IsIdentity
? Matrix.Identity
: new Matrix(transform.M11,transform.M12,transform.M21,transform.M22,transform.M31,transform.M32);
return xamlTransform;
}
}
}

Просмотреть файл

@ -5,6 +5,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Numerics;
namespace Microsoft.Maui.Graphics.Xaml
{
@ -229,7 +230,7 @@ namespace Microsoft.Maui.Graphics.Xaml
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
CurrentState.XamlConcatenateTransform(transform);
}

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Numerics;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
@ -406,7 +407,7 @@ namespace Microsoft.Maui.Graphics.Xaml
_transformGroup.Children.Add(transform);
}
public void XamlConcatenateTransform(AffineTransform transform)
public void XamlConcatenateTransform(Matrix3x2 transform)
{
InitGroup();
var nativeTransform = transform.AsTransform();

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Windows.Media;
namespace Microsoft.Maui.Graphics.Xaml
@ -134,16 +135,14 @@ namespace Microsoft.Maui.Graphics.Xaml
return geometry;
}
public static Transform AsTransform(this AffineTransform transform)
public static Transform AsTransform(this in Matrix3x2 transform)
{
if (transform.IsIdentity)
{
return Transform.Identity;
}
var values = new float[6];
transform.GetMatrix(values);
return new MatrixTransform(values[0], values[1], values[2], values[3], values[4], values[5]);
return new MatrixTransform(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
}
}
}

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Maui.Graphics.Text;
namespace Microsoft.Maui.Graphics
@ -27,7 +29,7 @@ namespace Microsoft.Maui.Graphics
protected abstract void NativeRotate(float degrees, float radians);
protected abstract void NativeScale(float fx, float fy);
protected abstract void NativeTranslate(float tx, float ty);
protected abstract void NativeConcatenateTransform(AffineTransform transform);
protected abstract void NativeConcatenateTransform(Matrix3x2 transform);
protected AbstractCanvas(Func<object, TState> createNew, Func<TState, TState> createCopy)
{
@ -260,9 +262,10 @@ namespace Microsoft.Maui.Graphics
var radians = Geometry.DegreesToRadians(degrees);
var transform = _currentState.Transform;
transform.Translate(x, y);
transform.Rotate(radians);
transform.Translate(-x, -y);
transform = Matrix3x2.CreateTranslation(x, y) * transform;
transform = Matrix3x2.CreateRotation(radians) * transform;
transform = Matrix3x2.CreateTranslation(-x, -y) * transform;
_currentState.Transform = transform;
NativeRotate(degrees, radians, x, y);
}
@ -270,29 +273,36 @@ namespace Microsoft.Maui.Graphics
public void Rotate(float degrees)
{
var radians = Geometry.DegreesToRadians(degrees);
_currentState.Transform.Rotate(radians);
var transform = _currentState.Transform;
transform = Matrix3x2.CreateRotation(radians) * transform;
_currentState.Transform = transform;
NativeRotate(degrees, radians);
}
public void Scale(float fx, float fy)
{
_currentState.Scale *= fx;
_currentState.Transform.Scale(fx, fy);
var transform = _currentState.Transform;
transform = Matrix3x2.CreateScale(fx, fy) * transform;
_currentState.Transform = transform;
NativeScale(fx, fy);
}
public void Translate(float tx, float ty)
{
_currentState.Transform.Translate(tx, ty);
var transform = _currentState.Transform;
transform = Matrix3x2.CreateTranslation(tx, ty) * transform;
_currentState.Transform = transform;
NativeTranslate(tx, ty);
}
public void ConcatenateTransform(AffineTransform transform)
public void ConcatenateTransform(Matrix3x2 transform)
{
_currentState.Scale *= transform.ScaleX;
_currentState.Transform.Concatenate(transform);
_currentState.Transform = transform * _currentState.Transform;
NativeConcatenateTransform(transform);
}
}

Просмотреть файл

@ -1,404 +0,0 @@
using System;
using System.Numerics;
namespace Microsoft.Maui.Graphics
{
public class AffineTransform
{
private const float Epsilon = 1E-10f;
private float _m00;
private float _m01;
private float _m02;
private float _m10;
private float _m11;
private float _m12;
public AffineTransform()
{
_m00 = _m11 = 1.0f;
_m10 = _m01 = _m02 = _m12 = 0.0f;
}
public AffineTransform(AffineTransform t)
{
_m00 = t._m00;
_m10 = t._m10;
_m01 = t._m01;
_m11 = t._m11;
_m02 = t._m02;
_m12 = t._m12;
}
public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12)
{
_m00 = m00;
_m10 = m10;
_m01 = m01;
_m11 = m11;
_m02 = m02;
_m12 = m12;
}
public AffineTransform(float[] matrix)
{
_m00 = matrix[0];
_m10 = matrix[1];
_m01 = matrix[2];
_m11 = matrix[3];
if (matrix.Length > 4)
{
_m02 = matrix[4];
_m12 = matrix[5];
}
}
public AffineTransform(in Matrix3x2 matrix)
{
_m00 = matrix.M11;
_m10 = matrix.M12;
_m01 = matrix.M21;
_m11 = matrix.M22;
_m02 = matrix.M31;
_m12 = matrix.M32;
}
public void SetMatrix(float m00, float m10, float m01, float m11, float m02, float m12)
{
_m00 = m00;
_m10 = m10;
_m01 = m01;
_m11 = m11;
_m02 = m02;
_m12 = m12;
}
public void SetMatrix(in Matrix3x2 matrix)
{
_m00 = matrix.M11;
_m10 = matrix.M12;
_m01 = matrix.M21;
_m11 = matrix.M22;
_m02 = matrix.M31;
_m12 = matrix.M32;
}
public float ScaleX => _m00;
public float ScaleY => _m11;
public float ShearX => _m01;
public float ShearY => _m10;
public float TranslateX => _m02;
public float TranslateY => _m12;
public void GetMatrix(float[] matrix)
{
matrix[0] = _m00;
matrix[1] = _m10;
matrix[2] = _m01;
matrix[3] = _m11;
if (matrix.Length > 4)
{
matrix[4] = _m02;
matrix[5] = _m12;
}
}
public float GetDeterminant()
{
return _m00 * _m11 - _m01 * _m10;
}
public void SetTransform(float m00, float m10, float m01, float m11, float m02, float m12)
{
_m00 = m00;
_m10 = m10;
_m01 = m01;
_m11 = m11;
_m02 = m02;
_m12 = m12;
}
public void SetTransform(in Matrix3x2 matrix)
{
_m00 = matrix.M11;
_m10 = matrix.M12;
_m01 = matrix.M21;
_m11 = matrix.M22;
_m02 = matrix.M31;
_m12 = matrix.M32;
}
public void SetTransform(AffineTransform t)
{
SetTransform(t._m00, t._m10, t._m01, t._m11, t._m02, t._m12);
}
public void SetToIdentity()
{
_m00 = _m11 = 1.0f;
_m10 = _m01 = _m02 = _m12 = 0.0f;
}
public void SetToTranslation(float mx, float my)
{
_m00 = _m11 = 1.0f;
_m01 = _m10 = 0.0f;
_m02 = mx;
_m12 = my;
}
public void SetToScale(float scx, float scy)
{
_m00 = scx;
_m11 = scy;
_m10 = _m01 = _m02 = _m12 = 0.0f;
}
public void SetToShear(float shx, float shy)
{
_m00 = _m11 = 1.0f;
_m02 = _m12 = 0.0f;
_m01 = shx;
_m10 = shy;
}
public void SetToRotation(float angle)
{
float sin = (float) Math.Sin(angle);
float cos = (float) Math.Cos(angle);
if (Math.Abs(cos) < Epsilon)
{
cos = 0.0f;
sin = sin > 0.0f ? 1.0f : -1.0f;
}
else if (Math.Abs(sin) < Epsilon)
{
sin = 0.0f;
cos = cos > 0.0f ? 1.0f : -1.0f;
}
_m00 = _m11 = cos;
_m01 = -sin;
_m10 = sin;
_m02 = _m12 = 0.0f;
}
public void SetToRotation(float angle, float px, float py)
{
SetToRotation(angle);
_m02 = px * (1.0f - _m00) + py * _m10;
_m12 = py * (1.0f - _m00) - px * _m10;
}
public static AffineTransform GetTranslateInstance(float mx, float my)
{
var t = new AffineTransform();
t.SetToTranslation(mx, my);
return t;
}
public static AffineTransform GetScaleInstance(float scx, float scY)
{
var t = new AffineTransform();
t.SetToScale(scx, scY);
return t;
}
public static AffineTransform GetShearInstance(float shx, float shy)
{
var m = new AffineTransform();
m.SetToShear(shx, shy);
return m;
}
public static AffineTransform GetRotateInstance(float angle)
{
var t = new AffineTransform();
t.SetToRotation(angle);
return t;
}
public static AffineTransform GetRotateInstance(float angle, float x, float y)
{
var t = new AffineTransform();
t.SetToRotation(angle, x, y);
return t;
}
public void Translate(float mx, float my)
{
Concatenate(GetTranslateInstance(mx, my));
}
public void Scale(float scx, float scy)
{
Concatenate(GetScaleInstance(scx, scy));
}
public void Shear(float shx, float shy)
{
Concatenate(GetShearInstance(shx, shy));
}
public void RotateInDegrees(float angle)
{
Rotate(Geometry.DegreesToRadians(angle));
}
public void RotateInDegrees(float angle, float px, float py)
{
Rotate(Geometry.DegreesToRadians(angle), px, py);
}
public void Rotate(float angle)
{
Concatenate(GetRotateInstance(angle));
}
public void Rotate(float angle, float px, float py)
{
Concatenate(GetRotateInstance(angle, px, py));
}
/// <summary>
/// Multiply two AffineTransform objects
/// </summary>
/// <param name="t1">the multiplicand</param>
/// <param name="t2">the multiplier</param>
/// <returns>an AffineTransform object that is a result of t1 multiplied by t2</returns>
private AffineTransform Multiply(AffineTransform t1, AffineTransform t2)
{
return new AffineTransform(
t1._m00 * t2._m00 + t1._m10 * t2._m01, // m00
t1._m00 * t2._m10 + t1._m10 * t2._m11, // m01
t1._m01 * t2._m00 + t1._m11 * t2._m01, // m10
t1._m01 * t2._m10 + t1._m11 * t2._m11, // m11
t1._m02 * t2._m00 + t1._m12 * t2._m01 + t2._m02, // m02
t1._m02 * t2._m10 + t1._m12 * t2._m11 + t2._m12); // m12
}
public void Concatenate(AffineTransform t)
{
SetTransform(Multiply(t, this));
}
public void PreConcatenate(AffineTransform t)
{
SetTransform(Multiply(this, t));
}
public AffineTransform CreateInverse()
{
float det = GetDeterminant();
if (Math.Abs(det) < Epsilon)
throw new Exception("Determinant is zero");
return new AffineTransform(
_m11 / det,
-_m10 / det,
-_m01 / det,
_m00 / det,
(_m01 * _m12 - _m11 * _m02) / det,
(_m10 * _m02 - _m00 * _m12) / det
);
}
public PointF Transform(PointF src)
{
return Transform(src.X, src.Y);
}
public PointF Transform(float x, float y)
{
return new PointF(x * _m00 + y * _m01 + _m02, x * _m10 + y * _m11 + _m12);
}
public PointF InverseTransform(PointF src)
{
float det = GetDeterminant();
if (Math.Abs(det) < Epsilon)
throw new Exception("Unable to inverse this transform.");
float x = src.X - _m02;
float y = src.Y - _m12;
return new PointF((x * _m11 - y * _m01) / det, (y * _m00 - x * _m10) / det);
}
public void Transform(float[] src, int srcOff, float[] dst, int dstOff, int length)
{
int step = 2;
if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2)
{
srcOff = srcOff + length * 2 - 2;
dstOff = dstOff + length * 2 - 2;
step = -2;
}
while (--length >= 0)
{
float x = src[srcOff + 0];
float y = src[srcOff + 1];
dst[dstOff + 0] = x * _m00 + y * _m01 + _m02;
dst[dstOff + 1] = x * _m10 + y * _m11 + _m12;
srcOff += step;
dstOff += step;
}
}
public bool IsUnityTransform()
{
return !(HasScale() || HasRotate() || HasTranslate());
}
private bool HasScale()
{
// ReSharper disable CompareOfFloatsByEqualityOperator
return _m00 != 1.0 || _m11 != 1.0;
// ReSharper restore CompareOfFloatsByEqualityOperator
}
private bool HasRotate()
{
// ReSharper disable CompareOfFloatsByEqualityOperator
return _m10 != 0.0 || _m01 != 0.0;
// ReSharper restore CompareOfFloatsByEqualityOperator
}
private bool HasTranslate()
{
// ReSharper disable CompareOfFloatsByEqualityOperator
return _m02 != 0.0 || _m12 != 0.0;
// ReSharper restore CompareOfFloatsByEqualityOperator
}
public bool OnlyTranslate()
{
return !HasRotate() && !HasScale();
}
public bool OnlyTranslateOrScale()
{
return !HasRotate();
}
public bool OnlyScale()
{
return !HasRotate() && !HasTranslate();
}
public static implicit operator AffineTransform(Matrix3x2 matrix) => new AffineTransform(matrix);
public static explicit operator Matrix3x2(AffineTransform matrix)
{
return new Matrix3x2(matrix._m00, matrix._m10, matrix._m01, matrix._m11, matrix._m02, matrix._m12);
}
public bool IsIdentity => _m00 == 1.0f && _m11 == 1.0f && _m10 == 0.0f && _m01 == 0.0f && _m02 == 0.0f && _m12 == 0.0f;
}
}

Просмотреть файл

@ -1,6 +1,7 @@
using Android.Graphics;
using Android.Text;
using System;
using System.Numerics;
namespace Microsoft.Maui.Graphics.Native
{
@ -65,11 +66,16 @@ namespace Microsoft.Maui.Graphics.Native
return new global::Android.Graphics.PointF(target.X, target.Y);
}
public static Matrix AsMatrix(this AffineTransform transform)
public static Matrix AsMatrix(this Matrix3x2 transform)
{
var values = new float[9];
transform.GetMatrix(values);
values[Matrix.MscaleX] = transform.M11;
values[Matrix.MskewX] = transform.M21;
values[Matrix.MtransY] = transform.M31;
values[Matrix.MskewY] = transform.M12;
values[Matrix.MscaleY] = transform.M22;
values[Matrix.MtransX] = transform.M32;
values[Matrix.Mpersp0] = 0; // 6
values[Matrix.Mpersp1] = 0; // 7
values[Matrix.Mpersp2] = 1; // 8

Просмотреть файл

@ -4,6 +4,7 @@ using Android.Content;
using Android.Graphics;
using Android.Text;
using System;
using System.Numerics;
namespace Microsoft.Maui.Graphics.Native
{
@ -780,7 +781,7 @@ namespace Microsoft.Maui.Graphics.Native
_canvas.Translate(tx * CurrentState.ScaleX, ty * CurrentState.ScaleY);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
var matrix = new Matrix(_canvas.Matrix);
matrix.PostConcat(transform.AsMatrix());

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Numerics;
namespace Microsoft.Maui.Graphics
{
@ -6,22 +7,51 @@ namespace Microsoft.Maui.Graphics
{
public float[] StrokeDashPattern { get; set; }
public float StrokeSize { get; set; } = 1;
public float Scale { get; set; } = 1;
public AffineTransform Transform { get; set; }
private Matrix3x2 _transform = Matrix3x2.Identity;
private float _scale = 1;
private float _scaleX = 1;
private float _scaleY = 1;
public Matrix3x2 Transform
{
get => this._transform;
set
{
if (this._transform == value) return;
this._transform = value;
value.DeconstructScales(out _scale, out _scaleX, out _scaleY);
TransformChanged();
}
}
public float Scale => this._scale;
public float ScaleX => this._scaleX;
public float ScaleY => this._scaleY;
protected CanvasState()
{
Transform = new AffineTransform();
}
protected CanvasState(CanvasState prototype)
{
StrokeDashPattern = prototype.StrokeDashPattern;
StrokeSize = prototype.StrokeSize;
Transform = new AffineTransform(prototype.Transform);
Scale = prototype.Scale;
this._transform = prototype._transform;
this._scale = prototype._scale;
this._scaleX = prototype._scaleX;
this._scaleY = prototype._scaleY;
}
protected virtual void TransformChanged()
{
// let derived classes handle the transform change if needed.
}
protected static float GetLengthScale(Matrix3x2 matrix) => matrix.GetLengthScale();
public virtual void Dispose()
{
// Do nothing right now

Просмотреть файл

@ -1,3 +1,5 @@
using System.Numerics;
using Microsoft.Maui.Graphics.Text;
namespace Microsoft.Maui.Graphics
@ -76,7 +78,7 @@ namespace Microsoft.Maui.Graphics
public void Translate(float tx, float ty);
public void ConcatenateTransform(AffineTransform transform);
public void ConcatenateTransform(Matrix3x2 transform);
public void SaveState();

Просмотреть файл

@ -8,6 +8,7 @@ using CoreGraphics;
using CoreText;
using Foundation;
using System;
using System.Numerics;
namespace Microsoft.Maui.Graphics.Native
{
@ -1356,7 +1357,7 @@ namespace Microsoft.Maui.Graphics.Native
_context.TranslateCTM(tx, ty);
}
protected override void NativeConcatenateTransform(AffineTransform transform)
protected override void NativeConcatenateTransform(Matrix3x2 transform)
{
_context.ConcatCTM(transform.AsCGAffineTransform());
}

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.Numerics;
using CoreGraphics;
namespace Microsoft.Maui.Graphics.Native
@ -386,23 +388,9 @@ namespace Microsoft.Maui.Graphics.Native
}
}
public static CGAffineTransform AsCGAffineTransform(this AffineTransform transform)
public static CGAffineTransform AsCGAffineTransform(this in Matrix3x2 transform)
{
if (transform != null)
{
var matrix = new float[6];
transform.GetMatrix(matrix);
float xx = matrix[0];
float yx = matrix[1];
float xy = matrix[2];
float yy = matrix[3];
float x0 = matrix[4];
float y0 = matrix[5];
return new CGAffineTransform(xx, yx, xy, yy, x0, y0);
}
return CGAffineTransform.MakeIdentity();
return new CGAffineTransform(transform.M11, transform.M12, transform.M21, transform.M22, transform.M31, transform.M32);
}
public static CGColor AsCGColor(this Color color)

Просмотреть файл

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.Maui.Graphics.Tests")]
namespace Microsoft.Maui.Graphics
{
static class Matrix3x2Extensions
{
public static bool IsZero(this in Matrix3x2 matrix)
{
if (matrix.M11 != 0) return false;
if (matrix.M12 != 0) return false;
if (matrix.M21 != 0) return false;
if (matrix.M22 != 0) return false;
if (matrix.M31 != 0) return false;
if (matrix.M32 != 0) return false;
return true;
}
public static bool IsFinite(this in Matrix3x2 matrix)
{
bool result = true;
#if NETSTANDARD2_0 || TIZEN40
result &= !(float.IsNaN(matrix.M11) || float.IsInfinity(matrix.M11));
result &= !(float.IsNaN(matrix.M12) || float.IsInfinity(matrix.M12));
result &= !(float.IsNaN(matrix.M21) || float.IsInfinity(matrix.M21));
result &= !(float.IsNaN(matrix.M22) || float.IsInfinity(matrix.M22));
result &= !(float.IsNaN(matrix.M31) || float.IsInfinity(matrix.M31));
result &= !(float.IsNaN(matrix.M32) || float.IsInfinity(matrix.M32));
#else
result &= float.IsFinite(matrix.M11);
result &= float.IsFinite(matrix.M12);
result &= float.IsFinite(matrix.M21);
result &= float.IsFinite(matrix.M22);
result &= float.IsFinite(matrix.M31);
result &= float.IsFinite(matrix.M32);
#endif
return result;
}
public static Vector2 GetScale(this in Matrix3x2 matrix)
{
var sx = matrix.M12 == 0 ? Math.Abs(matrix.M11) : new Vector2(matrix.M11, matrix.M12).Length();
var sy = matrix.M21 == 0 ? Math.Abs(matrix.M22) : new Vector2(matrix.M21, matrix.M22).Length();
if (matrix.GetDeterminant() < 0) sy = -sy;
return new Vector2(sx, sy);
}
public static float GetRotation(this in Matrix3x2 matrix)
{
return (float)Math.Atan2(matrix.M12, matrix.M11);
}
public static Vector2 GetTranslation(this in Matrix3x2 matrix)
{
return matrix.Translation;
}
public static Matrix3x2 WithScale(this Matrix3x2 matrix, Vector2 scale)
{
var sx = matrix.M12 == 0 ? Math.Abs(matrix.M11) : new Vector2(matrix.M11, matrix.M12).Length();
var sy = matrix.M21 == 0 ? Math.Abs(matrix.M22) : new Vector2(matrix.M21, matrix.M22).Length();
// if (matrix.GetDeterminant() < 0) sy = -sy;
scale /= new Vector2(sx, sy);
matrix.M11 *= scale.X;
matrix.M12 *= scale.X;
matrix.M21 *= scale.Y;
matrix.M22 *= scale.Y;
return matrix;
// var t = matrix.Translation;
// var r = matrix.GetRotation();
// return CreateMatrix3x2(scale, r, t);
}
public static Matrix3x2 WithoutScale(this in Matrix3x2 matrix)
{
return matrix.WithScale(Vector2.One);
}
public static Matrix3x2 WithRotation(this in Matrix3x2 matrix, float radians)
{
var t = matrix.Translation;
var s = matrix.GetScale();
return CreateMatrix3x2(s, radians, t);
}
public static Matrix3x2 WithoutRotation(this in Matrix3x2 matrix)
{
var t = matrix.Translation;
var s = matrix.GetScale();
return CreateMatrix3x2(s, 0, t);
}
public static Matrix3x2 WithTranslation(this Matrix3x2 matrix, Vector2 translation)
{
matrix.Translation = translation;
return matrix;
}
/// <summary>
/// Creates a matrix from an SRT.
/// </summary>
/// <param name="scale">The scale</param>
/// <param name="rotation">The rotation, in radians</param>
/// <param name="translation">the translation</param>
/// <returns>A Matrix3x2</returns>
/// <remarks>
/// This is equivalent to:<br/>
/// <c>
/// m = Matrix3x2.CreateScale(scale)<br/>
/// m *= Matri3x2.CreateRotation(rotation)<br/>
/// m *= Matri3x2.CreateTranslation(translation)<br/>
/// </c>
/// </remarks>
internal static Matrix3x2 CreateMatrix3x2(Vector2 scale, float rotation, Vector2 translation)
{
var m = Matrix3x2.CreateRotation(rotation);
m.M11 *= scale.X;
m.M12 *= scale.X;
m.M21 *= scale.Y;
m.M22 *= scale.Y;
m.M31 = translation.X;
m.M32 = translation.Y;
return m;
}
public static float GetLengthScale(this in Matrix3x2 matrix)
{
var determinant = matrix.GetDeterminant();
var areaScale = Math.Abs(determinant);
return (float)Math.Sqrt(areaScale);
}
public static void CopyTo(this in Matrix3x2 matrix, float[] dst, int offset = 0, int count = 6)
{
count = Math.Min(dst.Length, count);
dst[offset + 0] = matrix.M11;
dst[offset + 1] = matrix.M12;
dst[offset + 2] = matrix.M21;
dst[offset + 3] = matrix.M22;
if (count > 4)
{
dst[offset + 4] = matrix.M31;
dst[offset + 5] = matrix.M32;
}
}
public static void DeconstructScales(this in Matrix3x2 value, out float scale, out float scalex, out float scaley)
{
var det = value.GetDeterminant();
scale = (float)Math.Sqrt(Math.Abs(det));
scalex = value.M12 == 0 ? Math.Abs(value.M11) : new Vector2(value.M11, value.M12).Length();
scaley = value.M21 == 0 ? Math.Abs(value.M22) : new Vector2(value.M21, value.M22).Length();
if (det < 0) scaley = -scaley;
}
}
}

Просмотреть файл

@ -1,5 +1,6 @@
using System.Globalization;
using System.IO;
using System.Numerics;
namespace Microsoft.Maui.Graphics
{
@ -67,7 +68,7 @@ namespace Microsoft.Maui.Graphics
float scale)
{
var scaledPath = new PathF(target);
var transform = AffineTransform.GetScaleInstance(scale, scale);
var transform = Matrix3x2.CreateScale(scale);
scaledPath.Transform(transform);
return scaledPath;
}

Просмотреть файл

@ -920,10 +920,10 @@ namespace Microsoft.Maui.Graphics
return Geometry.RotatePoint(pivotPoint, point, angle);
}
public void Transform(AffineTransform transform)
public void Transform(Matrix3x2 transform)
{
for (var i = 0; i < _points.Count; i++)
_points[i] = transform.Transform(_points[i]);
_points[i] = Vector2.Transform((Vector2)_points[i], transform);
Invalidate();
}

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Maui.Graphics.Text;
namespace Microsoft.Maui.Graphics
@ -220,7 +222,7 @@ namespace Microsoft.Maui.Graphics
_commands.Add(canvas => canvas.Translate(tx, ty));
}
public void ConcatenateTransform(AffineTransform transform)
public void ConcatenateTransform(Matrix3x2 transform)
{
_commands.Add(canvas => canvas.ConcatenateTransform(transform));
}

Просмотреть файл

@ -14,7 +14,7 @@ namespace Microsoft.Maui.Graphics
public float Y { get; set; }
public static PointF Zero = new PointF();
public static readonly PointF Zero = new PointF();
public override string ToString()
{
@ -69,6 +69,11 @@ namespace Microsoft.Maui.Graphics
return p;
}
public PointF TransformBy(in Matrix3x2 transform)
{
return Vector2.Transform((Vector2)this, transform);
}
public PointF Round()
{
return new PointF((float)Math.Round(X), (float)Math.Round(Y));

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Maui.Graphics.Text;
namespace Microsoft.Maui.Graphics
@ -221,10 +223,11 @@ namespace Microsoft.Maui.Graphics
_canvas.Translate(tx, ty);
}
public void ConcatenateTransform(AffineTransform transform)
public void ConcatenateTransform(Matrix3x2 transform)
{
_scaleX *= transform.ScaleX;
_scaleY *= transform.ScaleY;
transform.DeconstructScales(out _, out var sx, out var sy);
_scaleX *= sx;
_scaleY *= sy;
_canvas.ConcatenateTransform(transform);
}

Просмотреть файл

@ -69,6 +69,11 @@ namespace Microsoft.Maui.Graphics
}
}
public SizeF TransformNormalBy(in Matrix3x2 transform)
{
return (SizeF)Vector2.TransformNormal((Vector2)this, transform);
}
public static SizeF operator +(SizeF s1, SizeF s2)
{
return new SizeF(s1._width + s2._width, s1._height + s2._height);
@ -104,6 +109,11 @@ namespace Microsoft.Maui.Graphics
return new Vector2(size.Width, size.Height);
}
public static explicit operator SizeF(Vector2 size)
{
return new SizeF(size.X, size.Y);
}
public bool Equals(SizeF other)
{
return _width.Equals(other._width) && _height.Equals(other._height);
@ -134,6 +144,7 @@ namespace Microsoft.Maui.Graphics
width = Width;
height = Height;
}
public static implicit operator Size(SizeF s) => new Size(s.Width, s.Height);
public static bool TryParse(string value, out SizeF sizeF)

Просмотреть файл

@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.Maui.Graphics.Tests
{
public class Matrix3x2ExtensionsTests
{
public static IEnumerable<object[]> ScaleRotationData()
{
yield return new object[] { 0, 1, 1 };
yield return new object[] { 0, -1, -1 };
yield return new object[] { 1, 2, 3 };
yield return new object[] { -2, 1, 1 };
yield return new object[] { -1, 5, 2 };
yield return new object[] { -3, 1, -2 };
yield return new object[] { 0, -1, 2 };
yield return new object[] { 0, 1, -2 };
yield return new object[] { 3, -1, -2 };
yield return new object[] { 1, -2, -2 };
yield return new object[] { -1, -2, -2 };
}
[Fact]
public void TestEquivalence()
{
// the identity matrix should be equivalent to a matrix
// with negative scale and rotated 180 degrees
var a = Matrix3x2.Identity;
var b = Matrix3x2.Identity;
b *= Matrix3x2.CreateScale(-1, -1);
b *= Matrix3x2.CreateRotation((float)Math.PI);
AssertEqual(a, b, 6);
}
[Theory]
[MemberData(nameof(ScaleRotationData))]
public void TestDeconstruct(float rotation, float scaleX, float scaleY)
{
var translation = new Vector2(5, 9);
var reference = Matrix3x2.Identity;
reference *= Matrix3x2.CreateScale(scaleX, scaleY);
reference *= Matrix3x2.CreateRotation(rotation);
reference *= Matrix3x2.CreateTranslation(translation);
ReferenceDeconstruct(reference, out var refScale, out var refRot, out var refTrans);
// deconstruct
var s = reference.GetScale();
var r = reference.GetRotation();
var t = reference.GetTranslation();
AssertEqual(refScale, s, 5);
Assert.Equal(refRot, r, 5);
AssertEqual(refTrans, t, 5);
var m = Matrix3x2Extensions.CreateMatrix3x2(s, r, t);
AssertEqual(reference, m, 5);
}
[Theory]
[MemberData(nameof(ScaleRotationData))]
public void TestRotationAndScale(float rotation, float scaleX, float scaleY)
{
var translation = new Vector2(5, 9);
var reference = Matrix3x2.Identity;
reference *= Matrix3x2.CreateScale(scaleX, scaleY);
reference *= Matrix3x2.CreateRotation(rotation);
reference *= Matrix3x2.CreateTranslation(translation);
ReferenceDeconstruct(reference, out var refScale, out var refRot, out var refTrans);
// concatenation
// notice that AffineTransform concatenation order
// is in reverse order compared to Matrix3x2
var m1 = Matrix3x2.Identity;
m1 *= Matrix3x2.CreateScale(scaleX, scaleY);
m1 *= Matrix3x2.CreateRotation(rotation);
m1 *= Matrix3x2.CreateTranslation(translation);
AssertOrthogonal(m1, 7);
AssertEqual(reference, m1, 7);
// concatenation and rotation assign
m1 = Matrix3x2.Identity;
m1 *= Matrix3x2.CreateScale(scaleX, scaleY);
m1 = m1.WithRotation(scaleX >= 0 ? rotation : rotation + (float)Math.PI);
m1 *= Matrix3x2.CreateTranslation(translation);
AssertOrthogonal(m1, 7);
AssertEqual(reference, m1, 4);
// concatenation and scale assign
m1 = Matrix3x2.Identity;
m1 = m1.WithScale(new Vector2(scaleX, scaleY));
m1 *= Matrix3x2.CreateRotation(rotation);
m1 *= Matrix3x2.CreateTranslation(translation);
AssertOrthogonal(m1, 7);
AssertEqual(reference, m1, 7);
// deconstruct/reconstruct
m1 = Matrix3x2Extensions.CreateMatrix3x2(new Vector2(scaleX, scaleY), rotation, translation);
var s = m1.GetScale();
var r = m1.GetRotation();
var t = m1.GetTranslation();
m1 = Matrix3x2Extensions.CreateMatrix3x2(s, r, t);
AssertOrthogonal(m1, 7);
AssertEqual(reference, m1, 5);
}
[Fact]
public void TestSettingScale()
{
var m = Matrix3x2.CreateRotation(2);
m = m.WithScale(new Vector2(3, 4));
Assert.Equal(2, m.GetRotation(), 4);
AssertEqual(new Vector2(3, 4), m.GetScale(), 5);
m = m.WithScale(new Vector2(2, 2));
Assert.Equal(2, m.GetRotation(), 4);
AssertEqual(new Vector2(2, 2), m.GetScale(), 5);
}
[Fact]
public void TestAverageScale()
{
for (float r = 0; r < 3; r += 1f)
{
var rm = Matrix3x2.CreateRotation(r);
Assert.Equal(1, (Matrix3x2.CreateScale(1, 1) * rm).GetLengthScale(), 6);
Assert.Equal(1, (Matrix3x2.CreateScale(-1, -1) * rm).GetLengthScale(), 6);
Assert.Equal(2, (Matrix3x2.CreateScale(2, 2) * rm).GetLengthScale(), 6);
Assert.Equal(1.414214f, (Matrix3x2.CreateScale(1, 2) * rm).GetLengthScale(), 5);
Assert.Equal(1.414214f, (Matrix3x2.CreateScale(2, 1) * rm).GetLengthScale(), 5);
Assert.Equal(1.414214f, (Matrix3x2.CreateScale(-2, 1) * rm).GetLengthScale(), 5);
}
}
private static void AssertEqual(Vector2 a, Vector2 b, int precision)
{
Assert.Equal(a.X, b.X, precision);
Assert.Equal(a.Y, b.Y, precision);
}
private static void AssertEqual(Matrix3x2 a, Matrix3x2 b, int precision)
{
Assert.Equal(a.M11, b.M11, precision);
Assert.Equal(a.M21, b.M21, precision);
Assert.Equal(a.M31, b.M31, precision);
Assert.Equal(a.M12, b.M12, precision);
Assert.Equal(a.M22, b.M22, precision);
Assert.Equal(a.M32, b.M32, precision);
}
private static void AssertOrthogonal(Matrix3x2 transform, int precission)
{
var x = Vector2.Normalize(new Vector2(transform.M11, transform.M12));
var y = Vector2.Normalize(new Vector2(transform.M21, transform.M22));
var d = Vector2.Dot(x, y);
if (d < -1) d = -1;
if (d > 1) d = 1;
var skewAngle = (float)((Math.PI / 2) - Math.Acos(d));
Assert.Equal(0, skewAngle, precission);
}
private static void ReferenceDeconstruct(Matrix3x2 m, out Vector2 scale, out float rotation, out Vector2 translation)
{
// Matrix3x2 lacks decompose, but Matrix4x4 has it.
Matrix4x4.Decompose(new Matrix4x4(m), out var s, out Quaternion r, out var t);
scale = new Vector2(s.X, s.Y);
translation = new Vector2(t.X, t.Y);
var hand = Vector3.Transform(Vector3.UnitX, r);
rotation = (float)Math.Atan2(hand.Y, hand.X);
}
}
}