From c7ff09c347e929a969ed458e65ed4730b89ea207 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 11 Jan 2024 11:53:00 +0100 Subject: [PATCH] [WiP] Add `ReadOnlySpan` / `ReadOnlyMemory` overloads Implements: - `Source.NewFromMemory`, `Image.FindLoadBuffer` and `Image.NewFromBuffer` for `ReadOnlySpan`. - `Image.NewFromMemory` for `ReadOnlyMemory`. --- src/NetVips/Internal/Vips.cs | 20 +++++ src/NetVips/NetVips.csproj | 1 + src/NetVips/Source.cs | 2 +- src/NetVips/net6.0/Image.cs | 137 +++++++++++++++++++++++++++++++++++ src/NetVips/net6.0/Source.cs | 43 +++++++++++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/NetVips/net6.0/Image.cs create mode 100644 src/NetVips/net6.0/Source.cs diff --git a/src/NetVips/Internal/Vips.cs b/src/NetVips/Internal/Vips.cs index 10740bcf..b7a97db4 100644 --- a/src/NetVips/Internal/Vips.cs +++ b/src/NetVips/Internal/Vips.cs @@ -295,6 +295,10 @@ internal static class VipsBlob [SuppressUnmanagedCodeSecurity] [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_blob_get")] internal static extern IntPtr Get(VipsBlobManaged blob, out UIntPtr length); + + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_blob_copy")] + internal static extern unsafe VipsBlobManaged Copy(void* data, UIntPtr length); } internal static class VipsArea @@ -422,6 +426,12 @@ internal static class VipsImage internal static extern IntPtr NewFromMemory(IntPtr data, UIntPtr size, int width, int height, int bands, BandFormat format); + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_image_new_from_memory")] + internal static extern unsafe IntPtr NewFromMemory(void* data, UIntPtr size, int width, int height, + int bands, BandFormat format); + [SuppressUnmanagedCodeSecurity] [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_image_new_from_memory_copy")] @@ -582,6 +592,11 @@ internal static class VipsForeign EntryPoint = "vips_foreign_find_load_buffer")] internal static extern IntPtr FindLoadBuffer(IntPtr data, ulong size); + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_foreign_find_load_buffer")] + internal static extern unsafe IntPtr FindLoadBuffer(void* data, ulong size); + [SuppressUnmanagedCodeSecurity] [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_foreign_find_load_source")] @@ -637,6 +652,11 @@ internal static class VipsSource EntryPoint = "vips_source_new_from_file")] internal static extern IntPtr NewFromFile(byte[] filename); + [SuppressUnmanagedCodeSecurity] + [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, + EntryPoint = "vips_source_new_from_blob")] + internal static extern IntPtr NewFromBlob(VipsBlobManaged blob); + [SuppressUnmanagedCodeSecurity] [DllImport(Libraries.Vips, CallingConvention = CallingConvention.Cdecl, EntryPoint = "vips_source_new_from_memory")] diff --git a/src/NetVips/NetVips.csproj b/src/NetVips/NetVips.csproj index d2c763e9..1a46cd08 100644 --- a/src/NetVips/NetVips.csproj +++ b/src/NetVips/NetVips.csproj @@ -11,6 +11,7 @@ x64;x86;ARM64;ARM32 true + true false true true diff --git a/src/NetVips/Source.cs b/src/NetVips/Source.cs index 7695020f..cddb03e1 100644 --- a/src/NetVips/Source.cs +++ b/src/NetVips/Source.cs @@ -7,7 +7,7 @@ namespace NetVips /// /// An input connection. /// - public class Source : Connection + public partial class Source : Connection { // private static Logger logger = LogManager.GetCurrentClassLogger(); diff --git a/src/NetVips/net6.0/Image.cs b/src/NetVips/net6.0/Image.cs new file mode 100644 index 00000000..d26870c3 --- /dev/null +++ b/src/NetVips/net6.0/Image.cs @@ -0,0 +1,137 @@ +#if NET6_0_OR_GREATER + +namespace NetVips +{ + using System; + using System.Runtime.InteropServices; + using Internal; + + /// + /// Wrap a object. + /// + public partial class Image + { + #region helpers + + /// + /// Find the name of the load operation vips will use to load a buffer. + /// + /// + /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what + /// options to pass to . + /// + /// The buffer to test. + /// Length of the buffer. + /// The name of the load operation, or . + internal static unsafe string FindLoadBuffer(void* data, ulong size) => + Marshal.PtrToStringAnsi(VipsForeign.FindLoadBuffer(data, size)); + + /// + /// Find the name of the load operation vips will use to load a buffer. + /// + /// + /// For example "VipsForeignLoadJpegBuffer". You can use this to work out what + /// options to pass to . + /// + /// The buffer to test. + /// The name of the load operation, or . + public static unsafe string FindLoadBuffer(ReadOnlySpan data) + { + fixed (byte* dataFixed = data) + { + return FindLoadBuffer(dataFixed, (ulong)data.Length); + } + } + + #endregion + + #region constructors + + /// + /// Load a formatted image from memory. + /// + /// + /// This behaves exactly as , but the image is + /// loaded from the memory object rather than from a file. The memory + /// object can be a string or buffer. + /// + /// The memory object to load the image from. + /// Load options as a string. Use for no options. + /// Hint the expected access pattern for the image. + /// The type of error that will cause load to fail. By + /// default, loaders are permissive, that is, . + /// Optional options that depend on the load operation. + /// A new . + /// If unable to load from . + public static unsafe Image NewFromBuffer( + ReadOnlySpan data, + string strOptions = "", + Enums.Access? access = null, + Enums.FailOn? failOn = null, + VOption kwargs = null) + { + fixed (byte* dataFixed = data) + { + var operationName = FindLoadBuffer(dataFixed, (ulong)data.Length); + if (operationName == null) + { + throw new VipsException("unable to load from buffer"); + } + + var options = new VOption(); + if (kwargs != null) + { + options.Merge(kwargs); + } + + options.AddIfPresent(nameof(access), access); + options.AddFailOn(failOn); + + options.Add("string_options", strOptions); + + using var blob = Internal.VipsBlob.Copy(dataFixed, (UIntPtr)data.Length); + return Operation.Call(operationName, options, blob) as Image; + } + } + + /// + /// Wrap an image around a memory array. + /// + /// A . + /// Image width in pixels. + /// Image height in pixels. + /// Number of bands. + /// Band format. + /// A new . + /// If unable to make image from . + public static unsafe Image NewFromMemory( + ReadOnlyMemory data, + int width, + int height, + int bands, + Enums.BandFormat format) where T : unmanaged + { + var handle = data.Pin(); + var vi = VipsImage.NewFromMemory(handle.Pointer, (UIntPtr)data.Length, width, height, bands, + format); + + if (vi == IntPtr.Zero) + { + handle.Dispose(); + + throw new VipsException("unable to make image from memory"); + } + + var image = new Image(vi) { MemoryPressure = data.Length }; + + // Need to release the pinned MemoryHandle when the image is closed. + image.OnPostClose += () => handle.Dispose(); + + return image; + } + + #endregion + } +} + +#endif \ No newline at end of file diff --git a/src/NetVips/net6.0/Source.cs b/src/NetVips/net6.0/Source.cs new file mode 100644 index 00000000..16763d7b --- /dev/null +++ b/src/NetVips/net6.0/Source.cs @@ -0,0 +1,43 @@ +#if NET6_0_OR_GREATER + +namespace NetVips +{ + using System; + + /// + /// An input connection. + /// + public partial class Source + { + /// + /// Make a new source from a memory object. + /// + /// + /// Make a new source that is attached to the memory object. For example: + /// + /// using var source = Source.NewFromMemory(data); + /// + /// You can pass this source to (for example) . + /// + /// The memory object. + /// A new . + /// If unable to create a new from . + public static unsafe Source NewFromMemory(ReadOnlySpan data) + { + fixed (byte* dataFixed = data) + { + using var blob = Internal.VipsBlob.Copy(dataFixed, (UIntPtr)data.Length); + var pointer = Internal.VipsSource.NewFromBlob(blob); + + if (pointer == IntPtr.Zero) + { + throw new VipsException("can't create input source from memory"); + } + + return new Source(pointer); + } + } + } +} + +#endif \ No newline at end of file