binding/SkiaSharp/SKMatrix.cs (344 lines of code) (raw):

#nullable disable using System; namespace SkiaSharp { public unsafe partial struct SKMatrix { internal const float DegreesToRadians = (float)Math.PI / 180.0f; public readonly static SKMatrix Empty; public readonly static SKMatrix Identity = new SKMatrix { scaleX = 1, scaleY = 1, persp2 = 1 }; private class Indices { public const int ScaleX = 0; public const int SkewX = 1; public const int TransX = 2; public const int SkewY = 3; public const int ScaleY = 4; public const int TransY = 5; public const int Persp0 = 6; public const int Persp1 = 7; public const int Persp2 = 8; public const int Count = 9; } public SKMatrix (float[] values) { if (values == null) throw new ArgumentNullException (nameof (values)); if (values.Length != Indices.Count) throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (values)); scaleX = values[Indices.ScaleX]; skewX = values[Indices.SkewX]; transX = values[Indices.TransX]; skewY = values[Indices.SkewY]; scaleY = values[Indices.ScaleY]; transY = values[Indices.TransY]; persp0 = values[Indices.Persp0]; persp1 = values[Indices.Persp1]; persp2 = values[Indices.Persp2]; } public SKMatrix ( float scaleX, float skewX, float transX, float skewY, float scaleY, float transY, float persp0, float persp1, float persp2) { this.scaleX = scaleX; this.skewX = skewX; this.transX = transX; this.skewY = skewY; this.scaleY = scaleY; this.transY = transY; this.persp0 = persp0; this.persp1 = persp1; this.persp2 = persp2; } public readonly bool IsIdentity => Equals (Identity); // Values public float[] Values { readonly get => new float[9] { scaleX, skewX, transX, skewY, scaleY, transY, persp0, persp1, persp2 }; set { if (value == null) throw new ArgumentNullException (nameof (Values)); if (value.Length != Indices.Count) throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (Values)); scaleX = value[Indices.ScaleX]; skewX = value[Indices.SkewX]; transX = value[Indices.TransX]; skewY = value[Indices.SkewY]; scaleY = value[Indices.ScaleY]; transY = value[Indices.TransY]; persp0 = value[Indices.Persp0]; persp1 = value[Indices.Persp1]; persp2 = value[Indices.Persp2]; } } public readonly void GetValues (float[] values) { if (values == null) throw new ArgumentNullException (nameof (values)); if (values.Length != Indices.Count) throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (values)); values[Indices.ScaleX] = scaleX; values[Indices.SkewX] = skewX; values[Indices.TransX] = transX; values[Indices.SkewY] = skewY; values[Indices.ScaleY] = scaleY; values[Indices.TransY] = transY; values[Indices.Persp0] = persp0; values[Indices.Persp1] = persp1; values[Indices.Persp2] = persp2; } // Create* public static SKMatrix CreateIdentity () => new SKMatrix { scaleX = 1, scaleY = 1, persp2 = 1 }; public static SKMatrix CreateTranslation (float x, float y) { if (x == 0 && y == 0) return Identity; return new SKMatrix { scaleX = 1, scaleY = 1, transX = x, transY = y, persp2 = 1, }; } public static SKMatrix CreateScale (float x, float y) { if (x == 1 && y == 1) return Identity; return new SKMatrix { scaleX = x, scaleY = y, persp2 = 1, }; } public static SKMatrix CreateScale (float x, float y, float pivotX, float pivotY) { if (x == 1 && y == 1) return Identity; var tx = pivotX - x * pivotX; var ty = pivotY - y * pivotY; return new SKMatrix { scaleX = x, scaleY = y, transX = tx, transY = ty, persp2 = 1, }; } public static SKMatrix CreateRotation (float radians) { if (radians == 0) return Identity; var sin = (float)Math.Sin (radians); var cos = (float)Math.Cos (radians); var matrix = Identity; SetSinCos (ref matrix, sin, cos); return matrix; } public static SKMatrix CreateRotation (float radians, float pivotX, float pivotY) { if (radians == 0) return Identity; var sin = (float)Math.Sin (radians); var cos = (float)Math.Cos (radians); var matrix = Identity; SetSinCos (ref matrix, sin, cos, pivotX, pivotY); return matrix; } public static SKMatrix CreateRotationDegrees (float degrees) { if (degrees == 0) return Identity; return CreateRotation (degrees * DegreesToRadians); } public static SKMatrix CreateRotationDegrees (float degrees, float pivotX, float pivotY) { if (degrees == 0) return Identity; return CreateRotation (degrees * DegreesToRadians, pivotX, pivotY); } public static SKMatrix CreateSkew (float x, float y) { if (x == 0 && y == 0) return Identity; return new SKMatrix { scaleX = 1, skewX = x, skewY = y, scaleY = 1, persp2 = 1, }; } public static SKMatrix CreateScaleTranslation (float sx, float sy, float tx, float ty) { if (sx == 0 && sy == 0 && tx == 0 && ty == 0) return Identity; return new SKMatrix { scaleX = sx, skewX = 0, transX = tx, skewY = 0, scaleY = sy, transY = ty, persp0 = 0, persp1 = 0, persp2 = 1, }; } // Invert public readonly bool IsInvertible { get { fixed (SKMatrix* t = &this) { return SkiaApi.sk_matrix_try_invert (t, null); } } } public readonly bool TryInvert (out SKMatrix inverse) { fixed (SKMatrix* i = &inverse) fixed (SKMatrix* t = &this) { return SkiaApi.sk_matrix_try_invert (t, i); } } public readonly SKMatrix Invert () { if (TryInvert (out var matrix)) return matrix; return Empty; } // *Concat public static SKMatrix Concat (SKMatrix first, SKMatrix second) { SKMatrix target; SkiaApi.sk_matrix_concat (&target, &first, &second); return target; } public readonly SKMatrix PreConcat (SKMatrix matrix) { var target = this; SkiaApi.sk_matrix_pre_concat (&target, &matrix); return target; } public readonly SKMatrix PostConcat (SKMatrix matrix) { var target = this; SkiaApi.sk_matrix_post_concat (&target, &matrix); return target; } public static void Concat (ref SKMatrix target, SKMatrix first, SKMatrix second) { fixed (SKMatrix* t = &target) { SkiaApi.sk_matrix_concat (t, &first, &second); } } // MapRect public readonly SKRect MapRect (SKRect source) { SKRect dest; fixed (SKMatrix* m = &this) { SkiaApi.sk_matrix_map_rect (m, &dest, &source); } return dest; } // MapPoints public readonly SKPoint MapPoint (SKPoint point) => MapPoint (point.X, point.Y); public readonly SKPoint MapPoint (float x, float y) { SKPoint result; fixed (SKMatrix* t = &this) { SkiaApi.sk_matrix_map_xy (t, x, y, &result); } return result; } public readonly void MapPoints (SKPoint[] result, SKPoint[] points) { if (result == null) throw new ArgumentNullException (nameof (result)); if (points == null) throw new ArgumentNullException (nameof (points)); if (result.Length != points.Length) throw new ArgumentException ("Buffers must be the same size."); fixed (SKMatrix* t = &this) fixed (SKPoint* rp = result) fixed (SKPoint* pp = points) { SkiaApi.sk_matrix_map_points (t, rp, pp, result.Length); } } public readonly SKPoint[] MapPoints (SKPoint[] points) { if (points == null) throw new ArgumentNullException (nameof (points)); var res = new SKPoint[points.Length]; MapPoints (res, points); return res; } // MapVectors public readonly SKPoint MapVector (SKPoint vector) => MapVector (vector.X, vector.Y); public readonly SKPoint MapVector (float x, float y) { SKPoint result; fixed (SKMatrix* t = &this) { SkiaApi.sk_matrix_map_vector (t, x, y, &result); } return result; } public readonly void MapVectors (SKPoint[] result, SKPoint[] vectors) { if (result == null) throw new ArgumentNullException (nameof (result)); if (vectors == null) throw new ArgumentNullException (nameof (vectors)); if (result.Length != vectors.Length) throw new ArgumentException ("Buffers must be the same size."); fixed (SKMatrix* t = &this) fixed (SKPoint* rp = result) fixed (SKPoint* pp = vectors) { SkiaApi.sk_matrix_map_vectors (t, rp, pp, result.Length); } } public readonly SKPoint[] MapVectors (SKPoint[] vectors) { if (vectors == null) throw new ArgumentNullException (nameof (vectors)); var res = new SKPoint[vectors.Length]; MapVectors (res, vectors); return res; } // MapRadius public readonly float MapRadius (float radius) { fixed (SKMatrix* t = &this) { return SkiaApi.sk_matrix_map_radius (t, radius); } } // private private static void SetSinCos (ref SKMatrix matrix, float sin, float cos) { matrix.scaleX = cos; matrix.skewX = -sin; matrix.transX = 0; matrix.skewY = sin; matrix.scaleY = cos; matrix.transY = 0; matrix.persp0 = 0; matrix.persp1 = 0; matrix.persp2 = 1; } private static void SetSinCos (ref SKMatrix matrix, float sin, float cos, float pivotx, float pivoty) { float oneMinusCos = 1 - cos; matrix.scaleX = cos; matrix.skewX = -sin; matrix.transX = Dot (sin, pivoty, oneMinusCos, pivotx); matrix.skewY = sin; matrix.scaleY = cos; matrix.transY = Dot (-sin, pivotx, oneMinusCos, pivoty); matrix.persp0 = 0; matrix.persp1 = 0; matrix.persp2 = 1; } private static float Dot (float a, float b, float c, float d) => a * b + c * d; private static float Cross (float a, float b, float c, float d) => a * b - c * d; } }