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; } } }