feat(core): add SurfaceDecoder for A8R8G8B8 and BCn formats

This commit is contained in:
Erik 2026-04-10 17:56:15 +02:00
parent f915a13263
commit dbf913ebb4
4 changed files with 170 additions and 0 deletions

View file

@ -0,0 +1,10 @@
namespace AcDream.Core.Textures;
public sealed record DecodedTexture(byte[] Rgba8, int Width, int Height)
{
/// <summary>1x1 magenta fallback for missing/unsupported textures.</summary>
public static readonly DecodedTexture Magenta = new(
Rgba8: [0xFF, 0x00, 0xFF, 0xFF],
Width: 1,
Height: 1);
}

View file

@ -0,0 +1,71 @@
using BCnEncoder.Decoder;
using BCnEncoder.Shared;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Enums;
namespace AcDream.Core.Textures;
public static class SurfaceDecoder
{
private static readonly BcDecoder BcDecoder = new();
/// <summary>
/// Decode a RenderSurface's pixel bytes into RGBA8. Returns <see cref="DecodedTexture.Magenta"/>
/// for unsupported formats, null data, or corrupt sizing.
/// </summary>
public static DecodedTexture DecodeRenderSurface(RenderSurface rs)
{
if (rs.SourceData is null || rs.Width <= 0 || rs.Height <= 0)
return DecodedTexture.Magenta;
try
{
return rs.Format switch
{
PixelFormat.PFID_A8R8G8B8 => DecodeA8R8G8B8(rs),
PixelFormat.PFID_DXT1 => DecodeBc(rs, CompressionFormat.Bc1),
PixelFormat.PFID_DXT3 => DecodeBc(rs, CompressionFormat.Bc2),
PixelFormat.PFID_DXT5 => DecodeBc(rs, CompressionFormat.Bc3),
_ => DecodedTexture.Magenta,
};
}
catch
{
return DecodedTexture.Magenta;
}
}
private static DecodedTexture DecodeA8R8G8B8(RenderSurface rs)
{
int expected = rs.Width * rs.Height * 4;
if (rs.SourceData.Length < expected)
return DecodedTexture.Magenta;
var rgba = new byte[expected];
// Source layout per pixel: B, G, R, A → swap to R, G, B, A
for (int i = 0; i < rs.Width * rs.Height; i++)
{
int s = i * 4;
rgba[s + 0] = rs.SourceData[s + 2]; // R <- R
rgba[s + 1] = rs.SourceData[s + 1]; // G <- G
rgba[s + 2] = rs.SourceData[s + 0]; // B <- B
rgba[s + 3] = rs.SourceData[s + 3]; // A <- A
}
return new DecodedTexture(rgba, rs.Width, rs.Height);
}
private static DecodedTexture DecodeBc(RenderSurface rs, CompressionFormat format)
{
var pixels = BcDecoder.DecodeRaw(rs.SourceData, rs.Width, rs.Height, format);
var rgba = new byte[rs.Width * rs.Height * 4];
for (int i = 0; i < pixels.Length; i++)
{
int s = i * 4;
rgba[s + 0] = pixels[i].r;
rgba[s + 1] = pixels[i].g;
rgba[s + 2] = pixels[i].b;
rgba[s + 3] = pixels[i].a;
}
return new DecodedTexture(rgba, rs.Width, rs.Height);
}
}