juicysfplugin/modules/juce_graphics/image_formats/juce_GIFLoader.cpp

452 lines
12 KiB
C++
Raw Normal View History

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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)) == 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