MoonWorks/src/Graphics/ImageUtils.cs

234 lines
5.9 KiB
C#
Raw Normal View History

2024-02-23 08:06:04 +00:00
using System;
using System.IO;
using System.Runtime.InteropServices;
using RefreshCS;
namespace MoonWorks.Graphics
{
public static class ImageUtils
{
/// <summary>
/// Gets pointer to pixel data from compressed image byte data.
///
/// The returned pointer must be freed by calling FreePixelData.
/// </summary>
public static unsafe IntPtr GetPixelDataFromBytes(
Span<byte> 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;
}
}
/// <summary>
/// Gets pointer to pixel data from a compressed image stream.
///
/// The returned pointer must be freed by calling FreePixelData.
/// </summary>
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<byte>(buffer, (int) length);
stream.ReadExactly(span);
var pixelData = GetPixelDataFromBytes(span, out width, out height, out sizeInBytes);
NativeMemory.Free(buffer);
return pixelData;
}
/// <summary>
/// Gets pointer to pixel data from a compressed image file.
///
/// The returned pointer must be freed by calling FreePixelData.
/// </summary>
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);
}
/// <summary>
/// Get metadata from compressed image bytes.
/// </summary>
public static unsafe bool ImageInfoFromBytes(
Span<byte> 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);
}
}
/// <summary>
/// Get metadata from a compressed image stream.
/// </summary>
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<byte>(buffer, (int) length);
stream.ReadExactly(span);
var result = ImageInfoFromBytes(span, out width, out height, out sizeInBytes);
NativeMemory.Free(buffer);
return result;
}
/// <summary>
/// Get metadata from a compressed image file.
/// </summary>
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);
}
/// <summary>
/// Frees pixel data obtained from GetPixelData methods.
/// </summary>
public static void FreePixelData(IntPtr pixels)
{
Refresh.Refresh_Image_Free(pixels);
}
/// <summary>
/// Decodes image data into a CpuBuffer to prepare for image upload.
/// </summary>
public static unsafe uint DecodeIntoCpuBuffer(
Span<byte> 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<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
FreePixelData(pixelData);
return length;
}
/// <summary>
/// Decodes an image stream into a CpuBuffer to prepare for image upload.
/// </summary>
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<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
FreePixelData(pixelData);
return length;
}
/// <summary>
/// Decodes an image file into a CpuBuffer to prepare for image upload.
/// </summary>
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<byte>((void*) pixelData, (int) sizeInBytes), bufferOffsetInBytes, option);
FreePixelData(pixelData);
return length;
}
/// <summary>
/// Saves pixel data contained in a CpuBuffer to a PNG file.
/// </summary>
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<byte>(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<byte>(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);
}
}
}