| /* |
| * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #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; |
| } |
| |
| // Take the data and store it. |
| void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) |
| { |
| if (m_failed) |
| return; |
| |
| // Cache our new data. |
| ImageDecoder::setData(data, allDataReceived); |
| |
| // Our frame count is now unknown. |
| m_frameCountValid = false; |
| |
| // Create the GIF reader. |
| if (!m_reader && !m_failed) |
| m_reader = new GIFImageDecoderPrivate(this); |
| } |
| |
| // Whether or not the size information has been decoded yet. |
| bool GIFImageDecoder::isSizeAvailable() const |
| { |
| // 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); |
| } |
| |
| return m_sizeAvailable; |
| } |
| |
| // 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() |
| { |
| // If the decoder had an earlier error, we will just return what we had decoded |
| // so far. |
| if (!m_frameCountValid) { |
| // FIXME: Scanning all the data has O(n^2) behavior if the data were to come in really |
| // slowly. Might be interesting to try to clone our existing read session to preserve |
| // state, but for now we just crawl all the data. Note that this is no worse than what |
| // ImageIO does on Mac right now (it also crawls all the data again). |
| GIFImageDecoderPrivate reader; |
| reader.decode(m_data->buffer(), GIFFrameCountQuery); |
| m_frameCountValid = true; |
| m_frameBufferCache.resize(reader.frameCount()); |
| } |
| |
| return m_frameBufferCache.size(); |
| } |
| |
| // The number of repetitions to perform for an animation loop. |
| int GIFImageDecoder::repetitionCount() const |
| { |
| // We don't have to do any decoding to determine this, since the loop count was determined after |
| // the initial query for size. |
| 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 this frame. |
| decode(GIFFullQuery, index+1); |
| return &frame; |
| } |
| |
| // Feed data to the GIF reader. |
| 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; |
| } |
| } |
| |
| // Callbacks from the GIF reader. |
| 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) |
| { |
| // Initialize the frame rect in our buffer. |
| IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(), |
| m_reader->frameWidth(), m_reader->frameHeight()); |
| |
| // Make sure the frameRect doesn't extend past the bottom-right of the buffer. |
| 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) { |
| // This is the first frame, so we're not relying on any previous data. |
| prepEmptyFrameBuffer(buffer); |
| } else { |
| // The starting state for this frame depends on the previous frame's |
| // disposal method. |
| // |
| // Frames that use the DisposeOverwritePrevious method are effectively |
| // no-ops in terms of changing the starting state of a frame compared to |
| // the starting state of the previous frame, so skip over them. (If the |
| // first frame specifies this method, it will get treated like |
| // DisposeOverwriteBgcolor below and reset to a completely empty image.) |
| 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)) { |
| // Preserve the last frame as the starting state for this frame. |
| buffer->bytes() = prevBuffer->bytes(); |
| buffer->setHasAlpha(prevBuffer->hasAlpha()); |
| } 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), m_size))) { |
| // Clearing the first frame, or a frame the size of the whole |
| // image, results in a completely empty image. |
| prepEmptyFrameBuffer(buffer); |
| } else { |
| // Copy the whole previous buffer, then clear just its frame. |
| 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); |
| } |
| } |
| } |
| |
| // Update our status to be partially complete. |
| buffer->setStatus(RGBA32Buffer::FramePartial); |
| |
| // Reset the alpha pixel tracker for this frame. |
| 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, // Pointer to single scanline temporary buffer |
| unsigned char* rowEnd, |
| unsigned rowNumber, // The row index |
| unsigned repeatCount, // How many times to repeat the row |
| 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) >= m_size.height()) |
| return; |
| |
| 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) * 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; |
| // We may or may not need to write transparent pixels to the buffer. |
| // If we're compositing against a previous image, it's wrong, and if |
| // we're writing atop a cleared, fully transparent buffer, it's |
| // unnecessary; but if we're decoding an interlaced gif and |
| // displaying it "Haeberli"-style, we must write these for passes |
| // beyond the first, or the initial passes will "show through" the |
| // later ones. |
| if (writeTransparentPixels) |
| RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0); |
| } |
| currDst++; |
| currentRowByte++; |
| } |
| |
| if (repeatCount > 1) { |
| // Copy the row |repeatCount|-1 times. |
| 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) // Protect against a buffer overrun from a bogus repeatCount. |
| break; |
| memcpy(currDst, dst, 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); |
| } |
| |
| 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) { |
| // 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), m_size))) { |
| buffer.setHasAlpha(false); |
| } else if (frameIndex > 0) { |
| // Tricky case. This frame does not have alpha only if everywhere |
| // outside its rect doesn't have alpha. To know whether this is |
| // true, we check the start state of the frame -- if it doesn't have |
| // alpha, we're safe. |
| // |
| // First skip over prior DisposeOverwritePrevious frames (since they |
| // don't affect the start state of this frame) the same way we do in |
| // initFrameBuffer(). |
| const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; |
| while ((frameIndex > 0) && |
| (prevBuffer->disposalMethod() == |
| RGBA32Buffer::DisposeOverwritePrevious)) |
| prevBuffer = &m_frameBufferCache[--frameIndex]; |
| |
| // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then |
| // we can say we have no alpha if that frame had no alpha. But |
| // since in initFrameBuffer() we already copied that frame's alpha |
| // state into the current frame's, we need do nothing at all here. |
| // |
| // The only remaining case is a DisposeOverwriteBgcolor frame. If |
| // it had no alpha, and its rect is contained in the current frame's |
| // rect, we know the current frame has no alpha. |
| 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) |