|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 6 End-User License
- Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
-
- End User License Agreement: www.juce.com/juce-6-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER
- Image juce_loadWithCoreImage (InputStream& input);
- #else
-
- //==============================================================================
- class GIFLoader
- {
- public:
- GIFLoader (InputStream& in)
- : input (in),
- dataBlockIsZero (false), fresh (false), finished (false),
- currentBit (0), lastBit (0), lastByteIndex (0),
- codeSize (0), setCodeSize (0), maxCode (0), maxCodeSize (0),
- firstcode (0), oldcode (0), clearCode (0), endCode (0)
- {
- int imageWidth, imageHeight;
- if (! getSizeFromHeader (imageWidth, imageHeight))
- return;
-
- uint8 buf [16];
- if (in.read (buf, 3) != 3)
- return;
-
- int numColours = 2 << (buf[0] & 7);
- int transparent = -1;
-
- if ((buf[0] & 0x80) != 0)
- readPalette (numColours);
-
- for (;;)
- {
- if (input.read (buf, 1) != 1 || buf[0] == ';')
- break;
-
- if (buf[0] == '!')
- {
- if (readExtension (transparent))
- continue;
-
- break;
- }
-
- if (buf[0] != ',')
- continue;
-
- if (input.read (buf, 9) == 9)
- {
- imageWidth = (int) ByteOrder::littleEndianShort (buf + 4);
- imageHeight = (int) ByteOrder::littleEndianShort (buf + 6);
-
- numColours = 2 << (buf[8] & 7);
-
- if ((buf[8] & 0x80) != 0)
- if (! readPalette (numColours))
- break;
-
- image = Image (transparent >= 0 ? Image::ARGB : Image::RGB,
- imageWidth, imageHeight, transparent >= 0);
-
- image.getProperties()->set ("originalImageHadAlpha", transparent >= 0);
-
- readImage ((buf[8] & 0x40) != 0, transparent);
- }
-
- break;
- }
- }
-
- Image image;
-
- private:
- InputStream& input;
- uint8 buffer [260];
- PixelARGB palette [256];
- bool dataBlockIsZero, fresh, finished;
- int currentBit, lastBit, lastByteIndex;
- int codeSize, setCodeSize;
- int maxCode, maxCodeSize;
- int firstcode, oldcode;
- int clearCode, endCode;
- enum { maxGifCode = 1 << 12 };
- int table [2] [maxGifCode];
- int stack [2 * maxGifCode];
- int* sp;
-
- bool getSizeFromHeader (int& w, int& h)
- {
- char b[6];
-
- if (input.read (b, 6) == 6
- && (strncmp ("GIF87a", b, 6) == 0
- || strncmp ("GIF89a", b, 6) == 0))
- {
- if (input.read (b, 4) == 4)
- {
- w = (int) ByteOrder::littleEndianShort (b);
- h = (int) ByteOrder::littleEndianShort (b + 2);
- return w > 0 && h > 0;
- }
- }
-
- return false;
- }
-
- bool readPalette (const int numCols)
- {
- for (int i = 0; i < numCols; ++i)
- {
- uint8 rgb[4];
- input.read (rgb, 3);
-
- palette[i].setARGB (0xff, rgb[0], rgb[1], rgb[2]);
- palette[i].premultiply();
- }
-
- return true;
- }
-
- int readDataBlock (uint8* const dest)
- {
- uint8 n;
- if (input.read (&n, 1) == 1)
- {
- dataBlockIsZero = (n == 0);
-
- if (dataBlockIsZero || (input.read (dest, n) == n))
- return n;
- }
-
- return -1;
- }
-
- int readExtension (int& transparent)
- {
- uint8 type;
- if (input.read (&type, 1) != 1)
- return false;
-
- uint8 b [260];
- int n = 0;
-
- if (type == 0xf9)
- {
- n = readDataBlock (b);
- if (n < 0)
- return 1;
-
- if ((b[0] & 1) != 0)
- transparent = b[3];
- }
-
- do
- {
- n = readDataBlock (b);
- }
- while (n > 0);
-
- return n >= 0;
- }
-
- void clearTable()
- {
- int i;
- for (i = 0; i < clearCode; ++i)
- {
- table[0][i] = 0;
- table[1][i] = i;
- }
-
- for (; i < maxGifCode; ++i)
- {
- table[0][i] = 0;
- table[1][i] = 0;
- }
- }
-
- void initialise (const int inputCodeSize)
- {
- setCodeSize = inputCodeSize;
- codeSize = setCodeSize + 1;
- clearCode = 1 << setCodeSize;
- endCode = clearCode + 1;
- maxCodeSize = 2 * clearCode;
- maxCode = clearCode + 2;
-
- getCode (0, true);
-
- fresh = true;
- clearTable();
- sp = stack;
- }
-
- int readLZWByte()
- {
- if (fresh)
- {
- fresh = false;
-
- for (;;)
- {
- firstcode = oldcode = getCode (codeSize, false);
-
- if (firstcode != clearCode)
- return firstcode;
- }
- }
-
- if (sp > stack)
- return *--sp;
-
- int code;
-
- while ((code = getCode (codeSize, false)) >= 0)
- {
- if (code == clearCode)
- {
- clearTable();
- codeSize = setCodeSize + 1;
- maxCodeSize = 2 * clearCode;
- maxCode = clearCode + 2;
- sp = stack;
- firstcode = oldcode = getCode (codeSize, false);
- return firstcode;
- }
- else if (code == endCode)
- {
- if (dataBlockIsZero)
- return -2;
-
- uint8 buf [260];
- int n;
-
- while ((n = readDataBlock (buf)) > 0)
- {}
-
- if (n != 0)
- return -2;
- }
-
- const int incode = code;
-
- if (code >= maxCode)
- {
- *sp++ = firstcode;
- code = oldcode;
- }
-
- while (code >= clearCode)
- {
- *sp++ = table[1][code];
- if (code == table[0][code])
- return -2;
-
- code = table[0][code];
- }
-
- *sp++ = firstcode = table[1][code];
-
- if ((code = maxCode) < maxGifCode)
- {
- table[0][code] = oldcode;
- table[1][code] = firstcode;
- ++maxCode;
-
- if (maxCode >= maxCodeSize && maxCodeSize < maxGifCode)
- {
- maxCodeSize <<= 1;
- ++codeSize;
- }
- }
-
- oldcode = incode;
-
- if (sp > stack)
- return *--sp;
- }
-
- return code;
- }
-
- int getCode (const int codeSize_, const bool shouldInitialise)
- {
- if (shouldInitialise)
- {
- currentBit = 0;
- lastBit = 0;
- finished = false;
- return 0;
- }
-
- if ((currentBit + codeSize_) >= lastBit)
- {
- if (finished)
- return -1;
-
- buffer[0] = buffer [lastByteIndex - 2];
- buffer[1] = buffer [lastByteIndex - 1];
-
- const int n = readDataBlock (buffer + 2);
-
- if (n == 0)
- finished = true;
-
- lastByteIndex = 2 + n;
- currentBit = (currentBit - lastBit) + 16;
- lastBit = (2 + n) * 8 ;
- }
-
- int result = 0;
- int i = currentBit;
-
- for (int j = 0; j < codeSize_; ++j)
- {
- result |= ((buffer[i >> 3] & (1 << (i & 7))) != 0) << j;
- ++i;
- }
-
- currentBit += codeSize_;
- return result;
- }
-
- bool readImage (const int interlace, const int transparent)
- {
- uint8 c;
- if (input.read (&c, 1) != 1)
- return false;
-
- initialise (c);
-
- if (transparent >= 0)
- palette [transparent].setARGB (0, 0, 0, 0);
-
- int xpos = 0, ypos = 0, yStep = 8, pass = 0;
-
- const Image::BitmapData destData (image, Image::BitmapData::writeOnly);
- uint8* p = destData.getPixelPointer (0, 0);
- const bool hasAlpha = image.hasAlphaChannel();
-
- for (;;)
- {
- const int index = readLZWByte();
- if (index < 0)
- break;
-
- if (hasAlpha)
- ((PixelARGB*) p)->set (palette [index]);
- else
- ((PixelRGB*) p)->set (palette [index]);
-
- p += destData.pixelStride;
-
- if (++xpos == destData.width)
- {
- xpos = 0;
-
- if (interlace)
- {
- ypos += yStep;
-
- while (ypos >= destData.height)
- {
- switch (++pass)
- {
- case 1: ypos = 4; yStep = 8; break;
- case 2: ypos = 2; yStep = 4; break;
- case 3: ypos = 1; yStep = 2; break;
- default: return true;
- }
- }
- }
- else
- {
- if (++ypos >= destData.height)
- break;
- }
-
- p = destData.getPixelPointer (xpos, ypos);
- }
- }
-
- return true;
- }
-
- JUCE_DECLARE_NON_COPYABLE (GIFLoader)
- };
-
- #endif
-
- //==============================================================================
- GIFImageFormat::GIFImageFormat() {}
- GIFImageFormat::~GIFImageFormat() {}
-
- String GIFImageFormat::getFormatName() { return "GIF"; }
- bool GIFImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("gif"); }
-
- bool GIFImageFormat::canUnderstand (InputStream& in)
- {
- char header [4];
-
- return (in.read (header, sizeof (header)) == (int) sizeof (header))
- && header[0] == 'G'
- && header[1] == 'I'
- && header[2] == 'F';
- }
-
- Image GIFImageFormat::decodeImage (InputStream& in)
- {
- #if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER
- return juce_loadWithCoreImage (in);
- #else
- const std::unique_ptr<GIFLoader> loader (new GIFLoader (in));
- return loader->image;
- #endif
- }
-
- bool GIFImageFormat::writeImageToStream (const Image& /*sourceImage*/, OutputStream& /*destStream*/)
- {
- jassertfalse; // writing isn't implemented for GIFs!
- return false;
- }
-
- } // namespace juce
|