using System; using System.IO; using System.Runtime.InteropServices; using RefreshCS; namespace MoonWorks.Graphics { public static class ImageUtils { /// /// Gets pointer to pixel data from compressed image byte data. /// /// The returned pointer must be freed by calling FreePixelData. /// public static unsafe IntPtr GetPixelDataFromBytes( Span data, out uint width, out uint height, out uint sizeInBytes ) { fixed (byte* ptr = data) { var pixelData = Refresh.Refresh_Image_Load( (nint) ptr, data.Length, out var w, out var h, out var len ); width = (uint) w; height = (uint) h; sizeInBytes = (uint) len; return pixelData; } } /// /// Gets pointer to pixel data from a compressed image stream. /// /// The returned pointer must be freed by calling FreePixelData. /// public static unsafe IntPtr GetPixelDataFromStream( Stream stream, out uint width, out uint height, out uint sizeInBytes ) { var length = stream.Length; var buffer = NativeMemory.Alloc((nuint) length); var span = new Span(buffer, (int) length); stream.ReadExactly(span); var pixelData = GetPixelDataFromBytes(span, out width, out height, out sizeInBytes); NativeMemory.Free(buffer); return pixelData; } /// /// Gets pointer to pixel data from a compressed image file. /// /// The returned pointer must be freed by calling FreePixelData. /// public static IntPtr GetPixelDataFromFile( string path, out uint width, out uint height, out uint sizeInBytes ) { var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); return GetPixelDataFromStream(fileStream, out width, out height, out sizeInBytes); } /// /// Get metadata from compressed image bytes. /// public static unsafe bool ImageInfoFromBytes( Span data, out uint width, out uint height, out uint sizeInBytes ) { fixed (byte* ptr = data) { var result = Refresh.Refresh_Image_Info( (nint) ptr, data.Length, out var w, out var h, out var len ); width = (uint) w; height = (uint) h; sizeInBytes = (uint) len; return Conversions.ByteToBool(result); } } /// /// Get metadata from a compressed image stream. /// public static unsafe bool ImageInfoFromStream( Stream stream, out uint width, out uint height, out uint sizeInBytes ) { var length = stream.Length; var buffer = NativeMemory.Alloc((nuint) length); var span = new Span(buffer, (int) length); stream.ReadExactly(span); var result = ImageInfoFromBytes(span, out width, out height, out sizeInBytes); NativeMemory.Free(buffer); return result; } /// /// Get metadata from a compressed image file. /// public static bool ImageInfoFromFile( string path, out uint width, out uint height, out uint sizeInBytes ) { var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); return ImageInfoFromStream(fileStream, out width, out height, out sizeInBytes); } /// /// Frees pixel data obtained from GetPixelData methods. /// public static void FreePixelData(IntPtr pixels) { Refresh.Refresh_Image_Free(pixels); } /// /// Decodes image data into a CpuBuffer to prepare for image upload. /// public static unsafe uint DecodeIntoCpuBuffer( Span data, CpuBuffer cpuBuffer, uint bufferOffsetInBytes, SetDataOptions option ) { var pixelData = GetPixelDataFromBytes(data, out var w, out var h, out var sizeInBytes); var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option); FreePixelData(pixelData); return length; } /// /// Decodes an image stream into a CpuBuffer to prepare for image upload. /// public static unsafe uint DecodeIntoCpuBuffer( Stream stream, CpuBuffer cpuBuffer, uint bufferOffsetInBytes, SetDataOptions option ) { var pixelData = GetPixelDataFromStream(stream, out var w, out var h, out var sizeInBytes); var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option); FreePixelData(pixelData); return length; } /// /// Decodes an image file into a CpuBuffer to prepare for image upload. /// public static unsafe uint DecodeIntoCpuBuffer( string path, CpuBuffer cpuBuffer, uint bufferOffsetInBytes, SetDataOptions option ) { var pixelData = GetPixelDataFromFile(path, out var w, out var h, out var sizeInBytes); var length = cpuBuffer.SetData(new Span((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option); FreePixelData(pixelData); return length; } /// /// Saves pixel data contained in a CpuBuffer to a PNG file. /// public static unsafe void SavePNG( string path, CpuBuffer cpuBuffer, uint bufferOffsetInBytes, int width, int height, bool bgra ) { var sizeInBytes = width * height * 4; var pixelsPtr = NativeMemory.Alloc((nuint) sizeInBytes); var pixelsSpan = new Span(pixelsPtr, sizeInBytes); cpuBuffer.GetData(pixelsSpan, bufferOffsetInBytes); if (bgra) { // if data is bgra, we have to swap the R and B channels var rgbaPtr = NativeMemory.Alloc((nuint) sizeInBytes); var rgbaSpan = new Span(rgbaPtr, sizeInBytes); for (var i = 0; i < sizeInBytes; i += 4) { rgbaSpan[i] = pixelsSpan[i + 2]; rgbaSpan[i + 1] = pixelsSpan[i + 1]; rgbaSpan[i + 2] = pixelsSpan[i]; rgbaSpan[i + 3] = pixelsSpan[i + 3]; } NativeMemory.Free(pixelsPtr); pixelsPtr = rgbaPtr; } Refresh.Refresh_Image_SavePNG(path, (nint) pixelsPtr, width, height); NativeMemory.Free(pixelsPtr); } } }