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