summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/image-decoders/gif
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2009-08-11 17:01:47 +0100
committerBen Murdoch <benm@google.com>2009-08-11 18:21:02 +0100
commit0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch)
tree2943df35f62d885c89d01063cc528dd73b480fea /WebCore/platform/image-decoders/gif
parent7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff)
downloadexternal_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.zip
external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.gz
external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.bz2
Merge in WebKit r47029.
Diffstat (limited to 'WebCore/platform/image-decoders/gif')
-rw-r--r--WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp146
-rw-r--r--WebCore/platform/image-decoders/gif/GIFImageDecoder.h19
-rw-r--r--WebCore/platform/image-decoders/gif/GIFImageReader.cpp21
3 files changed, 79 insertions, 107 deletions
diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
index 62d8b5b..87036c9 100644
--- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
+++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -113,24 +113,17 @@ void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
}
// Whether or not the size information has been decoded yet.
-bool GIFImageDecoder::isSizeAvailable() const
+bool GIFImageDecoder::isSizeAvailable()
{
- // If we have pending data to decode, send it to the GIF reader now.
- if (!m_sizeAvailable && m_reader) {
- if (m_failed)
- return false;
-
- // The decoder will go ahead and aggressively consume everything up until the first
- // size is encountered.
- decode(GIFSizeQuery, 0);
- }
+ if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader)
+ decode(GIFSizeQuery, 0);
- return m_sizeAvailable;
+ return ImageDecoder::isSizeAvailable();
}
// The total number of frames for the image. Will scan the image data for the answer
// (without necessarily decoding all of the individual frames).
-int GIFImageDecoder::frameCount()
+size_t GIFImageDecoder::frameCount()
{
// If the decoder had an earlier error, we will just return what we had decoded
// so far.
@@ -177,7 +170,7 @@ int GIFImageDecoder::repetitionCount() const
RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index)
{
- if (index >= static_cast<size_t>(frameCount()))
+ if (index >= frameCount())
return 0;
RGBA32Buffer& frame = m_frameBufferCache[index];
@@ -237,7 +230,7 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
}
// Feed data to the GIF reader.
-void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const
+void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame)
{
if (m_failed)
return;
@@ -251,10 +244,9 @@ void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const
}
// Callbacks from the GIF reader.
-void GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height)
+bool GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height)
{
- m_size = IntSize(width, height);
- m_sizeAvailable = true;
+ return setSize(width, height);
}
void GIFImageDecoder::decodingHalted(unsigned bytesLeft)
@@ -262,7 +254,7 @@ void GIFImageDecoder::decodingHalted(unsigned bytesLeft)
m_reader->setReadOffset(m_data->size() - bytesLeft);
}
-void GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
+bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
{
// Initialize the frame rect in our buffer.
IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(),
@@ -279,7 +271,10 @@ void GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
if (frameIndex == 0) {
// This is the first frame, so we're not relying on any previous data.
- prepEmptyFrameBuffer(buffer);
+ if (!buffer->setSize(size().width(), size().height())) {
+ m_failed = true;
+ return false;
+ }
} else {
// The starting state for this frame depends on the previous frame's
// disposal method.
@@ -302,26 +297,25 @@ void GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) ||
(prevMethod == RGBA32Buffer::DisposeKeep)) {
// Preserve the last frame as the starting state for this frame.
- buffer->bytes() = prevBuffer->bytes();
- buffer->setHasAlpha(prevBuffer->hasAlpha());
+ buffer->copyBitmapData(*prevBuffer);
} else {
// We want to clear the previous frame to transparent, without
// affecting pixels in the image outside of the frame.
const IntRect& prevRect = prevBuffer->rect();
if ((frameIndex == 0)
- || prevRect.contains(IntRect(IntPoint(0, 0), size()))) {
+ || prevRect.contains(IntRect(IntPoint(), size()))) {
// Clearing the first frame, or a frame the size of the whole
// image, results in a completely empty image.
- prepEmptyFrameBuffer(buffer);
+ if (!buffer->setSize(size().width(), size().height())) {
+ m_failed = true;
+ return false;
+ }
} else {
// Copy the whole previous buffer, then clear just its frame.
- buffer->bytes() = prevBuffer->bytes();
- buffer->setHasAlpha(prevBuffer->hasAlpha());
+ buffer->copyBitmapData(*prevBuffer);
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);
+ buffer->setRGBA(x, y, 0, 0, 0, 0);
}
if ((prevRect.width() > 0) && (prevRect.height() > 0))
buffer->setHasAlpha(true);
@@ -334,57 +328,47 @@ void GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
// Reset the alpha pixel tracker for this frame.
m_currentBufferSawAlpha = false;
-}
-
-void GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const
-{
- buffer->bytes().resize(size().width() * size().height());
- buffer->bytes().fill(0);
- buffer->setHasAlpha(true);
+ return true;
}
void GIFImageDecoder::haveDecodedRow(unsigned frameIndex,
- unsigned char* rowBuffer, // Pointer to single scanline temporary buffer
+ unsigned char* rowBuffer,
unsigned char* rowEnd,
- unsigned rowNumber, // The row index
- unsigned repeatCount, // How many times to repeat the row
+ unsigned rowNumber,
+ unsigned repeatCount,
bool writeTransparentPixels)
{
- // Initialize the frame if necessary.
- RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
- if (buffer.status() == RGBA32Buffer::FrameEmpty)
- initFrameBuffer(frameIndex);
-
- // Do nothing for bogus data.
- if (rowBuffer == 0 || static_cast<int>(m_reader->frameYOffset() + rowNumber) >= size().height())
+ // The pixel data and coordinates supplied to us are relative to the frame's
+ // origin within the entire image size, i.e.
+ // (m_reader->frameXOffset(), m_reader->frameYOffset()).
+ int x = m_reader->frameXOffset();
+ const int y = m_reader->frameYOffset() + rowNumber;
+
+ // Sanity-check the arguments.
+ if ((rowBuffer == 0) || (y >= size().height()))
return;
+ // Get the colormap.
unsigned colorMapSize;
unsigned char* colorMap;
m_reader->getColorMap(colorMap, colorMapSize);
if (!colorMap)
return;
- // The buffers that we draw are the entire image's width and height, so a final output frame is
- // width * height RGBA32 values in size.
- //
- // A single GIF frame, however, can be smaller than the entire image, i.e., it can represent some sub-rectangle
- // within the overall image. The rows we are decoding are within this
- // sub-rectangle. This means that if the GIF frame's sub-rectangle is (x,y,w,h) then row 0 is really row
- // y, and each row goes from x to x+w.
- unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * size().width() + m_reader->frameXOffset();
- unsigned* dst = buffer.bytes().data() + 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);
+ // Initialize the frame if necessary.
+ RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
+ if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex))
+ return;
+
+ // Write one row's worth of data into the frame. There is no guarantee that
+ // (rowEnd - rowBuffer) == (size().width() - m_reader->frameXOffset()), so
+ // we must ensure we don't run off the end of either the source data or the
+ // row's X-coordinates.
+ for (unsigned char* sourceAddr = rowBuffer; (sourceAddr != rowEnd) && (x < size().width()); ++sourceAddr, ++x) {
+ const unsigned char sourceValue = *sourceAddr;
+ if ((!m_reader->isTransparent() || (sourceValue != m_reader->transparentPixel())) && (sourceValue < colorMapSize)) {
+ const size_t colorIndex = static_cast<size_t>(sourceValue) * 3;
+ buffer.setRGBA(x, y, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255);
} else {
m_currentBufferSawAlpha = true;
// We may or may not need to write transparent pixels to the buffer.
@@ -395,30 +379,13 @@ void GIFImageDecoder::haveDecodedRow(unsigned frameIndex,
// beyond the first, or the initial passes will "show through" the
// later ones.
if (writeTransparentPixels)
- RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0);
+ buffer.setRGBA(x, y, 0, 0, 0, 0);
}
- currDst++;
- currentRowByte++;
}
- if (repeatCount > 1) {
- // Copy the row |repeatCount|-1 times.
- unsigned num = currDst - dst;
- unsigned data_size = num * sizeof(unsigned);
- unsigned width = size().width();
- unsigned* end = buffer.bytes().data() + width * size().height();
- currDst = dst + width;
- for (unsigned i = 1; i < repeatCount; i++) {
- if (currDst + num > end) // Protect against a buffer overrun from a bogus repeatCount.
- break;
- memcpy(currDst, dst, data_size);
- currDst += width;
- }
- }
-
- // Our partial height is rowNumber + 1, e.g., row 2 is the 3rd row, so that's a height of 3.
- // Adding in repeatCount - 1 to rowNumber + 1 works out to just be rowNumber + repeatCount.
- buffer.ensureHeight(rowNumber + repeatCount);
+ // Tell the frame to copy the row data if need be.
+ if (repeatCount > 1)
+ buffer.copyRowNTimes(m_reader->frameXOffset(), x, y, std::min(y + static_cast<int>(repeatCount), size().height()));
}
void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod)
@@ -426,10 +393,9 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration,
// Initialize the frame if necessary. Some GIFs insert do-nothing frames,
// in which case we never reach haveDecodedRow() before getting here.
RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
- if (buffer.status() == RGBA32Buffer::FrameEmpty)
- initFrameBuffer(frameIndex);
+ if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex))
+ return;
- buffer.ensureHeight(m_size.height());
buffer.setStatus(RGBA32Buffer::FrameComplete);
buffer.setDuration(frameDuration);
buffer.setDisposalMethod(disposalMethod);
@@ -437,7 +403,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration,
if (!m_currentBufferSawAlpha) {
// The whole frame was non-transparent, so it's possible that the entire
// resulting buffer was non-transparent, and we can setHasAlpha(false).
- if (buffer.rect().contains(IntRect(IntPoint(0, 0), size())))
+ if (buffer.rect().contains(IntRect(IntPoint(), size())))
buffer.setHasAlpha(false);
else if (frameIndex > 0) {
// Tricky case. This frame does not have alpha only if everywhere
diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h
index abb55a4..5227ea3 100644
--- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h
+++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h
@@ -44,11 +44,11 @@ namespace WebCore {
virtual void setData(SharedBuffer* data, bool allDataReceived);
// Whether or not the size information has been decoded yet.
- virtual bool isSizeAvailable() const;
+ virtual bool isSizeAvailable();
// The total number of frames for the image. Will scan the image data for the answer
// (without necessarily decoding all of the individual frames).
- virtual int frameCount();
+ virtual size_t frameCount();
// The number of repetitions to perform for an animation loop.
virtual int repetitionCount() const;
@@ -61,10 +61,10 @@ namespace WebCore {
enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery };
- void decode(GIFQuery, unsigned haltAtFrame) const;
+ void decode(GIFQuery, unsigned haltAtFrame);
// Callbacks from the GIF reader.
- void sizeNowAvailable(unsigned width, unsigned height);
+ bool sizeNowAvailable(unsigned width, unsigned height);
void decodingHalted(unsigned bytesLeft);
void haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber,
unsigned repeatCount, bool writeTransparentPixels);
@@ -73,17 +73,14 @@ namespace WebCore {
private:
// Called to initialize the frame buffer with the given index, based on the
- // previous frame's disposal method.
- void initFrameBuffer(unsigned frameIndex);
-
- // A helper for initFrameBuffer(), this sets the size of the buffer, and
- // fills it with transparent pixels.
- void prepEmptyFrameBuffer(RGBA32Buffer*) const;
+ // previous frame's disposal method. Returns true on success. On failure,
+ // this will mark the image as failed.
+ bool initFrameBuffer(unsigned frameIndex);
bool m_frameCountValid;
bool m_currentBufferSawAlpha;
mutable int m_repetitionCount;
- mutable GIFImageDecoderPrivate* m_reader;
+ GIFImageDecoderPrivate* m_reader;
};
} // namespace WebCore
diff --git a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp
index 95ab40d..002f67a 100644
--- a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp
+++ b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp
@@ -311,9 +311,13 @@ int GIFImageReader::do_lzw(const unsigned char *q)
while (code >= clear_code)
{
- if (code == prefix[code])
+ if (code >= MAX_BITS || code == prefix[code])
return -1;
+ // Even though suffix[] only holds characters through suffix[avail - 1],
+ // allowing code >= avail here lets us be more tolerant of malformed
+ // data. As long as code < MAX_BITS, the only risk is a garbled image,
+ // which is no worse than refusing to display it.
*stackp++ = suffix[code];
code = prefix[code];
@@ -445,7 +449,10 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len,
{
/* Initialize LZW parser/decoder */
int datasize = *q;
- if (datasize > MAX_LZW_BITS) {
+ // Since we use a codesize of 1 more than the datasize, we need to ensure
+ // that our datasize is strictly less than the MAX_LZW_BITS value (12).
+ // This sets the largest possible codemask correctly at 4095.
+ if (datasize >= MAX_LZW_BITS) {
state = gif_error;
break;
}
@@ -468,6 +475,8 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len,
/* init the tables */
if (!frame_reader->suffix)
frame_reader->suffix = new unsigned char[MAX_BITS];
+ // Clearing the whole suffix table lets us be more tolerant of bad data.
+ memset(frame_reader->suffix, 0, MAX_BITS);
for (int i = 0; i < frame_reader->clear_code; i++)
frame_reader->suffix[i] = i;
@@ -508,8 +517,8 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len,
screen_height = GETINT16(q + 2);
// CALLBACK: Inform the decoderplugin of our size.
- if (clientptr)
- clientptr->sizeNowAvailable(screen_width, screen_height);
+ if (clientptr && !clientptr->sizeNowAvailable(screen_width, screen_height))
+ return false;
screen_bgcolor = q[5];
global_colormap_size = 2<<(q[4]&0x07);
@@ -734,8 +743,8 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len,
y_offset = 0;
// CALLBACK: Inform the decoderplugin of our size.
- if (clientptr)
- clientptr->sizeNowAvailable(screen_width, screen_height);
+ if (clientptr && !clientptr->sizeNowAvailable(screen_width, screen_height))
+ return false;
}
/* Work around more broken GIF files that have zero image