Skip to content

Commit

Permalink
Add more SKPicture APIs (#2883)
Browse files Browse the repository at this point in the history
Adding a few new APIs to help with determining picture and drawable "size" 
as well as adding the `Playback` method to `SKPicture`.

The playback method is different to draw in that draw results in a single
"draw the picture" command, but playback effectively replicates what
was done to create the picture. There is a small different to the latter
in that the picture may optimize/combine commands as well as split 
commands to best fit into the picture's storage.
  • Loading branch information
mattleibow committed Jun 3, 2024
1 parent bc19136 commit c629919
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 1 deletion.
3 changes: 3 additions & 0 deletions binding/SkiaSharp/SKDrawable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ protected override void DisposeNative ()
}
}

public int ApproximateBytesUsed =>
(int)SkiaApi.sk_drawable_approximate_bytes_used (Handle);

public void Draw (SKCanvas canvas, in SKMatrix matrix)
{
fixed (SKMatrix* m = &matrix)
Expand Down
19 changes: 19 additions & 0 deletions binding/SkiaSharp/SKPicture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ internal SKPicture (IntPtr h, bool owns)
}
}

public int ApproximateBytesUsed =>
(int)SkiaApi.sk_picture_approximate_bytes_used (Handle);

public int ApproximateOperationCount =>
GetApproximateOperationCount (false);

public int GetApproximateOperationCount(bool includeNested) =>
SkiaApi.sk_picture_approximate_op_count (Handle, includeNested);

// Serialize

public SKData Serialize () =>
Expand All @@ -48,6 +57,16 @@ public void Serialize (SKWStream stream)
SkiaApi.sk_picture_serialize_to_stream (Handle, stream.Handle);
}

// Playback

public void Playback (SKCanvas canvas)
{
if (canvas is null)
throw new ArgumentNullException (nameof (canvas));

SkiaApi.sk_picture_playback (Handle, canvas.Handle);
}

// ToShader

public SKShader ToShader () =>
Expand Down
56 changes: 56 additions & 0 deletions binding/SkiaSharp/SkiaApi.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3751,6 +3751,20 @@ private partial class Delegates {

#region sk_drawable.h

// size_t sk_drawable_approximate_bytes_used(const sk_drawable_t* drawable)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern /* size_t */ IntPtr sk_drawable_approximate_bytes_used (sk_drawable_t drawable);
#else
private partial class Delegates {
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
internal delegate /* size_t */ IntPtr sk_drawable_approximate_bytes_used (sk_drawable_t drawable);
}
private static Delegates.sk_drawable_approximate_bytes_used sk_drawable_approximate_bytes_used_delegate;
internal static /* size_t */ IntPtr sk_drawable_approximate_bytes_used (sk_drawable_t drawable) =>
(sk_drawable_approximate_bytes_used_delegate ??= GetSymbol<Delegates.sk_drawable_approximate_bytes_used> ("sk_drawable_approximate_bytes_used")).Invoke (drawable);
#endif

// void sk_drawable_draw(sk_drawable_t*, sk_canvas_t*, const sk_matrix_t*)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
Expand Down Expand Up @@ -7937,6 +7951,34 @@ private partial class Delegates {

#region sk_picture.h

// size_t sk_picture_approximate_bytes_used(const sk_picture_t* picture)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern /* size_t */ IntPtr sk_picture_approximate_bytes_used (sk_picture_t picture);
#else
private partial class Delegates {
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
internal delegate /* size_t */ IntPtr sk_picture_approximate_bytes_used (sk_picture_t picture);
}
private static Delegates.sk_picture_approximate_bytes_used sk_picture_approximate_bytes_used_delegate;
internal static /* size_t */ IntPtr sk_picture_approximate_bytes_used (sk_picture_t picture) =>
(sk_picture_approximate_bytes_used_delegate ??= GetSymbol<Delegates.sk_picture_approximate_bytes_used> ("sk_picture_approximate_bytes_used")).Invoke (picture);
#endif

// int sk_picture_approximate_op_count(const sk_picture_t* picture, bool nested)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 sk_picture_approximate_op_count (sk_picture_t picture, [MarshalAs (UnmanagedType.I1)] bool nested);
#else
private partial class Delegates {
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
internal delegate Int32 sk_picture_approximate_op_count (sk_picture_t picture, [MarshalAs (UnmanagedType.I1)] bool nested);
}
private static Delegates.sk_picture_approximate_op_count sk_picture_approximate_op_count_delegate;
internal static Int32 sk_picture_approximate_op_count (sk_picture_t picture, [MarshalAs (UnmanagedType.I1)] bool nested) =>
(sk_picture_approximate_op_count_delegate ??= GetSymbol<Delegates.sk_picture_approximate_op_count> ("sk_picture_approximate_op_count")).Invoke (picture, nested);
#endif

// sk_picture_t* sk_picture_deserialize_from_data(sk_data_t* data)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
Expand Down Expand Up @@ -8035,6 +8077,20 @@ private partial class Delegates {
(sk_picture_make_shader_delegate ??= GetSymbol<Delegates.sk_picture_make_shader> ("sk_picture_make_shader")).Invoke (src, tmx, tmy, mode, localMatrix, tile);
#endif

// void sk_picture_playback(const sk_picture_t* picture, sk_canvas_t* canvas)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_picture_playback (sk_picture_t picture, sk_canvas_t canvas);
#else
private partial class Delegates {
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
internal delegate void sk_picture_playback (sk_picture_t picture, sk_canvas_t canvas);
}
private static Delegates.sk_picture_playback sk_picture_playback_delegate;
internal static void sk_picture_playback (sk_picture_t picture, sk_canvas_t canvas) =>
(sk_picture_playback_delegate ??= GetSymbol<Delegates.sk_picture_playback> ("sk_picture_playback")).Invoke (picture, canvas);
#endif

// sk_canvas_t* sk_picture_recorder_begin_recording(sk_picture_recorder_t*, const sk_rect_t*)
#if !USE_DELEGATES
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
Expand Down
2 changes: 1 addition & 1 deletion externals/skia
18 changes: 18 additions & 0 deletions tests/Tests/SkiaSharp/SKDrawableTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ public void CanAccessBounds()
}
}

[SkippableFact]
public void CanAccessApproxBytes()
{
using (var drawable = new TestDrawable())
{
Assert.Equal(123, drawable.ApproximateBytesUsed);
Assert.Equal(1, drawable.ApproxBytesCount);
}
}

[Trait(Traits.FailingOn.Key, Traits.FailingOn.Values.macOS)] // Something with sk_sp<SkPicture> SkDrawable::onMakePictureSnapshot() is causing issues
[Trait(Traits.FailingOn.Key, Traits.FailingOn.Values.iOS)] // Something with sk_sp<SkPicture> SkDrawable::onMakePictureSnapshot() is causing issues
[Trait(Traits.FailingOn.Key, Traits.FailingOn.Values.MacCatalyst)] // Something with sk_sp<SkPicture> SkDrawable::onMakePictureSnapshot() is causing issues
Expand Down Expand Up @@ -95,6 +105,7 @@ class TestDrawable : SKDrawable
public int DrawFireCount;
public int BoundsFireCount;
public int SnapshotFireCount;
public int ApproxBytesCount;

protected override void OnDraw(SKCanvas canvas)
{
Expand All @@ -116,5 +127,12 @@ protected override SKPicture OnSnapshot()

return base.OnSnapshot();
}

protected override int OnGetApproximateBytesUsed ()
{
ApproxBytesCount++;

return 123;
}
}
}
42 changes: 42 additions & 0 deletions tests/Tests/SkiaSharp/SKPictureTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,47 @@ public void CanDeserializeFromStream()

ValidateTestBitmap(bmp);
}

[SkippableFact]
public void CanPlayback()
{
using var picture = CreateTestPicture();

using var bmp = new SKBitmap(40, 40);
using var cnv = new SKCanvas(bmp);

picture.Playback(cnv);

ValidateTestBitmap(bmp);
}

[SkippableFact]
public void CanDrawPicture()
{
using var picture = CreateTestPicture();

using var bmp = new SKBitmap(40, 40);
using var cnv = new SKCanvas(bmp);

cnv.DrawPicture(picture);

ValidateTestBitmap(bmp);
}

[SkippableFact]
public void CanGetApproximateOperationCount()
{
using var picture = CreateTestPicture();

Assert.Equal(5, picture.ApproximateOperationCount);
}

[SkippableFact]
public void CanGetApproximateBytesUsed()
{
using var picture = CreateTestPicture();

Assert.Equal(648, picture.ApproximateBytesUsed);
}
}
}

0 comments on commit c629919

Please sign in to comment.