GIFImageDecoder.cpp [plain text]
#include "GIFImageDecoder.h"
#include "GIFImageReader.h"
#if PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX)
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 >= frameCount())
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(unsigned frameIndex)
{
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());
RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex];
buffer->setRect(frameRect);
if (frameIndex == 0) {
prepEmptyFrameBuffer(buffer);
} else {
const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex];
RGBA32Buffer::FrameDisposalMethod prevMethod =
prevBuffer->disposalMethod();
while ((frameIndex > 0) &&
(prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) {
prevBuffer = &m_frameBufferCache[--frameIndex];
prevMethod = prevBuffer->disposalMethod();
}
if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) ||
(prevMethod == RGBA32Buffer::DisposeKeep)) {
buffer->bytes() = prevBuffer->bytes();
buffer->setHasAlpha(prevBuffer->hasAlpha());
} else {
const IntRect& prevRect = prevBuffer->rect();
if ((frameIndex == 0) ||
prevRect.contains(IntRect(IntPoint(0, 0), m_size))) {
prepEmptyFrameBuffer(buffer);
} else {
buffer->bytes() = prevBuffer->bytes();
buffer->setHasAlpha(prevBuffer->hasAlpha());
for (int y = prevRect.y(); y < prevRect.bottom(); ++y) {
unsigned* const currentRow =
buffer->bytes().data() + (y * m_size.width());
for (int x = prevRect.x(); x < prevRect.right(); ++x)
buffer->setRGBA(*(currentRow + x), 0, 0, 0, 0);
}
if ((prevRect.width() > 0) && (prevRect.height() > 0))
buffer->setHasAlpha(true);
}
}
}
buffer->setStatus(RGBA32Buffer::FramePartial);
m_currentBufferSawAlpha = false;
}
void GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const
{
buffer->bytes().resize(m_size.width() * m_size.height());
buffer->bytes().fill(0);
buffer->setHasAlpha(true);
}
void GIFImageDecoder::haveDecodedRow(unsigned frameIndex,
unsigned char* rowBuffer, unsigned char* rowEnd,
unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels)
{
RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
if (buffer.status() == RGBA32Buffer::FrameEmpty)
initFrameBuffer(frameIndex);
if (rowBuffer == 0 || static_cast<int>(m_reader->frameYOffset() + rowNumber) >= m_size.height())
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;
while (currentRowByte != rowEnd && currDst < dstEnd) {
if ((!m_reader->isTransparent() || *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 {
m_currentBufferSawAlpha = true;
if (writeTransparentPixels)
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, RGBA32Buffer::FrameDisposalMethod disposalMethod)
{
RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
buffer.ensureHeight(m_size.height());
buffer.setStatus(RGBA32Buffer::FrameComplete);
buffer.setDuration(frameDuration);
buffer.setDisposalMethod(disposalMethod);
if (!m_currentBufferSawAlpha) {
if (buffer.rect().contains(IntRect(IntPoint(0, 0), m_size))) {
buffer.setHasAlpha(false);
} else if (frameIndex > 0) {
const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex];
while ((frameIndex > 0) &&
(prevBuffer->disposalMethod() ==
RGBA32Buffer::DisposeOverwritePrevious))
prevBuffer = &m_frameBufferCache[--frameIndex];
if ((prevBuffer->disposalMethod() ==
RGBA32Buffer::DisposeOverwriteBgcolor) &&
!prevBuffer->hasAlpha() &&
buffer.rect().contains(prevBuffer->rect()))
buffer.setHasAlpha(false);
}
}
}
void GIFImageDecoder::gifComplete()
{
delete m_reader;
m_reader = 0;
}
}
#endif // PLATFORM(CAIRO)