GIFImageDecoder.cpp [plain text]
#include "config.h"
#include "GIFImageDecoder.h"
#include "GIFImageReader.h"
namespace WebCore {
class GIFImageDecoderPrivate {
public:
GIFImageDecoderPrivate(GIFImageDecoder* decoder = 0)
: m_reader(decoder)
, m_readOffset(0)
{
}
~GIFImageDecoderPrivate()
{
m_reader.close();
}
bool decode(SharedBuffer* 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_repetitionCount(cAnimationLoopOnce)
, 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 (!ImageDecoder::isSizeAvailable() && m_reader) {
if (m_failed)
return false;
decode(GIFSizeQuery, 0);
}
return !m_failed && ImageDecoder::isSizeAvailable();
}
int GIFImageDecoder::frameCount()
{
if (!m_frameCountValid) {
GIFImageDecoderPrivate reader;
reader.decode(m_data.get(), GIFFrameCountQuery);
m_frameCountValid = true;
m_frameBufferCache.resize(reader.frameCount());
}
return m_frameBufferCache.size();
}
int GIFImageDecoder::repetitionCount() const
{
if (m_reader) {
const int repetitionCount = m_reader->repetitionCount();
if (repetitionCount != cLoopCountNotSeen)
m_repetitionCount = repetitionCount;
}
return m_repetitionCount;
}
RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index)
{
if (index >= static_cast<size_t>(frameCount()))
return 0;
RGBA32Buffer& frame = m_frameBufferCache[index];
if (frame.status() != RGBA32Buffer::FrameComplete && m_reader)
decode(GIFFullQuery, index+1); return &frame;
}
void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
{
if (m_frameBufferCache.isEmpty())
return; clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1);
const Vector<RGBA32Buffer>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame);
for (Vector<RGBA32Buffer>::iterator i(m_frameBufferCache.begin()); i != end; ++i) {
if (i->status() == RGBA32Buffer::FrameEmpty)
continue;
if ((i->status() == RGBA32Buffer::FramePartial) || ((i + 1)->status() != RGBA32Buffer::FrameComplete))
break;
i->clear();
}
}
void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const
{
if (m_failed)
return;
m_failed = !m_reader->decode(m_data.get(), query, haltAtFrame);
if (m_failed) {
delete m_reader;
m_reader = 0;
}
}
bool GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height)
{
return setSize(width, height);
}
void GIFImageDecoder::decodingHalted(unsigned bytesLeft)
{
m_reader->setReadOffset(m_data->size() - bytesLeft);
}
bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
{
IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(),
m_reader->frameWidth(), m_reader->frameHeight());
if (frameRect.right() > size().width())
frameRect.setWidth(size().width() - m_reader->frameXOffset());
if (frameRect.bottom() > size().height())
frameRect.setHeight(size().height() - m_reader->frameYOffset());
RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex];
buffer->setRect(frameRect);
if (frameIndex == 0) {
if (!prepEmptyFrameBuffer(buffer)) {
buffer->setStatus(RGBA32Buffer::FrameComplete);
m_failed = true;
return false;
}
} else {
const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex];
ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete);
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->copyBitmapData(*prevBuffer);
buffer->setHasAlpha(prevBuffer->hasAlpha());
} else {
const IntRect& prevRect = prevBuffer->rect();
if ((frameIndex == 0)
|| prevRect.contains(IntRect(IntPoint(0, 0), size()))) {
prepEmptyFrameBuffer(buffer);
} else {
buffer->copyBitmapData(*prevBuffer);
buffer->setHasAlpha(prevBuffer->hasAlpha());
SkBitmap& bitmap = buffer->bitmap();
for (int y = prevRect.y(); y < prevRect.bottom(); ++y) {
for (int x = prevRect.x(); x < prevRect.right(); ++x)
buffer->setRGBA(bitmap.getAddr32(x, y), 0, 0, 0, 0);
}
if ((prevRect.width() > 0) && (prevRect.height() > 0))
buffer->setHasAlpha(true);
}
}
}
buffer->setStatus(RGBA32Buffer::FramePartial);
m_currentBufferSawAlpha = false;
return true;
}
bool GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const
{
if (!buffer->setSize(size().width(), size().height()))
return false;
buffer->setHasAlpha(true);
return 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))
return;
if (rowBuffer == 0 || static_cast<int>(m_reader->frameYOffset() + rowNumber) >= size().height())
return;
unsigned colorMapSize;
unsigned char* colorMap;
m_reader->getColorMap(colorMap, colorMapSize);
if (!colorMap)
return;
unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * size().width() + m_reader->frameXOffset();
unsigned* dst = buffer.bitmap().getAddr32(0, 0) + dstPos;
unsigned* dstEnd = dst + 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 data_size = num * sizeof(unsigned);
unsigned width = size().width();
unsigned* end = buffer.bitmap().getAddr32(0, 0) + width * size().height();
currDst = dst + width;
for (unsigned i = 1; i < repeatCount; i++) {
if (currDst + num > end) break;
memcpy(currDst, dst, data_size);
currDst += width;
}
}
}
void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod)
{
RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex))
return;
buffer.setStatus(RGBA32Buffer::FrameComplete);
buffer.setDuration(frameDuration);
buffer.setDisposalMethod(disposalMethod);
if (!m_currentBufferSawAlpha) {
if (buffer.rect().contains(IntRect(IntPoint(0, 0), 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()
{
if (m_reader)
m_repetitionCount = m_reader->repetitionCount();
delete m_reader;
m_reader = 0;
}
}