Compare commits
16 Commits
v1.1.0-rc0
...
master
Author | SHA1 | Date | |
---|---|---|---|
b7983c96b2 | |||
cb75b7c244 | |||
6ffcd38cbc | |||
09b5238b24 | |||
7157db6bd0 | |||
df61cdc5aa | |||
50c37e58fc | |||
8e8c86b2c1 | |||
ff83cb20f9 | |||
3801bc29f8 | |||
bcd82900c7 | |||
e759efacf6 | |||
d9a9e3962f | |||
f976af3833 | |||
9b21bb837b | |||
e7d7f5f826 |
@ -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 .)
|
||||||
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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>disable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>7.3</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>1.1.0-rc0</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.
|
||||||
@ -21,6 +22,21 @@
|
|||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
|
||||||
<PackageIcon>images\icon.png</PackageIcon>
|
<PackageIcon>images\icon.png</PackageIcon>
|
||||||
|
<PackageProjectUrl>https://refuel.mixedup.dev/docs/ReFuel.StbImage.html</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://git.mixedup.dev/ReFuel/ReFuel.StbImage</RepositoryUrl>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<PackageTags>stb; stb_image; stbi; image; load; save; read; write</PackageTags>
|
||||||
|
<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
|
||||||
|
* Moved native function calls to ReFuel.Stb.Native</PackageReleaseNotes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@ -30,8 +46,8 @@
|
|||||||
that determines a lot of load paths for projects.
|
that determines a lot of load paths for projects.
|
||||||
-->
|
-->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="*.md" Pack="true" PackagePath="/"/>
|
<Content Include="*.md" Pack="true" PackagePath="/" />
|
||||||
<None Include="rf_stbimage.png" Pack="true" PackagePath="images\icon.png"/>
|
<None Include="rf_stbimage.png" Pack="true" PackagePath="images\icon.png" />
|
||||||
<Content Include="runtimes/linux-arm/native/*.so">
|
<Content Include="runtimes/linux-arm/native/*.so">
|
||||||
<PackagePath>runtimes/linux-arm/native/</PackagePath>
|
<PackagePath>runtimes/linux-arm/native/</PackagePath>
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
@ -40,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>
|
||||||
|
399
StbImage.cs
399
StbImage.cs
@ -1,4 +1,6 @@
|
|||||||
|
using ReFuel.Stb.Native;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@ -31,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>
|
||||||
@ -50,6 +53,72 @@ namespace ReFuel.Stb
|
|||||||
Dispose(false);
|
Dispose(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a safe span to the image pointer.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type.</typeparam>
|
||||||
|
/// <returns>A span to the image data.</returns>
|
||||||
|
/// <exception cref="Exception">The image uses an unexpected image format.</exception>
|
||||||
|
public ReadOnlySpan<T> AsSpan<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
int sz = Format switch
|
||||||
|
{
|
||||||
|
StbiImageFormat.Grey => 1,
|
||||||
|
StbiImageFormat.GreyAlpha => 2,
|
||||||
|
StbiImageFormat.Rgb => 3,
|
||||||
|
StbiImageFormat.Rgba => 4,
|
||||||
|
_ => throw new Exception("unknown image format")
|
||||||
|
} * (IsFloat ? sizeof(float) : sizeof(byte));
|
||||||
|
|
||||||
|
return new ReadOnlySpan<T>((T*)ImagePointer, Width * Height * sz / sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WritePng(Stream dest) => WritePng(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a BMP file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteBmp(Stream dest) => WriteBmp(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a TGA file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Ignores alpha channel. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteTga(Stream dest) => WriteTga(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a float format. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteHdr(Stream dest) => WriteHdr(AsSpan<byte>(), Width, Height, Format, dest, isFloat: IsFloat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dest">The destination stream.</param>
|
||||||
|
/// <param name="quality">The JPEG quality factor.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Incurs a conversion cost if the image format is not a byte format. Ignores alpha channel. Affected by non-thread safe global options.
|
||||||
|
/// </remarks>
|
||||||
|
public void WriteJpg(Stream dest, int quality = 90) => WriteJpg(AsSpan<byte>(), Width, Height, Format, dest, quality: quality, isFloat: IsFloat);
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
public void Dispose() => Dispose(true);
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
private void Dispose(bool disposing)
|
||||||
@ -68,7 +137,12 @@ namespace ReFuel.Stb
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set to flip the y-axis of loaded images on load.
|
/// Set to flip the y-axis of loaded images on load.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool FlipVerticallyOnLoad { set => Stbi.set_flip_vertically_on_load(1); }
|
public static bool FlipVerticallyOnLoad { set => Stbi.set_flip_vertically_on_load(value ? 1 : 0); }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set to flip the y-axis of saved images.
|
||||||
|
/// </summary>
|
||||||
|
public static bool FlipVerticallyOnSave { set => Stbi.flip_vertically_on_write(value ? 1 : 0); }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set to unpremultiply images on load.
|
/// Set to unpremultiply images on load.
|
||||||
@ -77,7 +151,51 @@ 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>
|
||||||
|
/// Force a filter on PNG filter when saving.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// -1 for auto, 0 through 5 to pick a filter. Higher is more. Not thread safe.
|
||||||
|
/// </remarks>
|
||||||
|
public static int WriteForcePngFilter
|
||||||
|
{
|
||||||
|
get => Stbi.write_force_png_filter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < -1 || value > 5)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), "The PNG filter must be in the range 0 to 5, or -1 for auto.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Stbi.write_force_png_filter = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change the PNG compression level on save.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Higher is more. Defaults to 8. Not thread safe.
|
||||||
|
/// </remarks>
|
||||||
|
public static int WritePngCompressionLevel
|
||||||
|
{
|
||||||
|
get => Stbi.write_png_compression_level;
|
||||||
|
set => Stbi.write_png_compression_level = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enable run length encoding on TGA images on save.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Not thread safe.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool WriteTgaEnableRLE
|
||||||
|
{
|
||||||
|
get => Stbi.write_tga_with_rle != 0;
|
||||||
|
set => Stbi.write_tga_with_rle = value ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try loading an image, without raising exceptions.
|
/// Try loading an image, without raising exceptions.
|
||||||
@ -85,27 +203,29 @@ 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(out StbImage image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = 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 (isFloat)
|
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)
|
||||||
{
|
{
|
||||||
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, isFloat);
|
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, asFloat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -121,26 +241,26 @@ 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<T>(out StbImage image, ReadOnlySpan<T> span, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false)
|
public static bool TryLoad([NotNullWhen(true)] out StbImage? image, ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
where T : unmanaged
|
|
||||||
{
|
{
|
||||||
IntPtr imagePtr = IntPtr.Zero;
|
IntPtr imagePtr = IntPtr.Zero;
|
||||||
int x, y, iFormat;
|
int x, y, iFormat;
|
||||||
fixed (byte *ptr = MemoryMarshal.AsBytes(span))
|
fixed (byte *ptr = MemoryMarshal.AsBytes(span))
|
||||||
{
|
{
|
||||||
if (isFloat)
|
if (asFloat)
|
||||||
{
|
{
|
||||||
imagePtr = (IntPtr)Stbi.loadf_from_memory(ptr, span.Length * sizeof(T), &x, &y, &iFormat, (int)format);
|
imagePtr = (IntPtr)Stbi.loadf_from_memory(ptr, span.Length, &x, &y, &iFormat, (int)format);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
imagePtr = (IntPtr)Stbi.load_from_memory(ptr, span.Length * sizeof(T), &x, &y, &iFormat, (int)format);
|
imagePtr = (IntPtr)Stbi.load_from_memory(ptr, span.Length, &x, &y, &iFormat, (int)format);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imagePtr != IntPtr.Zero)
|
if (imagePtr != IntPtr.Zero)
|
||||||
{
|
{
|
||||||
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, isFloat);
|
image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, asFloat);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -156,15 +276,16 @@ 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 isFloat = false)
|
public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
{
|
{
|
||||||
if (TryLoad(out StbImage image, stream, format, isFloat))
|
if (TryLoad(out StbImage? image, stream, format, asFloat))
|
||||||
{
|
{
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason());
|
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason())!;
|
||||||
throw new Exception($"Failed to load image: {reason}");
|
throw new Exception($"Failed to load image: {reason}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,16 +294,16 @@ 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<T>(ReadOnlySpan<T> span, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false)
|
public static StbImage Load(ReadOnlySpan<byte> span, StbiImageFormat format = StbiImageFormat.Default, bool asFloat = false)
|
||||||
where T : unmanaged
|
|
||||||
{
|
{
|
||||||
if (TryLoad(out StbImage image, span, format, isFloat))
|
if (TryLoad(out StbImage? image, span, format, asFloat))
|
||||||
{
|
{
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason());
|
string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason())!;
|
||||||
throw new Exception($"Failed to load image: {reason}");
|
throw new Exception($"Failed to load image: {reason}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,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;
|
||||||
@ -230,5 +353,223 @@ namespace ReFuel.Stb
|
|||||||
return result != 0;
|
return result != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int Components(StbiImageFormat format) => format switch
|
||||||
|
{
|
||||||
|
StbiImageFormat.Grey => 1,
|
||||||
|
StbiImageFormat.GreyAlpha => 2,
|
||||||
|
StbiImageFormat.Rgb => 3,
|
||||||
|
StbiImageFormat.Rgba => 4,
|
||||||
|
_ => throw new ArgumentException("Expected a fully qualified format.")
|
||||||
|
};
|
||||||
|
|
||||||
|
private static byte[] ConvertFloatToByte<T>(ReadOnlySpan<T> source, int width, int height, int components)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
byte[] conversion = new byte[width * height * components];
|
||||||
|
ReadOnlySpan<float> dataAsFloat = MemoryMarshal.Cast<T, float>(source);
|
||||||
|
|
||||||
|
for (int i = 0; i<conversion.Length; i++)
|
||||||
|
{
|
||||||
|
conversion[i] = (byte) Math.Clamp(MathF.Round(dataAsFloat[i]* 255), 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float[] ConvertByteToFloat<T>(ReadOnlySpan<T> source, int width, int height, int components)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
float[] conversion = new float[width * height * components];
|
||||||
|
ReadOnlySpan<byte> dataAsByte = MemoryMarshal.Cast<T, byte>(source);
|
||||||
|
|
||||||
|
for (int i = 0; i < conversion.Length; i++)
|
||||||
|
{
|
||||||
|
conversion[i] = Math.Clamp(dataAsByte[i]/255f, 0f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</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="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a byte format. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WritePng<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte *ptr = source)
|
||||||
|
Stbi.write_png_to_func(wrapper, null, width, height, components, ptr, width * components);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a BMP file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</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="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <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.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteBmp<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte* ptr = source)
|
||||||
|
Stbi.write_bmp_to_func(wrapper, null, width, height, components, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a TGA file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</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="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a byte format. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteTga<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte* ptr = source)
|
||||||
|
Stbi.write_tga_to_func(wrapper, null, width, height, components, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</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="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</param>
|
||||||
|
/// <param name="destination">The destination stream.</param>
|
||||||
|
/// <param name="isFloat">True if the pixel format in data is a floating point format.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will incur a conversion cost if the pixel format is not a float format. Affected by global non-thread safe options.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteHdr<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<float> source;
|
||||||
|
float[]? conversion;
|
||||||
|
|
||||||
|
if (!isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertByteToFloat(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.Cast<T, float>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (float* ptr = source)
|
||||||
|
Stbi.write_hdr_to_func(wrapper, null, width, height, components, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write any image to a PNG file.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Any packed byte or float array structure.</typeparam>
|
||||||
|
/// <param name="data">Span of pixel data.</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="format">Color format of the pixel data. Must not be <see cref="StbiImageFormat"/>.</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>
|
||||||
|
/// <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.
|
||||||
|
/// </remarks>
|
||||||
|
public static void WriteJpg<T>(ReadOnlySpan<T> data, int width, int height, StbiImageFormat format, Stream destination, int quality = 90, bool isFloat = false)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
int components = Components(format);
|
||||||
|
ReadOnlySpan<byte> source;
|
||||||
|
byte[]? conversion;
|
||||||
|
|
||||||
|
if (isFloat)
|
||||||
|
{
|
||||||
|
conversion = ConvertFloatToByte(data, width, height, components);
|
||||||
|
source = conversion;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source = MemoryMarshal.AsBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
StbiWriteStreamWrapper wrapper = new StbiWriteStreamWrapper(destination);
|
||||||
|
|
||||||
|
fixed (byte* ptr = source)
|
||||||
|
Stbi.write_jpg_to_func(wrapper, null, width, height, components, ptr, quality);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace ReFuel.Stb
|
namespace ReFuel.Stb.Native
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Direct access to the native STBI function calls.
|
||||||
|
/// </summary>
|
||||||
public unsafe static partial class Stbi
|
public unsafe static partial class Stbi
|
||||||
{
|
{
|
||||||
private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);
|
private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function);
|
||||||
|
private static IntPtr stbiHandle;
|
||||||
|
|
||||||
private static readonly string[] LibraryNames = new string[]
|
private static readonly string[] LibraryNames = new string[]
|
||||||
{
|
{
|
||||||
@ -29,22 +31,33 @@ namespace ReFuel.Stb
|
|||||||
static Stbi()
|
static Stbi()
|
||||||
{
|
{
|
||||||
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);
|
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver);
|
||||||
|
|
||||||
|
// Dummy call to fail_reason so we have a handle to STBI.
|
||||||
|
failure_reason();
|
||||||
|
|
||||||
|
// Load global address pointers.
|
||||||
|
|
||||||
|
_tga_with_rle_ptr = (int*)NativeLibrary.GetExport(stbiHandle, "stbi_write_tga_with_rle");
|
||||||
|
_png_compression_level_ptr = (int*)NativeLibrary.GetExport(stbiHandle, "stbi_write_png_compression_level");
|
||||||
|
_force_png_filter_ptr = (int*)NativeLibrary.GetExport(stbiHandle, "stbi_write_force_png_filter");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||||||
{
|
{
|
||||||
if (libraryName != "stbi")
|
if (libraryName != "stbi")
|
||||||
return IntPtr.Zero;
|
return IntPtr.Zero;
|
||||||
|
else if (stbiHandle != IntPtr.Zero)
|
||||||
|
return stbiHandle;
|
||||||
|
|
||||||
foreach (string name in LibraryNames)
|
foreach (string name in LibraryNames)
|
||||||
{
|
{
|
||||||
if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle))
|
if (NativeLibrary.TryLoad(name, assembly, searchPath, out stbiHandle))
|
||||||
{
|
{
|
||||||
return handle;
|
return stbiHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NativeLibrary.Load(libraryName);
|
return stbiHandle = NativeLibrary.Load(libraryName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
2
Stbi.cs
2
Stbi.cs
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ReFuel.Stb
|
namespace ReFuel.Stb.Native
|
||||||
{
|
{
|
||||||
public enum StbiEnum : uint
|
public enum StbiEnum : uint
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
|
using ReFuel.Stb.Native;
|
||||||
|
|
||||||
namespace ReFuel.Stb
|
namespace ReFuel.Stb
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration of supported STBI image formats.
|
||||||
|
/// </summary>
|
||||||
public enum StbiImageFormat
|
public enum StbiImageFormat
|
||||||
{
|
{
|
||||||
Default = (int)StbiEnum.STBI_default,
|
Default = (int)StbiEnum.STBI_default,
|
||||||
|
@ -1,17 +1,45 @@
|
|||||||
|
using ReFuel.Stb.Native;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ReFuel.Stb
|
namespace ReFuel.Stb
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to STBI stream read function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userdata">User provided userdata pointer.</param>
|
||||||
|
/// <param name="buffer">C array to read into.</param>
|
||||||
|
/// <param name="count">Size of the C array in bytes.</param>
|
||||||
|
/// <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>
|
||||||
|
/// Pointer to STBI stream skip function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userdata">User provided userdata pointer.</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>
|
||||||
|
/// Pointer to STBI stream end of file function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userdata">User provided userdata pointer.</param>
|
||||||
|
/// <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>
|
||||||
|
/// An easy-to-use stream wrapper for use with STBI image load functions.
|
||||||
|
/// </summary>
|
||||||
public unsafe class StbiStreamWrapper : IDisposable
|
public unsafe class StbiStreamWrapper : IDisposable
|
||||||
{
|
{
|
||||||
private Stream _stream;
|
public readonly stbi_io_callbacks Callbacks;
|
||||||
private bool _keepOpen;
|
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly bool _keepOpen;
|
||||||
private bool _isDisposed;
|
private bool _isDisposed;
|
||||||
|
|
||||||
private StbiReadProc _readCb;
|
private StbiReadProc _readCb;
|
||||||
@ -28,14 +56,11 @@ namespace ReFuel.Stb
|
|||||||
_readCb = ReadCb;
|
_readCb = ReadCb;
|
||||||
_skipCb = SkipCb;
|
_skipCb = SkipCb;
|
||||||
_eofCb = EofCb;
|
_eofCb = EofCb;
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateCallbacks(out stbi_io_callbacks cb)
|
Callbacks = default;
|
||||||
{
|
Callbacks.read = Marshal.GetFunctionPointerForDelegate<StbiReadProc>(_readCb);
|
||||||
cb = default;
|
Callbacks.skip = Marshal.GetFunctionPointerForDelegate<StbiSkipProc>(_skipCb);
|
||||||
cb.read = Marshal.GetFunctionPointerForDelegate<StbiReadProc>(_readCb);
|
Callbacks.eof = Marshal.GetFunctionPointerForDelegate<StbiEofProc>(_eofCb);
|
||||||
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)
|
||||||
@ -65,4 +90,33 @@ namespace ReFuel.Stb
|
|||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// An easy to use stream wrapper for STBI image write functions.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Keep object alive for the duration of the write operation.</remarks>
|
||||||
|
public class StbiWriteStreamWrapper
|
||||||
|
{
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly StbiWriteProc _cb;
|
||||||
|
|
||||||
|
public IntPtr Callback { get; }
|
||||||
|
|
||||||
|
public StbiWriteStreamWrapper(Stream stream)
|
||||||
|
{
|
||||||
|
_stream = stream;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
_cb = WriteCb;
|
||||||
|
}
|
||||||
|
Callback = Marshal.GetFunctionPointerForDelegate(_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void WriteCb(void *context, void *data, int size)
|
||||||
|
{
|
||||||
|
_stream.Write(new ReadOnlySpan<byte>((byte*)data, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator IntPtr(in StbiWriteStreamWrapper wrapper) => wrapper.Callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
17
StbiWrite.cs
17
StbiWrite.cs
@ -1,15 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ReFuel.Stb
|
namespace ReFuel.Stb.Native
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Procedure to STBI image write function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">User provided context pointer.</param>
|
||||||
|
/// <param name="data">C Array of data to write.</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
|
||||||
{
|
{
|
||||||
private static readonly int* _tga_with_rle_ptr;
|
private static readonly int* _tga_with_rle_ptr;
|
||||||
private static readonly int* _png_compression_level_ptr;
|
private static readonly int* _png_compression_level_ptr;
|
||||||
private static readonly int* _forced_png_filter_ptr;
|
private static readonly int* _force_png_filter_ptr;
|
||||||
|
|
||||||
public static int write_tga_with_rle
|
public static int write_tga_with_rle
|
||||||
{
|
{
|
||||||
@ -18,15 +25,15 @@ namespace ReFuel.Stb
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int write_png_compression_level
|
public static int write_png_compression_level
|
||||||
{
|
{
|
||||||
get => *_png_compression_level_ptr;
|
get => *_png_compression_level_ptr;
|
||||||
set => *_png_compression_level_ptr = value;
|
set => *_png_compression_level_ptr = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int write_force_png_filter
|
public static int write_force_png_filter
|
||||||
{
|
{
|
||||||
get => *_forced_png_filter_ptr;
|
get => *_force_png_filter_ptr;
|
||||||
set => *_forced_png_filter_ptr = value;
|
set => *_force_png_filter_ptr = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_png_to_func")]
|
[DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_write_png_to_func")]
|
||||||
|
@ -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
|
@ -7,6 +7,7 @@
|
|||||||
#define STBI_NO_THREAD_LOCALS 1
|
#define STBI_NO_THREAD_LOCALS 1
|
||||||
#include "stb/stb_image.h"
|
#include "stb/stb_image.h"
|
||||||
|
|
||||||
|
#define STBIWDEF RFEXTERN
|
||||||
#define STBI_WRITE_NO_STDIO 1
|
#define STBI_WRITE_NO_STDIO 1
|
||||||
#include "stb/stb_image_write.h"
|
#include "stb/stb_image_write.h"
|
||||||
|
|
||||||
|
2
stb
2
stb
@ -1 +1 @@
|
|||||||
Subproject commit 013ac3beddff3dbffafd5177e7972067cd2b5083
|
Subproject commit f7f20f39fe4f206c6f19e26ebfef7b261ee59ee4
|
Loading…
x
Reference in New Issue
Block a user