diff options
author | Ben Murdoch <benm@google.com> | 2009-08-11 17:01:47 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2009-08-11 18:21:02 +0100 |
commit | 0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch) | |
tree | 2943df35f62d885c89d01063cc528dd73b480fea /WebCore/platform/image-decoders/gif | |
parent | 7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff) | |
download | external_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')
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 |