Teach PNG decoder to handle indexed bit depths of 1, 2 or 4.

The PNG decoder, when dealing when indexed images with a palette, could only decode a bit depth of 8. Teach it to decode depths of 1, 2 and 4 as well. As the palette data is exposed to consumers of the PNG class, unpack the data into a 8 bit depth so consumers don't need to also handle the new bit depths.
This commit is contained in:
RoosterDragon
2023-10-20 18:22:24 +01:00
committed by Matthias Mailänder
parent 304fc458eb
commit 678b238c1c

View File

@@ -45,6 +45,7 @@ namespace OpenRA.FileFormats
var data = new List<byte>();
Type = SpriteFrameType.Rgba32;
byte bitDepth = 8;
while (true)
{
var length = IPAddress.NetworkToHostOrder(s.ReadInt32());
@@ -66,7 +67,7 @@ namespace OpenRA.FileFormats
Width = IPAddress.NetworkToHostOrder(ms.ReadInt32());
Height = IPAddress.NetworkToHostOrder(ms.ReadInt32());
var bitDepth = ms.ReadUInt8();
bitDepth = ms.ReadUInt8();
var colorType = (PngColorType)ms.ReadUInt8();
if (IsPaletted(bitDepth, colorType))
Type = SpriteFrameType.Indexed8;
@@ -136,14 +137,34 @@ namespace OpenRA.FileFormats
{
var pxStride = PixelStride;
var rowStride = Width * pxStride;
var pixelsPerByte = 8 / bitDepth;
var sourceRowStride = Exts.IntegerDivisionRoundingAwayFromZero(rowStride, pixelsPerByte);
Span<byte> prevLine = new byte[rowStride];
for (var y = 0; y < Height; y++)
{
var filter = (PngFilter)ds.ReadUInt8();
ds.ReadBytes(Data, y * rowStride, rowStride);
ds.ReadBytes(Data, y * rowStride, sourceRowStride);
var line = Data.AsSpan(y * rowStride, rowStride);
// If the source has a bit depth of 1, 2 or 4 it packs multiple pixels per byte.
// Unpack to bit depth of 8, yielding 1 pixel per byte.
// This makes life easier for consumers of palleted data.
if (bitDepth < 8)
{
var mask = 0xFF >> (8 - bitDepth);
for (var i = sourceRowStride - 1; i >= 0; i--)
{
var packed = line[i];
for (var j = 0; j < pixelsPerByte; j++)
{
var dest = i * pixelsPerByte + j;
if (dest < line.Length) // Guard against last byte being only partially packed
line[dest] = (byte)(packed >> (8 - (j + 1) * bitDepth) & mask);
}
}
}
switch (filter)
{
case PngFilter.None:
@@ -269,7 +290,7 @@ namespace OpenRA.FileFormats
static bool IsPaletted(byte bitDepth, PngColorType colorType)
{
if (bitDepth == 8 && colorType == (PngColorType.Indexed | PngColorType.Color))
if (bitDepth <= 8 && colorType == (PngColorType.Indexed | PngColorType.Color))
return true;
if (bitDepth == 8 && colorType == (PngColorType.Color | PngColorType.Alpha))