Compare commits

...

4 Commits

1 changed files with 50 additions and 28 deletions

View File

@ -35,11 +35,9 @@ public class GifDecoder
LogicalScreenHeight = reader.ReadUInt16(); LogicalScreenHeight = reader.ReadUInt16();
var flags = reader.ReadByte(); var flags = reader.ReadByte();
HasGlobalColorTable = ((flags >> 7) & 0x1) != 0; HasGlobalColorTable = ((flags >> 7) & 0x1) != 0;
// Godot.GD.Print($"HALLO WE HAVE GLOBAL TABLE? {HasGlobalColorTable}");
BitsPerColorChannel = (byte)(((flags >> 4) & 0x7) + 1); BitsPerColorChannel = (byte)(((flags >> 4) & 0x7) + 1);
IsGlobalColorTableSorted = ((flags >> 3) & 0x1) != 0; IsGlobalColorTableSorted = ((flags >> 3) & 0x1) != 0;
GlobalColorTableSize = (int)Math.Pow(2, (flags & 0x7) + 1); GlobalColorTableSize = (int)Math.Pow(2, (flags & 0x7) + 1);
// Godot.GD.Print($"Global colour size? {GlobalColorTableSize}");
BackgroundColorIndex = reader.ReadByte(); BackgroundColorIndex = reader.ReadByte();
var rawAspectRatio = reader.ReadByte(); var rawAspectRatio = reader.ReadByte();
if (rawAspectRatio == 0) if (rawAspectRatio == 0)
@ -86,6 +84,7 @@ public class GifDecoder
private byte[] _colors; private byte[] _colors;
private byte[] _pixelIndices; private byte[] _pixelIndices;
private int _transparentIndex; private int _transparentIndex;
private readonly bool _interlaced;
public ImageData(BinaryReader reader, byte[] globalColors, int transparentIndex) public ImageData(BinaryReader reader, byte[] globalColors, int transparentIndex)
{ {
@ -95,7 +94,7 @@ public class GifDecoder
var height = reader.ReadUInt16(); var height = reader.ReadUInt16();
var flags = reader.ReadByte(); var flags = reader.ReadByte();
var hasLocalColorTable = ((flags >> 7) & 0x1) != 0; var hasLocalColorTable = ((flags >> 7) & 0x1) != 0;
var interlaced = ((flags >> 6) & 0x1) != 0; // See appendix E for interlacing _interlaced = ((flags >> 6) & 0x1) != 0; // See appendix E for interlacing
var isLocalColorTableSorted = ((flags >> 5) & 0x1) != 0; var isLocalColorTableSorted = ((flags >> 5) & 0x1) != 0;
var sizeOfLocalColorTable = (byte)Math.Pow(2, (flags & 0x7) + 1); var sizeOfLocalColorTable = (byte)Math.Pow(2, (flags & 0x7) + 1);
byte[] colors; byte[] colors;
@ -140,7 +139,6 @@ public class GifDecoder
compressedBytes.AddRange(bytes); compressedBytes.AddRange(bytes);
blockSize = reader.ReadByte(); blockSize = reader.ReadByte();
} }
// Godot.GD.Print($"End of image data: {reader.BaseStream.Position}");
using MemoryStream compressedStream = new(compressedBytes.ToArray()); using MemoryStream compressedStream = new(compressedBytes.ToArray());
using BinaryReader compressedReader = new(compressedStream, Encoding.UTF8, false); using BinaryReader compressedReader = new(compressedStream, Encoding.UTF8, false);
@ -180,7 +178,6 @@ public class GifDecoder
} }
else if (code == endCode) else if (code == endCode)
{ {
// Godot.GD.Print($"CompressedBytesLeft: {compressedReader.BaseStream.Length - compressedReader.BaseStream.Position}");
break; break;
} }
else if (code < codeCount) else if (code < codeCount)
@ -256,17 +253,49 @@ public class GifDecoder
{ {
var bytesIdx = 0; var bytesIdx = 0;
var bytes = new byte[Width * Height * 4]; var bytes = new byte[Width * Height * 4];
void WritePixelBytes(int bytesOffset, int colorIndex)
{
bytes[bytesOffset] = _colors[colorIndex * 3];
bytes[bytesOffset + 1] = _colors[colorIndex * 3 + 1];
bytes[bytesOffset + 2] = _colors[colorIndex * 3 + 2];
bytes[bytesOffset + 3] = (byte)((colorIndex == _transparentIndex) ? 0 : 255);
}
if (!_interlaced)
{
foreach (var colorIndex in _pixelIndices) foreach (var colorIndex in _pixelIndices)
{ {
bytes[bytesIdx++] = _colors[colorIndex * 3]; WritePixelBytes(bytesIdx, colorIndex);
bytes[bytesIdx++] = _colors[colorIndex * 3 + 1]; bytesIdx += 4;
bytes[bytesIdx++] = _colors[colorIndex * 3 + 2];
// Fuck you LGS hardcoding the transparent index suck my balls
// bytes[bytesIdx++] = (byte)((colorIndex == _transparentIndex) ? 0 : 255);
bytes[bytesIdx++] = (byte)((colorIndex == 0) ? 0 : 255);
} }
return bytes; return bytes;
} }
var pass = 0;
var inc = 8;
var y = 0;
var indicesOffset = 0;
var bytesPerRow = Width * 4;
for (var i = 0; i < Height; i++)
{
bytesIdx = y * bytesPerRow;
for (var x = 0; x < Width; x++)
{
WritePixelBytes(bytesIdx + x * 4, _pixelIndices[indicesOffset++]);
}
y += inc;
if (y >= Height)
{
pass += 1;
y = (int)(8 / Math.Pow(2, pass));
inc = y * 2;
}
}
return bytes;
}
} }
Header _header; Header _header;
@ -280,15 +309,12 @@ public class GifDecoder
throw new FileNotFoundException(); throw new FileNotFoundException();
} }
// Godot.GD.Print($"Decoding image at: {path}");
using MemoryStream stream = new(File.ReadAllBytes(path)); using MemoryStream stream = new(File.ReadAllBytes(path));
using BinaryReader reader = new(stream, Encoding.UTF8, false); using BinaryReader reader = new(stream, Encoding.UTF8, false);
_header = new Header(reader); _header = new Header(reader);
_globalColors = ReadColorTable(reader, _header.HasGlobalColorTable ? _header.GlobalColorTableSize : 0); _globalColors = ReadColorTable(reader, _header.HasGlobalColorTable ? _header.GlobalColorTableSize : 0);
var images = new List<ImageData>(); var images = new List<ImageData>();
var transparentIndex = -1;
while (true) while (true)
{ {
var id = reader.ReadByte(); var id = reader.ReadByte();
@ -296,22 +322,18 @@ public class GifDecoder
switch (id) switch (id)
{ {
case 0x2C: // Image case 0x2C: // Image
// Godot.GD.Print("Adding new image!!"); images.Add(new ImageData(reader, _globalColors, 0));
images.Add(new ImageData(reader, _globalColors, transparentIndex));
transparentIndex = -1;
break; break;
case 0x21: // Extension case 0x21: // Extension
var extId = reader.ReadByte(); // We don't need to actually handle any extensions. The only one that's
if (extId == 0xF9) // potentially relevant is GraphicsControl, but Dark uses a hardcoded
// transparency palette index and doesn't use multi-frame GIFs with timing.
reader.ReadByte();
var blockSize = reader.ReadByte();
while (blockSize != 0)
{ {
var graphicsControl = new GraphicsControl(reader); reader.ReadBytes(blockSize);
transparentIndex = graphicsControl.TransparencyIndex; blockSize = reader.ReadByte();
// Godot.GD.Print($"We set the transparent index: {transparentIndex} for {path}");
}
else
{
// We don't support Comment (0xFE), Text (0x01), or Application (0xFF) extensions
throw new InvalidDataException($"Unknown or unsupported extension identifier in GIF file: {extId}");
} }
break; break;
case 0x3B: case 0x3B: