From 953c5700a91c8743505fd24c8f27528f87a9e057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Laban?= Date: Mon, 18 Sep 2023 16:11:01 -0400 Subject: [PATCH 01/15] fix: Restore nosimd builds for edge enhanced security and safari (#2612) --- native/wasm/build.cake | 2 +- scripts/azure-templates-stages.yml | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/native/wasm/build.cake b/native/wasm/build.cake index 8163367d89..8256df7d89 100644 --- a/native/wasm/build.cake +++ b/native/wasm/build.cake @@ -114,7 +114,7 @@ Task("libHarfBuzzSharp") .WithCriteria(IsRunningOnLinux()) .Does(() => { - bool hasSimdEnabled = EMSCRIPTEN_FEATURES.Contains("simd") || EMSCRIPTEN_FEATURES.Contains("_simd");; + bool hasSimdEnabled = EMSCRIPTEN_FEATURES.Contains("simd") || EMSCRIPTEN_FEATURES.Contains("_simd"); bool hasThreadingEnabled = EMSCRIPTEN_FEATURES.Contains("mt"); bool hasWasmEH = EMSCRIPTEN_FEATURES.Contains("_wasmeh"); diff --git a/scripts/azure-templates-stages.yml b/scripts/azure-templates-stages.yml index 59d63b02ab..b8bd1035ca 100644 --- a/scripts/azure-templates-stages.yml +++ b/scripts/azure-templates-stages.yml @@ -307,15 +307,23 @@ stages: version: 3.1.12 features: mt,simd - # .NET 8 Preview 4 + # .NET 8 - 3.1.34: displayName: 3.1.34 version: 3.1.34 - features: _wasmeh,_simd,st + features: _wasmeh,st - 3.1.34: displayName: '3.1.34_Threading' version: 3.1.34 - features: _wasmeh,_simd,mt + features: _wasmeh,mt + - 3.1.34: + displayName: '3.1.34_SIMD' + version: 3.1.34 + features: _wasmeh,simd,st + - 3.1.34: + displayName: '3.1.34_SIMD_Threading' + version: 3.1.34 + features: _wasmeh,simd,mt - ${{ if ne(parameters.buildPipelineType, 'tests') }}: - stage: managed From 11ad3645be6a1d20576ed1a47f00a9a5667a4006 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Tue, 19 Sep 2023 19:56:58 +0200 Subject: [PATCH 02/15] Update webp to 1.3.2 (#2622) --- externals/skia | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/skia b/externals/skia index 3c98e7cdf2..83c17a6dee 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit 3c98e7cdf210a560c614933893b4f5566f0b83cc +Subproject commit 83c17a6dee5af2db80af57197627f6fbbe4ad272 From 06c4abb1ef33707c69297a3b5e5c34efa856d993 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Thu, 21 Sep 2023 20:04:18 +0200 Subject: [PATCH 03/15] Update README.md (#2625) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 19ee571cb9..cf40ee6a12 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,6 @@ However, these are easy to install as they are found on the various websites. If Here are some links to show the differences in our code as compared to Google's code. -What version are we on? [**m88**](https://github.com/google/skia/tree/chrome/m88) -Are we up-to-date with Google? [Compare](https://github.com/mono/skia/compare/xamarin-mobile-bindings...google:chrome/m88) -What have we added? [Compare](https://github.com/google/skia/compare/chrome/m88...mono:xamarin-mobile-bindings) +What version are we on? [**m88**](https://github.com/google/skia/tree/chrome/m115) +Are we up-to-date with Google? [Compare](https://github.com/mono/skia/compare/skiasharp...google:chrome/m115) +What have we added? [Compare](https://github.com/google/skia/compare/chrome/m115...mono:skiasharp) From 25e70a390e2128e5a54d28795365bf9fdaa7161c Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Fri, 22 Sep 2023 17:01:19 +0200 Subject: [PATCH 04/15] Update README.md (#2627) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf40ee6a12..81a4dc0f4f 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,6 @@ However, these are easy to install as they are found on the various websites. If Here are some links to show the differences in our code as compared to Google's code. -What version are we on? [**m88**](https://github.com/google/skia/tree/chrome/m115) +What version are we on? [**m115**](https://github.com/google/skia/tree/chrome/m115) Are we up-to-date with Google? [Compare](https://github.com/mono/skia/compare/skiasharp...google:chrome/m115) What have we added? [Compare](https://github.com/google/skia/compare/chrome/m115...mono:skiasharp) From 3767655a0deb784bbd2231f4672240c4fa9b7437 Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Fri, 10 Nov 2023 03:44:49 -0500 Subject: [PATCH 05/15] Convert many arrays to spans, or add span overloads where array overloads are still useful. Some null checks have been removed as they are immediately followed by minimum length checks, which effectively accomplish the same goal. --- binding/SkiaSharp/SKCanvas.cs | 49 ++++++--- binding/SkiaSharp/SKCodec.cs | 10 ++ binding/SkiaSharp/SKColorFilter.cs | 10 +- binding/SkiaSharp/SKColorSpace.cs | 5 +- binding/SkiaSharp/SKColorSpaceStructs.cs | 120 ++++++++++++++++++----- binding/SkiaSharp/SKData.cs | 6 ++ binding/SkiaSharp/SKFont.cs | 5 +- binding/SkiaSharp/SKMaskFilter.cs | 4 +- binding/SkiaSharp/SKMatrix.cs | 57 +++++------ binding/SkiaSharp/SKPMColor.cs | 24 ++++- binding/SkiaSharp/SKPath.cs | 33 +++++-- binding/SkiaSharp/SKRuntimeEffect.cs | 8 +- binding/SkiaSharp/SKShader.cs | 60 ++++++------ binding/SkiaSharp/SKStream.cs | 20 +++- binding/SkiaSharp/SKString.cs | 31 ++++-- binding/SkiaSharp/SKTypeface.cs | 11 +++ binding/SkiaSharp/SKVertices.cs | 8 +- 17 files changed, 321 insertions(+), 140 deletions(-) diff --git a/binding/SkiaSharp/SKCanvas.cs b/binding/SkiaSharp/SKCanvas.cs index b4124c5b96..98f2f56dc3 100644 --- a/binding/SkiaSharp/SKCanvas.cs +++ b/binding/SkiaSharp/SKCanvas.cs @@ -409,7 +409,7 @@ public void DrawPath (SKPath path, SKPaint paint) // DrawPoints - public void DrawPoints (SKPointMode mode, SKPoint[] points, SKPaint paint) + public void DrawPoints (SKPointMode mode, ReadOnlySpan points, SKPaint paint) { if (paint == null) throw new ArgumentNullException (nameof (paint)); @@ -732,6 +732,13 @@ public SKData DrawUrlAnnotation (SKRect rect, string value) return data; } + public SKData DrawUrlAnnotation (SKRect rect, ReadOnlySpan value) + { + var data = SKData.FromCString (value); + DrawUrlAnnotation (rect, data); + return data; + } + public void DrawNamedDestinationAnnotation (SKPoint point, SKData value) { SkiaApi.sk_canvas_draw_named_destination_annotation (Handle, &point, value == null ? IntPtr.Zero : value.Handle); @@ -744,6 +751,13 @@ public SKData DrawNamedDestinationAnnotation (SKPoint point, string value) return data; } + public SKData DrawNamedDestinationAnnotation (SKPoint point, ReadOnlySpan value) + { + var data = SKData.FromCString (value); + DrawNamedDestinationAnnotation (point, data); + return data; + } + public void DrawLinkDestinationAnnotation (SKRect rect, SKData value) { SkiaApi.sk_canvas_draw_link_destination_annotation (Handle, &rect, value == null ? IntPtr.Zero : value.Handle); @@ -756,6 +770,13 @@ public SKData DrawLinkDestinationAnnotation (SKRect rect, string value) return data; } + public SKData DrawLinkDestinationAnnotation (SKRect rect, ReadOnlySpan value) + { + var data = SKData.FromCString (value); + DrawLinkDestinationAnnotation (rect, data); + return data; + } + // Draw*NinePatch public void DrawBitmapNinePatch (SKBitmap bitmap, SKRectI center, SKRect dst, SKPaint paint = null) => @@ -879,25 +900,25 @@ public SKMatrix44 TotalMatrix44 { // DrawVertices - public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKColor[] colors, SKPaint paint) + public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan colors, SKPaint paint) { var vert = SKVertices.CreateCopy (vmode, vertices, colors); DrawVertices (vert, SKBlendMode.Modulate, paint); } - public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKPaint paint) + public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan texs, ReadOnlySpan colors, SKPaint paint) { var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors); DrawVertices (vert, SKBlendMode.Modulate, paint); } - public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, UInt16[] indices, SKPaint paint) + public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan texs, ReadOnlySpan colors, ReadOnlySpan indices, SKPaint paint) { var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices); DrawVertices (vert, SKBlendMode.Modulate, paint); } - public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKBlendMode mode, UInt16[] indices, SKPaint paint) + public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan texs, ReadOnlySpan colors, SKBlendMode mode, ReadOnlySpan indices, SKPaint paint) { var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices); DrawVertices (vert, mode, paint); @@ -937,25 +958,25 @@ public void DrawRoundRectDifference (SKRoundRect outer, SKRoundRect inner, SKPai // DrawAtlas - public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKPaint paint) => + public void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, SKPaint paint) => DrawAtlas (atlas, sprites, transforms, null, SKBlendMode.Dst, SKSamplingOptions.Default, null, paint); - public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKSamplingOptions sampling, SKPaint paint) => + public void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, SKSamplingOptions sampling, SKPaint paint) => DrawAtlas (atlas, sprites, transforms, null, SKBlendMode.Dst, sampling, null, paint); - public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKPaint paint) => + public void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, ReadOnlySpan colors, SKBlendMode mode, SKPaint paint) => DrawAtlas (atlas, sprites, transforms, colors, mode, SKSamplingOptions.Default, null, paint); - public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKSamplingOptions sampling, SKPaint paint) => + public void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, ReadOnlySpan colors, SKBlendMode mode, SKSamplingOptions sampling, SKPaint paint) => DrawAtlas (atlas, sprites, transforms, colors, mode, sampling, null, paint); - public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKRect cullRect, SKPaint paint) => + public void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, ReadOnlySpan colors, SKBlendMode mode, SKRect cullRect, SKPaint paint) => DrawAtlas (atlas, sprites, transforms, colors, mode, SKSamplingOptions.Default, &cullRect, paint); - public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKSamplingOptions sampling, SKRect cullRect, SKPaint paint) => + public void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, ReadOnlySpan colors, SKBlendMode mode, SKSamplingOptions sampling, SKRect cullRect, SKPaint paint) => DrawAtlas (atlas, sprites, transforms, colors, mode, sampling, &cullRect, paint); - private void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKColor[] colors, SKBlendMode mode, SKSamplingOptions sampling, SKRect* cullRect, SKPaint paint) + private void DrawAtlas (SKImage atlas, ReadOnlySpan sprites, ReadOnlySpan transforms, ReadOnlySpan colors, SKBlendMode mode, SKSamplingOptions sampling, SKRect* cullRect, SKPaint paint) { if (atlas == null) throw new ArgumentNullException (nameof (atlas)); @@ -978,10 +999,10 @@ private void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] // DrawPatch - public void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKPaint paint) => + public void DrawPatch (ReadOnlySpan cubics, ReadOnlySpan colors, ReadOnlySpan texCoords, SKPaint paint) => DrawPatch (cubics, colors, texCoords, SKBlendMode.Modulate, paint); - public void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKBlendMode mode, SKPaint paint) + public void DrawPatch (ReadOnlySpan cubics, ReadOnlySpan colors, ReadOnlySpan texCoords, SKBlendMode mode, SKPaint paint) { if (cubics == null) throw new ArgumentNullException (nameof (cubics)); diff --git a/binding/SkiaSharp/SKCodec.cs b/binding/SkiaSharp/SKCodec.cs index 859f8b7825..9065a017e1 100644 --- a/binding/SkiaSharp/SKCodec.cs +++ b/binding/SkiaSharp/SKCodec.cs @@ -110,6 +110,16 @@ public SKCodecResult GetPixels (SKImageInfo info, byte[] pixels) } } + public SKCodecResult GetPixels (SKImageInfo info, Span pixels) + { + if (pixels == null) + throw new ArgumentNullException (nameof (pixels)); + + fixed (byte* p = pixels) { + return GetPixels (info, (IntPtr)p, info.RowBytes, SKCodecOptions.Default); + } + } + public SKCodecResult GetPixels (SKImageInfo info, IntPtr pixels) => GetPixels (info, pixels, info.RowBytes, SKCodecOptions.Default); diff --git a/binding/SkiaSharp/SKColorFilter.cs b/binding/SkiaSharp/SKColorFilter.cs index 31bb1a9ced..c9df6ef9a2 100644 --- a/binding/SkiaSharp/SKColorFilter.cs +++ b/binding/SkiaSharp/SKColorFilter.cs @@ -38,10 +38,8 @@ public static SKColorFilter CreateCompose(SKColorFilter outer, SKColorFilter inn return GetObject (SkiaApi.sk_colorfilter_new_compose(outer.Handle, inner.Handle)); } - public static SKColorFilter CreateColorMatrix(float[] matrix) + public static SKColorFilter CreateColorMatrix (ReadOnlySpan matrix) { - if (matrix == null) - throw new ArgumentNullException(nameof(matrix)); if (matrix.Length != 20) throw new ArgumentException("Matrix must have a length of 20.", nameof(matrix)); fixed (float* m = matrix) { @@ -54,10 +52,8 @@ public static SKColorFilter CreateLumaColor() return GetObject (SkiaApi.sk_colorfilter_new_luma_color()); } - public static SKColorFilter CreateTable(byte[] table) + public static SKColorFilter CreateTable (ReadOnlySpan table) { - if (table == null) - throw new ArgumentNullException(nameof(table)); if (table.Length != TableMaxLength) throw new ArgumentException($"Table must have a length of {TableMaxLength}.", nameof(table)); fixed (byte* t = table) { @@ -65,7 +61,7 @@ public static SKColorFilter CreateTable(byte[] table) } } - public static SKColorFilter CreateTable(byte[] tableA, byte[] tableR, byte[] tableG, byte[] tableB) + public static SKColorFilter CreateTable (ReadOnlySpan tableA, ReadOnlySpan tableR, ReadOnlySpan tableG, ReadOnlySpan tableB) { if (tableA != null && tableA.Length != TableMaxLength) throw new ArgumentException($"Table A must have a length of {TableMaxLength}.", nameof(tableA)); diff --git a/binding/SkiaSharp/SKColorSpace.cs b/binding/SkiaSharp/SKColorSpace.cs index 1b5680df8f..8e815ba074 100644 --- a/binding/SkiaSharp/SKColorSpace.cs +++ b/binding/SkiaSharp/SKColorSpace.cs @@ -73,7 +73,10 @@ public static bool Equal (SKColorSpace left, SKColorSpace right) public static SKColorSpace CreateIcc (IntPtr input, long length) => CreateIcc (SKColorSpaceIccProfile.Create (input, length)); - public static SKColorSpace CreateIcc (byte[] input, long length) + public static SKColorSpace CreateIcc (byte[] input, long length) => + CreateIcc (input.AsSpan (), length); + + public static SKColorSpace CreateIcc (ReadOnlySpan input, long length) { if (input == null) throw new ArgumentNullException (nameof (input)); diff --git a/binding/SkiaSharp/SKColorSpaceStructs.cs b/binding/SkiaSharp/SKColorSpaceStructs.cs index 35c51a1193..4c0d63f1e1 100644 --- a/binding/SkiaSharp/SKColorSpaceStructs.cs +++ b/binding/SkiaSharp/SKColorSpaceStructs.cs @@ -9,10 +9,8 @@ public unsafe partial struct SKColorSpacePrimaries { public static readonly SKColorSpacePrimaries Empty; - public SKColorSpacePrimaries (float[] values) + public SKColorSpacePrimaries (ReadOnlySpan values) { - if (values == null) - throw new ArgumentNullException (nameof (values)); if (values.Length != 8) throw new ArgumentException ("The values must have exactly 8 items, one for each of [RX, RY, GX, GY, BX, BY, WX, WY].", nameof (values)); @@ -41,6 +39,36 @@ public SKColorSpacePrimaries (float rx, float ry, float gx, float gy, float bx, public readonly float[] Values => new[] { fRX, fRY, fGX, fGY, fBX, fBY, fWX, fWY }; + public readonly void GetValues (Span values) + { + if (values.Length != 8) + throw new ArgumentException ("The values must have exactly 8 items, one for each of [RX, RY, GX, GY, BX, BY, WX, WY].", nameof (values)); + + values[0] = fRX; + values[1] = fRY; + values[2] = fGX; + values[3] = fGY; + values[4] = fBX; + values[5] = fBY; + values[6] = fWX; + values[7] = fWY; + } + + public void SetValues (ReadOnlySpan values) + { + if (values.Length != 8) + throw new ArgumentException ("The values must have exactly 8 items, one for each of [RX, RY, GX, GY, BX, BY, WX, WY].", nameof (values)); + + fRX = values[0]; + fRY = values[1]; + fGX = values[2]; + fGY = values[3]; + fBX = values[4]; + fBY = values[5]; + fWX = values[6]; + fWY = values[7]; + } + public readonly bool ToColorSpaceXyz (out SKColorSpaceXyz toXyzD50) { fixed (SKColorSpacePrimaries* t = &this) @@ -105,10 +133,8 @@ public static SKColorSpaceTransferFn Hlg { public static readonly SKColorSpaceTransferFn Empty; - public SKColorSpaceTransferFn (float[] values) + public SKColorSpaceTransferFn (ReadOnlySpan values) { - if (values == null) - throw new ArgumentNullException (nameof (values)); if (values.Length != 7) throw new ArgumentException ("The values must have exactly 7 items, one for each of [G, A, B, C, D, E, F].", nameof (values)); @@ -135,6 +161,35 @@ public SKColorSpaceTransferFn (float g, float a, float b, float c, float d, floa public readonly float[] Values => new[] { fG, fA, fB, fC, fD, fE, fF }; + + public readonly void GetValues (Span values) + { + if (values.Length != 7) + throw new ArgumentException ("The values must have exactly 7 items, one for each of [G, A, B, C, D, E, F].", nameof (values)); + + values[0] = fG; + values[1] = fA; + values[2] = fB; + values[3] = fC; + values[4] = fD; + values[5] = fE; + values[6] = fF; + } + + public void SetValues (ReadOnlySpan values) + { + if (values.Length != 8) + throw new ArgumentException ("The values must have exactly 7 items, one for each of [G, A, B, C, D, E, F].", nameof (values)); + + fG = values[0]; + fA = values[1]; + fB = values[2]; + fC = values[3]; + fD = values[4]; + fE = values[5]; + fF = values[6]; + } + public readonly SKColorSpaceTransferFn Invert () { SKColorSpaceTransferFn inverted; @@ -217,12 +272,10 @@ public SKColorSpaceXyz (float value) fM22 = value; } - public SKColorSpaceXyz (float[] values) + public SKColorSpaceXyz (ReadOnlySpan values) { - if (values == null) - throw new ArgumentNullException (nameof (values)); if (values.Length != 9) - throw new ArgumentException ("The matrix array must have a length of 9.", nameof (values)); + throw new ArgumentException ("The values must have a length of 9.", nameof (values)); fM00 = values[0]; fM01 = values[1]; @@ -261,22 +314,43 @@ public float[] Values { fM10, fM11, fM12, fM20, fM21, fM22, }; - set { - if (value.Length != 9) - throw new ArgumentException ("The matrix array must have a length of 9.", nameof (value)); + set => SetValues (new ReadOnlySpan (value)); + } - fM00 = value[0]; - fM01 = value[1]; - fM02 = value[2]; + public readonly void GetValues (Span values) + { + if (values.Length != 9) + throw new ArgumentException ("The values must have a length of 9.", nameof(values)); - fM10 = value[3]; - fM11 = value[4]; - fM12 = value[5]; + values[0] = fM00; + values[1] = fM01; + values[2] = fM02; - fM20 = value[6]; - fM21 = value[7]; - fM22 = value[8]; - } + values[3] = fM10; + values[4] = fM11; + values[5] = fM12; + + values[6] = fM20; + values[7] = fM21; + values[8] = fM22; + } + + public void SetValues (ReadOnlySpan values) + { + if (values.Length != 9) + throw new ArgumentException ("The values must have a length of 9.", nameof(values)); + + fM00 = values[0]; + fM01 = values[1]; + fM02 = values[2]; + + fM10 = values[3]; + fM11 = values[4]; + fM12 = values[5]; + + fM20 = values[6]; + fM21 = values[7]; + fM22 = values[8]; } public readonly float this[int x, int y] { diff --git a/binding/SkiaSharp/SKData.cs b/binding/SkiaSharp/SKData.cs index e0ef2e59d0..ffdb00ee79 100644 --- a/binding/SkiaSharp/SKData.cs +++ b/binding/SkiaSharp/SKData.cs @@ -193,6 +193,12 @@ internal static SKData FromCString (string str) return SKData.CreateCopy (bytes, (ulong)(bytes.Length + 1)); // + 1 for the terminating char } + internal static SKData FromCString (ReadOnlySpan str) + { + var bytes = Encoding.ASCII.GetBytes (str); + return SKData.CreateCopy (bytes, (ulong)(bytes.Length + 1)); // + 1 for the terminating char + } + // Subset public SKData Subset (ulong offset, ulong length) diff --git a/binding/SkiaSharp/SKFont.cs b/binding/SkiaSharp/SKFont.cs index 3c3148d693..0559bc2b45 100644 --- a/binding/SkiaSharp/SKFont.cs +++ b/binding/SkiaSharp/SKFont.cs @@ -232,7 +232,10 @@ public bool ContainsGlyphs (IntPtr text, int length, SKTextEncoding encoding) => ContainsGlyphs (GetGlyphs (text, length, encoding)); private bool ContainsGlyphs (ushort[] glyphs) => - Array.IndexOf (glyphs, (ushort)0) == -1; + ContainsGlyphs (new ReadOnlySpan (glyphs)); + + private bool ContainsGlyphs (ReadOnlySpan glyphs) => + glyphs.IndexOf ((ushort)0) == -1; // CountGlyphs diff --git a/binding/SkiaSharp/SKMaskFilter.cs b/binding/SkiaSharp/SKMaskFilter.cs index 012dad4c8d..1504996634 100644 --- a/binding/SkiaSharp/SKMaskFilter.cs +++ b/binding/SkiaSharp/SKMaskFilter.cs @@ -41,10 +41,8 @@ public static SKMaskFilter CreateBlur (SKBlurStyle blurStyle, float sigma, bool return GetObject (SkiaApi.sk_maskfilter_new_blur_with_flags (blurStyle, sigma, respectCTM)); } - public static SKMaskFilter CreateTable (byte[] table) + public static SKMaskFilter CreateTable (ReadOnlySpan table) { - if (table == null) - throw new ArgumentNullException (nameof (table)); if (table.Length != TableMaxLength) throw new ArgumentException ("Table must have a length of {SKColorTable.MaxLength}.", nameof (table)); fixed (byte* t = table) { diff --git a/binding/SkiaSharp/SKMatrix.cs b/binding/SkiaSharp/SKMatrix.cs index 5ee7a54467..d7701e0d8f 100644 --- a/binding/SkiaSharp/SKMatrix.cs +++ b/binding/SkiaSharp/SKMatrix.cs @@ -27,12 +27,10 @@ private class Indices public const int Count = 9; } - public SKMatrix (float[] values) + public SKMatrix (ReadOnlySpan 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)); + throw new ArgumentException ($"The values must have a length of {Indices.Count}.", nameof (values)); scaleX = values[Indices.ScaleX]; skewX = values[Indices.SkewX]; @@ -74,32 +72,13 @@ public float[] Values { 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]; - } + set => SetValues (new ReadOnlySpan (value)); } - public readonly void GetValues (float[] values) + public readonly void GetValues (Span 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)); + throw new ArgumentException ($"The values must have a length of {Indices.Count}.", nameof (values)); values[Indices.ScaleX] = scaleX; values[Indices.SkewX] = skewX; @@ -114,6 +93,24 @@ public readonly void GetValues (float[] values) values[Indices.Persp2] = persp2; } + public void SetValues (ReadOnlySpan values) + { + if (values.Length != Indices.Count) + throw new ArgumentException ($"The values 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]; + } + // Create* public static SKMatrix CreateIdentity () => @@ -319,7 +316,7 @@ public readonly SKPoint MapPoint (float x, float y) return result; } - public readonly void MapPoints (SKPoint[] result, SKPoint[] points) + public readonly void MapPoints (Span result, ReadOnlySpan points) { if (result == null) throw new ArgumentNullException (nameof (result)); @@ -335,7 +332,7 @@ public readonly void MapPoints (SKPoint[] result, SKPoint[] points) } } - public readonly SKPoint[] MapPoints (SKPoint[] points) + public readonly SKPoint[] MapPoints (ReadOnlySpan points) { if (points == null) throw new ArgumentNullException (nameof (points)); @@ -359,7 +356,7 @@ public readonly SKPoint MapVector (float x, float y) return result; } - public readonly void MapVectors (SKPoint[] result, SKPoint[] vectors) + public readonly void MapVectors (Span result, ReadOnlySpan vectors) { if (result == null) throw new ArgumentNullException (nameof (result)); @@ -375,7 +372,7 @@ public readonly void MapVectors (SKPoint[] result, SKPoint[] vectors) } } - public readonly SKPoint[] MapVectors (SKPoint[] vectors) + public readonly SKPoint[] MapVectors (ReadOnlySpan vectors) { if (vectors == null) throw new ArgumentNullException (nameof (vectors)); diff --git a/binding/SkiaSharp/SKPMColor.cs b/binding/SkiaSharp/SKPMColor.cs index 4be0398ae4..a137f841b3 100644 --- a/binding/SkiaSharp/SKPMColor.cs +++ b/binding/SkiaSharp/SKPMColor.cs @@ -23,7 +23,7 @@ public SKPMColor (uint value) public static SKPMColor PreMultiply (SKColor color) => SkiaApi.sk_color_premultiply ((uint)color); - public static SKPMColor[] PreMultiply (SKColor[] colors) + public static SKPMColor[] PreMultiply (ReadOnlySpan colors) { var pmcolors = new SKPMColor[colors.Length]; fixed (SKColor* c = colors) @@ -33,12 +33,22 @@ public static SKPMColor[] PreMultiply (SKColor[] colors) return pmcolors; } + public static void PreMultiply (Span pmcolors, ReadOnlySpan colors) + { + if (pmcolors.Length != colors.Length) + throw new ArgumentException ("The length of pmcolors must be the same as the length of colors.", nameof (pmcolors)); + fixed (SKColor* c = colors) + fixed (SKPMColor* pm = pmcolors) { + SkiaApi.sk_color_premultiply_array ((uint*)c, colors.Length, (uint*)pm); + } + } + // UnPreMultiply public static SKColor UnPreMultiply (SKPMColor pmcolor) => SkiaApi.sk_color_unpremultiply ((uint)pmcolor); - public static SKColor[] UnPreMultiply (SKPMColor[] pmcolors) + public static SKColor[] UnPreMultiply (ReadOnlySpan pmcolors) { var colors = new SKColor[pmcolors.Length]; fixed (SKColor* c = colors) @@ -48,6 +58,16 @@ public static SKColor[] UnPreMultiply (SKPMColor[] pmcolors) return colors; } + public static void UnPreMultiply (Span colors, ReadOnlySpan pmcolors) + { + if (colors.Length != pmcolors.Length) + throw new ArgumentException ("The length of colors must be the same as the length of pmcolors.", nameof (colors)); + fixed (SKColor* c = colors) + fixed (SKPMColor* pm = pmcolors) { + SkiaApi.sk_color_unpremultiply_array ((uint*)pm, pmcolors.Length, (uint*)c); + } + } + public static explicit operator SKPMColor (SKColor color) => SKPMColor.PreMultiply (color); diff --git a/binding/SkiaSharp/SKPath.cs b/binding/SkiaSharp/SKPath.cs index d1a0d7f0e1..28166bda0d 100644 --- a/binding/SkiaSharp/SKPath.cs +++ b/binding/SkiaSharp/SKPath.cs @@ -122,13 +122,26 @@ public SKRoundRect GetRoundRect () public SKPoint[] GetLine () { - var temp = new SKPoint[2]; - fixed (SKPoint* t = temp) { - var result = SkiaApi.sk_path_is_line (Handle, t); + Span temp = stackalloc SKPoint[2]; + if (TryGetLine (temp)) { + return temp.ToArray (); + } else { + return null; + } + } + + public bool TryGetLine (Span points) + { + if (points.Length != 2) + throw new ArgumentException ("Points must have a length of 2."); + + fixed (SKPoint* p = points) { + var result = SkiaApi.sk_path_is_line (Handle, p); if (result) { - return temp; + return true; } else { - return null; + points.Clear (); + return false; } } } @@ -168,8 +181,10 @@ public SKPoint[] GetPoints (int max) return points; } - public int GetPoints (SKPoint[] points, int max) + public int GetPoints (Span points, int max) { + if (points == null) + throw new ArgumentNullException (nameof (points)); fixed (SKPoint* p = points) { return SkiaApi.sk_path_get_points (Handle, p, max); } @@ -379,7 +394,7 @@ public void AddRoundRect (SKRect rect, float rx, float ry, SKPathDirection dir = public void AddCircle (float x, float y, float radius, SKPathDirection dir = SKPathDirection.Clockwise) => SkiaApi.sk_path_add_circle (Handle, x, y, radius, dir); - public void AddPoly (SKPoint[] points, bool close = true) + public void AddPoly (ReadOnlySpan points, bool close = true) { if (points == null) throw new ArgumentNullException (nameof (points)); @@ -489,10 +504,10 @@ public static int ConvertConicToQuads (SKPoint p0, SKPoint p1, SKPoint p2, float var quadCount = 1 << pow2; var ptCount = 2 * quadCount + 1; pts = new SKPoint[ptCount]; - return ConvertConicToQuads (p0, p1, p2, w, pts, pow2); + return ConvertConicToQuads (p0, p1, p2, w, new Span (pts), pow2); } - public static int ConvertConicToQuads (SKPoint p0, SKPoint p1, SKPoint p2, float w, SKPoint[] pts, int pow2) + public static int ConvertConicToQuads (SKPoint p0, SKPoint p1, SKPoint p2, float w, Span pts, int pow2) { if (pts == null) throw new ArgumentNullException (nameof (pts)); diff --git a/binding/SkiaSharp/SKRuntimeEffect.cs b/binding/SkiaSharp/SKRuntimeEffect.cs index 8487aa8f86..3092262553 100644 --- a/binding/SkiaSharp/SKRuntimeEffect.cs +++ b/binding/SkiaSharp/SKRuntimeEffect.cs @@ -19,7 +19,7 @@ internal SKRuntimeEffect (IntPtr handle, bool owns) // Create* - public static SKRuntimeEffect CreateShader (string sksl, out string errors) + public static SKRuntimeEffect CreateShader (ReadOnlySpan sksl, out string errors) { using var s = new SKString (sksl); using var errorString = new SKString (); @@ -30,7 +30,7 @@ public static SKRuntimeEffect CreateShader (string sksl, out string errors) return effect; } - public static SKRuntimeEffect CreateColorFilter (string sksl, out string errors) + public static SKRuntimeEffect CreateColorFilter (ReadOnlySpan sksl, out string errors) { using var s = new SKString (sksl); using var errorString = new SKString (); @@ -43,14 +43,14 @@ public static SKRuntimeEffect CreateColorFilter (string sksl, out string errors) // Build* - public static SKRuntimeShaderBuilder BuildShader (string sksl) + public static SKRuntimeShaderBuilder BuildShader (ReadOnlySpan sksl) { var effect = CreateShader (sksl, out var errors); ValidateResult (effect, errors); return new SKRuntimeShaderBuilder (effect); } - public static SKRuntimeColorFilterBuilder BuildColorFilter (string sksl) + public static SKRuntimeColorFilterBuilder BuildColorFilter (ReadOnlySpan sksl) { var effect = CreateColorFilter (sksl, out var errors); ValidateResult (effect, errors); diff --git a/binding/SkiaSharp/SKShader.cs b/binding/SkiaSharp/SKShader.cs index 9216985672..63ad300f89 100644 --- a/binding/SkiaSharp/SKShader.cs +++ b/binding/SkiaSharp/SKShader.cs @@ -111,10 +111,10 @@ public static SKShader CreatePicture (SKPicture src, SKShaderTileMode tmx, SKSha // CreateLinearGradient - public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor[] colors, SKShaderTileMode mode) => + public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, ReadOnlySpan colors, SKShaderTileMode mode) => CreateLinearGradient (start, end, colors, null, mode); - public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor[] colors, float[] colorPos, SKShaderTileMode mode) + public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode mode) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -128,7 +128,7 @@ public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor } } - public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor[] colors, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix) + public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode mode, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -142,10 +142,10 @@ public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor } } - public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode mode) => + public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, ReadOnlySpan colors, SKColorSpace colorspace, SKShaderTileMode mode) => CreateLinearGradient (start, end, colors, colorspace, null, mode); - public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode) + public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode mode) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -159,7 +159,7 @@ public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor } } - public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix) + public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode mode, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -175,10 +175,10 @@ public static SKShader CreateLinearGradient (SKPoint start, SKPoint end, SKColor // CreateRadialGradient - public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColor[] colors, SKShaderTileMode mode) => + public static SKShader CreateRadialGradient (SKPoint center, float radius, ReadOnlySpan colors, SKShaderTileMode mode) => CreateRadialGradient (center, radius, colors, null, mode); - public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode) + public static SKShader CreateRadialGradient (SKPoint center, float radius, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode mode) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -191,7 +191,7 @@ public static SKShader CreateRadialGradient (SKPoint center, float radius, SKCol } } - public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix) + public static SKShader CreateRadialGradient (SKPoint center, float radius, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode mode, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -204,10 +204,10 @@ public static SKShader CreateRadialGradient (SKPoint center, float radius, SKCol } } - public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode mode) => + public static SKShader CreateRadialGradient (SKPoint center, float radius, ReadOnlySpan colors, SKColorSpace colorspace, SKShaderTileMode mode) => CreateRadialGradient (center, radius, colors, colorspace, null, mode); - public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode) + public static SKShader CreateRadialGradient (SKPoint center, float radius, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode mode) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -220,7 +220,7 @@ public static SKShader CreateRadialGradient (SKPoint center, float radius, SKCol } } - public static SKShader CreateRadialGradient (SKPoint center, float radius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix) + public static SKShader CreateRadialGradient (SKPoint center, float radius, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode mode, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -235,19 +235,19 @@ public static SKShader CreateRadialGradient (SKPoint center, float radius, SKCol // CreateSweepGradient - public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors) => CreateSweepGradient (center, colors, null, SKShaderTileMode.Clamp, 0, 360); - public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, ReadOnlySpan colorPos) => CreateSweepGradient (center, colors, colorPos, SKShaderTileMode.Clamp, 0, 360); - public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos, SKMatrix localMatrix) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, ReadOnlySpan colorPos, SKMatrix localMatrix) => CreateSweepGradient (center, colors, colorPos, SKShaderTileMode.Clamp, 0, 360, localMatrix); - public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, SKShaderTileMode tileMode, float startAngle, float endAngle) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKShaderTileMode tileMode, float startAngle, float endAngle) => CreateSweepGradient (center, colors, null, tileMode, startAngle, endAngle); - public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle) + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -260,7 +260,7 @@ public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, fl } } - public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle, SKMatrix localMatrix) + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -273,19 +273,19 @@ public static SKShader CreateSweepGradient (SKPoint center, SKColor[] colors, fl } } - public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKColorSpace colorspace) => CreateSweepGradient (center, colors, colorspace, null, SKShaderTileMode.Clamp, 0, 360); - public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos) => CreateSweepGradient (center, colors, colorspace, colorPos, SKShaderTileMode.Clamp, 0, 360); - public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKMatrix localMatrix) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKMatrix localMatrix) => CreateSweepGradient (center, colors, colorspace, colorPos, SKShaderTileMode.Clamp, 0, 360, localMatrix); - public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode tileMode, float startAngle, float endAngle) => + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKColorSpace colorspace, SKShaderTileMode tileMode, float startAngle, float endAngle) => CreateSweepGradient (center, colors, colorspace, null, tileMode, startAngle, endAngle); - public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle) + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -298,7 +298,7 @@ public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, S } } - public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle, SKMatrix localMatrix) + public static SKShader CreateSweepGradient (SKPoint center, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode tileMode, float startAngle, float endAngle, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -313,10 +313,10 @@ public static SKShader CreateSweepGradient (SKPoint center, SKColorF[] colors, S // CreateTwoPointConicalGradient - public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColor[] colors, SKShaderTileMode mode) => + public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, ReadOnlySpan colors, SKShaderTileMode mode) => CreateTwoPointConicalGradient (start, startRadius, end, endRadius, colors, null, mode); - public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode) + public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode mode) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -329,7 +329,7 @@ public static SKShader CreateTwoPointConicalGradient (SKPoint start, float start } } - public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColor[] colors, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix) + public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, ReadOnlySpan colors, ReadOnlySpan colorPos, SKShaderTileMode mode, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -342,10 +342,10 @@ public static SKShader CreateTwoPointConicalGradient (SKPoint start, float start } } - public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColorF[] colors, SKColorSpace colorspace, SKShaderTileMode mode) => + public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, ReadOnlySpan colors, SKColorSpace colorspace, SKShaderTileMode mode) => CreateTwoPointConicalGradient (start, startRadius, end, endRadius, colors, colorspace, null, mode); - public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode) + public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode mode) { if (colors == null) throw new ArgumentNullException (nameof (colors)); @@ -358,7 +358,7 @@ public static SKShader CreateTwoPointConicalGradient (SKPoint start, float start } } - public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, SKColorF[] colors, SKColorSpace colorspace, float[] colorPos, SKShaderTileMode mode, SKMatrix localMatrix) + public static SKShader CreateTwoPointConicalGradient (SKPoint start, float startRadius, SKPoint end, float endRadius, ReadOnlySpan colors, SKColorSpace colorspace, ReadOnlySpan colorPos, SKShaderTileMode mode, SKMatrix localMatrix) { if (colors == null) throw new ArgumentNullException (nameof (colors)); diff --git a/binding/SkiaSharp/SKStream.cs b/binding/SkiaSharp/SKStream.cs index af0191695e..3912d90492 100644 --- a/binding/SkiaSharp/SKStream.cs +++ b/binding/SkiaSharp/SKStream.cs @@ -11,7 +11,7 @@ internal SKStream (IntPtr handle, bool owns) : base (handle, owns) { } - + public bool IsAtEnd { get { return SkiaApi.sk_stream_is_at_end (Handle); @@ -124,6 +124,13 @@ public int Read (byte[] buffer, int size) } } + public int Read (Span buffer) + { + fixed (byte* b = buffer) { + return Read ((IntPtr)b, buffer.Length); + } + } + public int Read (IntPtr buffer, int size) { return (int)SkiaApi.sk_stream_read (Handle, (void*)buffer, (IntPtr)size); @@ -175,7 +182,7 @@ public int Position { get { return (int)SkiaApi.sk_stream_get_position (Handle); } - set { + set { Seek (value); } } @@ -378,7 +385,7 @@ internal SKWStream (IntPtr handle, bool owns) : base (handle, owns) { } - + public virtual int BytesWritten { get { return (int)SkiaApi.sk_wstream_bytes_written (Handle); @@ -392,6 +399,13 @@ public virtual bool Write (byte[] buffer, int size) } } + public virtual bool Write (ReadOnlySpan buffer) + { + fixed (byte* b = buffer) { + return SkiaApi.sk_wstream_write (Handle, (void*)b, (IntPtr)buffer.Length); + } + } + public bool NewLine () { return SkiaApi.sk_wstream_newline (Handle); diff --git a/binding/SkiaSharp/SKString.cs b/binding/SkiaSharp/SKString.cs index b48a84f23f..5c23a46e8b 100644 --- a/binding/SkiaSharp/SKString.cs +++ b/binding/SkiaSharp/SKString.cs @@ -19,32 +19,37 @@ public SKString () throw new InvalidOperationException ("Unable to create a new SKString instance."); } } - - public SKString (byte [] src, long length) + + public SKString (ReadOnlySpan src, long length) : base (CreateCopy (src, length), true) { if (Handle == IntPtr.Zero) { throw new InvalidOperationException ("Unable to copy the SKString instance."); } } - - private static IntPtr CreateCopy (byte [] src, long length) + + private static IntPtr CreateCopy (ReadOnlySpan src, long length) { fixed (byte* s = src) { return SkiaApi.sk_string_new_with_copy (s, (IntPtr)length); } } - public SKString (byte [] src) + public SKString (ReadOnlySpan src) : this (src, src.Length) { } - + public SKString (string str) - : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf8)) + : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf8).AsSpan ()) + { + } + + public SKString (ReadOnlySpan str) + : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf16).AsSpan ()) { } - + public override string ToString () { var cstr = SkiaApi.sk_string_get_c_str (Handle); @@ -56,7 +61,7 @@ public static explicit operator string (SKString skString) { return skString.ToString (); } - + internal static SKString Create (string str) { if (str == null) { @@ -65,6 +70,14 @@ internal static SKString Create (string str) return new SKString (str); } + internal static SKString Create (ReadOnlySpan str) + { + if (str == null) { + return null; + } + return new SKString (str); + } + protected override void Dispose (bool disposing) => base.Dispose (disposing); diff --git a/binding/SkiaSharp/SKTypeface.cs b/binding/SkiaSharp/SKTypeface.cs index 3455c8ec29..fc97931197 100644 --- a/binding/SkiaSharp/SKTypeface.cs +++ b/binding/SkiaSharp/SKTypeface.cs @@ -304,6 +304,17 @@ public int[] GetKerningPairAdjustments (ReadOnlySpan glyphs) return adjustments; } + public void GetKerningPairAdjustments (ReadOnlySpan glyphs, Span adjustments) + { + if (glyphs.Length != adjustments.Length) + throw new ArgumentException ("Length of adjustments must be the same as the length of glyphs.", nameof(adjustments)); + + fixed (ushort* gp = glyphs) + fixed (int* ap = adjustments) { + SkiaApi.sk_typeface_get_kerning_pair_adjustments (Handle, gp, glyphs.Length, ap); + } + } + // internal static SKTypeface GetObject (IntPtr handle) => diff --git a/binding/SkiaSharp/SKVertices.cs b/binding/SkiaSharp/SKVertices.cs index c50d33d919..75f6325b2e 100644 --- a/binding/SkiaSharp/SKVertices.cs +++ b/binding/SkiaSharp/SKVertices.cs @@ -22,17 +22,17 @@ protected override void Dispose (bool disposing) => void ISKNonVirtualReferenceCounted.UnreferenceNative () => SkiaApi.sk_vertices_unref (Handle); - public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SKColor[] colors) + public static SKVertices CreateCopy (SKVertexMode vmode, ReadOnlySpan positions, ReadOnlySpan colors) { return CreateCopy (vmode, positions, null, colors, null); } - public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SKPoint[] texs, SKColor[] colors) + public static SKVertices CreateCopy (SKVertexMode vmode, ReadOnlySpan positions, ReadOnlySpan texs, ReadOnlySpan colors) { return CreateCopy (vmode, positions, texs, colors, null); } - public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SKPoint[] texs, SKColor[] colors, UInt16[] indices) + public static SKVertices CreateCopy (SKVertexMode vmode, ReadOnlySpan positions, ReadOnlySpan texs, ReadOnlySpan colors, ReadOnlySpan indices) { if (positions == null) throw new ArgumentNullException (nameof (positions)); @@ -43,7 +43,7 @@ public static SKVertices CreateCopy (SKVertexMode vmode, SKPoint[] positions, SK throw new ArgumentException ("The number of colors must match the number of vertices.", nameof (colors)); var vertexCount = positions.Length; - var indexCount = indices?.Length ?? 0; + var indexCount = indices.Length; fixed (SKPoint* p = positions) fixed (SKPoint* t = texs) From 4abd792922f1febf4161716bbbe8fee253968dd3 Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Fri, 10 Nov 2023 03:49:17 -0500 Subject: [PATCH 06/15] Remove some unnecessary null checks. The length checks immediately following the null checks accomplish the same goal. --- binding/SkiaSharp/SKMatrix44.cs | 6 ------ binding/SkiaSharp/SKRoundRect.cs | 2 -- 2 files changed, 8 deletions(-) diff --git a/binding/SkiaSharp/SKMatrix44.cs b/binding/SkiaSharp/SKMatrix44.cs index dfbe3754df..052d4445e9 100644 --- a/binding/SkiaSharp/SKMatrix44.cs +++ b/binding/SkiaSharp/SKMatrix44.cs @@ -267,8 +267,6 @@ internal float[] MapScalars (float x, float y, float z, float w) internal float[] MapScalars (ReadOnlySpan srcVector4) { - if (srcVector4 == null) - throw new ArgumentNullException (nameof (srcVector4)); if (srcVector4.Length != 4) throw new ArgumentException ("The source vector array must be 4 entries.", nameof (srcVector4)); @@ -279,12 +277,8 @@ internal float[] MapScalars (ReadOnlySpan srcVector4) internal void MapScalars (ReadOnlySpan srcVector4, Span dstVector4) { - if (srcVector4 == null) - throw new ArgumentNullException (nameof (srcVector4)); if (srcVector4.Length != 4) throw new ArgumentException ("The source vector array must be 4 entries.", nameof (srcVector4)); - if (dstVector4 == null) - throw new ArgumentNullException (nameof (dstVector4)); if (dstVector4.Length != 4) throw new ArgumentException ("The destination vector array must be 4 entries.", nameof (dstVector4)); diff --git a/binding/SkiaSharp/SKRoundRect.cs b/binding/SkiaSharp/SKRoundRect.cs index 399590fb64..f6f5dff07a 100644 --- a/binding/SkiaSharp/SKRoundRect.cs +++ b/binding/SkiaSharp/SKRoundRect.cs @@ -120,8 +120,6 @@ public void SetNinePatch (SKRect rect, float leftRadius, float topRadius, float public void SetRectRadii (SKRect rect, ReadOnlySpan radii) { - if (radii == null) - throw new ArgumentNullException (nameof (radii)); if (radii.Length != 4) throw new ArgumentException ("Radii must have a length of 4.", nameof (radii)); From b6a95d2edeb29b6620047fbdc4f9950c7e30def9 Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Fri, 10 Nov 2023 03:51:10 -0500 Subject: [PATCH 07/15] Convert non-obsolete DrawText & DrawTextOnPath overloads to use ReadOnlySpan instead of strings. --- binding/SkiaSharp/SKCanvas.cs | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/binding/SkiaSharp/SKCanvas.cs b/binding/SkiaSharp/SKCanvas.cs index 98f2f56dc3..6d7bd29451 100644 --- a/binding/SkiaSharp/SKCanvas.cs +++ b/binding/SkiaSharp/SKCanvas.cs @@ -603,28 +603,28 @@ public void DrawText (SKTextBlob text, float x, float y, SKPaint paint) // DrawText - [Obsolete ("Use DrawText(string text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete ("Use DrawText(ReadOnlySpan text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public void DrawText (string text, SKPoint p, SKPaint paint) => - DrawText (text, p, paint.TextAlign, paint.GetFont (), paint); + DrawText (text.AsSpan (), p, paint.TextAlign, paint.GetFont (), paint); - [Obsolete ("Use DrawText(string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete ("Use DrawText(ReadOnlySpan text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public void DrawText (string text, float x, float y, SKPaint paint) => - DrawText (text, x, y, paint.TextAlign, paint.GetFont (), paint); + DrawText (text.AsSpan (), x, y, paint.TextAlign, paint.GetFont (), paint); - public void DrawText (string text, SKPoint p, SKFont font, SKPaint paint) => + public void DrawText (ReadOnlySpan text, SKPoint p, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) DrawText (text, p, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public void DrawText (string text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) => + public void DrawText (ReadOnlySpan text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) => DrawText (text, p.X, p.Y, textAlign, font, paint); - public void DrawText (string text, float x, float y, SKFont font, SKPaint paint) => + public void DrawText (ReadOnlySpan text, float x, float y, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) DrawText (text, x, y, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public void DrawText (string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) + public void DrawText (ReadOnlySpan text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) { if (text == null) throw new ArgumentNullException (nameof (text)); @@ -649,40 +649,40 @@ public void DrawText (string text, float x, float y, SKTextAlign textAlign, SKFo // DrawTextOnPath - [Obsolete ("Use DrawTextOnPath(string text, SKPath path, float hOffset, float vOffset, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete ("Use DrawTextOnPath(ReadOnlySpan text, SKPath path, float hOffset, float vOffset, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public void DrawTextOnPath (string text, SKPath path, SKPoint offset, SKPaint paint) => DrawTextOnPath (text, path, offset, true, paint); - [Obsolete ("Use DrawTextOnPath(string text, SKPath path, float hOffset, float vOffset, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete ("Use DrawTextOnPath(ReadOnlySpan text, SKPath path, float hOffset, float vOffset, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public void DrawTextOnPath (string text, SKPath path, float hOffset, float vOffset, SKPaint paint) => DrawTextOnPath (text, path, new SKPoint (hOffset, vOffset), true, paint); - [Obsolete ("Use DrawTextOnPath(string text, SKPath path, SKPoint offset, bool warpGlyphs, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete ("Use DrawTextOnPath(ReadOnlySpan text, SKPath path, SKPoint offset, bool warpGlyphs, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public void DrawTextOnPath (string text, SKPath path, SKPoint offset, bool warpGlyphs, SKPaint paint) => - DrawTextOnPath (text, path, offset, warpGlyphs, paint.GetFont (), paint); + DrawTextOnPath (text.AsSpan (), path, offset, warpGlyphs, paint.GetFont (), paint); - public void DrawTextOnPath (string text, SKPath path, SKPoint offset, SKFont font, SKPaint paint) => + public void DrawTextOnPath (ReadOnlySpan text, SKPath path, SKPoint offset, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) DrawTextOnPath (text, path, offset, true, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public void DrawTextOnPath (string text, SKPath path, SKPoint offset, SKTextAlign textAlign, SKFont font, SKPaint paint) => + public void DrawTextOnPath (ReadOnlySpan text, SKPath path, SKPoint offset, SKTextAlign textAlign, SKFont font, SKPaint paint) => DrawTextOnPath (text, path, offset, true, textAlign, font, paint); - public void DrawTextOnPath (string text, SKPath path, float hOffset, float vOffset, SKFont font, SKPaint paint) => + public void DrawTextOnPath (ReadOnlySpan text, SKPath path, float hOffset, float vOffset, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) DrawTextOnPath (text, path, new SKPoint (hOffset, vOffset), true, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public void DrawTextOnPath (string text, SKPath path, float hOffset, float vOffset, SKTextAlign textAlign, SKFont font, SKPaint paint) => + public void DrawTextOnPath (ReadOnlySpan text, SKPath path, float hOffset, float vOffset, SKTextAlign textAlign, SKFont font, SKPaint paint) => DrawTextOnPath (text, path, new SKPoint (hOffset, vOffset), true, textAlign, font, paint); - public void DrawTextOnPath (string text, SKPath path, SKPoint offset, bool warpGlyphs, SKFont font, SKPaint paint) => + public void DrawTextOnPath (ReadOnlySpan text, SKPath path, SKPoint offset, bool warpGlyphs, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) DrawTextOnPath (text, path, offset, warpGlyphs, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public void DrawTextOnPath (string text, SKPath path, SKPoint offset, bool warpGlyphs, SKTextAlign textAlign, SKFont font, SKPaint paint) + public void DrawTextOnPath (ReadOnlySpan text, SKPath path, SKPoint offset, bool warpGlyphs, SKTextAlign textAlign, SKFont font, SKPaint paint) { if (text == null) throw new ArgumentNullException (nameof (text)); From 7b3e666298fd06edaf4cebef669122f06ba1a82e Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Fri, 10 Nov 2023 03:52:34 -0500 Subject: [PATCH 08/15] Swap out `new T[0]` for `Array.Empty ()` --- binding/SkiaSharp/SKFont.cs | 24 +++++++++---------- binding/SkiaSharp/Util.cs | 4 ++-- .../SkiaSharp.HarfBuzz/SKShaper.cs | 6 ++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/binding/SkiaSharp/SKFont.cs b/binding/SkiaSharp/SKFont.cs index 0559bc2b45..187ff57695 100644 --- a/binding/SkiaSharp/SKFont.cs +++ b/binding/SkiaSharp/SKFont.cs @@ -188,11 +188,11 @@ public void GetGlyphs (IntPtr text, int length, SKTextEncoding encoding, Span (); var n = CountGlyphs (text, length, encoding); if (n <= 0) - return new ushort[0]; + return Array.Empty (); var glyphs = new ushort[n]; GetGlyphs (text, length, encoding, glyphs); @@ -436,11 +436,11 @@ public void GetGlyphPositions (IntPtr text, int length, SKTextEncoding encoding, internal SKPoint[] GetGlyphPositions (void* text, int length, SKTextEncoding encoding, SKPoint origin) { if (!ValidateTextArgs (text, length, encoding)) - return new SKPoint[0]; + return Array.Empty (); var n = CountGlyphs (text, length, encoding); if (n <= 0) - return new SKPoint[0]; + return Array.Empty (); var positions = new SKPoint[n]; GetGlyphPositions (text, length, encoding, positions, origin); @@ -526,11 +526,11 @@ public void GetGlyphOffsets (IntPtr text, int length, SKTextEncoding encoding, S internal float[] GetGlyphOffsets (void* text, int length, SKTextEncoding encoding, float origin) { if (!ValidateTextArgs (text, length, encoding)) - return new float[0]; + return Array.Empty (); var n = CountGlyphs (text, length, encoding); if (n <= 0) - return new float[0]; + return Array.Empty (); var offsets = new float[n]; GetGlyphOffsets (text, length, encoding, offsets, origin); @@ -636,11 +636,11 @@ public void GetGlyphWidths (IntPtr text, int length, SKTextEncoding encoding, Sp internal float[] GetGlyphWidths (void* text, int length, SKTextEncoding encoding, SKPaint paint) { if (!ValidateTextArgs (text, length, encoding)) - return new float[0]; + return Array.Empty (); var n = CountGlyphs (text, length, encoding); if (n <= 0) - return new float[0]; + return Array.Empty (); var widths = new float[n]; GetGlyphWidths (text, length, encoding, widths, Span.Empty, paint); @@ -650,14 +650,14 @@ internal float[] GetGlyphWidths (void* text, int length, SKTextEncoding encoding internal float[] GetGlyphWidths (void* text, int length, SKTextEncoding encoding, out SKRect[] bounds, SKPaint paint) { if (!ValidateTextArgs (text, length, encoding)) { - bounds = new SKRect[0]; - return new float[0]; + bounds = Array.Empty (); + return Array.Empty (); } var n = CountGlyphs (text, length, encoding); if (n <= 0) { - bounds = new SKRect[0]; - return new float[0]; + bounds = Array.Empty (); + return Array.Empty (); } bounds = new SKRect[n]; diff --git a/binding/SkiaSharp/Util.cs b/binding/SkiaSharp/Util.cs index 3846d58dcb..67a1a9597f 100644 --- a/binding/SkiaSharp/Util.cs +++ b/binding/SkiaSharp/Util.cs @@ -45,12 +45,12 @@ internal static bool NearlyEqual (float a, float b, float tolerance) => internal static byte[] GetBytes (this Encoding encoding, ReadOnlySpan text) { if (text.Length == 0) - return new byte[0]; + return Array.Empty (); fixed (char* t = text) { var byteCount = encoding.GetByteCount (t, text.Length); if (byteCount == 0) - return new byte[0]; + return Array.Empty (); var bytes = new byte[byteCount]; fixed (byte* b = bytes) { diff --git a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs index ae6998dd78..b318bbced3 100644 --- a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs +++ b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs @@ -156,9 +156,9 @@ public class Result { public Result() { - Codepoints = new uint[0]; - Clusters = new uint[0]; - Points = new SKPoint[0]; + Codepoints = Array.Empty(); + Clusters = Array.Empty(); + Points = Array.Empty(); Width = 0f; } From a846dce7534557e3c8401d6e84cd1302d36d00f9 Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Fri, 10 Nov 2023 03:53:36 -0500 Subject: [PATCH 09/15] Fix apparent memory leak when drawing vertices --- binding/SkiaSharp/SKCanvas.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/binding/SkiaSharp/SKCanvas.cs b/binding/SkiaSharp/SKCanvas.cs index 6d7bd29451..9c6cd57ecb 100644 --- a/binding/SkiaSharp/SKCanvas.cs +++ b/binding/SkiaSharp/SKCanvas.cs @@ -902,25 +902,25 @@ public SKMatrix44 TotalMatrix44 { public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan colors, SKPaint paint) { - var vert = SKVertices.CreateCopy (vmode, vertices, colors); + using var vert = SKVertices.CreateCopy (vmode, vertices, colors); DrawVertices (vert, SKBlendMode.Modulate, paint); } public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan texs, ReadOnlySpan colors, SKPaint paint) { - var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors); + using var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors); DrawVertices (vert, SKBlendMode.Modulate, paint); } public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan texs, ReadOnlySpan colors, ReadOnlySpan indices, SKPaint paint) { - var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices); + using var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices); DrawVertices (vert, SKBlendMode.Modulate, paint); } public void DrawVertices (SKVertexMode vmode, ReadOnlySpan vertices, ReadOnlySpan texs, ReadOnlySpan colors, SKBlendMode mode, ReadOnlySpan indices, SKPaint paint) { - var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices); + using var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices); DrawVertices (vert, mode, paint); } From 74a4d0b8a08f9df1f163e5a5e973ddc1640624db Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Fri, 10 Nov 2023 03:53:55 -0500 Subject: [PATCH 10/15] Minor memory improvements --- binding/SkiaSharp/SKRuntimeEffect.cs | 14 ++++++++++---- binding/SkiaSharp/SKTypeface.cs | 21 ++++++--------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/binding/SkiaSharp/SKRuntimeEffect.cs b/binding/SkiaSharp/SKRuntimeEffect.cs index 3092262553..0698dc0879 100644 --- a/binding/SkiaSharp/SKRuntimeEffect.cs +++ b/binding/SkiaSharp/SKRuntimeEffect.cs @@ -476,11 +476,17 @@ public static implicit operator SKRuntimeEffectUniform (SKColorF value) => public static implicit operator SKRuntimeEffectUniform (float[][] value) { - var floats = new List (); - foreach (var array in value) { - floats.AddRange (array); + var totalLength = value.Sum (static f => f.Length); + var floats = new float[totalLength]; + + var offset = 0; + foreach (var f in value) + { + Array.Copy (f, 0, floats, offset, f.Length); + offset += f.Length; } - return floats.ToArray (); + + return floats; } public static implicit operator SKRuntimeEffectUniform (SKMatrix value) => value.Values; diff --git a/binding/SkiaSharp/SKTypeface.cs b/binding/SkiaSharp/SKTypeface.cs index fc97931197..4e314ac5ea 100644 --- a/binding/SkiaSharp/SKTypeface.cs +++ b/binding/SkiaSharp/SKTypeface.cs @@ -227,23 +227,14 @@ public ushort[] GetGlyphs (ReadOnlySpan codepoints) => public ushort[] GetGlyphs (string text) => GetGlyphs (text.AsSpan ()); - public ushort[] GetGlyphs (ReadOnlySpan text) - { - using var font = ToFont (); - return font.GetGlyphs (text); - } + public ushort[] GetGlyphs (ReadOnlySpan text) => + GetFont ().GetGlyphs (text); - public ushort[] GetGlyphs (ReadOnlySpan text, SKTextEncoding encoding) - { - using var font = ToFont (); - return font.GetGlyphs (text, encoding); - } + public ushort[] GetGlyphs (ReadOnlySpan text, SKTextEncoding encoding) => + GetFont ().GetGlyphs (text, encoding); - public ushort[] GetGlyphs (IntPtr text, int length, SKTextEncoding encoding) - { - using var font = ToFont (); - return font.GetGlyphs (text, length * encoding.GetCharacterByteSize (), encoding); - } + public ushort[] GetGlyphs (IntPtr text, int length, SKTextEncoding encoding) => + GetFont ().GetGlyphs (text, length * encoding.GetCharacterByteSize (), encoding); // ContainsGlyph From dd83aecaf6d128033a49c44b7c8bef592f3e5e5a Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Mon, 13 Nov 2023 09:30:11 -0500 Subject: [PATCH 11/15] Fix typo --- binding/SkiaSharp/SKString.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binding/SkiaSharp/SKString.cs b/binding/SkiaSharp/SKString.cs index 5c23a46e8b..5e50615f1b 100644 --- a/binding/SkiaSharp/SKString.cs +++ b/binding/SkiaSharp/SKString.cs @@ -46,7 +46,7 @@ public SKString (string str) } public SKString (ReadOnlySpan str) - : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf16).AsSpan ()) + : this (StringUtilities.GetEncodedText (str, SKTextEncoding.Utf8).AsSpan ()) { } From b006c08ba24a3b0fa100d64e1c6b97ae7da7f726 Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Mon, 13 Nov 2023 23:59:28 -0500 Subject: [PATCH 12/15] Add ReadOnlySpan overloads to SKShaper.Shape and convert DrawShapedText to use ReadOnlySpan --- .../SkiaSharp.HarfBuzz/CanvasExtensions.cs | 36 +++++++++---------- .../SkiaSharp.HarfBuzz/SKShaper.cs | 18 ++++++++++ 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/CanvasExtensions.cs b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/CanvasExtensions.cs index d03462caf8..02a4575bb7 100644 --- a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/CanvasExtensions.cs +++ b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/CanvasExtensions.cs @@ -4,60 +4,60 @@ namespace SkiaSharp.HarfBuzz { public static class CanvasExtensions { - [Obsolete("Use DrawShapedText(string text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete("Use DrawShapedText(ReadOnlySpan text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public static void DrawShapedText(this SKCanvas canvas, string text, SKPoint p, SKPaint paint) => - canvas.DrawShapedText(text, p.X, p.Y, paint.TextAlign, paint.GetFont(), paint); + canvas.DrawShapedText(text.AsSpan(), p.X, p.Y, paint.TextAlign, paint.GetFont(), paint); - public static void DrawShapedText(this SKCanvas canvas, string text, SKPoint p, SKFont font, SKPaint paint) => + public static void DrawShapedText(this SKCanvas canvas, ReadOnlySpan text, SKPoint p, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) canvas.DrawShapedText(text, p.X, p.Y, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public static void DrawShapedText(this SKCanvas canvas, string text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) => + public static void DrawShapedText(this SKCanvas canvas, ReadOnlySpan text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) => canvas.DrawShapedText(text, p.X, p.Y, textAlign, font, paint); - [Obsolete("Use DrawShapedText(string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete("Use DrawShapedText(ReadOnlySpan text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public static void DrawShapedText(this SKCanvas canvas, string text, float x, float y, SKPaint paint) => - canvas.DrawShapedText(text, x, y, paint.TextAlign, paint.GetFont(), paint); + canvas.DrawShapedText(text.AsSpan(), x, y, paint.TextAlign, paint.GetFont(), paint); - public static void DrawShapedText(this SKCanvas canvas, string text, float x, float y, SKFont font, SKPaint paint) => + public static void DrawShapedText(this SKCanvas canvas, ReadOnlySpan text, float x, float y, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) canvas.DrawShapedText(text, x, y, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public static void DrawShapedText(this SKCanvas canvas, string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) + public static void DrawShapedText(this SKCanvas canvas, ReadOnlySpan text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) { - if (string.IsNullOrEmpty(text)) + if (text.IsEmpty) return; using var shaper = new SKShaper(font.Typeface); canvas.DrawShapedText(shaper, text, x, y, textAlign, font, paint); } - [Obsolete("Use DrawShapedText(SKShaper shaper, string text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete("Use DrawShapedText(SKShaper shaper, ReadOnlySpan text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, SKPoint p, SKPaint paint) => - canvas.DrawShapedText(shaper, text, p.X, p.Y, paint.TextAlign, paint.GetFont(), paint); + canvas.DrawShapedText(shaper, text.AsSpan(), p.X, p.Y, paint.TextAlign, paint.GetFont(), paint); - public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, SKPoint p, SKFont font, SKPaint paint) => + public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, ReadOnlySpan text, SKPoint p, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) canvas.DrawShapedText(shaper, text, p.X, p.Y, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) => + public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, ReadOnlySpan text, SKPoint p, SKTextAlign textAlign, SKFont font, SKPaint paint) => canvas.DrawShapedText(shaper, text, p.X, p.Y, textAlign, font, paint); - [Obsolete("Use DrawShapedText(SKShaper shaper, string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] + [Obsolete("Use DrawShapedText(SKShaper shaper, ReadOnlySpan text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) instead.")] public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, float x, float y, SKPaint paint) => - canvas.DrawShapedText(shaper, text, x, y, paint.TextAlign, paint.GetFont(), paint); + canvas.DrawShapedText(shaper, text.AsSpan(), x, y, paint.TextAlign, paint.GetFont(), paint); - public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, float x, float y, SKFont font, SKPaint paint) => + public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, ReadOnlySpan text, float x, float y, SKFont font, SKPaint paint) => #pragma warning disable CS0618 // Type or member is obsolete (TODO: replace paint.TextAlign with SKTextAlign.Left) canvas.DrawShapedText(shaper, text, x, y, paint.TextAlign, font, paint); #pragma warning restore CS0618 // Type or member is obsolete - public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) + public static void DrawShapedText(this SKCanvas canvas, SKShaper shaper, ReadOnlySpan text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint) { - if (string.IsNullOrEmpty(text)) + if (text.IsEmpty) return; if (canvas == null) diff --git a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs index b318bbced3..6db9f2ddf9 100644 --- a/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs +++ b/source/SkiaSharp.HarfBuzz/SkiaSharp.HarfBuzz/SKShaper.cs @@ -152,6 +152,24 @@ public Result Shape(string text, float xOffset, float yOffset, SKFont font) return Shape(buffer, xOffset, yOffset, font); } + public Result Shape(ReadOnlySpan text, SKFont font) => + Shape(text, 0, 0, font); + + public Result Shape(ReadOnlySpan text, float xOffset, float yOffset, SKFont font) + { + if (text.IsEmpty) + { + return new Result(); + } + + using var buffer = new Buffer(); + buffer.AddUtf16(text); + + buffer.GuessSegmentProperties(); + + return Shape(buffer, xOffset, yOffset, font); + } + public class Result { public Result() From c6cf62d5f2ab3a6d057c728576e0c7ebc54b9c30 Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Tue, 14 Nov 2023 00:00:35 -0500 Subject: [PATCH 13/15] Add tests for SKShaper.Shape with ReadOnlySpan --- tests/Tests/SkiaSharp/SKShaperTest.cs | 41 ++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/Tests/SkiaSharp/SKShaperTest.cs b/tests/Tests/SkiaSharp/SKShaperTest.cs index 7f14de8a27..2ad0d2bb5b 100644 --- a/tests/Tests/SkiaSharp/SKShaperTest.cs +++ b/tests/Tests/SkiaSharp/SKShaperTest.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using HarfBuzzSharp; @@ -54,6 +55,25 @@ public void CorrectlyShapesArabicScriptAtAnOffset() } } + [SkippableFact] + public void CorrectlyShapesArabicScriptAtAnOffsetWithSpan() + { + var clusters = new uint[] { 2, 1, 0 }; + var codepoints = new uint[] { 629, 668, 891 }; + var points = new SKPoint[] { new SKPoint(100, 200), new SKPoint(128.375f, 200), new SKPoint(142.125f, 200) }; + + using (var tf = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf"))) + using (var shaper = new SKShaper(tf)) + using (var font = new SKFont { Size = 64, Typeface = tf }) + { + var result = shaper.Shape("متن".AsSpan(), 100, 200, font); + + Assert.Equal(clusters, result.Clusters); + Assert.Equal(codepoints, result.Codepoints); + Assert.Equal(points, result.Points); + } + } + [SkippableFact] public void CorrectlyShapesArabicScript() { @@ -73,6 +93,25 @@ public void CorrectlyShapesArabicScript() } } + [SkippableFact] + public void CorrectlyShapesArabicScriptWithSpan() + { + var clusters = new uint[] { 2, 1, 0 }; + var codepoints = new uint[] { 629, 668, 891 }; + var points = new SKPoint[] { new SKPoint(0, 0), new SKPoint(28.375f, 0), new SKPoint(42.125f, 0) }; + + using (var tf = SKTypeface.FromFile(Path.Combine(PathToFonts, "content-font.ttf"))) + using (var shaper = new SKShaper(tf)) + using (var font = new SKFont { Size = 64, Typeface = tf }) + { + var result = shaper.Shape("متن".AsSpan(), font); + + Assert.Equal(clusters, result.Clusters); + Assert.Equal(codepoints, result.Codepoints); + Assert.Equal(points, result.Points); + } + } + [SkippableFact] public void CanCreateFaceShaperFromTypeface() { From 98cf75c29fa5c7365dd05f8c4a32d37bbb84627d Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Tue, 14 Nov 2023 01:41:52 -0500 Subject: [PATCH 14/15] Adjust some methods --- binding/SkiaSharp/SKCodec.cs | 10 ---------- binding/SkiaSharp/SKPath.cs | 18 +++++++++++------- binding/SkiaSharp/SKTypeface.cs | 2 +- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/binding/SkiaSharp/SKCodec.cs b/binding/SkiaSharp/SKCodec.cs index 9065a017e1..5f593479fe 100644 --- a/binding/SkiaSharp/SKCodec.cs +++ b/binding/SkiaSharp/SKCodec.cs @@ -100,16 +100,6 @@ public SKCodecResult GetPixels (SKImageInfo info, out byte[] pixels) return GetPixels (info, pixels); } - public SKCodecResult GetPixels (SKImageInfo info, byte[] pixels) - { - if (pixels == null) - throw new ArgumentNullException (nameof (pixels)); - - fixed (byte* p = pixels) { - return GetPixels (info, (IntPtr)p, info.RowBytes, SKCodecOptions.Default); - } - } - public SKCodecResult GetPixels (SKImageInfo info, Span pixels) { if (pixels == null) diff --git a/binding/SkiaSharp/SKPath.cs b/binding/SkiaSharp/SKPath.cs index 28166bda0d..04f465c1cb 100644 --- a/binding/SkiaSharp/SKPath.cs +++ b/binding/SkiaSharp/SKPath.cs @@ -123,10 +123,13 @@ public SKRoundRect GetRoundRect () public SKPoint[] GetLine () { Span temp = stackalloc SKPoint[2]; - if (TryGetLine (temp)) { - return temp.ToArray (); - } else { - return null; + fixed (SKPoint* t = temp) { + var result = SkiaApi.sk_path_is_line (Handle, t); + if (result) { + return temp.ToArray (); + } else { + return null; + } } } @@ -135,12 +138,13 @@ public bool TryGetLine (Span points) if (points.Length != 2) throw new ArgumentException ("Points must have a length of 2."); - fixed (SKPoint* p = points) { - var result = SkiaApi.sk_path_is_line (Handle, p); + Span temp = stackalloc SKPoint[2]; + fixed (SKPoint* t = temp) { + var result = SkiaApi.sk_path_is_line (Handle, t); if (result) { + temp.CopyTo (points); return true; } else { - points.Clear (); return false; } } diff --git a/binding/SkiaSharp/SKTypeface.cs b/binding/SkiaSharp/SKTypeface.cs index 4e314ac5ea..aed8f11686 100644 --- a/binding/SkiaSharp/SKTypeface.cs +++ b/binding/SkiaSharp/SKTypeface.cs @@ -295,7 +295,7 @@ public int[] GetKerningPairAdjustments (ReadOnlySpan glyphs) return adjustments; } - public void GetKerningPairAdjustments (ReadOnlySpan glyphs, Span adjustments) + public void GetKerningPairAdjustments (Span adjustments, ReadOnlySpan glyphs) { if (glyphs.Length != adjustments.Length) throw new ArgumentException ("Length of adjustments must be the same as the length of glyphs.", nameof(adjustments)); From ec93cb3b25b37d708a6c17e8a90a3bf24780455c Mon Sep 17 00:00:00 2001 From: ScrubN <72096833+ScrubN@users.noreply.github.com> Date: Tue, 14 Nov 2023 01:42:38 -0500 Subject: [PATCH 15/15] Add some span-array overload comparison tests --- tests/Tests/SkiaSharp/SKColorTest.cs | 24 ++++++++++++++++++++++++ tests/Tests/SkiaSharp/SKPathTest.cs | 15 +++++++++++++++ tests/Tests/SkiaSharp/SKTypefaceTest.cs | 16 +++++++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/Tests/SkiaSharp/SKColorTest.cs b/tests/Tests/SkiaSharp/SKColorTest.cs index e738a3161f..40b1f6b3f5 100644 --- a/tests/Tests/SkiaSharp/SKColorTest.cs +++ b/tests/Tests/SkiaSharp/SKColorTest.cs @@ -268,5 +268,29 @@ public void CanUnPreultiplyArrays() Assert.Equal(colors, upm); } + + [SkippableFact] + public void PreMultiplySpanMatchesArray() + { + var colors = new SKColor[] { 0x33008200, 0x33008200, 0x33008200, 0x33008200, 0x33008200 }; + + var pm1 = SKPMColor.PreMultiply(colors); + var pm2 = new SKPMColor[colors.Length]; + SKPMColor.PreMultiply(pm2, colors); + + Assert.Equal(pm1, pm2); + } + + [SkippableFact] + public void UnPreMultiplySpanMatchesArray() + { + var pmcolors = new SKPMColor[] { 0x33001A00, 0x33001A00, 0x33001A00, 0x33001A00, 0x33001A00 }; + + var upm1 = SKPMColor.UnPreMultiply(pmcolors); + var upm2 = new SKColor[pmcolors.Length]; + SKPMColor.UnPreMultiply(upm2, pmcolors); + + Assert.Equal(upm1, upm2); + } } } diff --git a/tests/Tests/SkiaSharp/SKPathTest.cs b/tests/Tests/SkiaSharp/SKPathTest.cs index 8b0fee109c..5dbcea47b1 100644 --- a/tests/Tests/SkiaSharp/SKPathTest.cs +++ b/tests/Tests/SkiaSharp/SKPathTest.cs @@ -397,6 +397,21 @@ public void OvalPathIsOval() } } + [SkippableFact] + public void TryGetLineMatchesGetLine() + { + using (var path = new SKPath()) + { + path.LineTo(new SKPoint(100, 100)); + + var getLine = path.GetLine(); + var tryGetLine = new SKPoint[2]; + path.TryGetLine(tryGetLine); + + Assert.Equal(getLine, tryGetLine); + } + } + [SkippableFact] public void TrimPathEffectWorksInverted() { diff --git a/tests/Tests/SkiaSharp/SKTypefaceTest.cs b/tests/Tests/SkiaSharp/SKTypefaceTest.cs index 18978e0a10..b322e6511e 100644 --- a/tests/Tests/SkiaSharp/SKTypefaceTest.cs +++ b/tests/Tests/SkiaSharp/SKTypefaceTest.cs @@ -495,7 +495,7 @@ public unsafe void FromFamilyReturnsSameObject() public unsafe void FontStyleIsNotTheSame() { var tf = SKTypeface.FromFamilyName(DefaultFontFamily); - + var fs1 = tf.FontStyle; var fs2 = tf.FontStyle; @@ -556,5 +556,19 @@ static IntPtr DoWork() return tf1.Handle; } } + + [SkippableFact] + public void GetKerningPairAdjustmentsSpanMatchesArray() + { + using var tf = SKTypeface.FromFamilyName(DefaultFontFamily); + + var glyphs = new ushort[] { 54, 78, 76, 68, 54, 75, 68, 85, 83 }; + + var adjustments1 = tf.GetKerningPairAdjustments(glyphs); + var adjustments2 = new int[glyphs.Length]; + tf.GetKerningPairAdjustments(adjustments2, glyphs); + + Assert.Equal(adjustments1, adjustments2); + } } }