From 648f44f12d5db9a175478e26957a8d9a4965f8cf Mon Sep 17 00:00:00 2001 From: "H. Utku Maden" Date: Sun, 9 Jul 2023 17:33:35 +0300 Subject: [PATCH] Create nicer wrapper of Stbi. --- Quik.StbImage/StbImage.cs | 132 +++++++++++++++++++++++++++++++ Quik.StbImage/StbiImageFormat.cs | 11 +++ 2 files changed, 143 insertions(+) create mode 100644 Quik.StbImage/StbImage.cs create mode 100644 Quik.StbImage/StbiImageFormat.cs diff --git a/Quik.StbImage/StbImage.cs b/Quik.StbImage/StbImage.cs new file mode 100644 index 0000000..0a2d5ce --- /dev/null +++ b/Quik.StbImage/StbImage.cs @@ -0,0 +1,132 @@ +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) + { + if (TryLoad(out StbImage image, stream, format)) + { + return image; + } + + string reason = Marshal.PtrToStringUTF8((IntPtr)Stbi.failure_reason()); + throw new Exception($"Failed to load image: {reason}"); + } + } +} \ No newline at end of file diff --git a/Quik.StbImage/StbiImageFormat.cs b/Quik.StbImage/StbiImageFormat.cs new file mode 100644 index 0000000..a4c0c61 --- /dev/null +++ b/Quik.StbImage/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