From c5045e55589f3de541fdd10c6ba74e2565d499b3 Mon Sep 17 00:00:00 2001 From: ahmed walid Date: Tue, 30 Jul 2024 23:17:25 +0300 Subject: [PATCH 1/2] feat(SaveLayerRec): implement SkCanvas::SaveLayerRec --- binding/SkiaSharp/SKCanvas.cs | 19 +++++++++++++++++++ binding/SkiaSharp/SkiaApi.generated.cs | 16 +++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/binding/SkiaSharp/SKCanvas.cs b/binding/SkiaSharp/SKCanvas.cs index d1d7300964..2357bf4ebe 100644 --- a/binding/SkiaSharp/SKCanvas.cs +++ b/binding/SkiaSharp/SKCanvas.cs @@ -68,6 +68,16 @@ public int SaveLayer (SKPaint paint) return SkiaApi.sk_canvas_save_layer (Handle, null, paint == null ? IntPtr.Zero : paint.Handle); } + public int SaveLayer (SKRect bounds, SKPaint paint, SKImageFilter backdrop, SKSaveLayerFlags saveLayerFlags = SKSaveLayerFlags.None) + { + return SkiaApi.sk_canvas_save_layer_rec (Handle, &bounds, paint == null ? IntPtr.Zero : paint.Handle, backdrop == null ? IntPtr.Zero : backdrop.Handle, (uint)saveLayerFlags); + } + + public int SaveLayer (SKRect bounds, SKPaint paint, SKSaveLayerFlags saveLayerFlags) + { + return SaveLayer (bounds, paint, null, saveLayerFlags); + } + public int SaveLayer () => SaveLayer (null); @@ -1066,4 +1076,13 @@ public void Restore () } } } + + [Flags] + public enum SKSaveLayerFlags + { + None = 0, + PreserveLCDText = 1 << 1, + InitWithPrevious = 1 << 2, + UseF16ColorType = 1 << 4, + } } diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index 8a204712aa..ab344a662d 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; #region Namespaces @@ -2301,6 +2301,20 @@ internal static Int32 sk_canvas_save_layer (sk_canvas_t ccanvas, SKRect* crect, (sk_canvas_save_layer_delegate ??= GetSymbol ("sk_canvas_save_layer")).Invoke (ccanvas, crect, cpaint); #endif + // int sk_canvas_save_layer_rec(sk_canvas_t* ccanvas, const sk_rect_t* cbounds, const sk_paint_t* cpaint, const sk_imagefilter_t* cfilter, uint32_t flags) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKRect* cbounds, sk_paint_t cpaint, sk_imagefilter_t cfilter, uint flags); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKRect* cbounds, sk_paint_t cpaint, sk_imagefilter_t cfilter, uint flags); + } + private static Delegates.sk_canvas_save_layer_rec sk_canvas_save_layer_rec_delegate; + internal static Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKRect* cbounds, sk_paint_t cpaint, sk_imagefilter_t cfilter, uint flags) => + (sk_canvas_save_layer_rec_delegate ??= GetSymbol ("sk_canvas_save_layer_rec")).Invoke (ccanvas, cbounds, cpaint, cfilter, flags); + #endif + // void sk_canvas_scale(sk_canvas_t* ccanvas, float sx, float sy) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] From d033ffe8158bd40a63bf07f38c37e94b062174b0 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Tue, 29 Oct 2024 00:09:11 +0800 Subject: [PATCH 2/2] Update the API to handle the future --- binding/SkiaSharp/SKCanvas.cs | 53 +++++++++++-------- binding/SkiaSharp/SkiaApi.generated.cs | 70 +++++++++++++++++++++++--- binding/libSkiaSharp.json | 7 +++ externals/skia | 2 +- tests/Tests/SkiaSharp/SKCanvasTest.cs | 54 ++++++++++++++++++++ 5 files changed, 157 insertions(+), 29 deletions(-) diff --git a/binding/SkiaSharp/SKCanvas.cs b/binding/SkiaSharp/SKCanvas.cs index d2024babca..611d9d1936 100644 --- a/binding/SkiaSharp/SKCanvas.cs +++ b/binding/SkiaSharp/SKCanvas.cs @@ -51,6 +51,7 @@ public bool QuickReject (SKPath path) // Save* +#nullable enable public int Save () { if (Handle == IntPtr.Zero) @@ -58,28 +59,24 @@ public int Save () return SkiaApi.sk_canvas_save (Handle); } - public int SaveLayer (SKRect limit, SKPaint paint) - { - return SkiaApi.sk_canvas_save_layer (Handle, &limit, paint == null ? IntPtr.Zero : paint.Handle); - } - - public int SaveLayer (SKPaint paint) - { - return SkiaApi.sk_canvas_save_layer (Handle, null, paint == null ? IntPtr.Zero : paint.Handle); - } + public int SaveLayer (SKRect limit, SKPaint? paint) => + SkiaApi.sk_canvas_save_layer (Handle, &limit, paint?.Handle ?? IntPtr.Zero); + + public int SaveLayer (SKPaint? paint) => + SkiaApi.sk_canvas_save_layer (Handle, null, paint?.Handle ?? IntPtr.Zero); - public int SaveLayer (SKRect bounds, SKPaint paint, SKImageFilter backdrop, SKSaveLayerFlags saveLayerFlags = SKSaveLayerFlags.None) + public int SaveLayer (SKCanvasSaveLayerRec rec) { - return SkiaApi.sk_canvas_save_layer_rec (Handle, &bounds, paint == null ? IntPtr.Zero : paint.Handle, backdrop == null ? IntPtr.Zero : backdrop.Handle, (uint)saveLayerFlags); - } + if (rec is null) + return SaveLayer (); - public int SaveLayer (SKRect bounds, SKPaint paint, SKSaveLayerFlags saveLayerFlags) - { - return SaveLayer (bounds, paint, null, saveLayerFlags); + var native = rec.ToNative (); + return SkiaApi.sk_canvas_save_layer_rec (Handle, &native); } public int SaveLayer () => - SaveLayer (null); + SkiaApi.sk_canvas_save_layer (Handle, null, IntPtr.Zero); +#nullable disable // DrawColor @@ -1091,12 +1088,24 @@ public void Restore () } } - [Flags] - public enum SKSaveLayerFlags +#nullable enable + public unsafe class SKCanvasSaveLayerRec { - None = 0, - PreserveLCDText = 1 << 1, - InitWithPrevious = 1 << 2, - UseF16ColorType = 1 << 4, + public SKRect? Bounds { get; set; } + + public SKPaint? Paint { get; set; } + + public SKImageFilter? Backdrop { get; set; } + + public SKCanvasSaveLayerRecFlags Flags { get; set; } + + internal SKCanvasSaveLayerRecNative ToNative () => + new SKCanvasSaveLayerRecNative { + fBounds = Bounds is { } bounds ? &bounds : (SKRect*)null, + fPaint = Paint?.Handle ?? IntPtr.Zero, + fBackdrop = Backdrop?.Handle ?? IntPtr.Zero, + fFlags = Flags + }; } +#nullable disable } diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index 0d4a51a59d..01e7042e3f 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; @@ -3088,18 +3088,23 @@ internal static Int32 sk_canvas_save_layer (sk_canvas_t ccanvas, SKRect* crect, (sk_canvas_save_layer_delegate ??= GetSymbol ("sk_canvas_save_layer")).Invoke (ccanvas, crect, cpaint); #endif - // int sk_canvas_save_layer_rec(sk_canvas_t* ccanvas, const sk_rect_t* cbounds, const sk_paint_t* cpaint, const sk_imagefilter_t* cfilter, uint32_t flags) + // int sk_canvas_save_layer_rec(sk_canvas_t* ccanvas, const sk_canvas_savelayerrec_t* crec) #if !USE_DELEGATES + #if USE_LIBRARY_IMPORT + [LibraryImport (SKIA)] + internal static partial Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec); + #else // !USE_LIBRARY_IMPORT [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] - internal static extern Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKRect* cbounds, sk_paint_t cpaint, sk_imagefilter_t cfilter, uint flags); + internal static extern Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec); + #endif #else private partial class Delegates { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] - internal delegate Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKRect* cbounds, sk_paint_t cpaint, sk_imagefilter_t cfilter, uint flags); + internal delegate Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec); } private static Delegates.sk_canvas_save_layer_rec sk_canvas_save_layer_rec_delegate; - internal static Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKRect* cbounds, sk_paint_t cpaint, sk_imagefilter_t cfilter, uint flags) => - (sk_canvas_save_layer_rec_delegate ??= GetSymbol ("sk_canvas_save_layer_rec")).Invoke (ccanvas, cbounds, cpaint, cfilter, flags); + internal static Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec) => + (sk_canvas_save_layer_rec_delegate ??= GetSymbol ("sk_canvas_save_layer_rec")).Invoke (ccanvas, crec); #endif // void sk_canvas_scale(sk_canvas_t* ccanvas, float sx, float sy) @@ -17946,6 +17951,47 @@ public readonly override int GetHashCode () } + // sk_canvas_savelayerrec_t + [StructLayout (LayoutKind.Sequential)] + internal unsafe partial struct SKCanvasSaveLayerRecNative : IEquatable { + // public sk_rect_t* fBounds + public SKRect* fBounds; + + // public sk_paint_t* fPaint + public sk_paint_t fPaint; + + // public sk_imagefilter_t* fBackdrop + public sk_imagefilter_t fBackdrop; + + // public sk_canvas_savelayerrec_flags_t fFlags + public SKCanvasSaveLayerRecFlags fFlags; + + public readonly bool Equals (SKCanvasSaveLayerRecNative obj) => +#pragma warning disable CS8909 + fBounds == obj.fBounds && fPaint == obj.fPaint && fBackdrop == obj.fBackdrop && fFlags == obj.fFlags; +#pragma warning restore CS8909 + + public readonly override bool Equals (object obj) => + obj is SKCanvasSaveLayerRecNative f && Equals (f); + + public static bool operator == (SKCanvasSaveLayerRecNative left, SKCanvasSaveLayerRecNative right) => + left.Equals (right); + + public static bool operator != (SKCanvasSaveLayerRecNative left, SKCanvasSaveLayerRecNative right) => + !left.Equals (right); + + public readonly override int GetHashCode () + { + var hash = new HashCode (); + hash.Add (fBounds); + hash.Add (fPaint); + hash.Add (fBackdrop); + hash.Add (fFlags); + return hash.ToHashCode (); + } + + } + // sk_codec_frameinfo_t [StructLayout (LayoutKind.Sequential)] public unsafe partial struct SKCodecFrameInfo : IEquatable { @@ -20115,6 +20161,18 @@ public enum SKBlurStyle { Inner = 3, } + // sk_canvas_savelayerrec_flags_t + public enum SKCanvasSaveLayerRecFlags { + // NONE_SK_CANVAS_SAVELAYERREC_FLAGS = 0 + None = 0, + // PRESERVE_LCD_TEXT_SK_CANVAS_SAVELAYERREC_FLAGS = 1 << 1 + PreserveLcdText = 2, + // INITIALIZE_WITH_PREVIOUS_SK_CANVAS_SAVELAYERREC_FLAGS = 1 << 2 + InitializeWithPrevious = 4, + // F16_COLOR_TYPE_SK_CANVAS_SAVELAYERREC_FLAGS = 1 << 4 + F16ColorType = 16, + } + // sk_clipop_t public enum SKClipOperation { // DIFFERENCE_SK_CLIPOP = 0 diff --git a/binding/libSkiaSharp.json b/binding/libSkiaSharp.json index adeb369440..b7e12e8503 100644 --- a/binding/libSkiaSharp.json +++ b/binding/libSkiaSharp.json @@ -98,6 +98,13 @@ "cs": "GRContextOptionsNative", "internal": true }, + "sk_canvas_savelayerrec_t": { + "cs": "SKCanvasSaveLayerRecNative", + "internal": true + }, + "sk_canvas_savelayerrec_flags_t": { + "cs": "SKCanvasSaveLayerRecFlags" + }, "sk_color4f_t": { "cs": "SKColorF", "readonly": true, diff --git a/externals/skia b/externals/skia index 5ab716e1f4..fb802116d2 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit 5ab716e1f47fd2f5f258380c6feabc5997728dc7 +Subproject commit fb802116d22f7c5c026c388436d97e9942f66ab5 diff --git a/tests/Tests/SkiaSharp/SKCanvasTest.cs b/tests/Tests/SkiaSharp/SKCanvasTest.cs index e40a11f35f..4a168821e0 100644 --- a/tests/Tests/SkiaSharp/SKCanvasTest.cs +++ b/tests/Tests/SkiaSharp/SKCanvasTest.cs @@ -689,5 +689,59 @@ public void DrawPointReplacesColor(uint initial, uint color) canvas.DrawPoint(0, 0, color); Assert.Equal(color, bmp.GetPixel(0, 0)); } + + [SkippableFact] + public void SaveLayerRecWithPaintIsCorrect() + { + using var bmp = new SKBitmap(100, 100); + using var canvas = new SKCanvas(bmp); + canvas.Clear(SKColors.Green); + + var rec = new SKCanvasSaveLayerRec + { + Paint = new SKPaint + { + BlendMode = SKBlendMode.Plus + }, + Flags = SKCanvasSaveLayerRecFlags.InitializeWithPrevious + }; + canvas.SaveLayer(rec); + + using var paint = new SKPaint + { + BlendMode = SKBlendMode.Clear + }; + canvas.DrawCircle(50, 50, 40, paint); + + canvas.Restore(); + + Assert.Equal(SKColors.Green, bmp.GetPixel(50, 50)); + Assert.Equal(SKColors.Green, bmp.GetPixel(15, 50)); + Assert.Equal((SKColor)0xFF00FF00, bmp.GetPixel(15, 15)); + } + + [SkippableFact] + public void SaveLayerRecWithImageFilterIsCorrect() + { + using var bmp = new SKBitmap(80, 80); + using var canvas = new SKCanvas(bmp); + canvas.Clear(SKColors.White); + + canvas.DrawCircle(10, 10, 10, new SKPaint { Color = SKColors.Red }); + + var rec = new SKCanvasSaveLayerRec + { + Backdrop = SKImageFilter.CreateMatrix(SKMatrix.CreateScale(4, 4), SKSamplingOptions.Default), + }; + canvas.SaveLayer(rec); + + canvas.DrawCircle(40, 40, 10, new SKPaint { Color = SKColors.Green }); + + canvas.Restore(); + + Assert.Equal(SKColors.Red, bmp.GetPixel(25, 40)); + Assert.Equal(SKColors.Red, bmp.GetPixel(40, 25)); + Assert.Equal(SKColors.Green, bmp.GetPixel(40, 40)); + } } }