protected override long Load(Stream stream)
{
base.Load(stream);
// Read partial header to determine full header size
byte[] temp = new byte[19]; // for later
stream.Read(temp, 0, 13); // Partial header only 13 bytes long
if (!CheckIdentifier(temp))
throw new FormatException("Stream is not a GIF Image");
// Read version (not used)
Version = "8";
for(int i = 4; i < 6; i++)
Version += (char)temp[i];
if (Version != "89a" && Version != "87a")
Console.WriteLine($"Header version ({Version}) is incorrect. Must be 89a or 87a.");
// Canvas Width
CanvasWidth = BitConverter.ToInt16(temp, 6);
// Canvas Height
CanvasHeight = BitConverter.ToInt16(temp, 8);
// Packed Field
byte tempByte = temp[9];
HasGlobalColourTable = (tempByte & 0x80) == 0x80;
ColourResolution_BPP = tempByte & 0x70;
ColourSortFlag = (tempByte & 0x08) == 0x08;
GlobalColourTableSize = tempByte & 0x07;
// Background colour index
BackgroundColourIndex = temp[10];
PixelAspectRatio = temp[11];
// Read colour table
if (HasGlobalColourTable)
{
int estimatedSize = (int)Math.Pow(2, ColourResolution_BPP + 1);
if (GlobalColourTableSize != estimatedSize)
Console.WriteLine($"Global table size incorrect in header. Header = {GlobalColourTableSize}, should be {estimatedSize}");
GlobalColourTable = stream.ReadBytes(estimatedSize);
}
while (stream.Position < stream.Length)
{
stream.Read(temp, 0, 19);
// Need to search for some reason
bool found = false;
for (int i = 0; i < 19; i++)
{
if (temp[i] == 0x21 || temp[i] == 0x2C)
{
stream.Position -= (19-i);
found = true;
break;
}
}
// Nothing of interest found, start again from new position
if (!found)
continue;
// Refresh from new position
stream.Read(temp, 0, 19);
if (temp[0] == 0x21) // Extension indicator
{
if (temp[1] == 0xFF) // Animated properties indicator
{
// Ignore bytes 3-16 (1 based)
AnimationLoopCount = BitConverter.ToInt16(temp, 16);
}
else if (temp[1] == 0xF9)
{
// Skip byte size (why is it even there?)
DisposalMethod = temp[3] & 0x1C;
UserInputFlag = (temp[3] & 0x02) == 1;
TransparentColourFlag = (temp[3] & 0x01) == 1;
AnimationDelayTime = BitConverter.ToInt16(temp, 4);
TransparentColourIndex = temp[6];
}
else
stream.Seek(-18, SeekOrigin.Current); // Skip ignored optionals ideally, BUT this could also be false match i.e. not an extension block, so we can only skip that identifier and keep looking.
}
else
break;
}
// Read first image descriptor block
if (temp[0] != 0x2C)
throw new InvalidDataException($"Image Descriptor incorrect. Got: {temp[0]}, expected: {0x2C}.");
ImageLeft = BitConverter.ToInt16(temp, 1);
ImageTop = BitConverter.ToInt16(temp, 3);
Width = BitConverter.ToInt16(temp, 5);
Height = BitConverter.ToInt16(temp, 7);
// Packed byte
tempByte = temp[9];
HasLocalColourTables = (tempByte & 0x80) == 1;
IsInterlaced = (tempByte & 0x70) == 1;
LocalSortFlag = (tempByte & 0x40) == 1;
LocalColourTableSize = tempByte & 0x07;
return stream.Position;
}