diff --git a/binding/Binding/SKBitmap.cs b/binding/Binding/SKBitmap.cs index 5883fa93c5..23d9dbf1cd 100644 --- a/binding/Binding/SKBitmap.cs +++ b/binding/Binding/SKBitmap.cs @@ -37,8 +37,6 @@ public static SKFilterQuality ToFilterQuality (this SKBitmapResizeMethod method) } // TODO: keep in mind SKBitmap may be going away (according to Google) - // TODO: `ComputeIsOpaque` may be useful - // TODO: `GenerationID` may be useful // TODO: `GetAddr` and `GetPixel` are confusing public unsafe class SKBitmap : SKObject, ISKSkipObjectRegistration @@ -115,6 +113,47 @@ protected override void Dispose (bool disposing) => protected override void DisposeNative () => SkiaApi.sk_bitmap_destructor (Handle); + // Other + + public bool SetInfo (SKImageInfo info) => + SetInfo (info, 0); + + public bool SetInfo (SKImageInfo info, int rowBytes) + { + var cinfo = SKImageInfoNative.FromManaged (ref info); + return SkiaApi.sk_bitmap_set_info (Handle, &cinfo, (IntPtr)rowBytes); + } + + public bool ComputeIsOpaque () => + SkiaApi.sk_bitmap_compute_is_opaque (Handle); + + // AllocPixels + + public void AllocPixels (SKImageInfo info) => + AllocPixels (info, info.RowBytes); + + public void AllocPixels (SKImageInfo info, int rowBytes) + { + if (!TryAllocPixels (info, rowBytes)) { + SKImageInfo i = Info; + throw new OutOfMemoryException ("SkBitmap::tryAllocPixels failed " + + "ColorType:" + i.ColorType + "AlphaType:" + i.AlphaType + + "[w:" + i.Width + " h:" + i.Height + "] rb:" + RowBytes + ); + } + } + + public void AllocPixels (SKImageInfo info, SKBitmapAllocFlags flags) + { + if (!TryAllocPixels (info, flags)) { + SKImageInfo i = Info; + throw new OutOfMemoryException ("SkBitmap::tryAllocPixels failed " + + "ColorType:" + i.ColorType + "AlphaType:" + i.AlphaType + + "[w:" + i.Width + " h:" + i.Height + "] rb:" + RowBytes + ); + } + } + // TryAllocPixels public bool TryAllocPixels (SKImageInfo info) @@ -361,6 +400,9 @@ public int ByteCount { get { return (int)SkiaApi.sk_bitmap_get_byte_count (Handle); } } + public uint GenerationId => + (uint)SkiaApi.sk_bitmap_get_generation_id (Handle); + // *Pixels* public IntPtr GetPixels () => @@ -401,6 +443,14 @@ public void SetColorTable (SKColorTable ct) // more properties + public SKPixmap Pixmap { + get { + var pixmap = new SKPixmap (SkiaApi.sk_bitmap_get_pixmap (Handle), false); // pixmap is owned by this + pixmap.pixelSource = this; + return pixmap; + } + } + public byte[] Bytes { get { var array = GetPixelSpan ().ToArray (); @@ -723,6 +773,29 @@ public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKBitm return SkiaApi.sk_bitmap_install_pixels (Handle, &cinfo, (void*)pixels, (IntPtr)rowBytes, proxy, (void*)ctx); } + public bool ReadPixels (SKImageInfo info, IntPtr dstPixels, int rowBytes, int x, int y) + { + if (GetPixels () == IntPtr.Zero) + return false; + return Pixmap.ReadPixels (info, dstPixels, rowBytes, x, y); + } + + public bool ReadPixels (SKPixmap dstPixmap) => + ReadPixels (dstPixmap, 0, 0); + + public bool ReadPixels (SKPixmap dstPixmap, int x, int y) + { + if (GetPixels () == IntPtr.Zero) + return false; + return Pixmap.ReadPixels (dstPixmap.Info, dstPixmap.GetPixels (), dstPixmap.RowBytes, x, y); + } + + public bool WritePixels (SKPixmap pixmap) => + WritePixels (pixmap, 0, 0); + + public bool WritePixels (SKPixmap dstPixmap, int x, int y) => + SkiaApi.sk_bitmap_write_pixels_at_location (Handle, dstPixmap.Handle, x, y); + public bool InstallPixels (SKPixmap pixmap) { return SkiaApi.sk_bitmap_install_pixels_with_pixmap (Handle, pixmap.Handle); @@ -842,6 +915,9 @@ public static SKBitmap FromImage (SKImage image) return bmp; } + public SKImage ToImage () => + SKImage.FromBitmap (this); + // Encode public SKData Encode (SKEncodedImageFormat format, int quality) diff --git a/binding/Binding/SKCodec.cs b/binding/Binding/SKCodec.cs index 45f3c8ce49..21776e320a 100644 --- a/binding/Binding/SKCodec.cs +++ b/binding/Binding/SKCodec.cs @@ -4,7 +4,6 @@ namespace SkiaSharp { - // TODO: `Create(...)` should have overloads that accept a SKPngChunkReader // TODO: missing the `QueryYuv8` and `GetYuv8Planes` members public unsafe class SKCodec : SKObject, ISKSkipObjectRegistration @@ -42,6 +41,8 @@ public SKImageInfo Info { public SKEncodedImageFormat EncodedFormat => SkiaApi.sk_codec_get_encoded_format (Handle); + public SKSizeI EncodedDimensions => Info.Size; + public SKSizeI GetScaledDimensions (float desiredScale) { SKSizeI dimensions; @@ -277,12 +278,15 @@ public bool SkipScanlines (int countLines) => public int GetOutputScanline (int inputScanline) => SkiaApi.sk_codec_output_scanline (Handle, inputScanline); - // create (streams) + // create (filename) public static SKCodec Create (string filename) => - Create (filename, out var result); + Create (filename, null, SKCodecSelectionPolicy.PreferStillImage, out _); + + public static SKCodec Create (string filename, out SKCodecResult result) => + Create (filename, null, SKCodecSelectionPolicy.PreferStillImage, out result); - public static SKCodec Create (string filename, out SKCodecResult result) + public static SKCodec Create (string filename, SKPngChunkReader chunkReader, SKCodecSelectionPolicy selectionPolicy, out SKCodecResult result) { var stream = SKFileStream.OpenStream (filename); if (stream == null) { @@ -290,40 +294,56 @@ public static SKCodec Create (string filename, out SKCodecResult result) return null; } - return Create (stream, out result); + return Create (stream, chunkReader, selectionPolicy, out result); } + // create (Stream) + public static SKCodec Create (Stream stream) => - Create (stream, out var result); + Create (WrapManagedStream (stream), null, SKCodecSelectionPolicy.PreferStillImage, out _); public static SKCodec Create (Stream stream, out SKCodecResult result) => - Create (WrapManagedStream (stream), out result); + Create (WrapManagedStream (stream), null, SKCodecSelectionPolicy.PreferStillImage, out result); + + public static SKCodec Create (Stream stream, SKPngChunkReader chunkReader, SKCodecSelectionPolicy selectionPolicy, out SKCodecResult result) => + Create (WrapManagedStream (stream), chunkReader, selectionPolicy, out result); + + // create (SKStream) public static SKCodec Create (SKStream stream) => - Create (stream, out var result); + Create (stream, null, SKCodecSelectionPolicy.PreferStillImage, out _); + + public static SKCodec Create (SKStream stream, out SKCodecResult result) => + Create (stream, null, SKCodecSelectionPolicy.PreferStillImage, out result); - public static SKCodec Create (SKStream stream, out SKCodecResult result) + public static SKCodec Create (SKStream stream, SKPngChunkReader chunkReader, SKCodecSelectionPolicy selectionPolicy, out SKCodecResult result) { if (stream == null) throw new ArgumentNullException (nameof (stream)); if (stream is SKFileStream filestream && !filestream.IsValid) - throw new ArgumentException ("File stream was not valid.", nameof(stream)); + throw new ArgumentException ("File stream was not valid.", nameof (stream)); fixed (SKCodecResult* r = &result) { - var codec = GetObject (SkiaApi.sk_codec_new_from_stream (stream.Handle, r)); + var codec = GetObject (SkiaApi.sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy (stream.Handle, r, chunkReader?.Handle ?? IntPtr.Zero, selectionPolicy)); stream.RevokeOwnership (codec); + Referenced (codec, chunkReader); return codec; } } // create (data) - public static SKCodec Create (SKData data) + public static SKCodec Create (SKData data) => + Create (data, null); + + public static SKCodec Create (SKData data, SKPngChunkReader chunkReader) { if (data == null) throw new ArgumentNullException (nameof (data)); - return GetObject (SkiaApi.sk_codec_new_from_data (data.Handle)); + var codec = GetObject (SkiaApi.sk_codec_new_from_data_with_pngchunkreader (data.Handle, chunkReader?.Handle ?? IntPtr.Zero)); + Referenced (codec, chunkReader); + return codec; } // utils diff --git a/binding/Binding/SKObject.cs b/binding/Binding/SKObject.cs index d2e21c092d..b07fdc8d54 100644 --- a/binding/Binding/SKObject.cs +++ b/binding/Binding/SKObject.cs @@ -193,7 +193,7 @@ internal static T Owned (T owner, SKObject child) return owner; } - // indicate that the chile should not be garbage collected while + // indicate that the child should not be garbage collected while // the owner still lives internal static T Referenced (T owner, SKObject child) where T : SKObject diff --git a/binding/Binding/SKPngChunkReader.cs b/binding/Binding/SKPngChunkReader.cs new file mode 100644 index 0000000000..a737161c5c --- /dev/null +++ b/binding/Binding/SKPngChunkReader.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace SkiaSharp +{ + public unsafe abstract class SKPngChunkReader : SKObject, ISKSkipObjectRegistration + { + private static readonly SKManagedPngChunkReaderDelegates delegates; + private readonly IntPtr userData; + private int fromNative; + + static SKPngChunkReader () + { + delegates = new SKManagedPngChunkReaderDelegates { + fReadChunk = ReadChunkInternal, + fDestroy = DestroyInternal, + }; + + SkiaApi.sk_managedpngchunkreader_set_procs (delegates); + } + + protected SKPngChunkReader () + : base (IntPtr.Zero, true) + { + userData = DelegateProxies.CreateUserData (this, true); + Handle = SkiaApi.sk_managedpngchunkreader_new ((void*)userData); + + if (Handle == IntPtr.Zero) + throw new InvalidOperationException ("Unable to create a new SKPngChunkReader instance."); + } + + protected override void DisposeNative () + { + if (Interlocked.CompareExchange (ref fromNative, 0, 0) == 0) { + SkiaApi.sk_managedpngchunkreader_delete (Handle); + } + } + + protected abstract bool ReadChunk (string tag, IntPtr data, IntPtr length); + + // impl + + [MonoPInvokeCallback (typeof (SKManagedPngChunkReaderReadChunkProxyDelegate))] + private static bool ReadChunkInternal (IntPtr d, void* context, void* tag, void* data, IntPtr length) + { + var dump = DelegateProxies.GetUserData ((IntPtr)context, out _); + return dump.ReadChunk (Marshal.PtrToStringAnsi ((IntPtr)tag), (IntPtr)data, length); + } + + [MonoPInvokeCallback (typeof (SKManagedPngChunkReaderDestroyProxyDelegate))] + private static void DestroyInternal (IntPtr s, void* context) + { + var id = DelegateProxies.GetUserData ((IntPtr)context, out var gch); + if (id != null) { + Interlocked.Exchange (ref id.fromNative, 1); + id.Dispose (); + } + gch.Free (); + } + } +} diff --git a/binding/Binding/SkiaApi.generated.cs b/binding/Binding/SkiaApi.generated.cs index 910f5a142c..08e461bc5f 100644 --- a/binding/Binding/SkiaApi.generated.cs +++ b/binding/Binding/SkiaApi.generated.cs @@ -36,6 +36,7 @@ using sk_imagefilter_croprect_t = System.IntPtr; using sk_imagefilter_t = System.IntPtr; using sk_manageddrawable_t = System.IntPtr; +using sk_managedpngchunkreader_t = System.IntPtr; using sk_managedtracememorydump_t = System.IntPtr; using sk_maskfilter_t = System.IntPtr; using sk_matrix44_t = System.IntPtr; @@ -54,6 +55,7 @@ using sk_picture_t = System.IntPtr; using sk_pixelref_factory_t = System.IntPtr; using sk_pixmap_t = System.IntPtr; +using sk_pngchunkreader_t = System.IntPtr; using sk_refcnt_t = System.IntPtr; using sk_region_cliperator_t = System.IntPtr; using sk_region_iterator_t = System.IntPtr; @@ -930,6 +932,22 @@ internal static gr_vk_extensions_t gr_vk_extensions_new () => #region sk_bitmap.h + // bool sk_bitmap_compute_is_opaque(sk_bitmap_t* cbitmap) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal static extern bool sk_bitmap_compute_is_opaque (sk_bitmap_t cbitmap); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal delegate bool sk_bitmap_compute_is_opaque (sk_bitmap_t cbitmap); + } + private static Delegates.sk_bitmap_compute_is_opaque sk_bitmap_compute_is_opaque_delegate; + internal static bool sk_bitmap_compute_is_opaque (sk_bitmap_t cbitmap) => + (sk_bitmap_compute_is_opaque_delegate ??= GetSymbol ("sk_bitmap_compute_is_opaque")).Invoke (cbitmap); + #endif + // void sk_bitmap_destructor(sk_bitmap_t* cbitmap) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -1074,6 +1092,20 @@ private partial class Delegates { (sk_bitmap_get_byte_count_delegate ??= GetSymbol ("sk_bitmap_get_byte_count")).Invoke (cbitmap); #endif + // uint32_t sk_bitmap_get_generation_id(sk_bitmap_t* cbitmap) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern UInt32 sk_bitmap_get_generation_id (sk_bitmap_t cbitmap); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate UInt32 sk_bitmap_get_generation_id (sk_bitmap_t cbitmap); + } + private static Delegates.sk_bitmap_get_generation_id sk_bitmap_get_generation_id_delegate; + internal static UInt32 sk_bitmap_get_generation_id (sk_bitmap_t cbitmap) => + (sk_bitmap_get_generation_id_delegate ??= GetSymbol ("sk_bitmap_get_generation_id")).Invoke (cbitmap); + #endif + // void sk_bitmap_get_info(sk_bitmap_t* cbitmap, sk_imageinfo_t* info) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -1130,6 +1162,20 @@ private partial class Delegates { (sk_bitmap_get_pixels_delegate ??= GetSymbol ("sk_bitmap_get_pixels")).Invoke (cbitmap, length); #endif + // const sk_pixmap_t* sk_bitmap_get_pixmap(sk_bitmap_t* cbitmap) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern sk_pixmap_t sk_bitmap_get_pixmap (sk_bitmap_t cbitmap); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate sk_pixmap_t sk_bitmap_get_pixmap (sk_bitmap_t cbitmap); + } + private static Delegates.sk_bitmap_get_pixmap sk_bitmap_get_pixmap_delegate; + internal static sk_pixmap_t sk_bitmap_get_pixmap (sk_bitmap_t cbitmap) => + (sk_bitmap_get_pixmap_delegate ??= GetSymbol ("sk_bitmap_get_pixmap")).Invoke (cbitmap); + #endif + // size_t sk_bitmap_get_row_bytes(sk_bitmap_t* cbitmap) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -1326,6 +1372,22 @@ internal static void sk_bitmap_set_immutable (sk_bitmap_t cbitmap) => (sk_bitmap_set_immutable_delegate ??= GetSymbol ("sk_bitmap_set_immutable")).Invoke (cbitmap); #endif + // bool sk_bitmap_set_info(sk_bitmap_t* cbitmap, const sk_imageinfo_t* requestedInfo, size_t rowBytes) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal static extern bool sk_bitmap_set_info (sk_bitmap_t cbitmap, SKImageInfoNative* requestedInfo, /* size_t */ IntPtr rowBytes); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal delegate bool sk_bitmap_set_info (sk_bitmap_t cbitmap, SKImageInfoNative* requestedInfo, /* size_t */ IntPtr rowBytes); + } + private static Delegates.sk_bitmap_set_info sk_bitmap_set_info_delegate; + internal static bool sk_bitmap_set_info (sk_bitmap_t cbitmap, SKImageInfoNative* requestedInfo, /* size_t */ IntPtr rowBytes) => + (sk_bitmap_set_info_delegate ??= GetSymbol ("sk_bitmap_set_info")).Invoke (cbitmap, requestedInfo, rowBytes); + #endif + // void sk_bitmap_set_pixels(sk_bitmap_t* cbitmap, void* pixels) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -1386,6 +1448,22 @@ internal static bool sk_bitmap_try_alloc_pixels_with_flags (sk_bitmap_t cbitmap, (sk_bitmap_try_alloc_pixels_with_flags_delegate ??= GetSymbol ("sk_bitmap_try_alloc_pixels_with_flags")).Invoke (cbitmap, requestedInfo, flags); #endif + // bool sk_bitmap_write_pixels_at_location(sk_bitmap_t* cbitmap, const sk_pixmap_t* cpixmap, int x, int y) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal static extern bool sk_bitmap_write_pixels_at_location (sk_bitmap_t cbitmap, sk_pixmap_t cpixmap, Int32 x, Int32 y); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal delegate bool sk_bitmap_write_pixels_at_location (sk_bitmap_t cbitmap, sk_pixmap_t cpixmap, Int32 x, Int32 y); + } + private static Delegates.sk_bitmap_write_pixels_at_location sk_bitmap_write_pixels_at_location_delegate; + internal static bool sk_bitmap_write_pixels_at_location (sk_bitmap_t cbitmap, sk_pixmap_t cpixmap, Int32 x, Int32 y) => + (sk_bitmap_write_pixels_at_location_delegate ??= GetSymbol ("sk_bitmap_write_pixels_at_location")).Invoke (cbitmap, cpixmap, x, y); + #endif + #endregion #region sk_canvas.h @@ -2584,6 +2662,20 @@ internal static sk_codec_t sk_codec_new_from_data (sk_data_t data) => (sk_codec_new_from_data_delegate ??= GetSymbol ("sk_codec_new_from_data")).Invoke (data); #endif + // sk_codec_t* sk_codec_new_from_data_with_pngchunkreader(sk_data_t* data, sk_pngchunkreader_t* chunk_reader) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern sk_codec_t sk_codec_new_from_data_with_pngchunkreader (sk_data_t data, sk_pngchunkreader_t chunk_reader); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate sk_codec_t sk_codec_new_from_data_with_pngchunkreader (sk_data_t data, sk_pngchunkreader_t chunk_reader); + } + private static Delegates.sk_codec_new_from_data_with_pngchunkreader sk_codec_new_from_data_with_pngchunkreader_delegate; + internal static sk_codec_t sk_codec_new_from_data_with_pngchunkreader (sk_data_t data, sk_pngchunkreader_t chunk_reader) => + (sk_codec_new_from_data_with_pngchunkreader_delegate ??= GetSymbol ("sk_codec_new_from_data_with_pngchunkreader")).Invoke (data, chunk_reader); + #endif + // sk_codec_t* sk_codec_new_from_stream(sk_stream_t* stream, sk_codec_result_t* result) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -2598,6 +2690,20 @@ internal static sk_codec_t sk_codec_new_from_stream (sk_stream_t stream, SKCodec (sk_codec_new_from_stream_delegate ??= GetSymbol ("sk_codec_new_from_stream")).Invoke (stream, result); #endif + // sk_codec_t* sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy(sk_stream_t* stream, sk_codec_result_t* result, sk_pngchunkreader_t* chunk_reader, sk_codec_selection_policy_t policy) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern sk_codec_t sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy (sk_stream_t stream, SKCodecResult* result, sk_pngchunkreader_t chunk_reader, SKCodecSelectionPolicy policy); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate sk_codec_t sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy (sk_stream_t stream, SKCodecResult* result, sk_pngchunkreader_t chunk_reader, SKCodecSelectionPolicy policy); + } + private static Delegates.sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy_delegate; + internal static sk_codec_t sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy (sk_stream_t stream, SKCodecResult* result, sk_pngchunkreader_t chunk_reader, SKCodecSelectionPolicy policy) => + (sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy_delegate ??= GetSymbol ("sk_codec_new_from_stream_with_pngchunkreader_and_selection_policy")).Invoke (stream, result, chunk_reader, policy); + #endif + // int sk_codec_next_scanline(sk_codec_t* codec) #if !USE_DELEGATES [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] @@ -13214,6 +13320,52 @@ internal static void sk_manageddrawable_unref (sk_manageddrawable_t param0) => #endregion + #region sk_managedpngchunkreader.h + + // void sk_managedpngchunkreader_delete(sk_managedpngchunkreader_t*) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_managedpngchunkreader_delete (sk_managedpngchunkreader_t param0); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_managedpngchunkreader_delete (sk_managedpngchunkreader_t param0); + } + private static Delegates.sk_managedpngchunkreader_delete sk_managedpngchunkreader_delete_delegate; + internal static void sk_managedpngchunkreader_delete (sk_managedpngchunkreader_t param0) => + (sk_managedpngchunkreader_delete_delegate ??= GetSymbol ("sk_managedpngchunkreader_delete")).Invoke (param0); + #endif + + // sk_managedpngchunkreader_t* sk_managedpngchunkreader_new(void* context) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern sk_managedpngchunkreader_t sk_managedpngchunkreader_new (void* context); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate sk_managedpngchunkreader_t sk_managedpngchunkreader_new (void* context); + } + private static Delegates.sk_managedpngchunkreader_new sk_managedpngchunkreader_new_delegate; + internal static sk_managedpngchunkreader_t sk_managedpngchunkreader_new (void* context) => + (sk_managedpngchunkreader_new_delegate ??= GetSymbol ("sk_managedpngchunkreader_new")).Invoke (context); + #endif + + // void sk_managedpngchunkreader_set_procs(sk_managedpngchunkreader_procs_t procs) + #if !USE_DELEGATES + [DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)] + internal static extern void sk_managedpngchunkreader_set_procs (SKManagedPngChunkReaderDelegates procs); + #else + private partial class Delegates { + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal delegate void sk_managedpngchunkreader_set_procs (SKManagedPngChunkReaderDelegates procs); + } + private static Delegates.sk_managedpngchunkreader_set_procs sk_managedpngchunkreader_set_procs_delegate; + internal static void sk_managedpngchunkreader_set_procs (SKManagedPngChunkReaderDelegates procs) => + (sk_managedpngchunkreader_set_procs_delegate ??= GetSymbol ("sk_managedpngchunkreader_set_procs")).Invoke (procs); + #endif + + #endregion + #region sk_managedstream.h // void sk_managedstream_destroy(sk_stream_managedstream_t* s) @@ -13408,6 +13560,15 @@ namespace SkiaSharp { [UnmanagedFunctionPointer (CallingConvention.Cdecl)] internal unsafe delegate sk_picture_t SKManagedDrawableNewPictureSnapshotProxyDelegate(sk_manageddrawable_t d, void* context); + // typedef void (*)(sk_managedpngchunkreader_t* d, void* context)* sk_managedpngchunkreader_destroy_proc + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + internal unsafe delegate void SKManagedPngChunkReaderDestroyProxyDelegate(sk_managedpngchunkreader_t d, void* context); + + // typedef bool (*)(sk_managedpngchunkreader_t* d, void* context, const char[-1] tag, const void* data, size_t length)* sk_managedpngchunkreader_read_chunk_proc + [UnmanagedFunctionPointer (CallingConvention.Cdecl)] + [return: MarshalAs (UnmanagedType.I1)] + internal unsafe delegate bool SKManagedPngChunkReaderReadChunkProxyDelegate(sk_managedpngchunkreader_t d, void* context, /* char */ void* tag, void* data, /* size_t */ IntPtr length); + // typedef void (*)(sk_stream_managedstream_t* s, void* context)* sk_managedstream_destroy_proc [UnmanagedFunctionPointer (CallingConvention.Cdecl)] internal unsafe delegate void SKManagedStreamDestroyProxyDelegate(sk_stream_managedstream_t s, void* context); @@ -14923,6 +15084,37 @@ public readonly override int GetHashCode () } + // sk_managedpngchunkreader_procs_t + [StructLayout (LayoutKind.Sequential)] + internal unsafe partial struct SKManagedPngChunkReaderDelegates : IEquatable { + // public sk_managedpngchunkreader_read_chunk_proc fReadChunk + public SKManagedPngChunkReaderReadChunkProxyDelegate fReadChunk; + + // public sk_managedpngchunkreader_destroy_proc fDestroy + public SKManagedPngChunkReaderDestroyProxyDelegate fDestroy; + + public readonly bool Equals (SKManagedPngChunkReaderDelegates obj) => + fReadChunk == obj.fReadChunk && fDestroy == obj.fDestroy; + + public readonly override bool Equals (object obj) => + obj is SKManagedPngChunkReaderDelegates f && Equals (f); + + public static bool operator == (SKManagedPngChunkReaderDelegates left, SKManagedPngChunkReaderDelegates right) => + left.Equals (right); + + public static bool operator != (SKManagedPngChunkReaderDelegates left, SKManagedPngChunkReaderDelegates right) => + !left.Equals (right); + + public readonly override int GetHashCode () + { + var hash = new HashCode (); + hash.Add (fReadChunk); + hash.Add (fDestroy); + return hash.ToHashCode (); + } + + } + // sk_managedstream_procs_t [StructLayout (LayoutKind.Sequential)] internal unsafe partial struct SKManagedStreamDelegates : IEquatable { @@ -15769,6 +15961,14 @@ public enum SKCodecScanlineOrder { BottomUp = 1, } + // sk_codec_selection_policy_t + public enum SKCodecSelectionPolicy { + // PREFER_STILL_IMAGE_SK_CODEC_SELECTION_POLICY = 0 + PreferStillImage = 0, + // PREFER_ANIMATION_SK_CODEC_SELECTION_POLICY = 1 + PreferAnimation = 1, + } + // sk_codec_zero_initialized_t public enum SKZeroInitialized { // YES_SK_CODEC_ZERO_INITIALIZED = 0 diff --git a/binding/libSkiaSharp.json b/binding/libSkiaSharp.json index f11d6fa2c7..d9728a2249 100644 --- a/binding/libSkiaSharp.json +++ b/binding/libSkiaSharp.json @@ -341,6 +341,10 @@ "cs": "SKRunBufferInternal", "internal": true }, + "sk_managedpngchunkreader_procs_t": { + "cs": "SKManagedPngChunkReaderDelegates", + "internal": true + }, "sk_managedstream_procs_t": { "cs": "SKManagedStreamDelegates", "internal": true @@ -391,6 +395,12 @@ "-1": "IntPtr" } }, + "sk_managedpngchunkreader_read_chunk_proc": { + "cs": "SKManagedPngChunkReaderReadChunkProxyDelegate" + }, + "sk_managedpngchunkreader_destroy_proc": { + "cs": "SKManagedPngChunkReaderDestroyProxyDelegate" + }, "sk_managedwstream_write_proc": { "cs": "SKManagedWStreamWriteProxyDelegate" }, diff --git a/native/android/build.cake b/native/android/build.cake index 7da2b657bb..5e58268421 100644 --- a/native/android/build.cake +++ b/native/android/build.cake @@ -36,7 +36,7 @@ Task("libSkiaSharp") $"skia_use_system_zlib=false " + $"skia_use_vulkan={SUPPORT_VULKAN} ".ToLower () + $"skia_enable_skottie=true " + - $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_SYSCALL_GETRANDOM', '-DXML_DEV_URANDOM' ] " + + $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_SYSCALL_GETRANDOM', '-DXML_DEV_URANDOM', '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] " + $"ndk='{ANDROID_NDK_HOME}' " + $"ndk_api={(skiaArch == "x64" || skiaArch == "arm64" ? 21 : 16)}"); diff --git a/native/ios/build.cake b/native/ios/build.cake index 8a3f3e51bf..742712e514 100644 --- a/native/ios/build.cake +++ b/native/ios/build.cake @@ -42,7 +42,7 @@ Task("libSkiaSharp") $"skia_use_system_libwebp=false " + $"skia_use_system_zlib=false " + $"skia_enable_skottie=true " + - $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF' ] "); + $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF', '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] "); RunXCodeBuild("libSkiaSharp/libSkiaSharp.xcodeproj", "libSkiaSharp", sdk, arch, platform: VARIANT); diff --git a/native/linux/build.cake b/native/linux/build.cake index 2c1427aae5..890d8186d9 100644 --- a/native/linux/build.cake +++ b/native/linux/build.cake @@ -108,7 +108,7 @@ Task("libHarfBuzzSharp") $"target_cpu='{arch}' " + $"visibility_hidden=false " + $"extra_asmflags=[] " + - $"extra_cflags=[] " + + $"extra_cflags=[ '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] " + $"extra_ldflags=[ '-static-libstdc++', '-static-libgcc', '-Wl,--version-script={map}' ] " + COMPILERS + $"linux_soname_version='{soname}' " + diff --git a/native/macos/build.cake b/native/macos/build.cake index d52bb74cac..bdc399a96f 100644 --- a/native/macos/build.cake +++ b/native/macos/build.cake @@ -36,7 +36,9 @@ Task("libSkiaSharp") $"skia_use_system_libwebp=false " + $"skia_use_system_zlib=false " + $"skia_enable_skottie=true " + - $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF', '-stdlib=libc++' ] " + + $"extra_cflags=[ " + + $" '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF', '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED', " + + $" '-stdlib=libc++' ] " + $"extra_ldflags=[ '-stdlib=libc++' ]"); RunXCodeBuild("libSkiaSharp/libSkiaSharp.xcodeproj", "libSkiaSharp", "macosx", arch); diff --git a/native/tizen/build.cake b/native/tizen/build.cake index 2b0078e337..c2f7d3dd05 100644 --- a/native/tizen/build.cake +++ b/native/tizen/build.cake @@ -35,7 +35,7 @@ Task("libSkiaSharp") $"skia_use_system_libwebp=false " + $"skia_use_system_zlib=true " + $"skia_enable_skottie=true " + - $"extra_cflags=[ '-DSKIA_C_DLL', '-DXML_DEV_URANDOM', '-DSK_NO_MAKE_SHARED_PTR' ] " + + $"extra_cflags=[ '-DSKIA_C_DLL', '-DXML_DEV_URANDOM', '-DSK_NO_MAKE_SHARED_PTR', '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] " + $"ncli='{TIZEN_STUDIO_HOME}' " + $"ncli_version='4.0'"); diff --git a/native/tizen/libSkiaSharp/project_def.prop b/native/tizen/libSkiaSharp/project_def.prop index 8b62533891..bbbd04fcc3 100644 --- a/native/tizen/libSkiaSharp/project_def.prop +++ b/native/tizen/libSkiaSharp/project_def.prop @@ -51,4 +51,6 @@ USER_SRCS = $(skia_root)/src/xamarin/sk_xamarin.cpp \ $(skia_root)/src/xamarin/sk_manageddrawable.cpp \ $(skia_root)/src/xamarin/SkManagedDrawable.cpp \ $(skia_root)/src/xamarin/sk_managedtracememorydump.cpp \ + $(skia_root)/src/xamarin/sk_managedpngchunkreader.cpp \ + $(skia_root)/src/xamarin/SkManagedPngChunkReader.cpp \ $(skia_root)/src/xamarin/SkManagedTraceMemoryDump.cpp diff --git a/native/tvos/build.cake b/native/tvos/build.cake index 92beaae768..1f5097f272 100644 --- a/native/tvos/build.cake +++ b/native/tvos/build.cake @@ -31,7 +31,7 @@ Task("libSkiaSharp") $"skia_use_system_libwebp=false " + $"skia_use_system_zlib=false " + $"skia_enable_skottie=true " + - $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF' ] "); + $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF', '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] "); RunXCodeBuild("libSkiaSharp/libSkiaSharp.xcodeproj", "libSkiaSharp", sdk, arch); diff --git a/native/uwp/build.cake b/native/uwp/build.cake index d7a4c6f78f..2f9273a236 100644 --- a/native/uwp/build.cake +++ b/native/uwp/build.cake @@ -41,7 +41,8 @@ Task("libSkiaSharp") win_vcvars_version + $"extra_cflags=[ " + $" '-DSKIA_C_DLL', '/MD{d}', '/EHsc', '/Z7', " + - $" '-DSK_HAS_DWRITE_1_H', '-DSK_HAS_DWRITE_2_H', '-DNO_GETENV', '-D_HAS_AUTO_PTR_ETC=1' ] " + + $" '-DSK_HAS_DWRITE_1_H', '-DSK_HAS_DWRITE_2_H', '-DNO_GETENV', '-D_HAS_AUTO_PTR_ETC=1', " + + $" '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] " + $"extra_ldflags=[ '/DEBUG:FULL' ]"); var outDir = OUTPUT_PATH.Combine(dir); diff --git a/native/wasm/build.cake b/native/wasm/build.cake index dd3ad00549..902aef45a8 100644 --- a/native/wasm/build.cake +++ b/native/wasm/build.cake @@ -52,6 +52,7 @@ Task("libSkiaSharp") $"extra_cflags=[ " + $" '-DSKIA_C_DLL', '-DXML_POOR_ENTROPY', " + $" '-DSKNX_NO_SIMD', '-DSK_DISABLE_AAA', '-DGR_GL_CHECK_ALLOC_WITH_GET_ERROR=0', " + + $" '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED', " + $" '-s', 'WARN_UNALIGNED=1' " + // '-s', 'USE_WEBGL2=1' (experimental) $"] " + $"extra_cflags_cc=[ '-frtti' ] " + diff --git a/native/watchos/build.cake b/native/watchos/build.cake index 645bd5f272..cb85370854 100644 --- a/native/watchos/build.cake +++ b/native/watchos/build.cake @@ -33,7 +33,7 @@ Task("libSkiaSharp") $"skia_use_system_libwebp=false " + $"skia_use_system_zlib=false " + $"skia_enable_skottie=true " + - $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF' ] "); + $"extra_cflags=[ '-DSKIA_C_DLL', '-DHAVE_ARC4RANDOM_BUF', '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] "); RunXCodeBuild("libSkiaSharp/libSkiaSharp.xcodeproj", "libSkiaSharp", sdk, arch); diff --git a/native/windows/build.cake b/native/windows/build.cake index 0cda591bd3..f2fb3fda22 100644 --- a/native/windows/build.cake +++ b/native/windows/build.cake @@ -52,7 +52,9 @@ Task("libSkiaSharp") $"skia_use_vulkan={SUPPORT_VULKAN} ".ToLower () + clang + win_vcvars_version + - $"extra_cflags=[ '-DSKIA_C_DLL', '/MT{d}', '/EHsc', '/Z7', '-D_HAS_AUTO_PTR_ETC=1' ] " + + $"extra_cflags=[ " + + $" '-DSKIA_C_DLL', '/MT{d}', '/EHsc', '/Z7', '-D_HAS_AUTO_PTR_ETC=1', "+ + $" '-DPNG_READ_UNKNOWN_CHUNKS_SUPPORTED' ] " + $"extra_ldflags=[ '/DEBUG:FULL' ] " + ADDITIONAL_GN_ARGS); diff --git a/tests/Content/images/png_chunks/circle.9.png b/tests/Content/images/png_chunks/circle.9.png new file mode 100644 index 0000000000..9ae9518db8 Binary files /dev/null and b/tests/Content/images/png_chunks/circle.9.png differ diff --git a/tests/Content/images/png_chunks/good_idat_multiple.png b/tests/Content/images/png_chunks/good_idat_multiple.png new file mode 100644 index 0000000000..35f0c9e1de Binary files /dev/null and b/tests/Content/images/png_chunks/good_idat_multiple.png differ diff --git a/tests/Content/images/png_chunks/good_idat_some-empty.png b/tests/Content/images/png_chunks/good_idat_some-empty.png new file mode 100644 index 0000000000..05015e684c Binary files /dev/null and b/tests/Content/images/png_chunks/good_idat_some-empty.png differ diff --git a/tests/Content/images/png_chunks/good_itxt.png b/tests/Content/images/png_chunks/good_itxt.png new file mode 100644 index 0000000000..0238718bd4 Binary files /dev/null and b/tests/Content/images/png_chunks/good_itxt.png differ diff --git a/tests/Content/images/png_chunks/good_phys_96-dpi.png b/tests/Content/images/png_chunks/good_phys_96-dpi.png new file mode 100644 index 0000000000..3bb80fce72 Binary files /dev/null and b/tests/Content/images/png_chunks/good_phys_96-dpi.png differ diff --git a/tests/Content/images/png_chunks/good_splt.png b/tests/Content/images/png_chunks/good_splt.png new file mode 100644 index 0000000000..de93a1fe30 Binary files /dev/null and b/tests/Content/images/png_chunks/good_splt.png differ diff --git a/tests/Content/images/png_chunks/good_srgb.png b/tests/Content/images/png_chunks/good_srgb.png new file mode 100644 index 0000000000..1450fd5a3b Binary files /dev/null and b/tests/Content/images/png_chunks/good_srgb.png differ diff --git a/tests/Content/images/png_chunks/good_text.png b/tests/Content/images/png_chunks/good_text.png new file mode 100644 index 0000000000..087f0b61c8 Binary files /dev/null and b/tests/Content/images/png_chunks/good_text.png differ diff --git a/tests/Content/images/png_chunks/good_time_unix-epoch.png b/tests/Content/images/png_chunks/good_time_unix-epoch.png new file mode 100644 index 0000000000..b14449ccf5 Binary files /dev/null and b/tests/Content/images/png_chunks/good_time_unix-epoch.png differ diff --git a/tests/Content/images/png_chunks/good_ztxt.png b/tests/Content/images/png_chunks/good_ztxt.png new file mode 100644 index 0000000000..374334fe79 Binary files /dev/null and b/tests/Content/images/png_chunks/good_ztxt.png differ diff --git a/tests/Tests/SKCodecTest.cs b/tests/Tests/SKCodecTest.cs index e7619e621f..adc674293f 100644 --- a/tests/Tests/SKCodecTest.cs +++ b/tests/Tests/SKCodecTest.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net.Http; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Xunit; @@ -491,5 +493,31 @@ public void CanDecodeData(string image) Assert.Equal(SKCodecResult.Success, codec.GetPixels(out var pixels)); Assert.NotEmpty(pixels); } + + [SkippableFact] + public void CanReadPngChunks_NonStandard() + { + var path = Path.Combine(PathToImages, "png_chunks/circle.9.png"); + + using var chunkReader = new TestPngChunkReader(); + using var codec = SKCodec.Create(path, chunkReader, SKCodecSelectionPolicy.PreferStillImage, out var codecResult); + using var bmp = SKBitmap.Decode(codec); + + var chunks = chunkReader.Chunks; + + Assert.NotEmpty(chunks); + } + + unsafe class TestPngChunkReader : SKPngChunkReader + { + protected override bool ReadChunk(string tag, IntPtr data, IntPtr length) + { + var d = new ReadOnlySpan((void*)data, (int)length).ToArray(); + Chunks.Add((tag, d)); + return true; + } + + public List<(string, byte[])> Chunks { get; } = new List<(string, byte[])>(); + } } }