From 1e5e9e6fb5d7604c038f8a6a87c9be3d1d73e14a Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Tue, 19 Mar 2024 22:02:20 +0300 Subject: [PATCH] Move Quik.StbImage to its own repository. --- .gitignore | 4 + CMakeLists.txt | 11 +++ NativeTypeNameAttribute.cs | 12 +++ Quik.Common | 2 +- Quik.StbImage.csproj | 61 +++++++++++++ Quik.StbImage.sln | 25 ++++++ StbImage.cs | 144 ++++++++++++++++++++++++++++++ Stbi.Manual.cs | 65 ++++++++++++++ Stbi.cs | 174 +++++++++++++++++++++++++++++++++++++ StbiImageFormat.cs | 11 +++ StbiStreamWrapper.cs | 60 +++++++++++++ build_native.sh | 3 + clangsharp.rsp | 30 +++++++ docker-compose.yaml | 7 ++ quik_stbi.c | 4 + quik_stbi.h | 10 +++ 16 files changed, 622 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 NativeTypeNameAttribute.cs create mode 100644 Quik.StbImage.csproj create mode 100644 Quik.StbImage.sln create mode 100644 StbImage.cs create mode 100644 Stbi.Manual.cs create mode 100644 Stbi.cs create mode 100644 StbiImageFormat.cs create mode 100644 StbiStreamWrapper.cs create mode 100644 build_native.sh create mode 100644 clangsharp.rsp create mode 100644 docker-compose.yaml create mode 100644 quik_stbi.c create mode 100644 quik_stbi.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c2104a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +**/bin +**/obj +**/out +**/runtimes diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a4c74ed --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.0) + +project(quik_stbi LANGUAGES C VERSION 1.0) + +add_compile_options(-static-libgcc) + +add_library(stbi SHARED "quik_stbi.c") +install( + TARGETS stbi + RUNTIME DESTINATION . + LIBRARY DESTINATION .) diff --git a/NativeTypeNameAttribute.cs b/NativeTypeNameAttribute.cs new file mode 100644 index 0000000..becb962 --- /dev/null +++ b/NativeTypeNameAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Quik.Stb.Image +{ + [AttributeUsage(System.AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class NativeTypeNameAttribute : System.Attribute + { + public NativeTypeNameAttribute(string typename) + { + } + } +} diff --git a/Quik.Common b/Quik.Common index 11bb72a..ebfc8a6 160000 --- a/Quik.Common +++ b/Quik.Common @@ -1 +1 @@ -Subproject commit 11bb72a464215693d5a3a6896b25e568cc6e3313 +Subproject commit ebfc8a694615a8a6c9d35652279a9748ff43db3d diff --git a/Quik.StbImage.csproj b/Quik.StbImage.csproj new file mode 100644 index 0000000..c8f983f --- /dev/null +++ b/Quik.StbImage.csproj @@ -0,0 +1,61 @@ + + + + net6.0 + disable + 7.3 + True + linux-arm;linux-arm64;linux-x64;win-x86;win-x64 + + + + + True + Quik.StbImage + 1.0.0 + STBI Authors, H. Utku Maden + + A C# wrapper for the ubiquitous Stb Image library. + + + + + + + runtimes/linux-arm/native/ + true + PreserveNewest + + + runtimes/linux-arm64/native/ + true + PreserveNewest/ + + + runtimes/linux-x64/native/ + true + PreserveNewest + + + runtimes/linux-x86/native/ + true + PreserveNewest + + + runtimes/win-x64/native/ + true + PreserveNewest + + + runtimes/win-x86/native/ + true + PreserveNewest + + + + diff --git a/Quik.StbImage.sln b/Quik.StbImage.sln new file mode 100644 index 0000000..6b44dac --- /dev/null +++ b/Quik.StbImage.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Quik.StbImage", "Quik.StbImage.csproj", "{C808B4BC-C3AF-4682-8EDA-EAAC780800C3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C808B4BC-C3AF-4682-8EDA-EAAC780800C3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B04E5FBA-41E6-4B0B-BC31-F996BE353F5A} + EndGlobalSection +EndGlobal diff --git a/StbImage.cs b/StbImage.cs new file mode 100644 index 0000000..e2af68f --- /dev/null +++ b/StbImage.cs @@ -0,0 +1,144 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Quik.Stb +{ + /// + /// A class that encompasses all features of stb_image.h in a safe way. + /// + public unsafe class StbImage : IDisposable + { + private bool isDisposed = false; + + /// + /// Pointer to the image. + /// + public IntPtr ImagePointer { get; } + + /// + /// Width of the image. + /// + public int Width { get; } + + /// + /// Height of the image. + /// + /// + public int Height { get; } + + /// + /// Internal image format. + /// + public StbiImageFormat Format { get; } + public bool IsFloat { get; } + + private StbImage(IntPtr image, int x, int y, StbiImageFormat format, bool isFloat) + { + ImagePointer = image; + Width = x; + Height = y; + Format = format; + IsFloat = isFloat; + } + + ~StbImage() + { + Dispose(false); + } + + public void Dispose() => Dispose(true); + + private void Dispose(bool disposing) + { + if (isDisposed) return; + + if (disposing) + { + GC.SuppressFinalize(this); + } + + Stbi.image_free(ImagePointer.ToPointer()); + isDisposed = true; + } + + /// + /// Set to flip the y-axis of loaded images on load. + /// + public static bool FlipVerticallyOnLoad { set => Stbi.set_flip_vertically_on_load(1); } + + /// + /// Set to unpremultiply images on load. + /// + /// + /// According to the stb_image documentation, only iPhone PNG images + /// can come with premultiplied alpha. + /// + public static bool UnpremultiplyOnLoad { set => Stbi.set_unpremultiply_on_load(1); } + + /// + /// Try loading an image, without raising exceptions. + /// + /// The resulting image. + /// Source stream. + /// The desired image format. + /// True on success. + public static bool TryLoad(out StbImage image, Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false) + { + int x, y, iFormat; + StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true); + wrapper.CreateCallbacks(out stbi_io_callbacks cb); + + stream.Position = 0; + IntPtr imagePtr; + if (isFloat) + { + 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); + } + + if (imagePtr != IntPtr.Zero) + { + image = new StbImage(imagePtr, x, y, (StbiImageFormat)iFormat, isFloat); + return true; + } + else + { + image = null; + return false; + } + } + + /// + /// Load an image. + /// + /// The stream to load from. + /// The desired image format. + /// The image object. + public static StbImage Load(Stream stream, StbiImageFormat format = StbiImageFormat.Default, bool isFloat = false) + { + if (TryLoad(out StbImage image, stream, format, isFloat)) + { + return image; + } + + string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason()); + throw new Exception($"Failed to load image: {reason}"); + } + + public bool IsLoadable(Stream stream) + { + int x, y, iFormat; + StbiStreamWrapper wrapper = new StbiStreamWrapper(stream, true); + wrapper.CreateCallbacks(out stbi_io_callbacks cb); + + stream.Position = 0; + int result = Stbi.info_from_callbacks(&cb, null, &x, &y, &iFormat); + + return result != 0; + } + } +} \ No newline at end of file diff --git a/Stbi.Manual.cs b/Stbi.Manual.cs new file mode 100644 index 0000000..160efd6 --- /dev/null +++ b/Stbi.Manual.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Reflection; + +namespace Quik.Stb +{ + public unsafe static partial class Stbi + { + private delegate void FailedAssertProc(byte *expression, byte *file, int line, byte *function); + + private static readonly string[] LibraryNames = new string[] + { + //FIXME: This is wrong on so many levels, but, i need to do this + // in order to get a change of this running. + "runtimes/win-x64/native/libstbi.dll", + "runtimes/win-x86/native/libstbi.dll", + "runtimes/linux-arm/native/libstbi.so", + "runtimes/linux-arm64/native/libstbi.so", + "runtimes/linux-x64/native/libstbi.so", + "runtimes/native/libstbi.dylib", + "libstbi.dll", + "libstbi.so", + "libstbi.dylib", + }; + + static Stbi() + { + NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), Resolver); + + quik_stbi_failed_assert_store(Marshal.GetFunctionPointerForDelegate(FailedAssert)); + } + + private static IntPtr Resolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) + { + if (libraryName != "stbi") + return IntPtr.Zero; + + foreach (string name in LibraryNames) + { + if (NativeLibrary.TryLoad(name, assembly, searchPath, out IntPtr handle)) + { + return handle; + } + } + + return NativeLibrary.Load(libraryName); + } + + private static void FailedAssert(byte *expression, byte *file, int line, byte *function) + { + string expr = expression == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)expression); + string f = file == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)file); + string func = function == null ? string.Empty : Marshal.PtrToStringUTF8((IntPtr)function); + + Exception ex = new Exception("Assert failed in native stbi code."); + ex.Data.Add("Expression", expr); + ex.Data.Add("File", f); + ex.Data.Add("Line", line); + ex.Data.Add("Function", func); + throw ex; + } + } +} \ No newline at end of file diff --git a/Stbi.cs b/Stbi.cs new file mode 100644 index 0000000..0a9510c --- /dev/null +++ b/Stbi.cs @@ -0,0 +1,174 @@ +using System; +using System.Runtime.InteropServices; +using Quik.Stb.Image; + +namespace Quik.Stb +{ + [NativeTypeName("unsigned int")] + public enum StbiEnum : uint + { + STBI_default = 0, + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4, + } + + public partial struct stbi_io_callbacks + { + [NativeTypeName("int (*)(void *, char *, int)")] + public IntPtr read; + + [NativeTypeName("void (*)(void *, int)")] + public IntPtr skip; + + [NativeTypeName("int (*)(void *)")] + public IntPtr eof; + } + + public static unsafe partial class Stbi + { + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_memory", ExactSpelling = true)] + [return: NativeTypeName("stbi_uc *")] + public static extern byte* load_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_callbacks", ExactSpelling = true)] + [return: NativeTypeName("stbi_uc *")] + public static extern byte* load_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load", ExactSpelling = true)] + [return: NativeTypeName("stbi_uc *")] + public static extern byte* load([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file", ExactSpelling = true)] + [return: NativeTypeName("stbi_uc *")] + public static extern byte* load_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_gif_from_memory", ExactSpelling = true)] + [return: NativeTypeName("stbi_uc *")] + public static extern byte* load_gif_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int** delays, int* x, int* y, int* z, int* comp, int req_comp); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_memory", ExactSpelling = true)] + [return: NativeTypeName("stbi_us *")] + public static extern ushort* load_16_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16_from_callbacks", ExactSpelling = true)] + [return: NativeTypeName("stbi_us *")] + public static extern ushort* load_16_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_16", ExactSpelling = true)] + [return: NativeTypeName("stbi_us *")] + public static extern ushort* load_16([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_file_16", ExactSpelling = true)] + [return: NativeTypeName("stbi_us *")] + public static extern ushort* load_from_file_16([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_memory", ExactSpelling = true)] + public static extern float* loadf_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_callbacks", ExactSpelling = true)] + public static extern float* loadf_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf", ExactSpelling = true)] + public static extern float* loadf([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_loadf_from_file", ExactSpelling = true)] + public static extern float* loadf_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* channels_in_file, int desired_channels); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_hdr_to_ldr_gamma", ExactSpelling = true)] + public static extern void hdr_to_ldr_gamma(float gamma); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_hdr_to_ldr_scale", ExactSpelling = true)] + public static extern void hdr_to_ldr_scale(float scale); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_ldr_to_hdr_gamma", ExactSpelling = true)] + public static extern void ldr_to_hdr_gamma(float gamma); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_ldr_to_hdr_scale", ExactSpelling = true)] + public static extern void ldr_to_hdr_scale(float scale); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_callbacks", ExactSpelling = true)] + public static extern int is_hdr_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_memory", ExactSpelling = true)] + public static extern int is_hdr_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr", ExactSpelling = true)] + public static extern int is_hdr([NativeTypeName("const char *")] sbyte* filename); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_hdr_from_file", ExactSpelling = true)] + public static extern int is_hdr_from_file([NativeTypeName("FILE *")] void* f); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_failure_reason", ExactSpelling = true)] + [return: NativeTypeName("const char *")] + public static extern sbyte* failure_reason(); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_image_free", ExactSpelling = true)] + public static extern void image_free(void* retval_from_stbi_load); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_memory", ExactSpelling = true)] + public static extern int info_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len, int* x, int* y, int* comp); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_callbacks", ExactSpelling = true)] + public static extern int info_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user, int* x, int* y, int* comp); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_memory", ExactSpelling = true)] + public static extern int is_16_bit_from_memory([NativeTypeName("const stbi_uc *")] byte* buffer, int len); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_callbacks", ExactSpelling = true)] + public static extern int is_16_bit_from_callbacks([NativeTypeName("const stbi_io_callbacks *")] stbi_io_callbacks* clbk, void* user); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info", ExactSpelling = true)] + public static extern int info([NativeTypeName("const char *")] sbyte* filename, int* x, int* y, int* comp); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_info_from_file", ExactSpelling = true)] + public static extern int info_from_file([NativeTypeName("FILE *")] void* f, int* x, int* y, int* comp); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit", ExactSpelling = true)] + public static extern int is_16_bit([NativeTypeName("const char *")] sbyte* filename); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_is_16_bit_from_file", ExactSpelling = true)] + public static extern int is_16_bit_from_file([NativeTypeName("FILE *")] void* f); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_unpremultiply_on_load", ExactSpelling = true)] + public static extern void set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_convert_iphone_png_to_rgb", ExactSpelling = true)] + public static extern void convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_flip_vertically_on_load", ExactSpelling = true)] + public static extern void set_flip_vertically_on_load(int flag_true_if_should_flip); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_unpremultiply_on_load_thread", ExactSpelling = true)] + public static extern void set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_convert_iphone_png_to_rgb_thread", ExactSpelling = true)] + public static extern void convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_set_flip_vertically_on_load_thread", ExactSpelling = true)] + public static extern void set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize", ExactSpelling = true)] + [return: NativeTypeName("char *")] + public static extern sbyte* zlib_decode_malloc_guesssize([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc_guesssize_headerflag", ExactSpelling = true)] + [return: NativeTypeName("char *")] + public static extern sbyte* zlib_decode_malloc_guesssize_headerflag([NativeTypeName("const char *")] sbyte* buffer, int len, int initial_size, int* outlen, int parse_header); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_malloc", ExactSpelling = true)] + [return: NativeTypeName("char *")] + public static extern sbyte* zlib_decode_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_buffer", ExactSpelling = true)] + public static extern int zlib_decode_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_malloc", ExactSpelling = true)] + [return: NativeTypeName("char *")] + public static extern sbyte* zlib_decode_noheader_malloc([NativeTypeName("const char *")] sbyte* buffer, int len, int* outlen); + + [DllImport("stbi", CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_zlib_decode_noheader_buffer", ExactSpelling = true)] + public static extern int zlib_decode_noheader_buffer([NativeTypeName("char *")] sbyte* obuffer, int olen, [NativeTypeName("const char *")] sbyte* ibuffer, int ilen); + } +} diff --git a/StbiImageFormat.cs b/StbiImageFormat.cs new file mode 100644 index 0000000..a4c0c61 --- /dev/null +++ b/StbiImageFormat.cs @@ -0,0 +1,11 @@ +namespace Quik.Stb +{ + public enum StbiImageFormat + { + Default = (int)StbiEnum.STBI_default, + Grey = (int)StbiEnum.STBI_grey, + GreyAlpha = (int)StbiEnum.STBI_grey_alpha, + Rgb = (int)StbiEnum.STBI_rgb, + Rgba = (int)StbiEnum.STBI_rgb_alpha + } +} \ No newline at end of file diff --git a/StbiStreamWrapper.cs b/StbiStreamWrapper.cs new file mode 100644 index 0000000..4038c48 --- /dev/null +++ b/StbiStreamWrapper.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Quik.Stb +{ + public unsafe class StbiStreamWrapper : IDisposable + { + private Stream _stream; + private bool _keepOpen; + private bool _isDisposed; + + private delegate int ReadProc(void *userdata, byte* buffer, int count); + private delegate void SkipProc(void *userdata, int count); + private delegate int Eof(void *userdata); + + public StbiStreamWrapper(Stream stream, bool keepOpen = false) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + + _stream = stream; + _keepOpen = keepOpen; + } + + 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); + } + + private int ReadCb(void *userdata, byte* buffer, int count) + { + Span bytes = new Span(buffer, count); + return _stream.Read(bytes); + } + + private void SkipCb(void *userdata, int count) + { + _stream.Seek(count, SeekOrigin.Current); + } + + private int EofCb(void *userdata) + { + if (!_stream.CanRead || _stream.Position == _stream.Length) + return 1; + return 0; + } + + public void Dispose() + { + if (_isDisposed) return; + + if (!_keepOpen) _stream.Dispose(); + + _isDisposed = true; + } + } +} \ No newline at end of file diff --git a/build_native.sh b/build_native.sh new file mode 100644 index 0000000..9dded15 --- /dev/null +++ b/build_native.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd $(dirname "$0") +Quik.Common/sh/quik_build_native.sh . \ No newline at end of file diff --git a/clangsharp.rsp b/clangsharp.rsp new file mode 100644 index 0000000..d0ebfed --- /dev/null +++ b/clangsharp.rsp @@ -0,0 +1,30 @@ +-x + c +-l + stbi +--config + compatible-codegen + single-file + exclude-fnptr-codegen + generate-aggressive-inlining + generate-setslastsystemerror-attribute + unix-types +--include-directory + ../lib +--include-directory + ../Quik.StbImage +--include-directory + /usr/lib/llvm-14/lib/clang/14.0.6/include +--file + ../Quik.StbImage.redist/quik_stbi.h + ../lib/stb/stb_image.h +--methodClassName + Stbi +--namespace + Quik.Stb +--output + Stbi.cs +--prefixStrip + stbi_ +--with-type + FILE=void diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..f1c4ded --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,7 @@ + +services: + build: + image: git.mixedup.dev/quik/docker-cross-compiler + command: "/home/quik/src/build_native.sh" + volumes: + - .:/home/quik/src \ No newline at end of file diff --git a/quik_stbi.c b/quik_stbi.c new file mode 100644 index 0000000..312c61b --- /dev/null +++ b/quik_stbi.c @@ -0,0 +1,4 @@ +#include "quik_stbi.h" + +#define STB_IMAGE_IMPLEMENTATION 1 +#include "stb/stb_image.h" diff --git a/quik_stbi.h b/quik_stbi.h new file mode 100644 index 0000000..cd2b84b --- /dev/null +++ b/quik_stbi.h @@ -0,0 +1,10 @@ +#ifndef _QUIK_STBI_H_ +#define _QUIK_STBI_H_ + +#include "Quik.Common/include/quik_common.h" + +#define STBIDEF QEXTERN + +#include "stb/stb_image.h" + +#endif