Compare commits

...

9 Commits
v2.0 ... master

Author SHA1 Message Date
b7983c96b2 [v2.1.0] Bump version number.
All checks were successful
Build / build (push) Successful in 1m58s
2024-11-18 20:55:49 +03:00
cb75b7c244 Made Callbacks a public field in StbiStreamWrapper.cs
All checks were successful
Build / build (push) Successful in 1m55s
2024-11-18 20:47:28 +03:00
6ffcd38cbc Add GCHandle to StbiStreamWrapper in TryLoad routines.
All checks were successful
Build / build (push) Successful in 1m56s
2024-11-18 20:17:31 +03:00
09b5238b24 Bump version and release notes.
All checks were successful
Build / build (push) Successful in 2m5s
2024-11-18 19:59:29 +03:00
7157db6bd0 Additional documentation. 2024-11-18 19:57:50 +03:00
df61cdc5aa Fix bug in UnpremultiplyOnLoad. 2024-11-18 19:57:38 +03:00
50c37e58fc Add calling convention attributes to unmanaged function pointers. 2024-11-18 19:33:28 +03:00
8e8c86b2c1 Remove unnecessary attribute. 2024-11-18 19:32:23 +03:00
ff83cb20f9 Enabled optimzations.
All checks were successful
Build / build (push) Successful in 2m2s
2024-07-23 23:21:12 +03:00
9 changed files with 66 additions and 61 deletions

View File

@ -5,9 +5,10 @@ project(rf_stbi LANGUAGES C VERSION 1.0)
if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")) if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin"))
add_compile_options(-static-libgcc -static-libstdc++ -fno-exceptions) add_compile_options(-static-libgcc -static-libstdc++ -fno-exceptions)
endif() endif()
add_compile_options(-O3)
add_library(stbi SHARED "rf_stbi.c") add_library(stbi SHARED "rf_stbi.c")
install( install(
TARGETS stbi TARGETS stbi
RUNTIME DESTINATION . RUNTIME DESTINATION .
LIBRARY DESTINATION .) LIBRARY DESTINATION .)

View File

@ -1,12 +0,0 @@
using System;
namespace Quik.Stb
{
[AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)]
internal sealed class NativeTypeNameAttribute : System.Attribute
{
public NativeTypeNameAttribute(string typename)
{
}
}
}

View File

@ -1,19 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<RuntimeIdentifiers>linux-arm;linux-arm64;linux-x64;win-x86;win-x64;osx-arm64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>linux-arm;linux-arm64;linux-x64;win-x86;win-x64;osx-arm64;osx-x64</RuntimeIdentifiers>
<RootNamespace>ReFuel.Stb</RootNamespace> <RootNamespace>ReFuel.Stb</RootNamespace>
<Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<!-- Nuget Properties. --> <!-- Nuget Properties. -->
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>ReFuel.StbImage</PackageId> <PackageId>ReFuel.StbImage</PackageId>
<Version>2.0</Version> <Version>2.1.0</Version>
<Authors>STBI Authors, H. Utku Maden</Authors> <Authors>STBI Authors, H. Utku Maden</Authors>
<Description> <Description>
A C# wrapper for the ubiquitous stb_image.h and stb_image_write.h library. A C# wrapper for the ubiquitous stb_image.h and stb_image_write.h library.
@ -25,7 +26,15 @@
<RepositoryUrl>https://git.mixedup.dev/ReFuel/ReFuel.StbImage</RepositoryUrl> <RepositoryUrl>https://git.mixedup.dev/ReFuel/ReFuel.StbImage</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageTags>stb; stb_image; stbi; image; load; save; read; write</PackageTags> <PackageTags>stb; stb_image; stbi; image; load; save; read; write</PackageTags>
<PackageReleaseNotes># 2.0.0 <PackageReleaseNotes># 2.1.0 (ABI BRAKING)
* Fixed calling convention of unmanaged function pointers. (Thanks NogginBops!)
* 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.
# 2.0.0
* Exposed stbi_image_write.h * Exposed stbi_image_write.h
* Moved native function calls to ReFuel.Stb.Native</PackageReleaseNotes> * Moved native function calls to ReFuel.Stb.Native</PackageReleaseNotes>
</PropertyGroup> </PropertyGroup>
@ -47,7 +56,7 @@
<Content Include="runtimes/linux-arm64/native/*.so"> <Content Include="runtimes/linux-arm64/native/*.so">
<PackagePath>runtimes/linux-arm64/native/</PackagePath> <PackagePath>runtimes/linux-arm64/native/</PackagePath>
<Pack>true</Pack> <Pack>true</Pack>
<CopyToOutputDirectory>PreserveNewest/</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="runtimes/linux-x64/native/*.so"> <Content Include="runtimes/linux-x64/native/*.so">
<PackagePath>runtimes/linux-x64/native/</PackagePath> <PackagePath>runtimes/linux-x64/native/</PackagePath>

View File

@ -33,6 +33,7 @@ namespace ReFuel.Stb
/// Internal image format. /// Internal image format.
/// </summary> /// </summary>
public StbiImageFormat Format { get; } public StbiImageFormat Format { get; }
/// <summary> /// <summary>
/// True if the image is a floating point image. /// True if the image is a floating point image.
/// </summary> /// </summary>
@ -66,7 +67,7 @@ namespace ReFuel.Stb
StbiImageFormat.GreyAlpha => 2, StbiImageFormat.GreyAlpha => 2,
StbiImageFormat.Rgb => 3, StbiImageFormat.Rgb => 3,
StbiImageFormat.Rgba => 4, StbiImageFormat.Rgba => 4,
_ => throw new Exception("unkown image format") _ => throw new Exception("unknown image format")
} * (IsFloat ? sizeof(float) : sizeof(byte)); } * (IsFloat ? sizeof(float) : sizeof(byte));
return new ReadOnlySpan<T>((T*)ImagePointer, Width * Height * sz / sizeof(T)); return new ReadOnlySpan<T>((T*)ImagePointer, Width * Height * sz / sizeof(T));
@ -112,6 +113,7 @@ namespace ReFuel.Stb
/// Write image to a PNG file. /// Write image to a PNG file.
/// </summary> /// </summary>
/// <param name="dest">The destination stream.</param> /// <param name="dest">The destination stream.</param>
/// <param name="quality">The JPEG quality factor.</param>
/// <remarks> /// <remarks>
/// Incurs a conversion cost if the image format is not a byte format. Ignores alpha channel. Affected by non-thread safe global options. /// Incurs a conversion cost if the image format is not a byte format. Ignores alpha channel. Affected by non-thread safe global options.
/// </remarks> /// </remarks>
@ -149,7 +151,7 @@ namespace ReFuel.Stb
/// According to the stb_image documentation, only iPhone PNG images /// According to the stb_image documentation, only iPhone PNG images
/// can come with premultiplied alpha. /// can come with premultiplied alpha.
/// </remarks> /// </remarks>
public static bool UnpremultiplyOnLoad { set => Stbi.set_unpremultiply_on_load(1); } public static bool UnpremultiplyOnLoad { set => Stbi.set_unpremultiply_on_load(value ? 1 : 0); }
/// <summary> /// <summary>
/// Force a filter on PNG filter when saving. /// Force a filter on PNG filter when saving.
@ -201,22 +203,24 @@ namespace ReFuel.Stb
/// <param name="image">The resulting image.</param> /// <param name="image">The resulting image.</param>
/// <param name="stream">Source stream.</param> /// <param name="stream">Source stream.</param>
/// <param name="format">The desired image format.</param> /// <param name="format">The desired image format.</param>
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
/// <returns>True on success.</returns> /// <returns>True on success.</returns>
public static bool TryLoad([NotNullWhen(true)] out StbImage? image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false) public static bool TryLoad([NotNullWhen(true)] out StbImage? image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
{ {
int x, y, iFormat; int x, y, iFormat;
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true); StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
wrapper.CreateCallbacks(out stbi_io_callbacks cb);
stream.Position = 0; stream.Position = 0;
IntPtr imagePtr; IntPtr imagePtr;
if (asFloat) fixed (stbi_io_callbacks* cb = &wrapper.Callbacks)
{ {
imagePtr = (IntPtr)Stbi.loadf_from_callbacks(&cb, null, &x, &y, &iFormat, (int)format); if (asFloat)
} {
else imagePtr = (IntPtr)Stbi.loadf_from_callbacks(cb, null, &x, &y, &iFormat, (int)format);
{ }
imagePtr = (IntPtr)Stbi.load_from_callbacks(&cb, null, &x, &y, &iFormat, (int)format); else
{
imagePtr = (IntPtr)Stbi.load_from_callbacks(cb, null, &x, &y, &iFormat, (int)format);
}
} }
if (imagePtr != IntPtr.Zero) if (imagePtr != IntPtr.Zero)
@ -237,6 +241,7 @@ namespace ReFuel.Stb
/// <param name="image">The resulting image.</param> /// <param name="image">The resulting image.</param>
/// <param name="span">Source memory span.</param> /// <param name="span">Source memory span.</param>
/// <param name="format">The desired image format.</param> /// <param name="format">The desired image format.</param>
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
/// <returns>True on success.</returns> /// <returns>True on success.</returns>
public static bool TryLoad([NotNullWhen(true)] out StbImage? image, ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false) public static bool TryLoad([NotNullWhen(true)] out StbImage? image, ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
{ {
@ -271,6 +276,7 @@ namespace ReFuel.Stb
/// </summary> /// </summary>
/// <param name="stream">The stream to load from.</param> /// <param name="stream">The stream to load from.</param>
/// <param name="format">The desired image format.</param> /// <param name="format">The desired image format.</param>
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
/// <returns>The image object.</returns> /// <returns>The image object.</returns>
public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false) public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
{ {
@ -288,6 +294,7 @@ namespace ReFuel.Stb
/// </summary> /// </summary>
/// <param name="span">The span of memory to load from.</param> /// <param name="span">The span of memory to load from.</param>
/// <param name="format">The desired image format.</param> /// <param name="format">The desired image format.</param>
/// <param name="asFloat">The buffer uses floating point pixels if true.</param>
/// <returns>The image object.</returns> /// <returns>The image object.</returns>
public static StbImage Load(ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false) public static StbImage Load(ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
{ {
@ -312,10 +319,12 @@ namespace ReFuel.Stb
{ {
int x, y, iFormat; int x, y, iFormat;
StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true); StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true);
wrapper.CreateCallbacks(out stbi_io_callbacks cb); int result;
stream.Position = 0; 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; width = x;
height = y; height = y;
@ -389,7 +398,7 @@ namespace ReFuel.Stb
/// <param name="data">Span of pixel data.</param> /// <param name="data">Span of pixel data.</param>
/// <param name="width">Width of the pixel data in pixels.</param> /// <param name="width">Width of the pixel data in pixels.</param>
/// <param name="height">Height of the pixel data in pixels.</param> /// <param name="height">Height of the pixel data in pixels.</param>
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat.StbiImageFormat"/>.</param> /// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
/// <param name="destination">The destination stream.</param> /// <param name="destination">The destination stream.</param>
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param> /// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
/// <remarks> /// <remarks>
@ -425,7 +434,7 @@ namespace ReFuel.Stb
/// <param name="data">Span of pixel data.</param> /// <param name="data">Span of pixel data.</param>
/// <param name="width">Width of the pixel data in pixels.</param> /// <param name="width">Width of the pixel data in pixels.</param>
/// <param name="height">Height of the pixel data in pixels.</param> /// <param name="height">Height of the pixel data in pixels.</param>
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat.StbiImageFormat"/>.</param> /// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
/// <param name="destination">The destination stream.</param> /// <param name="destination">The destination stream.</param>
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param> /// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
/// <remarks> /// <remarks>
@ -461,7 +470,7 @@ namespace ReFuel.Stb
/// <param name="data">Span of pixel data.</param> /// <param name="data">Span of pixel data.</param>
/// <param name="width">Width of the pixel data in pixels.</param> /// <param name="width">Width of the pixel data in pixels.</param>
/// <param name="height">Height of the pixel data in pixels.</param> /// <param name="height">Height of the pixel data in pixels.</param>
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat.StbiImageFormat"/>.</param> /// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
/// <param name="destination">The destination stream.</param> /// <param name="destination">The destination stream.</param>
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param> /// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
/// <remarks> /// <remarks>
@ -497,7 +506,7 @@ namespace ReFuel.Stb
/// <param name="data">Span of pixel data.</param> /// <param name="data">Span of pixel data.</param>
/// <param name="width">Width of the pixel data in pixels.</param> /// <param name="width">Width of the pixel data in pixels.</param>
/// <param name="height">Height of the pixel data in pixels.</param> /// <param name="height">Height of the pixel data in pixels.</param>
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat.StbiImageFormat"/>.</param> /// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
/// <param name="destination">The destination stream.</param> /// <param name="destination">The destination stream.</param>
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param> /// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
/// <remarks> /// <remarks>
@ -533,8 +542,9 @@ namespace ReFuel.Stb
/// <param name="data">Span of pixel data.</param> /// <param name="data">Span of pixel data.</param>
/// <param name="width">Width of the pixel data in pixels.</param> /// <param name="width">Width of the pixel data in pixels.</param>
/// <param name="height">Height of the pixel data in pixels.</param> /// <param name="height">Height of the pixel data in pixels.</param>
/// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat.StbiImageFormat"/>.</param> /// <param name="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
/// <param name="destination">The destination stream.</param> /// <param name="destination">The destination stream.</param>
/// <param name="quality">The JPEG quality factor.</param>
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param> /// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
/// <remarks> /// <remarks>
/// This will incur a conversion cost if the pixel format is not a byte format. Ignores the alpha channel. Affected by global non-thread safe options. /// This will incur a conversion cost if the pixel format is not a byte format. Ignores the alpha channel. Affected by global non-thread safe options.

View File

@ -12,26 +12,32 @@ namespace ReFuel.Stb
/// <param name="buffer">C array to read into.</param> /// <param name="buffer">C array to read into.</param>
/// <param name="count">Size of the C array in bytes.</param> /// <param name="count">Size of the C array in bytes.</param>
/// <returns>The number of bytes read from the stream.</returns> /// <returns>The number of bytes read from the stream.</returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate int StbiReadProc(void *userdata, byte* buffer, int count); public unsafe delegate int StbiReadProc(void *userdata, byte* buffer, int count);
/// <summary> /// <summary>
/// Pointer to STBI stream skip function. /// Pointer to STBI stream skip function.
/// </summary> /// </summary>
/// <param name="userdata">User provided userdata pointer.</param> /// <param name="userdata">User provided userdata pointer.</param>
/// <param name="count">Number of bytes to skip.</param> /// <param name="count">Number of bytes to skip.</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void StbiSkipProc(void *userdata, int count); public unsafe delegate void StbiSkipProc(void *userdata, int count);
/// <summary> /// <summary>
/// Pointer to STBI stream end of file function. /// Pointer to STBI stream end of file function.
/// </summary> /// </summary>
/// <param name="userdata">User provided userdata pointer.</param> /// <param name="userdata">User provided userdata pointer.</param>
/// <returns>Non-zero value if the end of the stream has been reached.</returns> /// <returns>Non-zero value if the end of the stream has been reached.</returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate int StbiEofProc(void *userdata); public unsafe delegate int StbiEofProc(void *userdata);
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public unsafe class StbiStreamWrapper : IDisposable public unsafe class StbiStreamWrapper : IDisposable
{ {
private readonly stbi_io_callbacks _callbacks; public readonly stbi_io_callbacks Callbacks;
private readonly Stream _stream; private readonly Stream _stream;
private readonly bool _keepOpen; private readonly bool _keepOpen;
private bool _isDisposed; private bool _isDisposed;
@ -40,8 +46,6 @@ namespace ReFuel.Stb
private StbiSkipProc _skipCb; private StbiSkipProc _skipCb;
private StbiEofProc _eofCb; private StbiEofProc _eofCb;
public ref readonly stbi_io_callbacks Callbacks => ref _callbacks;
public StbiStreamWrapper(Stream stream, bool keepOpen = false) public StbiStreamWrapper(Stream stream, bool keepOpen = false)
{ {
if (stream == null) throw new ArgumentNullException(nameof(stream)); if (stream == null) throw new ArgumentNullException(nameof(stream));
@ -53,18 +57,10 @@ namespace ReFuel.Stb
_skipCb = SkipCb; _skipCb = SkipCb;
_eofCb = EofCb; _eofCb = EofCb;
_callbacks = default; Callbacks = default;
_callbacks.read = Marshal.GetFunctionPointerForDelegate<StbiReadProc>(_readCb); Callbacks.read = Marshal.GetFunctionPointerForDelegate<StbiReadProc>(_readCb);
_callbacks.skip = Marshal.GetFunctionPointerForDelegate<StbiSkipProc>(_skipCb); Callbacks.skip = Marshal.GetFunctionPointerForDelegate<StbiSkipProc>(_skipCb);
_callbacks.eof = Marshal.GetFunctionPointerForDelegate<StbiEofProc>(_eofCb); Callbacks.eof = Marshal.GetFunctionPointerForDelegate<StbiEofProc>(_eofCb);
}
public void CreateCallbacks(out stbi_io_callbacks cb)
{
cb = default;
cb.read = Marshal.GetFunctionPointerForDelegate<StbiReadProc>(_readCb);
cb.skip = Marshal.GetFunctionPointerForDelegate<StbiSkipProc>(_skipCb);
cb.eof = Marshal.GetFunctionPointerForDelegate<StbiEofProc>(_eofCb);
} }
private int ReadCb(void *userdata, byte* buffer, int count) private int ReadCb(void *userdata, byte* buffer, int count)

View File

@ -9,6 +9,7 @@ namespace ReFuel.Stb.Native
/// <param name="context">User provided context pointer.</param> /// <param name="context">User provided context pointer.</param>
/// <param name="data">C Array of data to write.</param> /// <param name="data">C Array of data to write.</param>
/// <param name="size">Size of the C array in bytes.</param> /// <param name="size">Size of the C array in bytes.</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void StbiWriteProc(void* context, void* data, int size); public unsafe delegate void StbiWriteProc(void* context, void* data, int size);
public unsafe partial class Stbi public unsafe partial class Stbi

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/bin/bash
cd $(dirname "$0") cd $(dirname "$0")
./docker-cross-compiler/sh/build_native.sh . ./docker-cross-compiler/sh/build_native.sh .
dotnet build dotnet build -c Release

@ -1 +1 @@
Subproject commit 5901e3e317571bce4a8772e2e994aaaadeae828e Subproject commit d0f5760c7a7995cec50bf31fef78f5408445f27c

2
stb

@ -1 +1 @@
Subproject commit 013ac3beddff3dbffafd5177e7972067cd2b5083 Subproject commit f7f20f39fe4f206c6f19e26ebfef7b261ee59ee4