Skip to content

Commit 7ef7ce9

Browse files
ahmed605mattleibow
andauthored
Implement SkCanvas::SaveLayerRec (#2962)
* feat(SaveLayerRec): implement SkCanvas::SaveLayerRec * Update the API to handle the future * Use a struct as a class is not necessary * Update externals --------- Co-authored-by: Matthew Leibowitz <[email protected]>
1 parent 55f1b2c commit 7ef7ce9

File tree

5 files changed

+166
-8
lines changed

5 files changed

+166
-8
lines changed

binding/SkiaSharp/SKCanvas.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,29 @@ public bool QuickReject (SKPath path)
5151

5252
// Save*
5353

54+
#nullable enable
5455
public int Save ()
5556
{
5657
if (Handle == IntPtr.Zero)
5758
throw new ObjectDisposedException ("SKCanvas");
5859
return SkiaApi.sk_canvas_save (Handle);
5960
}
6061

61-
public int SaveLayer (SKRect limit, SKPaint paint)
62-
{
63-
return SkiaApi.sk_canvas_save_layer (Handle, &limit, paint == null ? IntPtr.Zero : paint.Handle);
64-
}
62+
public int SaveLayer (SKRect limit, SKPaint? paint) =>
63+
SkiaApi.sk_canvas_save_layer (Handle, &limit, paint?.Handle ?? IntPtr.Zero);
64+
65+
public int SaveLayer (SKPaint? paint) =>
66+
SkiaApi.sk_canvas_save_layer (Handle, null, paint?.Handle ?? IntPtr.Zero);
6567

66-
public int SaveLayer (SKPaint paint)
68+
public int SaveLayer (in SKCanvasSaveLayerRec rec)
6769
{
68-
return SkiaApi.sk_canvas_save_layer (Handle, null, paint == null ? IntPtr.Zero : paint.Handle);
70+
var native = rec.ToNative ();
71+
return SkiaApi.sk_canvas_save_layer_rec (Handle, &native);
6972
}
7073

7174
public int SaveLayer () =>
72-
SaveLayer (null);
75+
SkiaApi.sk_canvas_save_layer (Handle, null, IntPtr.Zero);
76+
#nullable disable
7377

7478
// DrawColor
7579

@@ -1080,4 +1084,25 @@ public void Restore ()
10801084
}
10811085
}
10821086
}
1087+
1088+
#nullable enable
1089+
public unsafe struct SKCanvasSaveLayerRec
1090+
{
1091+
public SKRect? Bounds { readonly get; set; }
1092+
1093+
public SKPaint? Paint { readonly get; set; }
1094+
1095+
public SKImageFilter? Backdrop { readonly get; set; }
1096+
1097+
public SKCanvasSaveLayerRecFlags Flags { readonly get; set; }
1098+
1099+
internal readonly SKCanvasSaveLayerRecNative ToNative () =>
1100+
new SKCanvasSaveLayerRecNative {
1101+
fBounds = Bounds is { } bounds ? &bounds : (SKRect*)null,
1102+
fPaint = Paint?.Handle ?? IntPtr.Zero,
1103+
fBackdrop = Backdrop?.Handle ?? IntPtr.Zero,
1104+
fFlags = Flags
1105+
};
1106+
}
1107+
#nullable disable
10831108
}

binding/SkiaSharp/SkiaApi.generated.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,6 +3088,25 @@ internal static Int32 sk_canvas_save_layer (sk_canvas_t ccanvas, SKRect* crect,
30883088
(sk_canvas_save_layer_delegate ??= GetSymbol<Delegates.sk_canvas_save_layer> ("sk_canvas_save_layer")).Invoke (ccanvas, crect, cpaint);
30893089
#endif
30903090

3091+
// int sk_canvas_save_layer_rec(sk_canvas_t* ccanvas, const sk_canvas_savelayerrec_t* crec)
3092+
#if !USE_DELEGATES
3093+
#if USE_LIBRARY_IMPORT
3094+
[LibraryImport (SKIA)]
3095+
internal static partial Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec);
3096+
#else // !USE_LIBRARY_IMPORT
3097+
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
3098+
internal static extern Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec);
3099+
#endif
3100+
#else
3101+
private partial class Delegates {
3102+
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
3103+
internal delegate Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec);
3104+
}
3105+
private static Delegates.sk_canvas_save_layer_rec sk_canvas_save_layer_rec_delegate;
3106+
internal static Int32 sk_canvas_save_layer_rec (sk_canvas_t ccanvas, SKCanvasSaveLayerRecNative* crec) =>
3107+
(sk_canvas_save_layer_rec_delegate ??= GetSymbol<Delegates.sk_canvas_save_layer_rec> ("sk_canvas_save_layer_rec")).Invoke (ccanvas, crec);
3108+
#endif
3109+
30913110
// void sk_canvas_scale(sk_canvas_t* ccanvas, float sx, float sy)
30923111
#if !USE_DELEGATES
30933112
#if USE_LIBRARY_IMPORT
@@ -18008,6 +18027,47 @@ public readonly override int GetHashCode ()
1800818027

1800918028
}
1801018029

18030+
// sk_canvas_savelayerrec_t
18031+
[StructLayout (LayoutKind.Sequential)]
18032+
internal unsafe partial struct SKCanvasSaveLayerRecNative : IEquatable<SKCanvasSaveLayerRecNative> {
18033+
// public sk_rect_t* fBounds
18034+
public SKRect* fBounds;
18035+
18036+
// public sk_paint_t* fPaint
18037+
public sk_paint_t fPaint;
18038+
18039+
// public sk_imagefilter_t* fBackdrop
18040+
public sk_imagefilter_t fBackdrop;
18041+
18042+
// public sk_canvas_savelayerrec_flags_t fFlags
18043+
public SKCanvasSaveLayerRecFlags fFlags;
18044+
18045+
public readonly bool Equals (SKCanvasSaveLayerRecNative obj) =>
18046+
#pragma warning disable CS8909
18047+
fBounds == obj.fBounds && fPaint == obj.fPaint && fBackdrop == obj.fBackdrop && fFlags == obj.fFlags;
18048+
#pragma warning restore CS8909
18049+
18050+
public readonly override bool Equals (object obj) =>
18051+
obj is SKCanvasSaveLayerRecNative f && Equals (f);
18052+
18053+
public static bool operator == (SKCanvasSaveLayerRecNative left, SKCanvasSaveLayerRecNative right) =>
18054+
left.Equals (right);
18055+
18056+
public static bool operator != (SKCanvasSaveLayerRecNative left, SKCanvasSaveLayerRecNative right) =>
18057+
!left.Equals (right);
18058+
18059+
public readonly override int GetHashCode ()
18060+
{
18061+
var hash = new HashCode ();
18062+
hash.Add (fBounds);
18063+
hash.Add (fPaint);
18064+
hash.Add (fBackdrop);
18065+
hash.Add (fFlags);
18066+
return hash.ToHashCode ();
18067+
}
18068+
18069+
}
18070+
1801118071
// sk_codec_frameinfo_t
1801218072
[StructLayout (LayoutKind.Sequential)]
1801318073
public unsafe partial struct SKCodecFrameInfo : IEquatable<SKCodecFrameInfo> {
@@ -20177,6 +20237,18 @@ public enum SKBlurStyle {
2017720237
Inner = 3,
2017820238
}
2017920239

20240+
// sk_canvas_savelayerrec_flags_t
20241+
public enum SKCanvasSaveLayerRecFlags {
20242+
// NONE_SK_CANVAS_SAVELAYERREC_FLAGS = 0
20243+
None = 0,
20244+
// PRESERVE_LCD_TEXT_SK_CANVAS_SAVELAYERREC_FLAGS = 1 << 1
20245+
PreserveLcdText = 2,
20246+
// INITIALIZE_WITH_PREVIOUS_SK_CANVAS_SAVELAYERREC_FLAGS = 1 << 2
20247+
InitializeWithPrevious = 4,
20248+
// F16_COLOR_TYPE_SK_CANVAS_SAVELAYERREC_FLAGS = 1 << 4
20249+
F16ColorType = 16,
20250+
}
20251+
2018020252
// sk_clipop_t
2018120253
public enum SKClipOperation {
2018220254
// DIFFERENCE_SK_CLIPOP = 0

binding/libSkiaSharp.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@
9898
"cs": "GRContextOptionsNative",
9999
"internal": true
100100
},
101+
"sk_canvas_savelayerrec_t": {
102+
"cs": "SKCanvasSaveLayerRecNative",
103+
"internal": true
104+
},
105+
"sk_canvas_savelayerrec_flags_t": {
106+
"cs": "SKCanvasSaveLayerRecFlags"
107+
},
101108
"sk_color4f_t": {
102109
"cs": "SKColorF",
103110
"readonly": true,

tests/Tests/SkiaSharp/SKCanvasTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,5 +689,59 @@ public void DrawPointReplacesColor(uint initial, uint color)
689689
canvas.DrawPoint(0, 0, color);
690690
Assert.Equal(color, bmp.GetPixel(0, 0));
691691
}
692+
693+
[SkippableFact]
694+
public void SaveLayerRecWithPaintIsCorrect()
695+
{
696+
using var bmp = new SKBitmap(100, 100);
697+
using var canvas = new SKCanvas(bmp);
698+
canvas.Clear(SKColors.Green);
699+
700+
var rec = new SKCanvasSaveLayerRec
701+
{
702+
Paint = new SKPaint
703+
{
704+
BlendMode = SKBlendMode.Plus
705+
},
706+
Flags = SKCanvasSaveLayerRecFlags.InitializeWithPrevious
707+
};
708+
canvas.SaveLayer(rec);
709+
710+
using var paint = new SKPaint
711+
{
712+
BlendMode = SKBlendMode.Clear
713+
};
714+
canvas.DrawCircle(50, 50, 40, paint);
715+
716+
canvas.Restore();
717+
718+
Assert.Equal(SKColors.Green, bmp.GetPixel(50, 50));
719+
Assert.Equal(SKColors.Green, bmp.GetPixel(15, 50));
720+
Assert.Equal((SKColor)0xFF00FF00, bmp.GetPixel(15, 15));
721+
}
722+
723+
[SkippableFact]
724+
public void SaveLayerRecWithImageFilterIsCorrect()
725+
{
726+
using var bmp = new SKBitmap(80, 80);
727+
using var canvas = new SKCanvas(bmp);
728+
canvas.Clear(SKColors.White);
729+
730+
canvas.DrawCircle(10, 10, 10, new SKPaint { Color = SKColors.Red });
731+
732+
var rec = new SKCanvasSaveLayerRec
733+
{
734+
Backdrop = SKImageFilter.CreateMatrix(SKMatrix.CreateScale(4, 4), SKSamplingOptions.Default),
735+
};
736+
canvas.SaveLayer(rec);
737+
738+
canvas.DrawCircle(40, 40, 10, new SKPaint { Color = SKColors.Green });
739+
740+
canvas.Restore();
741+
742+
Assert.Equal(SKColors.Red, bmp.GetPixel(25, 40));
743+
Assert.Equal(SKColors.Red, bmp.GetPixel(40, 25));
744+
Assert.Equal(SKColors.Green, bmp.GetPixel(40, 40));
745+
}
692746
}
693747
}

0 commit comments

Comments
 (0)