GIFImageDecoder.cpp [plain text]
#include "GIFImageDecoder.h"
#include "GIFImageReader.h"
#if PLATFORM(CAIRO) || PLATFORM(QT)
namespace WebCore {
class GIFImageDecoderPrivate
{
public:
GIFImageDecoderPrivate(GIFImageDecoder* decoder = 0)
: m_reader(decoder)
{
m_readOffset = 0;
}
~GIFImageDecoderPrivate()
{
m_reader.close();
}
bool decode(const Vector<char>& data,
GIFImageDecoder::GIFQuery query = GIFImageDecoder::GIFFullQuery,
unsigned int haltFrame = -1)
{
return m_reader.read((const unsigned char*)data.data() + m_readOffset, data.size() - m_readOffset,
query,
haltFrame);
}
unsigned frameCount() const { return m_reader.images_count; }
int repetitionCount() const { return m_reader.loop_count; }
void setReadOffset(unsigned o) { m_readOffset = o; }
bool isTransparent() const { return m_reader.frame_reader->is_transparent; }
void getColorMap(unsigned char*& map, unsigned& size) const {
if (m_reader.frame_reader->is_local_colormap_defined) {
map = m_reader.frame_reader->local_colormap;
size = (unsigned)m_reader.frame_reader->local_colormap_size;
} else {
map = m_reader.global_colormap;
size = m_reader.global_colormap_size;
}
}
unsigned frameXOffset() const { return m_reader.frame_reader->x_offset; }
unsigned frameYOffset() const { return m_reader.frame_reader->y_offset; }
unsigned frameWidth() const { return m_reader.frame_reader->width; }
unsigned frameHeight() const { return m_reader.frame_reader->height; }
int transparentPixel() const { return m_reader.frame_reader->tpixel; }
unsigned duration() const { return m_reader.frame_reader->delay_time; }
private:
GIFImageReader m_reader;
unsigned m_readOffset;
};
GIFImageDecoder::GIFImageDecoder()
: m_frameCountValid(true), m_reader(0)
{}
GIFImageDecoder::~GIFImageDecoder()
{
delete m_reader;
}
void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
{
if (m_failed)
return;
ImageDecoder::setData(data, allDataReceived);
m_frameCountValid = false;
if (!m_reader && !m_failed)
m_reader = new GIFImageDecoderPrivate(this);
}
bool GIFImageDecoder::isSizeAvailable() const
{
if (!m_sizeAvailable && m_reader) {
if (m_failed)
return false;
decode(GIFSizeQuery, 0);
}
return m_sizeAvailable;
}
int GIFImageDecoder::frameCount()
{
if (!m_frameCountValid) {
GIFImageDecoderPrivate reader;
reader.decode(m_data->buffer(), GIFFrameCountQuery);
m_frameCountValid = true;
m_frameBufferCache.resize(reader.frameCount());
}
return m_frameBufferCache.size();
}
int GIFImageDecoder::repetitionCount() const
{
if (m_reader)
return m_reader->repetitionCount();
return cAnimationNone;
}
RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index)
{
if (index < 0 || index >= m_frameBufferCache.size())
return 0;
RGBA32Buffer& frame = m_frameBufferCache[index];
if (frame.status() != RGBA32Buffer::FrameComplete && m_reader)
decode(GIFFullQuery, index+1);
return &frame;
}
void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const
{
if (m_failed)
return;
m_failed = !m_reader->decode(m_data->buffer(), query, haltAtFrame);
if (m_failed) {
delete m_reader;
m_reader = 0;
}
}
void GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height)
{
m_size = IntSize(width, height);
m_sizeAvailable = true;
}
void GIFImageDecoder::decodingHalted(unsigned bytesLeft)
{
m_reader->setReadOffset(m_data->size() - bytesLeft);
}
void GIFImageDecoder::initFrameBuffer(RGBA32Buffer& buffer,
RGBA32Buffer* previousBuffer,
bool compositeWithPreviousFrame)
{
IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(),
m_reader->frameWidth(), m_reader->frameHeight());
if (frameRect.right() > m_size.width())
frameRect.setWidth(m_size.width() - m_reader->frameXOffset());
if (frameRect.bottom() > m_size.height())
frameRect.setHeight(m_size.height() - m_reader->frameYOffset());
buffer.setRect(frameRect);
bool isSubRect = (frameRect.x() > 0 || frameRect.y() > 0 ||
frameRect.width() < m_size.width() ||
frameRect.height() < m_size.height());
RGBA32Array& bytes = buffer.bytes();
if (previousBuffer && (compositeWithPreviousFrame || isSubRect)) {
bytes = previousBuffer->bytes();
buffer.ensureHeight(m_size.height());
buffer.setHasAlpha(previousBuffer->hasAlpha());
}
else bytes.resize(m_size.width() * m_size.height());
if (isSubRect) {
if (!previousBuffer) {
bytes.fill(0);
buffer.setHasAlpha(true);
} else if (!compositeWithPreviousFrame) {
if (previousBuffer->rect() != frameRect) {
RGBA32Buffer* firstBuffer = &m_frameBufferCache[0];
bool sawAlpha = buffer.hasAlpha();
IntRect prevRect = previousBuffer->rect();
unsigned end = prevRect.y() + prevRect.height();
ASSERT(IntRect(IntPoint(0,0), m_size).contains(firstBuffer->rect()));
for (unsigned i = prevRect.y(); i < end; i++) {
unsigned* curr = buffer.bytes().data() + (i * m_size.width() + prevRect.x());
unsigned* orig = firstBuffer->bytes().data() + (i * m_size.width() + prevRect.x());
unsigned* end = curr + prevRect.width();
unsigned* origEnd = orig + firstBuffer->rect().width();
while (curr != end && orig != origEnd) {
if (!sawAlpha) {
sawAlpha = true;
buffer.setHasAlpha(true);
}
*curr++ = *orig++;
}
}
}
}
}
buffer.setStatus(RGBA32Buffer::FramePartial);
}
void GIFImageDecoder::haveDecodedRow(unsigned frameIndex,
unsigned char* rowBuffer, unsigned char* rowEnd,
unsigned rowNumber, unsigned repeatCount) {
RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
RGBA32Buffer* previousBuffer = (frameIndex > 0) ? &m_frameBufferCache[frameIndex-1] : 0;
bool compositeWithPreviousFrame = previousBuffer && previousBuffer->includeInNextFrame();
if (buffer.status() == RGBA32Buffer::FrameEmpty)
initFrameBuffer(buffer, previousBuffer, compositeWithPreviousFrame);
if (rowBuffer == 0)
return;
unsigned colorMapSize;
unsigned char* colorMap;
m_reader->getColorMap(colorMap, colorMapSize);
if (!colorMap)
return;
unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * m_size.width() + m_reader->frameXOffset();
unsigned* dst = buffer.bytes().data() + dstPos;
unsigned* dstEnd = dst + m_size.width() - m_reader->frameXOffset();
unsigned* currDst = dst;
unsigned char* currentRowByte = rowBuffer;
bool hasAlpha = m_reader->isTransparent();
bool sawAlpha = false;
while (currentRowByte != rowEnd && currDst < dstEnd) {
if ((!hasAlpha || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) {
unsigned colorIndex = *currentRowByte * 3;
unsigned red = colorMap[colorIndex];
unsigned green = colorMap[colorIndex + 1];
unsigned blue = colorMap[colorIndex + 2];
RGBA32Buffer::setRGBA(*currDst, red, green, blue, 255);
} else {
if (!sawAlpha) {
sawAlpha = true;
buffer.setHasAlpha(true);
}
if (!compositeWithPreviousFrame)
RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0);
}
currDst++;
currentRowByte++;
}
if (repeatCount > 1) {
unsigned num = currDst - dst;
unsigned size = num * sizeof(unsigned);
unsigned width = m_size.width();
unsigned* end = buffer.bytes().data() + width * m_size.height();
currDst = dst + width;
for (unsigned i = 1; i < repeatCount; i++) {
if (currDst + num > end) break;
memcpy(currDst, dst, size);
currDst += width;
}
}
buffer.ensureHeight(rowNumber + repeatCount);
}
void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, bool includeInNextFrame)
{
RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
buffer.setStatus(RGBA32Buffer::FrameComplete);
buffer.setDuration(frameDuration);
buffer.setIncludeInNextFrame(includeInNextFrame);
buffer.ensureHeight(m_size.height());
}
void GIFImageDecoder::gifComplete()
{
delete m_reader;
m_reader = 0;
}
}
#endif // PLATFORM(CAIRO)