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