123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "ImageGif.h"
- // Editor
- #include "Util/Image.h"
- //---------------------------------------------------------------------------
- #define NEXTBYTE (*ptr++)
- #define IMAGESEP 0x2c
- #define GRAPHIC_EXT 0xf9
- #define PLAINTEXT_EXT 0x01
- #define APPLICATION_EXT 0xff
- #define COMMENT_EXT 0xfe
- #define START_EXTENSION 0x21
- #define INTERLACEMASK 0x40
- #define COLORMAPMASK 0x80
- #define CHK(x) x
- #pragma pack(push,1)
- struct SGIFRGBcolor
- {
- uint8 red, green, blue;
- };
- struct SGIFRGBPixel
- {
- uint8 red, green, blue, alpha;
- };
- #pragma pack(pop)
- static int BitOffset = 0, /* Bit Offset of next code */
- XC = 0, YC = 0, /* Output X and Y coords of current pixel */
- Pass = 0, /* Used by output routine if interlaced pic */
- OutCount = 0, /* Decompressor output 'stack count' */
- RWidth, RHeight, /* screen dimensions */
- Width, Height, /* image dimensions */
- LeftOfs, TopOfs, /* image offset */
- BitsPerPixel, /* Bits per pixel, read from GIF header */
- BytesPerScanline, /* bytes per scanline in output raster */
- ColorMapSize, /* number of colors */
- Background, /* background color */
- CodeSize, /* Code size, read from GIF header */
- InitCodeSize, /* Starting code size, used during Clear */
- Code, /* Value returned by ReadCode */
- MaxCode, /* limiting value for current code size */
- ClearCode, /* GIF clear code */
- EOFCode, /* GIF end-of-information code */
- CurCode, OldCode, InCode, /* Decompressor variables */
- FirstFree, /* First free code, generated per GIF spec */
- FreeCode, /* Decompressor, next free slot in hash table*/
- FinChar, /* Decompressor variable */
- BitMask, /* AND mask for data size */
- ReadMask; /* Code AND mask for current code size */
- static bool Interlace, HasColormap;
- static SGIFRGBPixel* Image; /* The result array */
- static SGIFRGBcolor* Palette; /* The palette that is used */
- static uint8* IndexImage;
- static uint8* Raster; /* The raster data stream, unblocked */
- static uint8 used[256];
- static int numused;
- const char* id87 = "GIF87a";
- const char* id89 = "GIF89a";
- /* Fetch the next code from the raster data stream. The codes can be
- * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
- * maintain our location in the Raster array as a BIT Offset. We compute
- * the uint8 Offset into the raster array by dividing this by 8, pick up
- * three bytes, compute the bit Offset into our 24-bit chunk, shift to
- * bring the desired code to the bottom, then mask it off and return it.
- */
- inline int ReadCode (void)
- {
- int RawCode, ByteOffset;
- ByteOffset = BitOffset / 8;
- RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]);
- if (CodeSize >= 8)
- {
- RawCode += (0x10000 * Raster[ByteOffset + 2]);
- }
- RawCode >>= (BitOffset % 8);
- BitOffset += CodeSize;
- return RawCode & ReadMask;
- }
- inline void AddToPixel (uint8 Index)
- {
- if (YC < Height)
- {
- SGIFRGBPixel* p = Image + YC * BytesPerScanline + XC;
- p->red = Palette[Index].red;
- p->green = Palette[Index].green;
- p->blue = Palette[Index].blue;
- p->alpha = 0;
- IndexImage[YC * BytesPerScanline + XC] = Index;
- }
- if (!used[Index])
- {
- used[Index] = 1;
- numused++;
- }
- /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
- if (++XC == Width)
- {
- /* If a non-interlaced picture, just increment YC to the next scan line.
- * If it's interlaced, deal with the interlace as described in the GIF
- * spec. Put the decoded scan line out to the screen if we haven't gone
- * past the bottom of it
- */
- XC = 0;
- if (!Interlace)
- {
- YC++;
- }
- else
- {
- switch (Pass)
- {
- case 0:
- YC += 8;
- if (YC >= Height)
- {
- Pass++;
- YC = 4;
- }
- break;
- case 1:
- YC += 8;
- if (YC >= Height)
- {
- Pass++;
- YC = 2;
- }
- break;
- case 2:
- YC += 4;
- if (YC >= Height)
- {
- Pass++;
- YC = 1;
- }
- break;
- case 3:
- YC += 2;
- break;
- default:
- break;
- }
- }
- }
- }
- bool CImageGif::Load(const QString& fileName, CImageEx& outImage)
- {
- bool ret = false;
- std::vector<uint8> data;
- CCryFile file;
- if (!file.Open(fileName.toUtf8().data(), "rb"))
- {
- CLogFile::FormatLine("File not found %s", fileName.toUtf8().data());
- return false;
- }
- long filesize = static_cast<long>(file.GetLength());
- data.resize(filesize);
- uint8* ptr = &data[0];
- file.ReadRaw(ptr, filesize);
- /* Detect if this is a GIF file */
- if (strncmp ((char*)ptr, "GIF87a", 6) && strncmp ((char*)ptr, "GIF89a", 6))
- {
- CLogFile::FormatLine("Bad GIF file format %s", fileName.toUtf8().data());
- return false;
- }
- unsigned char ch, ch1;
- uint8* ptr1;
- int i;
- TImage<uint8> outImageIndex;
- /* The hash table used by the decompressor */
- int* Prefix;
- int* Suffix;
- /* An output array used by the decompressor */
- int* OutCode;
- CHK (Prefix = new int [4096]);
- CHK (Suffix = new int [4096]);
- CHK (OutCode = new int [1025]);
- BitOffset = 0;
- XC = YC = 0;
- Pass = 0;
- OutCount = 0;
- Palette = nullptr;
- CHK (Raster = new uint8 [filesize]);
- if (strncmp((char*) ptr, id87, 6))
- {
- if (strncmp((char*) ptr, id89, 6))
- {
- CLogFile::FormatLine("Bad GIF file format %s",fileName.toUtf8().data());
- goto cleanup;
- }
- }
- ptr += 6;
- /* Get variables from the GIF screen descriptor */
- ch = NEXTBYTE;
- RWidth = ch + 0x100 * NEXTBYTE; /* screen dimensions... not used. */
- ch = NEXTBYTE;
- RHeight = ch + 0x100 * NEXTBYTE;
- ch = NEXTBYTE;
- HasColormap = ((ch & COLORMAPMASK) ? true : false);
- BitsPerPixel = (ch & 7) + 1;
- BitMask = ColorMapSize - 1;
- Background = NEXTBYTE; /* background color... not used. */
- if (NEXTBYTE) /* supposed to be NULL */
- {
- CLogFile::FormatLine("Bad GIF file format %s", fileName.toUtf8().data());
- goto cleanup;
- }
- /* Read in global colormap. */
- SGIFRGBcolor mspPal[1024];
- if (HasColormap)
- {
- for (i = 0; i < ColorMapSize; i++)
- {
- mspPal[i].red = NEXTBYTE;
- mspPal[i].green = NEXTBYTE;
- mspPal[i].blue = NEXTBYTE;
- used[i] = 0;
- }
- Palette = mspPal;
- numused = 0;
- } /* else no colormap in GIF file */
- /* look for image separator */
- for (ch = NEXTBYTE; ch != IMAGESEP; ch = NEXTBYTE)
- {
- i = ch;
- if (ch != START_EXTENSION)
- {
- CLogFile::FormatLine("Bad GIF file format %s", fileName.toUtf8().data());
- goto cleanup;
- }
- /* handle image extensions */
- switch (ch = NEXTBYTE)
- {
- case GRAPHIC_EXT:
- ch = NEXTBYTE;
- ptr += ch;
- break;
- case PLAINTEXT_EXT:
- break;
- case APPLICATION_EXT:
- break;
- case COMMENT_EXT:
- break;
- default:
- {
- CLogFile::FormatLine("Invalid GIF89 extension %s", fileName.toUtf8().data());
- goto cleanup;
- }
- }
- ch = NEXTBYTE;
- while (ch)
- {
- ptr += ch;
- ch = NEXTBYTE;
- }
- }
- /* Now read in values from the image descriptor */
- ch = NEXTBYTE;
- LeftOfs = ch + 0x100 * NEXTBYTE;
- ch = NEXTBYTE;
- TopOfs = ch + 0x100 * NEXTBYTE;
- ch = NEXTBYTE;
- Width = ch + 0x100 * NEXTBYTE;
- ch = NEXTBYTE;
- Height = ch + 0x100 * NEXTBYTE;
- Interlace = ((NEXTBYTE & INTERLACEMASK) ? true : false);
- // Set the dimensions which will also allocate the image data
- // buffer.
- outImage.Allocate(Width, Height);
- //mfSet_dimensions (Width, Height);
- Image = (SGIFRGBPixel*)outImage.GetData();
- outImageIndex.Allocate(Width, Height);
- IndexImage = outImageIndex.GetData();
- /* Note that I ignore the possible existence of a local color map.
- * I'm told there aren't many files around that use them, and the spec
- * says it's defined for future use. This could lead to an error
- * reading some files.
- */
- /* Start reading the raster data. First we get the intial code size
- * and compute decompressor constant values, based on this code size.
- */
- CodeSize = NEXTBYTE;
- ClearCode = (1 << CodeSize);
- EOFCode = ClearCode + 1;
- FreeCode = FirstFree = ClearCode + 2;
- /* The GIF spec has it that the code size is the code size used to
- * compute the above values is the code size given in the file, but the
- * code size used in compression/decompression is the code size given in
- * the file plus one. (thus the ++).
- */
- CodeSize++;
- InitCodeSize = CodeSize;
- MaxCode = (1 << CodeSize);
- ReadMask = MaxCode - 1;
- /* Read the raster data. Here we just transpose it from the GIF array
- * to the Raster array, turning it from a series of blocks into one long
- * data stream, which makes life much easier for ReadCode().
- */
- ptr1 = Raster;
- do
- {
- ch = ch1 = NEXTBYTE;
- while (ch--)
- {
- *ptr1++ = NEXTBYTE;
- }
- if ((ptr1 - Raster) > filesize)
- {
- CLogFile::FormatLine("Corrupted GIF file (unblock) %s", fileName.toUtf8().data());
- goto cleanup;
- }
- }
- while (ch1);
- BytesPerScanline = Width;
- /* Decompress the file, continuing until you see the GIF EOF code.
- * One obvious enhancement is to add checking for corrupt files here.
- */
- Code = ReadCode ();
- while (Code != EOFCode)
- {
- /* Clear code sets everything back to its initial value, then reads the
- * immediately subsequent code as uncompressed data.
- */
- if (Code == ClearCode)
- {
- CodeSize = InitCodeSize;
- MaxCode = (1 << CodeSize);
- ReadMask = MaxCode - 1;
- FreeCode = FirstFree;
- CurCode = OldCode = Code = ReadCode();
- FinChar = CurCode & BitMask;
- AddToPixel(static_cast<uint8>(FinChar));
- }
- else
- {
- /* If not a clear code, then must be data: save same as CurCode and InCode */
- CurCode = InCode = Code;
- /* If greater or equal to FreeCode, not in the hash table yet;
- * repeat the last character decoded
- */
- if (CurCode >= FreeCode)
- {
- CurCode = OldCode;
- OutCode[OutCount++] = FinChar;
- }
- /* Unless this code is raw data, pursue the chain pointed to by CurCode
- * through the hash table to its end; each code in the chain puts its
- * associated output code on the output queue.
- */
- while (CurCode > BitMask)
- {
- if (OutCount > 1024)
- {
- CLogFile::FormatLine("Corrupted GIF file (OutCount) %s", fileName.toUtf8().data());
- goto cleanup;
- }
- OutCode[OutCount++] = Suffix[CurCode];
- CurCode = Prefix[CurCode];
- }
- /* The last code in the chain is treated as raw data. */
- FinChar = CurCode & BitMask;
- OutCode[OutCount++] = FinChar;
- /* Now we put the data out to the Output routine.
- * It's been stacked LIFO, so deal with it that way...
- */
- for (i = OutCount - 1; i >= 0; i--)
- {
- AddToPixel(static_cast<uint8>(OutCode[i]));
- }
- OutCount = 0;
- /* Build the hash table on-the-fly. No table is stored in the file. */
- Prefix[FreeCode] = OldCode;
- Suffix[FreeCode] = FinChar;
- OldCode = InCode;
- /* Point to the next slot in the table. If we exceed the current
- * MaxCode value, increment the code size unless it's already 12. If it
- * is, do nothing: the next code decompressed better be CLEAR
- */
- FreeCode++;
- if (FreeCode >= MaxCode)
- {
- if (CodeSize < 12)
- {
- CodeSize++;
- MaxCode *= 2;
- ReadMask = (1 << CodeSize) - 1;
- }
- }
- }
- Code = ReadCode ();
- }
- ret = true;
- cleanup:
- if (Raster)
- {
- CHK (delete [] Raster);
- }
- if (Prefix)
- {
- CHK (delete [] Prefix);
- }
- if (Suffix)
- {
- CHK (delete [] Suffix);
- }
- if (OutCode)
- {
- CHK (delete [] OutCode);
- }
- return ret;
- }
|