From cb75b7c244ca63980c83d4139ce19cc08e791679 Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Mon, 18 Nov 2024 20:47:28 +0300 Subject: [PATCH] Made Callbacks a public field in StbiStreamWrapper.cs --- ReFuel.StbImage.csproj | 10 +++++----- StbImage.cs | 26 ++++++++++++++------------ StbiStreamWrapper.cs | 23 +++++++---------------- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/ReFuel.StbImage.csproj b/ReFuel.StbImage.csproj index 7026d6a..811403e 100644 --- a/ReFuel.StbImage.csproj +++ b/ReFuel.StbImage.csproj @@ -14,7 +14,7 @@ True ReFuel.StbImage - 2.0.2-rc.1 + 2.1.0-rc.0 STBI Authors, H. Utku Maden A C# wrapper for the ubiquitous stb_image.h and stb_image_write.h library. @@ -26,10 +26,10 @@ https://git.mixedup.dev/ReFuel/ReFuel.StbImage git stb; stb_image; stbi; image; load; save; read; write - # 2.0.2 + # 2.1.0 (ABI BRAKING) * Fixed calling convention of unmanaged function pointers. (Thanks NogginBops!) -* Allocating a GC handle to StbiStreamWrapper class and passing it as userdata into stbi in order to prevent - the object from being prematurely collected by the garbage collector when optimizations are enabled in Release mode. +* Modified StbiStreamWrapper in order to fixed backing delegates of function pointers from being prematurely collected + by release mode JIT and the GC. StbiStreamWrapper.Callbacks is now a readonly field. (ABI BREAKING) # 2.0.1 * Enabled optimizations across the board for native and managed assemblies. @@ -56,7 +56,7 @@ runtimes/linux-arm64/native/ true - PreserveNewest/ + PreserveNewest runtimes/linux-x64/native/ diff --git a/StbImage.cs b/StbImage.cs index fc57153..28a3f88 100644 --- a/StbImage.cs +++ b/StbImage.cs @@ -209,19 +209,19 @@ namespace ReFuel.Stb { int x, y, iFormat; StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true); - GCHandle gch = GCHandle.Alloc(wrapper, GCHandleType.Normal); - wrapper.CreateCallbacks(out stbi_io_callbacks cb); stream.Position = 0; IntPtr imagePtr; - if (asFloat) + fixed (stbi_io_callbacks* cb = &wrapper.Callbacks) { - imagePtr = (IntPtr)Stbi.loadf_from_callbacks(&cb, (void*)(IntPtr)gch, &x, &y, &iFormat, (int)format); + if (asFloat) + { + imagePtr = (IntPtr)Stbi.loadf_from_callbacks(cb, null, &x, &y, &iFormat, (int)format); + } + else + { + imagePtr = (IntPtr)Stbi.load_from_callbacks(cb, null, &x, &y, &iFormat, (int)format); + } } - else - { - imagePtr = (IntPtr)Stbi.load_from_callbacks(&cb, (void*)(IntPtr)gch, &x, &y, &iFormat, (int)format); - } - gch.Free(); if (imagePtr != IntPtr.Zero) { @@ -319,10 +319,12 @@ namespace ReFuel.Stb { int x, y, iFormat; StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true); - wrapper.CreateCallbacks(out stbi_io_callbacks cb); - + int result; stream.Position = 0; - int result = Stbi.info_from_callbacks(&cb, null, &x, &y, &iFormat); + fixed (stbi_io_callbacks* cb = &wrapper.Callbacks) + { + result = Stbi.info_from_callbacks(cb, null, &x, &y, &iFormat); + } width = x; height = y; diff --git a/StbiStreamWrapper.cs b/StbiStreamWrapper.cs index 37b6ad1..482ab93 100644 --- a/StbiStreamWrapper.cs +++ b/StbiStreamWrapper.cs @@ -32,11 +32,12 @@ namespace ReFuel.Stb public unsafe delegate int StbiEofProc(void *userdata); /// - /// An easy to use stream wrapper for use with STBI image load functions. + /// An easy-to-use stream wrapper for use with STBI image load functions. /// public unsafe class StbiStreamWrapper : IDisposable { - private readonly stbi_io_callbacks _callbacks; + public readonly stbi_io_callbacks Callbacks; + private readonly Stream _stream; private readonly bool _keepOpen; private bool _isDisposed; @@ -45,8 +46,6 @@ namespace ReFuel.Stb private StbiSkipProc _skipCb; private StbiEofProc _eofCb; - public ref readonly stbi_io_callbacks Callbacks => ref _callbacks; - public StbiStreamWrapper(Stream stream, bool keepOpen = false) { if (stream == null) throw new ArgumentNullException(nameof(stream)); @@ -58,18 +57,10 @@ namespace ReFuel.Stb _skipCb = SkipCb; _eofCb = EofCb; - _callbacks = default; - _callbacks.read = Marshal.GetFunctionPointerForDelegate(_readCb); - _callbacks.skip = Marshal.GetFunctionPointerForDelegate(_skipCb); - _callbacks.eof = Marshal.GetFunctionPointerForDelegate(_eofCb); - } - - public void CreateCallbacks(out stbi_io_callbacks cb) - { - cb = default; - cb.read = Marshal.GetFunctionPointerForDelegate(_readCb); - cb.skip = Marshal.GetFunctionPointerForDelegate(_skipCb); - cb.eof = Marshal.GetFunctionPointerForDelegate(_eofCb); + Callbacks = default; + Callbacks.read = Marshal.GetFunctionPointerForDelegate(_readCb); + Callbacks.skip = Marshal.GetFunctionPointerForDelegate(_skipCb); + Callbacks.eof = Marshal.GetFunctionPointerForDelegate(_eofCb); } private int ReadCb(void *userdata, byte* buffer, int count)