2009-01-23 Darin Fisher <darin@chromium.org>
authordarin@chromium.org <darin@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Jan 2009 18:53:20 +0000 (18:53 +0000)
committerdarin@chromium.org <darin@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Jan 2009 18:53:20 +0000 (18:53 +0000)
        Reviewed by Eric Seidel.

        https://bugs.webkit.org/show_bug.cgi?id=23506
        Copy existing image-decoders in preparation for landing Skia changes on top.

        * platform/image-decoders/skia: Added.
        * platform/image-decoders/skia/GIFImageDecoder.cpp: Copied from platform/image-decoders/gif/GIFImageDecoder.cpp.
        * platform/image-decoders/skia/GIFImageDecoder.h: Copied from platform/image-decoders/gif/GIFImageDecoder.h.
        * platform/image-decoders/skia/GIFImageReader.cpp: Copied from platform/image-decoders/gif/GIFImageReader.cpp.
        * platform/image-decoders/skia/GIFImageReader.h: Copied from platform/image-decoders/gif/GIFImageReader.h.
        * platform/image-decoders/skia/JPEGImageDecoder.cpp: Copied from platform/image-decoders/jpeg/JPEGImageDecoder.cpp.
        * platform/image-decoders/skia/JPEGImageDecoder.h: Copied from platform/image-decoders/jpeg/JPEGImageDecoder.h.
        * platform/image-decoders/skia/PNGImageDecoder.cpp: Copied from platform/image-decoders/png/PNGImageDecoder.cpp.
        * platform/image-decoders/skia/PNGImageDecoder.h: Copied from platform/image-decoders/png/PNGImageDecoder.h.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@40165 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp [new file with mode: 0644]
WebCore/platform/image-decoders/skia/GIFImageDecoder.h [new file with mode: 0644]
WebCore/platform/image-decoders/skia/GIFImageReader.cpp [new file with mode: 0644]
WebCore/platform/image-decoders/skia/GIFImageReader.h [new file with mode: 0644]
WebCore/platform/image-decoders/skia/JPEGImageDecoder.cpp [new file with mode: 0644]
WebCore/platform/image-decoders/skia/JPEGImageDecoder.h [new file with mode: 0644]
WebCore/platform/image-decoders/skia/PNGImageDecoder.cpp [new file with mode: 0644]
WebCore/platform/image-decoders/skia/PNGImageDecoder.h [new file with mode: 0644]

index cdb1597..e018eab 100644 (file)
@@ -1,3 +1,20 @@
+2009-01-23  Darin Fisher  <darin@chromium.org>
+
+        Reviewed by Eric Seidel.
+
+        https://bugs.webkit.org/show_bug.cgi?id=23506
+        Copy existing image-decoders in preparation for landing Skia changes on top.
+
+        * platform/image-decoders/skia: Added.
+        * platform/image-decoders/skia/GIFImageDecoder.cpp: Copied from platform/image-decoders/gif/GIFImageDecoder.cpp.
+        * platform/image-decoders/skia/GIFImageDecoder.h: Copied from platform/image-decoders/gif/GIFImageDecoder.h.
+        * platform/image-decoders/skia/GIFImageReader.cpp: Copied from platform/image-decoders/gif/GIFImageReader.cpp.
+        * platform/image-decoders/skia/GIFImageReader.h: Copied from platform/image-decoders/gif/GIFImageReader.h.
+        * platform/image-decoders/skia/JPEGImageDecoder.cpp: Copied from platform/image-decoders/jpeg/JPEGImageDecoder.cpp.
+        * platform/image-decoders/skia/JPEGImageDecoder.h: Copied from platform/image-decoders/jpeg/JPEGImageDecoder.h.
+        * platform/image-decoders/skia/PNGImageDecoder.cpp: Copied from platform/image-decoders/png/PNGImageDecoder.cpp.
+        * platform/image-decoders/skia/PNGImageDecoder.h: Copied from platform/image-decoders/png/PNGImageDecoder.h.
+
 2009-01-23  Holger Freyther  <zecke@selfish.org>
 
         Unreviewed build fix.
diff --git a/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp
new file mode 100644 (file)
index 0000000..843e65a
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * 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 "config.h"
+#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_repetitionCount(cAnimationLoopOnce), 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
+{
+    // This value can arrive at any point in the image data stream.  Most GIFs
+    // in the wild declare it near the beginning of the file, so it usually is
+    // set by the time we've decoded the size, but (depending on the GIF and the
+    // packets sent back by the webserver) not always.  Our caller is
+    // responsible for waiting until image decoding has finished to ask this if
+    // it needs an authoritative answer.  In the meantime, we should default to
+    // "loop once".
+    if (m_reader) {
+        // Added wrinkle: ImageSource::clear() may destroy the reader, making
+        // the result from the reader _less_ authoritative on future calls.  To
+        // detect this, the reader returns cLoopCountNotSeen (-2) instead of
+        // cAnimationLoopOnce (-1) when its current incarnation hasn't actually
+        // seen a loop count yet; in this case we return our previously-cached
+        // value.
+        const int repetitionCount = m_reader->repetitionCount();
+        if (repetitionCount != cLoopCountNotSeen)
+            m_repetitionCount = repetitionCount;
+    }
+    return m_repetitionCount;
+}
+
+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;
+}
+
+void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame)
+{
+    // In some cases, like if the decoder was destroyed while animating, we
+    // can be asked to clear more frames than we currently have.
+    if (m_frameBufferCache.isEmpty())
+        return;  // Nothing to do.
+    // The "-1" here is tricky.  It does not mean that |clearBeforeFrame| is the
+    // last frame we wish to preserve, but rather that we never want to clear
+    // the very last frame in the cache: it's empty (so clearing it is
+    // pointless), it's partial (so we don't want to clear it anyway), or the
+    // cache could be enlarged with a future setData() call and it could be
+    // needed to construct the next frame (see comments below).  Callers can
+    // always use ImageSource::clear(true, ...) to completely free the memory in
+    // this case.
+    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;  // Nothing to do.
+
+        // The layout of frames is:
+        // [empty frames][complete frames][partial frame][empty frames]
+        // ...where each of these groups may be empty.  We should not clear a
+        // partial frame since that's what's being decoded right now, and we
+        // also should not clear the last complete frame, since it may be needed
+        // when constructing the next frame.  Note that "i + 1" is safe since
+        // i < end < m_frameBufferCache.end().
+        if ((i->status() == RGBA32Buffer::FramePartial) || ((i + 1)->status() != RGBA32Buffer::FrameComplete))
+            break;
+
+        i->clear();
+    }
+}
+
+// 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];
+        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)) {
+            // 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)
+{
+    // 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);
+
+    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()
+{
+    if (m_reader)
+        m_repetitionCount = m_reader->repetitionCount();
+    delete m_reader;
+    m_reader = 0;
+}
+
+}
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/image-decoders/skia/GIFImageDecoder.h b/WebCore/platform/image-decoders/skia/GIFImageDecoder.h
new file mode 100644 (file)
index 0000000..02b43a2
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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. 
+ */
+
+#ifndef GIF_DECODER_H_
+#define GIF_DECODER_H_
+
+#include "ImageDecoder.h"
+
+namespace WebCore {
+
+class GIFImageDecoderPrivate;
+
+// This class decodes the GIF image format.
+class GIFImageDecoder : public ImageDecoder
+{
+public:
+    GIFImageDecoder();
+    ~GIFImageDecoder();
+
+    virtual String filenameExtension() const { return "gif"; }
+
+    // Take the data and store it.
+    virtual void setData(SharedBuffer* data, bool allDataReceived);
+
+    // Whether or not the size information has been decoded yet.
+    virtual bool isSizeAvailable() const;
+
+    // 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();
+
+    // The number of repetitions to perform for an animation loop.
+    virtual int repetitionCount() const;
+
+    virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+
+    virtual void clearFrameBufferCache(size_t clearBeforeFrame);
+
+    virtual unsigned frameDurationAtIndex(size_t index) { return 0; }
+
+    enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery };
+
+    void decode(GIFQuery query, unsigned haltAtFrame) const;
+
+    // Callbacks from the GIF reader.
+    void 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);
+    void frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod);
+    void gifComplete();
+
+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* buffer) const;
+
+    bool m_frameCountValid;
+    bool m_currentBufferSawAlpha;
+    mutable int m_repetitionCount;
+    mutable GIFImageDecoderPrivate* m_reader;
+};
+
+}
+
+#endif
diff --git a/WebCore/platform/image-decoders/skia/GIFImageReader.cpp b/WebCore/platform/image-decoders/skia/GIFImageReader.cpp
new file mode 100644 (file)
index 0000000..04347af
--- /dev/null
@@ -0,0 +1,943 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Saari <saari@netscape.com>
+ *   Apple Computer
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+The Graphics Interchange Format(c) is the copyright property of CompuServe
+Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
+enhance, alter, modify or change in any way the definition of the format.
+
+CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
+license for the use of the Graphics Interchange Format(sm) in computer
+software; computer software utilizing GIF(sm) must acknowledge ownership of the
+Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
+User and Technical Documentation. Computer software utilizing GIF, which is
+distributed or may be distributed without User or Technical Documentation must
+display to the screen or printer a message acknowledging ownership of the
+Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
+this case, the acknowledgement may be displayed in an opening screen or leading
+banner, or a closing screen or trailing banner. A message such as the following
+may be used:
+
+    "The Graphics Interchange Format(c) is the Copyright property of
+    CompuServe Incorporated. GIF(sm) is a Service Mark property of
+    CompuServe Incorporated."
+
+For further information, please contact :
+
+    CompuServe Incorporated
+    Graphics Technology Department
+    5000 Arlington Center Boulevard
+    Columbus, Ohio  43220
+    U. S. A.
+
+CompuServe Incorporated maintains a mailing list with all those individuals and
+organizations who wish to receive copies of this document when it is corrected
+or revised. This service is offered free of charge; please provide us with your
+mailing address.
+*/
+
+#include "config.h"
+#include "GIFImageReader.h"
+
+#include <string.h>
+#include "GIFImageDecoder.h"
+
+#if PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX)
+
+using WebCore::GIFImageDecoder;
+
+// Define the Mozilla macro setup so that we can leave the macros alone.
+#define PR_BEGIN_MACRO  do {
+#define PR_END_MACRO    } while (0)
+
+/*
+ * GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's'
+ *
+ * Note, the hold will never need to be bigger than 256 bytes to gather up in the hold,
+ * as each GIF block (except colormaps) can never be bigger than 256 bytes.
+ * Colormaps are directly copied in the resp. global_colormap or dynamically allocated local_colormap.
+ * So a fixed buffer in GIFImageReader is good enough.
+ * This buffer is only needed to copy left-over data from one GifWrite call to the next
+ */
+#define GETN(n,s)                    \
+  PR_BEGIN_MACRO                     \
+    bytes_to_consume = (n);      \
+    state = (s);                 \
+  PR_END_MACRO
+
+/* Get a 16-bit value stored in little-endian format */
+#define GETINT16(p)   ((p)[1]<<8|(p)[0])
+
+//******************************************************************************
+// Send the data to the display front-end.
+void GIFImageReader::output_row()
+{
+  GIFFrameReader* gs = frame_reader;
+
+  int drow_start, drow_end;
+
+  drow_start = drow_end = gs->irow;
+
+  /*
+   * Haeberli-inspired hack for interlaced GIFs: Replicate lines while
+   * displaying to diminish the "venetian-blind" effect as the image is
+   * loaded. Adjust pixel vertical positions to avoid the appearance of the
+   * image crawling up the screen as successive passes are drawn.
+   */
+  if (gs->progressive_display && gs->interlaced && gs->ipass < 4) {
+    unsigned row_dup = 0, row_shift = 0;
+
+    switch (gs->ipass) {
+    case 1:
+      row_dup = 7;
+      row_shift = 3;
+      break;
+    case 2:
+      row_dup = 3;
+      row_shift = 1;
+      break;
+    case 3:
+      row_dup = 1;
+      row_shift = 0;
+      break;
+    default:
+      break;
+    }
+
+    drow_start -= row_shift;
+    drow_end = drow_start + row_dup;
+
+    /* Extend if bottom edge isn't covered because of the shift upward. */
+    if (((gs->height - 1) - drow_end) <= row_shift)
+      drow_end = gs->height - 1;
+
+    /* Clamp first and last rows to upper and lower edge of image. */
+    if (drow_start < 0)
+      drow_start = 0;
+    if ((unsigned)drow_end >= gs->height)
+      drow_end = gs->height - 1;
+  }
+
+  /* Protect against too much image data */
+  if ((unsigned)drow_start >= gs->height)
+    return;
+
+  // CALLBACK: Let the client know we have decoded a row.
+  if (clientptr && frame_reader)
+    clientptr->haveDecodedRow(images_count - 1, frame_reader->rowbuf, frame_reader->rowend,
+                              drow_start, drow_end - drow_start + 1,
+                              gs->progressive_display && gs->interlaced && gs->ipass > 1);
+
+  gs->rowp = gs->rowbuf;
+
+  if (!gs->interlaced)
+    gs->irow++;
+  else {
+    do {
+      switch (gs->ipass)
+      {
+        case 1:
+          gs->irow += 8;
+          if (gs->irow >= gs->height) {
+            gs->ipass++;
+            gs->irow = 4;
+          }
+          break;
+
+        case 2:
+          gs->irow += 8;
+          if (gs->irow >= gs->height) {
+            gs->ipass++;
+            gs->irow = 2;
+          }
+          break;
+
+        case 3:
+          gs->irow += 4;
+          if (gs->irow >= gs->height) {
+            gs->ipass++;
+            gs->irow = 1;
+          }
+          break;
+
+        case 4:
+          gs->irow += 2;
+          if (gs->irow >= gs->height){
+            gs->ipass++;
+            gs->irow = 0;
+          }
+          break;
+
+        default:
+          break;
+      }
+    } while (gs->irow > (gs->height - 1));
+  }
+}
+
+//******************************************************************************
+/* Perform Lempel-Ziv-Welch decoding */
+int GIFImageReader::do_lzw(const unsigned char *q)
+{
+  GIFFrameReader* gs = frame_reader;
+  if (!gs)
+    return 0;
+
+  int code;
+  int incode;
+  const unsigned char *ch;
+  
+  /* Copy all the decoder state variables into locals so the compiler
+   * won't worry about them being aliased.  The locals will be homed
+   * back into the GIF decoder structure when we exit.
+   */
+  int avail       = gs->avail;
+  int bits        = gs->bits;
+  int cnt         = count;
+  int codesize    = gs->codesize;
+  int codemask    = gs->codemask;
+  int oldcode     = gs->oldcode;
+  int clear_code  = gs->clear_code;
+  unsigned char firstchar = gs->firstchar;
+  int datum     = gs->datum;
+
+  if (!gs->prefix) {
+    gs->prefix = new unsigned short[MAX_BITS];
+    memset(gs->prefix, 0, MAX_BITS * sizeof(short));
+  }
+
+  unsigned short *prefix  = gs->prefix;
+  unsigned char *stackp   = gs->stackp;
+  unsigned char *suffix   = gs->suffix;
+  unsigned char *stack    = gs->stack;
+  unsigned char *rowp     = gs->rowp;
+  unsigned char *rowend   = gs->rowend;
+  unsigned rows_remaining = gs->rows_remaining;
+
+  if (rowp == rowend)
+    return 0;
+
+#define OUTPUT_ROW                                                  \
+  PR_BEGIN_MACRO                                                        \
+    output_row();                                                     \
+    rows_remaining--;                                                   \
+    rowp = frame_reader->rowp;                                                    \
+    if (!rows_remaining)                                                \
+      goto END;                                                         \
+  PR_END_MACRO
+
+  for (ch = q; cnt-- > 0; ch++)
+  {
+    /* Feed the next byte into the decoder's 32-bit input buffer. */
+    datum += ((int) *ch) << bits;
+    bits += 8;
+
+    /* Check for underflow of decoder's 32-bit input buffer. */
+    while (bits >= codesize)
+    {
+      /* Get the leading variable-length symbol from the data stream */
+      code = datum & codemask;
+      datum >>= codesize;
+      bits -= codesize;
+
+      /* Reset the dictionary to its original state, if requested */
+      if (code == clear_code) {
+        codesize = gs->datasize + 1;
+        codemask = (1 << codesize) - 1;
+        avail = clear_code + 2;
+        oldcode = -1;
+        continue;
+      }
+
+      /* Check for explicit end-of-stream code */
+      if (code == (clear_code + 1)) {
+        /* end-of-stream should only appear after all image data */
+        if (rows_remaining != 0)
+          return -1;
+        return 0;
+      }
+
+      if (oldcode == -1) {
+        *rowp++ = suffix[code];
+        if (rowp == rowend)
+          OUTPUT_ROW;
+
+        firstchar = oldcode = code;
+        continue;
+      }
+
+      incode = code;
+      if (code >= avail) {
+        *stackp++ = firstchar;
+        code = oldcode;
+
+        if (stackp == stack + MAX_BITS)
+          return -1;
+      }
+
+      while (code >= clear_code)
+      {
+        if (code == prefix[code])
+          return -1;
+
+        *stackp++ = suffix[code];
+        code = prefix[code];
+
+        if (stackp == stack + MAX_BITS)
+          return -1;
+      }
+
+      *stackp++ = firstchar = suffix[code];
+
+      /* Define a new codeword in the dictionary. */
+      if (avail < 4096) {
+        prefix[avail] = oldcode;
+        suffix[avail] = firstchar;
+        avail++;
+
+        /* If we've used up all the codewords of a given length
+         * increase the length of codewords by one bit, but don't
+         * exceed the specified maximum codeword size of 12 bits.
+         */
+        if (((avail & codemask) == 0) && (avail < 4096)) {
+          codesize++;
+          codemask += avail;
+        }
+      }
+      oldcode = incode;
+
+        /* Copy the decoded data out to the scanline buffer. */
+      do {
+        *rowp++ = *--stackp;
+        if (rowp == rowend) {
+          OUTPUT_ROW;
+        }
+      } while (stackp > stack);
+    }
+  }
+
+  END:
+
+  /* Home the local copies of the GIF decoder state variables */
+  gs->avail = avail;
+  gs->bits = bits;
+  gs->codesize = codesize;
+  gs->codemask = codemask;
+  count = cnt;
+  gs->oldcode = oldcode;
+  gs->firstchar = firstchar;
+  gs->datum = datum;
+  gs->stackp = stackp;
+  gs->rowp = rowp;
+  gs->rows_remaining = rows_remaining;
+
+  return 0;
+}
+
+
+/******************************************************************************/
+/*
+ * process data arriving from the stream for the gif decoder
+ */
+
+bool GIFImageReader::read(const unsigned char *buf, unsigned len, 
+                     GIFImageDecoder::GIFQuery query, unsigned haltAtFrame)
+{
+  if (!len) {
+    // No new data has come in since the last call, just ignore this call.
+    return true;
+  }
+
+  const unsigned char *q = buf;
+
+  // Add what we have so far to the block
+  // If previous call to me left something in the hold first complete current block
+  // Or if we are filling the colormaps, first complete the colormap
+  unsigned char* p = 0;
+  if (state == gif_global_colormap)
+    p = global_colormap;
+  else if (state == gif_image_colormap)
+    p = frame_reader ? frame_reader->local_colormap : 0;
+  else if (bytes_in_hold)
+    p = hold;
+  else
+    p = 0;
+
+  if (p || (state == gif_global_colormap) || (state == gif_image_colormap)) {
+    // Add what we have sofar to the block
+    unsigned l = len < bytes_to_consume ? len : bytes_to_consume;
+    if (p)
+        memcpy(p + bytes_in_hold, buf, l);
+
+    if (l < bytes_to_consume) {
+      // Not enough in 'buf' to complete current block, get more
+      bytes_in_hold += l;
+      bytes_to_consume -= l;
+      if (clientptr)
+        clientptr->decodingHalted(0);
+      return true;
+    }
+    // Reset hold buffer count
+    bytes_in_hold = 0;
+    // Point 'q' to complete block in hold (or in colormap)
+    q = p;
+  }
+
+  // Invariant:
+  //    'q' is start of current to be processed block (hold, colormap or buf)
+  //    'bytes_to_consume' is number of bytes to consume from 'buf'
+  //    'buf' points to the bytes to be consumed from the input buffer
+  //    'len' is number of bytes left in input buffer from position 'buf'.
+  //    At entrance of the for loop will 'buf' will be moved 'bytes_to_consume'
+  //    to point to next buffer, 'len' is adjusted accordingly.
+  //    So that next round in for loop, q gets pointed to the next buffer.
+
+  for (;len >= bytes_to_consume; q=buf) {
+    // Eat the current block from the buffer, q keeps pointed at current block
+    buf += bytes_to_consume;
+    len -= bytes_to_consume;
+
+    switch (state)
+    {
+    case gif_lzw:
+      if (do_lzw(q) < 0) {
+        state = gif_error;
+        break;
+      }
+      GETN(1, gif_sub_block);
+      break;
+
+    case gif_lzw_start:
+    {
+      /* Initialize LZW parser/decoder */
+      int datasize = *q;
+      if (datasize > MAX_LZW_BITS) {
+        state = gif_error;
+        break;
+      }
+      int clear_code = 1 << datasize;
+      if (clear_code >= MAX_BITS) {
+        state = gif_error;
+        break;
+      }
+
+      if (frame_reader) {
+        frame_reader->datasize = datasize;
+        frame_reader->clear_code = clear_code;
+        frame_reader->avail = frame_reader->clear_code + 2;
+        frame_reader->oldcode = -1;
+        frame_reader->codesize = frame_reader->datasize + 1;
+        frame_reader->codemask = (1 << frame_reader->codesize) - 1;
+
+        frame_reader->datum = frame_reader->bits = 0;
+
+        /* init the tables */
+        if (!frame_reader->suffix)
+          frame_reader->suffix = new unsigned char[MAX_BITS];
+        for (int i = 0; i < frame_reader->clear_code; i++)
+          frame_reader->suffix[i] = i;
+
+        if (!frame_reader->stack)
+          frame_reader->stack = new unsigned char[MAX_BITS];
+        frame_reader->stackp = frame_reader->stack;
+      }
+
+      GETN(1, gif_sub_block);
+    }
+    break;
+
+    /* All GIF files begin with "GIF87a" or "GIF89a" */
+    case gif_type:
+    {
+      if (!strncmp((char*)q, "GIF89a", 6)) {
+        version = 89;
+      } else if (!strncmp((char*)q, "GIF87a", 6)) {
+        version = 87;
+      } else {
+        state = gif_error;
+        break;
+      }
+      GETN(7, gif_global_header);
+    }
+    break;
+
+    case gif_global_header:
+    {
+      /* This is the height and width of the "screen" or
+       * frame into which images are rendered.  The
+       * individual images can be smaller than the
+       * screen size and located with an origin anywhere
+       * within the screen.
+       */
+
+      screen_width = GETINT16(q);
+      screen_height = GETINT16(q + 2);
+
+      // CALLBACK: Inform the decoderplugin of our size.
+      if (clientptr)
+        clientptr->sizeNowAvailable(screen_width, screen_height);
+      
+      screen_bgcolor = q[5];
+      global_colormap_size = 2<<(q[4]&0x07);
+
+      if ((q[4] & 0x80) && global_colormap_size > 0) { /* global map */
+        // Get the global colormap
+        const unsigned size = 3*global_colormap_size;
+        
+        // Malloc the color map, but only if we're not just counting frames.
+        if (query != GIFImageDecoder::GIFFrameCountQuery)
+          global_colormap = new unsigned char[size];
+
+        if (len < size) {
+          // Use 'hold' pattern to get the global colormap
+          GETN(size, gif_global_colormap);
+          break;
+        }
+        
+        // Copy everything and go directly to gif_image_start.
+        if (global_colormap)
+            memcpy(global_colormap, buf, size);
+        buf += size;
+        len -= size;
+      }
+
+      GETN(1, gif_image_start);
+
+      // q[6] = Pixel Aspect Ratio
+      //   Not used
+      //   float aspect = (float)((q[6] + 15) / 64.0);
+    }
+    break;
+
+    case gif_global_colormap:
+      // Everything is already copied into global_colormap
+      GETN(1, gif_image_start);
+    break;
+
+    case gif_image_start:
+    {
+      if (*q == ';') { /* terminator */
+        state = gif_done;
+        break;
+      }
+
+      if (*q == '!') { /* extension */
+        GETN(2, gif_extension);
+        break;
+      }
+
+      /* If we get anything other than ',' (image separator), '!'
+       * (extension), or ';' (trailer), there is extraneous data
+       * between blocks. The GIF87a spec tells us to keep reading
+       * until we find an image separator, but GIF89a says such
+       * a file is corrupt. We follow GIF89a and bail out. */
+      if (*q != ',') {
+        if (images_decoded > 0) {
+          /* The file is corrupt, but one or more images have
+           * been decoded correctly. In this case, we proceed
+           * as if the file were correctly terminated and set
+           * the state to gif_done, so the GIF will display.
+           */
+          state = gif_done;
+        } else {
+          /* No images decoded, there is nothing to display. */
+          state = gif_error;
+        }
+        break;
+      } else
+        GETN(9, gif_image_header);
+    }
+    break;
+
+    case gif_extension:
+    {
+      int len = count = q[1];
+      gstate es = gif_skip_block;
+
+      switch (*q)
+      {
+      case 0xf9:
+        es = gif_control_extension;
+        break;
+
+      case 0x01:
+        // ignoring plain text extension
+        break;
+
+      case 0xff:
+        es = gif_application_extension;
+        break;
+
+      case 0xfe:
+        es = gif_consume_comment;
+        break;
+      }
+
+      if (len)
+        GETN(len, es);
+      else
+        GETN(1, gif_image_start);
+    }
+    break;
+
+    case gif_consume_block:
+      if (!*q)
+        GETN(1, gif_image_start);
+      else
+        GETN(*q, gif_skip_block);
+    break;
+
+    case gif_skip_block:
+      GETN(1, gif_consume_block);
+      break;
+
+    case gif_control_extension:
+    {
+      if (query != GIFImageDecoder::GIFFrameCountQuery) {
+          if (!frame_reader)
+            frame_reader = new GIFFrameReader();
+      }
+
+      if (frame_reader) {
+        if (*q & 0x1) {
+          frame_reader->tpixel = q[3];
+          frame_reader->is_transparent = true;
+        } else {
+          frame_reader->is_transparent = false;
+          // ignoring gfx control extension
+        }
+        // NOTE: This relies on the values in the FrameDisposalMethod enum
+        // matching those in the GIF spec!
+        frame_reader->disposal_method = (WebCore::RGBA32Buffer::FrameDisposalMethod)(((*q) >> 2) & 0x7);
+        // Some specs say 3rd bit (value 4), other specs say value 3
+        // Let's choose 3 (the more popular)
+        if (frame_reader->disposal_method == 4)
+          frame_reader->disposal_method = WebCore::RGBA32Buffer::DisposeOverwritePrevious;
+        frame_reader->delay_time = GETINT16(q + 1) * 10;
+      }
+      GETN(1, gif_consume_block);
+    }
+    break;
+
+    case gif_comment_extension:
+    {
+      if (*q)
+        GETN(*q, gif_consume_comment);
+      else
+        GETN(1, gif_image_start);
+    }
+    break;
+
+    case gif_consume_comment:
+      GETN(1, gif_comment_extension);
+    break;
+
+    case gif_application_extension:
+      /* Check for netscape application extension */
+      if (!strncmp((char*)q, "NETSCAPE2.0", 11) ||
+        !strncmp((char*)q, "ANIMEXTS1.0", 11))
+        GETN(1, gif_netscape_extension_block);
+      else
+        GETN(1, gif_consume_block);
+    break;
+
+    /* Netscape-specific GIF extension: animation looping */
+    case gif_netscape_extension_block:
+      if (*q)
+        GETN(*q, gif_consume_netscape_extension);
+      else
+        GETN(1, gif_image_start);
+    break;
+
+    /* Parse netscape-specific application extensions */
+    case gif_consume_netscape_extension:
+    {
+      int netscape_extension = q[0] & 7;
+
+      /* Loop entire animation specified # of times.  Only read the
+         loop count during the first iteration. */
+      if (netscape_extension == 1) {
+        loop_count = GETINT16(q + 1);
+
+        GETN(1, gif_netscape_extension_block);
+      }
+      /* Wait for specified # of bytes to enter buffer */
+      else if (netscape_extension == 2) {
+        // Don't do this, this extension doesn't exist (isn't used at all) 
+        // and doesn't do anything, as our streaming/buffering takes care of it all...
+        // See: http://semmix.pl/color/exgraf/eeg24.htm
+        GETN(1, gif_netscape_extension_block);
+      } else
+        state = gif_error; // 0,3-7 are yet to be defined netscape
+                               // extension codes
+
+      break;
+    }
+
+    case gif_image_header:
+    {
+      unsigned height, width, x_offset, y_offset;
+      
+      /* Get image offsets, with respect to the screen origin */
+      x_offset = GETINT16(q);
+      y_offset = GETINT16(q + 2);
+
+      /* Get image width and height. */
+      width  = GETINT16(q + 4);
+      height = GETINT16(q + 6);
+
+      /* Work around broken GIF files where the logical screen
+       * size has weird width or height.  We assume that GIF87a
+       * files don't contain animations.
+       */
+      if ((images_decoded == 0) &&
+          ((screen_height < height) || (screen_width < width) ||
+           (version == 87)))
+      {
+        screen_height = height;
+        screen_width = width;
+        x_offset = 0;
+        y_offset = 0;
+
+        // CALLBACK: Inform the decoderplugin of our size.
+        if (clientptr)
+          clientptr->sizeNowAvailable(screen_width, screen_height);
+      }
+
+      /* Work around more broken GIF files that have zero image
+         width or height */
+      if (!height || !width) {
+        height = screen_height;
+        width = screen_width;
+        if (!height || !width) {
+          state = gif_error;
+          break;
+        }
+      }
+
+      if (query == GIFImageDecoder::GIFSizeQuery || haltAtFrame == images_decoded) {
+        // The decoder needs to stop.  Hand back the number of bytes we consumed from
+        // buffer minus 9 (the amount we consumed to read the header).
+        if (clientptr)
+            clientptr->decodingHalted(len + 9);
+        GETN(9, gif_image_header);
+        return true;
+      }
+      
+      images_count = images_decoded + 1;
+
+      if (query == GIFImageDecoder::GIFFullQuery && !frame_reader)
+        frame_reader = new GIFFrameReader();
+
+      if (frame_reader) {
+        frame_reader->x_offset = x_offset;
+        frame_reader->y_offset = y_offset;
+        frame_reader->height = height;
+        frame_reader->width = width;
+
+        /* This case will never be taken if this is the first image */
+        /* being decoded. If any of the later images are larger     */
+        /* than the screen size, we need to reallocate buffers.     */
+        if (screen_width < width) {
+          /* XXX Deviant! */
+
+          delete []frame_reader->rowbuf;
+          screen_width = width;
+          frame_reader->rowbuf = new unsigned char[screen_width];
+        } else if (!frame_reader->rowbuf) {
+          frame_reader->rowbuf = new unsigned char[screen_width];
+        }
+
+        if (!frame_reader->rowbuf) {
+          state = gif_oom;
+          break;
+        }
+        if (screen_height < height)
+          screen_height = height;
+
+        if (q[8] & 0x40) {
+          frame_reader->interlaced = true;
+          frame_reader->ipass = 1;
+        } else {
+          frame_reader->interlaced = false;
+          frame_reader->ipass = 0;
+        }
+
+        if (images_decoded == 0) {
+          frame_reader->progressive_display = true;
+        } else {
+          /* Overlaying interlaced, transparent GIFs over
+             existing image data using the Haeberli display hack
+             requires saving the underlying image in order to
+             avoid jaggies at the transparency edges.  We are
+             unprepared to deal with that, so don't display such
+             images progressively */
+          frame_reader->progressive_display = false;
+        }
+
+        /* Clear state from last image */
+        frame_reader->irow = 0;
+        frame_reader->rows_remaining = frame_reader->height;
+        frame_reader->rowend = frame_reader->rowbuf + frame_reader->width;
+        frame_reader->rowp = frame_reader->rowbuf;
+
+        /* bits per pixel is q[8]&0x07 */
+      }
+      
+      if (q[8] & 0x80) /* has a local colormap? */
+      {
+        int num_colors = 2 << (q[8] & 0x7);
+        const unsigned size = 3*num_colors;
+        unsigned char *map = frame_reader ? frame_reader->local_colormap : 0;
+        if (frame_reader && (!map || (num_colors > frame_reader->local_colormap_size))) {
+          delete []map;
+          map = new unsigned char[size];
+          if (!map) {
+            state = gif_oom;
+            break;
+          }
+        }
+
+        /* Switch to the new local palette after it loads */
+        if (frame_reader) {
+          frame_reader->local_colormap = map;
+          frame_reader->local_colormap_size = num_colors;
+          frame_reader->is_local_colormap_defined = true;
+        }
+
+        if (len < size) {
+          // Use 'hold' pattern to get the image colormap
+          GETN(size, gif_image_colormap);
+          break;
+        }
+        // Copy everything and directly go to gif_lzw_start
+        if (frame_reader)
+          memcpy(frame_reader->local_colormap, buf, size);
+        buf += size;
+        len -= size;
+      } else if (frame_reader) {
+        /* Switch back to the global palette */
+        frame_reader->is_local_colormap_defined = false;
+      }
+      GETN(1, gif_lzw_start);
+    }
+    break;
+
+    case gif_image_colormap:
+      // Everything is already copied into local_colormap
+      GETN(1, gif_lzw_start);
+    break;
+
+    case gif_sub_block:
+    {
+      if ((count = *q) != 0)
+      /* Still working on the same image: Process next LZW data block */
+      {
+        /* Make sure there are still rows left. If the GIF data */
+        /* is corrupt, we may not get an explicit terminator.   */
+        if (frame_reader && frame_reader->rows_remaining == 0) {
+          /* This is an illegal GIF, but we remain tolerant. */
+          GETN(1, gif_sub_block);
+        }
+        GETN(count, gif_lzw);
+      }
+      else
+      /* See if there are any more images in this sequence. */
+      {
+        images_decoded++;
+
+        // CALLBACK: The frame is now complete.
+        if (clientptr && frame_reader)
+          clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, 
+                                   frame_reader->disposal_method);
+
+        /* Clear state from this image */
+        if (frame_reader) {
+            frame_reader->is_local_colormap_defined = false;
+            frame_reader->is_transparent = false;
+        }
+
+        GETN(1, gif_image_start);
+      }
+    }
+    break;
+
+    case gif_done:
+      // When the GIF is done, we can stop.
+      if (clientptr)
+        clientptr->gifComplete();
+      return true;
+
+    // Handle out of memory errors
+    case gif_oom:
+      return false;
+
+    // Handle general errors
+    case gif_error:
+      // nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count);
+      return false;
+
+    // We shouldn't ever get here.
+    default:
+      break;
+    }
+  }
+
+  // Copy the leftover into gs->hold
+  bytes_in_hold = len;
+  if (len) {
+    // Add what we have sofar to the block
+    unsigned char* p;
+    if (state == gif_global_colormap)
+      p = global_colormap;
+    else if (state == gif_image_colormap)
+      p = frame_reader ? frame_reader->local_colormap : 0;
+    else
+      p = hold;
+    if (p)
+      memcpy(p, buf, len);
+    bytes_to_consume -= len;
+  }
+
+  if (clientptr)
+    clientptr->decodingHalted(0);
+  return true;
+}
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/image-decoders/skia/GIFImageReader.h b/WebCore/platform/image-decoders/skia/GIFImageReader.h
new file mode 100644 (file)
index 0000000..855e6be
--- /dev/null
@@ -0,0 +1,216 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _GIF_H_
+#define _GIF_H_
+
+// Define ourselves as the clientPtr.  Mozilla just hacked their C++ callback class into this old C decoder,
+// so we will too.
+#include "GIFImageDecoder.h"
+
+#define MAX_LZW_BITS          12
+#define MAX_BITS            4097 /* 2^MAX_LZW_BITS+1 */
+#define MAX_COLORS           256
+#define MAX_HOLD_SIZE        256
+
+const int cLoopCountNotSeen = -2;
+
+/* gif2.h  
+   The interface for the GIF87/89a decoder. 
+*/
+// List of possible parsing states
+typedef enum {
+    gif_type,
+    gif_global_header,
+    gif_global_colormap,
+    gif_image_start,            
+    gif_image_header,
+    gif_image_colormap,
+    gif_image_body,
+    gif_lzw_start,
+    gif_lzw,
+    gif_sub_block,
+    gif_extension,
+    gif_control_extension,
+    gif_consume_block,
+    gif_skip_block,
+    gif_done,
+    gif_oom,
+    gif_error,
+    gif_comment_extension,
+    gif_application_extension,
+    gif_netscape_extension_block,
+    gif_consume_netscape_extension,
+    gif_consume_comment
+} gstate;
+
+struct GIFFrameReader {
+    /* LZW decoder state machine */
+    unsigned char *stackp;              /* Current stack pointer */
+    int datasize;
+    int codesize;
+    int codemask;
+    int clear_code;             /* Codeword used to trigger dictionary reset */
+    int avail;                  /* Index of next available slot in dictionary */
+    int oldcode;
+    unsigned char firstchar;
+    int bits;                   /* Number of unread bits in "datum" */
+    int datum;                /* 32-bit input buffer */
+
+    /* Output state machine */
+    int ipass;                  /* Interlace pass; Ranges 1-4 if interlaced. */
+    unsigned int rows_remaining;        /* Rows remaining to be output */
+    unsigned int irow;                  /* Current output row, starting at zero */
+    unsigned char *rowbuf;              /* Single scanline temporary buffer */
+    unsigned char *rowend;              /* Pointer to end of rowbuf */
+    unsigned char *rowp;                /* Current output pointer */
+
+    /* Parameters for image frame currently being decoded */
+    unsigned int x_offset, y_offset;    /* With respect to "screen" origin */
+    unsigned int height, width;
+    int tpixel;                 /* Index of transparent pixel */
+    WebCore::RGBA32Buffer::FrameDisposalMethod disposal_method;   /* Restore to background, leave in place, etc.*/
+    unsigned char *local_colormap;    /* Per-image colormap */
+    int local_colormap_size;    /* Size of local colormap array. */
+    
+    bool is_local_colormap_defined : 1;
+    bool progressive_display : 1;    /* If TRUE, do Haeberli interlace hack */
+    bool interlaced : 1;             /* TRUE, if scanlines arrive interlaced order */
+    bool is_transparent : 1;         /* TRUE, if tpixel is valid */
+
+    unsigned delay_time;        /* Display time, in milliseconds,
+                                   for this image in a multi-image GIF */
+
+
+    unsigned short*  prefix;          /* LZW decoding tables */
+    unsigned char*   suffix;          /* LZW decoding tables */
+    unsigned char*   stack;           /* Base of LZW decoder stack */
+
+
+    GIFFrameReader() {
+        stackp = 0;
+        datasize = codesize = codemask = clear_code = avail = oldcode = 0;
+        firstchar = 0;
+        bits = datum = 0;
+        ipass = 0;
+        rows_remaining = irow = 0;
+        rowbuf = rowend = rowp = 0;
+
+        x_offset = y_offset = width = height = 0;
+        tpixel = 0;
+        disposal_method = WebCore::RGBA32Buffer::DisposeNotSpecified;
+
+        local_colormap = 0;
+        local_colormap_size = 0;
+        is_local_colormap_defined = progressive_display = is_transparent = interlaced = false;
+
+        delay_time = 0;
+
+        prefix = 0;
+        suffix = stack = 0;
+    }
+    
+    ~GIFFrameReader() {
+        delete []rowbuf;
+        delete []local_colormap;
+        delete []prefix;
+        delete []suffix;
+        delete []stack;
+    }
+};
+
+struct GIFImageReader {
+    WebCore::GIFImageDecoder* clientptr;
+    /* Parsing state machine */
+    gstate state;                      /* Current decoder master state */
+    unsigned bytes_to_consume;         /* Number of bytes to accumulate */
+    unsigned bytes_in_hold;            /* bytes accumulated so far*/
+    unsigned char hold[MAX_HOLD_SIZE]; /* Accumulation buffer */
+    unsigned char* global_colormap;    /* (3* MAX_COLORS in size) Default colormap if local not supplied, 3 bytes for each color  */
+    
+     /* Global (multi-image) state */
+    int screen_bgcolor;         /* Logical screen background color */
+    int version;                /* Either 89 for GIF89 or 87 for GIF87 */
+    unsigned screen_width;       /* Logical screen width & height */
+    unsigned screen_height;
+    int global_colormap_size;   /* Size of global colormap array. */
+    int images_decoded;         /* Counts completed frames for animated GIFs */
+    int images_count;           /* Counted all frames seen so far (including incomplete frames) */
+    int loop_count;             /* Netscape specific extension block to control
+                                   the number of animation loops a GIF renders. */
+    
+    // Not really global, but convenient to locate here.
+    int count;                  /* Remaining # bytes in sub-block */
+    
+    GIFFrameReader* frame_reader;
+
+    GIFImageReader(WebCore::GIFImageDecoder* client = 0) {
+        clientptr = client;
+        state = gif_type;
+        bytes_to_consume = 6;
+        bytes_in_hold = 0;
+        frame_reader = 0;
+        global_colormap = 0;
+
+        screen_bgcolor = version = 0;
+        screen_width = screen_height = 0;
+        global_colormap_size = images_decoded = images_count = 0;
+        loop_count = cLoopCountNotSeen;
+        count = 0;
+    }
+
+    ~GIFImageReader() {
+        close();
+    }
+
+    void close() {
+        delete []global_colormap;
+        global_colormap = 0;
+        delete frame_reader;
+        frame_reader = 0;
+    }
+
+    bool read(const unsigned char * buf, unsigned int numbytes, 
+              WebCore::GIFImageDecoder::GIFQuery query = WebCore::GIFImageDecoder::GIFFullQuery, unsigned haltAtFrame = -1);
+
+private:
+    void output_row();
+    int do_lzw(const unsigned char *q);
+};
+
+#endif
+
diff --git a/WebCore/platform/image-decoders/skia/JPEGImageDecoder.cpp b/WebCore/platform/image-decoders/skia/JPEGImageDecoder.cpp
new file mode 100644 (file)
index 0000000..44e0e4c
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 2001-6 mozilla.org
+ *
+ * Other contributors:
+ *   Stuart Parmenter <stuart@mozilla.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above.  If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "config.h"
+#include "JPEGImageDecoder.h"
+#include <assert.h>
+#include <stdio.h>
+
+#if PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX)
+
+#if COMPILER(MSVC)
+// Remove warnings from warning level 4.
+#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
+
+// if ADDRESS_TAG_BIT is dfined, INT32 has been declared as a typedef in the PlatformSDK (BaseTsd.h),
+// so we need to stop jpeglib.h from trying to #define it 
+// see here for more info: http://www.cygwin.com/ml/cygwin/2004-07/msg01051.html
+# if defined(ADDRESS_TAG_BIT) && !defined(XMD_H)
+#  define XMD_H
+#  define VTK_JPEG_XMD_H
+# endif
+#endif // COMPILER(MSVC)
+
+extern "C" {
+#include "jpeglib.h"
+}
+
+#if COMPILER(MSVC)
+# if defined(VTK_JPEG_XMD_H)
+#  undef VTK_JPEG_XMD_H
+#  undef XMD_H
+# endif
+#endif // COMPILER(MSVC)
+
+#include <setjmp.h>
+
+namespace WebCore {
+
+struct decoder_error_mgr {
+    struct jpeg_error_mgr pub;  /* "public" fields for IJG library*/
+    jmp_buf setjmp_buffer;      /* For handling catastropic errors */
+};
+
+enum jstate {
+    JPEG_HEADER,                          /* Reading JFIF headers */
+    JPEG_START_DECOMPRESS,
+    JPEG_DECOMPRESS_PROGRESSIVE,          /* Output progressive pixels */
+    JPEG_DECOMPRESS_SEQUENTIAL,           /* Output sequential pixels */
+    JPEG_DONE,
+    JPEG_SINK_NON_JPEG_TRAILER,          /* Some image files have a */
+                                         /* non-JPEG trailer */
+    JPEG_ERROR    
+};
+
+void init_source(j_decompress_ptr jd);
+boolean fill_input_buffer(j_decompress_ptr jd);
+void skip_input_data(j_decompress_ptr jd, long num_bytes);
+void term_source(j_decompress_ptr jd);
+void error_exit(j_common_ptr cinfo);
+
+/*
+ *  Implementation of a JPEG src object that understands our state machine
+ */
+struct decoder_source_mgr {
+  /* public fields; must be first in this struct! */
+  struct jpeg_source_mgr pub;
+
+  JPEGImageReader *decoder;
+};
+
+class JPEGImageReader
+{
+public:
+    JPEGImageReader(JPEGImageDecoder* decoder)
+        : m_decoder(decoder)
+        , m_bufferLength(0)
+        , m_bytesToSkip(0)
+        , m_state(JPEG_HEADER)
+        , m_samples(0)
+    {
+        memset(&m_info, 0, sizeof(jpeg_decompress_struct));
+        /* We set up the normal JPEG error routines, then override error_exit. */
+        m_info.err = jpeg_std_error(&m_err.pub);
+        m_err.pub.error_exit = error_exit;
+
+        /* Allocate and initialize JPEG decompression object */
+        jpeg_create_decompress(&m_info);
+  
+        decoder_source_mgr* src = 0;
+        if (!m_info.src) {
+            src = (decoder_source_mgr*)fastCalloc(sizeof(decoder_source_mgr), 1);
+            if (!src) {
+                m_state = JPEG_ERROR;
+                return;
+            }
+        }
+
+        m_info.src = (jpeg_source_mgr*)src;
+
+        /* Set up callback functions. */
+        src->pub.init_source = init_source;
+        src->pub.fill_input_buffer = fill_input_buffer;
+        src->pub.skip_input_data = skip_input_data;
+        src->pub.resync_to_restart = jpeg_resync_to_restart;
+        src->pub.term_source = term_source;
+        src->decoder = this;
+    }
+
+    ~JPEGImageReader()
+    {
+        close();
+    }
+
+    void close() {
+        decoder_source_mgr* src = (decoder_source_mgr*)m_info.src;
+        if (src)
+            fastFree(src);
+        m_info.src = 0;
+
+        jpeg_destroy_decompress(&m_info);
+    }
+
+    void skipBytes(long num_bytes) {
+        decoder_source_mgr* src = (decoder_source_mgr*)m_info.src;
+        long bytesToSkip = std::min(num_bytes, (long)src->pub.bytes_in_buffer);
+        src->pub.bytes_in_buffer -= (size_t)bytesToSkip;
+        src->pub.next_input_byte += bytesToSkip;
+    
+        if (num_bytes > bytesToSkip)
+            m_bytesToSkip = (size_t)(num_bytes - bytesToSkip);
+        else
+            m_bytesToSkip = 0;
+    }
+
+    bool decode(const Vector<char>& data, bool sizeOnly) {
+        m_decodingSizeOnly = sizeOnly;
+        
+        unsigned newByteCount = data.size() - m_bufferLength;
+        unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer;
+
+        m_info.src->bytes_in_buffer += newByteCount;
+        m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset;
+        
+        // If we still have bytes to skip, try to skip those now.
+        if (m_bytesToSkip)
+            skipBytes(m_bytesToSkip);
+
+        m_bufferLength = data.size();
+        
+        // We need to do the setjmp here. Otherwise bad things will happen
+        if (setjmp(m_err.setjmp_buffer)) {
+            m_state = JPEG_SINK_NON_JPEG_TRAILER;
+            close();
+            return false;
+        }
+
+        switch (m_state) {
+            case JPEG_HEADER:
+            {
+                /* Read file parameters with jpeg_read_header() */
+                if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED)
+                    return true; /* I/O suspension */
+
+                /* let libjpeg take care of gray->RGB and YCbCr->RGB conversions */
+                switch (m_info.jpeg_color_space) {
+                    case JCS_GRAYSCALE:
+                    case JCS_RGB:
+                    case JCS_YCbCr:
+                        m_info.out_color_space = JCS_RGB;
+                        break;
+                    case JCS_CMYK:
+                    case JCS_YCCK:
+                    default:
+                        m_state = JPEG_ERROR;
+                        return false;
+                }
+
+                /*
+                 * Don't allocate a giant and superfluous memory buffer
+                 * when the image is a sequential JPEG.
+                 */
+                m_info.buffered_image = jpeg_has_multiple_scans(&m_info);
+
+                /* Used to set up image size so arrays can be allocated */
+                jpeg_calc_output_dimensions(&m_info);
+
+                /*
+                 * Make a one-row-high sample array that will go away
+                 * when done with image. Always make it big enough to
+                 * hold an RGB row.  Since this uses the IJG memory
+                 * manager, it must be allocated before the call to
+                 * jpeg_start_compress().
+                 */
+                int row_stride = m_info.output_width * 4; // RGBA buffer
+
+
+                m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info,
+                                           JPOOL_IMAGE,
+                                           row_stride, 1);
+
+                m_state = JPEG_START_DECOMPRESS;
+
+                // We can fill in the size now that the header is available.
+                m_decoder->setSize(m_info.image_width, m_info.image_height);
+
+                if (m_decodingSizeOnly) {
+                    // We can stop here.
+                    // Reduce our buffer length and available data.
+                    m_bufferLength -= m_info.src->bytes_in_buffer;
+                    m_info.src->bytes_in_buffer = 0;
+                    return true;
+                }
+            }
+
+            case JPEG_START_DECOMPRESS:
+            {
+                /* Set parameters for decompression */
+                /* FIXME -- Should reset dct_method and dither mode
+                 * for final pass of progressive JPEG
+                 */
+                m_info.dct_method =  JDCT_ISLOW;
+                m_info.dither_mode = JDITHER_FS;
+                m_info.do_fancy_upsampling = true;
+                m_info.enable_2pass_quant = false;
+                m_info.do_block_smoothing = true;
+
+                /* Start decompressor */
+                if (!jpeg_start_decompress(&m_info))
+                    return true; /* I/O suspension */
+
+                /* If this is a progressive JPEG ... */
+                m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
+            }
+    
+            case JPEG_DECOMPRESS_SEQUENTIAL:
+            {
+                if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) {
+      
+                    if (!m_decoder->outputScanlines())
+                        return true; /* I/O suspension */
+      
+                    /* If we've completed image output ... */
+                    assert(m_info.output_scanline == m_info.output_height);
+                    m_state = JPEG_DONE;
+                }
+            }
+
+            case JPEG_DECOMPRESS_PROGRESSIVE:
+            {
+                if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) {
+                    int status;
+                    do {
+                        status = jpeg_consume_input(&m_info);
+                    } while ((status != JPEG_SUSPENDED) &&
+                             (status != JPEG_REACHED_EOI));
+
+                    for (;;) {
+                        if (m_info.output_scanline == 0) {
+                            int scan = m_info.input_scan_number;
+
+                            /* if we haven't displayed anything yet (output_scan_number==0)
+                            and we have enough data for a complete scan, force output
+                            of the last full scan */
+                            if ((m_info.output_scan_number == 0) &&
+                                (scan > 1) &&
+                                (status != JPEG_REACHED_EOI))
+                                scan--;
+
+                            if (!jpeg_start_output(&m_info, scan))
+                                return true; /* I/O suspension */
+                        }
+
+                        if (m_info.output_scanline == 0xffffff)
+                            m_info.output_scanline = 0;
+
+                        if (!m_decoder->outputScanlines()) {
+                            if (m_info.output_scanline == 0)
+                                /* didn't manage to read any lines - flag so we don't call
+                                jpeg_start_output() multiple times for the same scan */
+                                m_info.output_scanline = 0xffffff;
+                            return true; /* I/O suspension */
+                        }
+
+                        if (m_info.output_scanline == m_info.output_height) {
+                            if (!jpeg_finish_output(&m_info))
+                                return true; /* I/O suspension */
+
+                            if (jpeg_input_complete(&m_info) &&
+                                (m_info.input_scan_number == m_info.output_scan_number))
+                                break;
+
+                            m_info.output_scanline = 0;
+                        }
+                    }
+
+                    m_state = JPEG_DONE;
+                }
+            }
+
+            case JPEG_DONE:
+            {
+                /* Finish decompression */
+                if (!jpeg_finish_decompress(&m_info))
+                    return true; /* I/O suspension */
+
+                m_state = JPEG_SINK_NON_JPEG_TRAILER;
+
+                /* we're done */
+                break;
+            }
+            
+            case JPEG_SINK_NON_JPEG_TRAILER:
+                break;
+
+            case JPEG_ERROR:
+                break;
+        }
+
+        return true;
+    }
+
+    jpeg_decompress_struct* info() { return &m_info; }
+    JSAMPARRAY samples() const { return m_samples; }
+    JPEGImageDecoder* decoder() { return m_decoder; }
+
+private:
+    JPEGImageDecoder* m_decoder;
+    unsigned m_bufferLength;
+    int m_bytesToSkip;
+    bool m_decodingSizeOnly;
+    bool m_initialized;
+
+    jpeg_decompress_struct m_info;
+    decoder_error_mgr m_err;
+    jstate m_state;
+
+    JSAMPARRAY m_samples;
+};
+
+/* Override the standard error method in the IJG JPEG decoder code. */
+void error_exit(j_common_ptr cinfo)
+{
+    /* Return control to the setjmp point. */
+    decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err;
+    longjmp(err->setjmp_buffer, -1);
+}
+
+void init_source(j_decompress_ptr jd)
+{
+}
+
+void skip_input_data(j_decompress_ptr jd, long num_bytes)
+{
+    decoder_source_mgr *src = (decoder_source_mgr *)jd->src;
+    src->decoder->skipBytes(num_bytes);
+}
+
+boolean fill_input_buffer(j_decompress_ptr jd)
+{
+    // Our decode step always sets things up properly, so if this method is ever
+    // called, then we have hit the end of the buffer.  A return value of FALSE indicates
+    // that we have no data to supply yet.
+    return false;
+}
+
+void term_source (j_decompress_ptr jd)
+{
+    decoder_source_mgr *src = (decoder_source_mgr *)jd->src;
+    src->decoder->decoder()->jpegComplete();
+}
+
+JPEGImageDecoder::JPEGImageDecoder()
+: m_reader(0)
+{}
+
+JPEGImageDecoder::~JPEGImageDecoder()
+{
+    delete m_reader;
+}
+
+// Take the data and store it.
+void JPEGImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
+{
+    if (m_failed)
+        return;
+
+    // Cache our new data.
+    ImageDecoder::setData(data, allDataReceived);
+
+    // Create the JPEG reader.
+    if (!m_reader && !m_failed)
+        m_reader = new JPEGImageReader(this);
+}
+
+// Whether or not the size information has been decoded yet.
+bool JPEGImageDecoder::isSizeAvailable() const
+{
+    // If we have pending data to decode, send it to the JPEG reader now.
+    if (!m_sizeAvailable && m_reader) {
+        if (m_failed)
+            return false;
+
+        // The decoder will go ahead and aggressively consume everything up until the
+        // size is encountered.
+        decode(true);
+    }
+
+    return m_sizeAvailable;
+}
+
+RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index)
+{
+    if (index)
+        return 0;
+
+    if (m_frameBufferCache.isEmpty())
+        m_frameBufferCache.resize(1);
+
+    RGBA32Buffer& frame = m_frameBufferCache[0];
+    if (frame.status() != RGBA32Buffer::FrameComplete && m_reader)
+        // Decode this frame.
+        decode();
+    return &frame;
+}
+
+// Feed data to the JPEG reader.
+void JPEGImageDecoder::decode(bool sizeOnly) const
+{
+    if (m_failed)
+        return;
+
+    m_failed = !m_reader->decode(m_data->buffer(), sizeOnly);
+
+    if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) {
+        delete m_reader;
+        m_reader = 0;
+    }
+}
+
+bool JPEGImageDecoder::outputScanlines()
+{
+    if (m_frameBufferCache.isEmpty())
+        return false;
+
+    // Resize to the width and height of the image.
+    RGBA32Buffer& buffer = m_frameBufferCache[0];
+    if (buffer.status() == RGBA32Buffer::FrameEmpty) {
+        // Let's resize our buffer now to the correct width/height.
+        RGBA32Array& bytes = buffer.bytes();
+        bytes.resize(m_size.width() * m_size.height());
+
+        // Update our status to be partially complete.
+        buffer.setStatus(RGBA32Buffer::FramePartial);
+
+        // For JPEGs, the frame always fills the entire image.
+        buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height()));
+
+        // We don't have alpha (this is the default when the buffer is constructed).
+    }
+
+    jpeg_decompress_struct* info = m_reader->info();
+    JSAMPARRAY samples = m_reader->samples();
+
+    unsigned* dst = buffer.bytes().data() + info->output_scanline * m_size.width();
+   
+    while (info->output_scanline < info->output_height) {
+        /* Request one scanline.  Returns 0 or 1 scanlines. */
+        if (jpeg_read_scanlines(info, samples, 1) != 1)
+            return false;
+        JSAMPLE *j1 = samples[0];
+        for (unsigned i = 0; i < info->output_width; ++i) {
+            unsigned r = *j1++;
+            unsigned g = *j1++;
+            unsigned b = *j1++;
+            RGBA32Buffer::setRGBA(*dst++, r, g, b, 0xFF);
+        }
+
+        buffer.ensureHeight(info->output_scanline);
+    }
+
+    return true;
+}
+
+void JPEGImageDecoder::jpegComplete()
+{
+    if (m_frameBufferCache.isEmpty())
+        return;
+
+    // Hand back an appropriately sized buffer, even if the image ended up being empty.
+    RGBA32Buffer& buffer = m_frameBufferCache[0];
+    buffer.setStatus(RGBA32Buffer::FrameComplete);
+}
+
+}
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/image-decoders/skia/JPEGImageDecoder.h b/WebCore/platform/image-decoders/skia/JPEGImageDecoder.h
new file mode 100644 (file)
index 0000000..b4d7b2a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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. 
+ */
+
+#ifndef JPEG_DECODER_H_
+#define JPEG_DECODER_H_
+
+#include "ImageDecoder.h"
+
+namespace WebCore {
+
+class JPEGImageReader;
+
+// This class decodes the JPEG image format.
+class JPEGImageDecoder : public ImageDecoder
+{
+public:
+    JPEGImageDecoder();
+    ~JPEGImageDecoder();
+
+    virtual String filenameExtension() const { return "jpg"; }
+
+    // Take the data and store it.
+    virtual void setData(SharedBuffer* data, bool allDataReceived);
+
+    // Whether or not the size information has been decoded yet.
+    virtual bool isSizeAvailable() const;
+
+    virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+    
+    virtual bool supportsAlpha() const { return false; }
+
+    void decode(bool sizeOnly = false) const;
+
+    JPEGImageReader* reader() { return m_reader; }
+
+    void setSize(int width, int height) {
+        if (!m_sizeAvailable) {
+            m_sizeAvailable = true;
+            m_size = IntSize(width, height);
+        }
+    }
+
+    bool outputScanlines();
+    void jpegComplete();
+
+private:
+    mutable JPEGImageReader* m_reader;
+};
+
+}
+
+#endif
diff --git a/WebCore/platform/image-decoders/skia/PNGImageDecoder.cpp b/WebCore/platform/image-decoders/skia/PNGImageDecoder.cpp
new file mode 100644 (file)
index 0000000..17143b1
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 2001 mozilla.org
+ *
+ * Other contributors:
+ *   Stuart Parmenter <stuart@mozilla.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Alternatively, the contents of this file may be used under the terms
+ * of either the Mozilla Public License Version 1.1, found at
+ * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
+ * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
+ * (the "GPL"), in which case the provisions of the MPL or the GPL are
+ * applicable instead of those above.  If you wish to allow use of your
+ * version of this file only under the terms of one of those two
+ * licenses (the MPL or the GPL) and not to allow others to use your
+ * version of this file under the LGPL, indicate your decision by
+ * deletingthe provisions above and replace them with the notice and
+ * other provisions required by the MPL or the GPL, as the case may be.
+ * If you do not delete the provisions above, a recipient may use your
+ * version of this file under any of the LGPL, the MPL or the GPL.
+ */
+
+#include "config.h"
+#include "PNGImageDecoder.h"
+#include "png.h"
+#include "assert.h"
+
+#if PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX)
+
+#if COMPILER(MSVC)
+// Remove warnings from warning level 4.
+#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
+#endif
+
+namespace WebCore {
+
+// Gamma constants.
+const double cMaxGamma = 21474.83;
+const double cDefaultGamma = 2.2;
+const double cInverseGamma = 0.45455;
+
+// Protect against large PNGs. See Mozilla's bug #251381 for more info.
+const long cMaxPNGSize = 1000000L;
+
+// Called if the decoding of the image fails.
+static void PNGAPI decodingFailed(png_structp png_ptr, png_const_charp error_msg);
+
+// Callbacks given to the read struct.  The first is for warnings (we want to treat a particular warning
+// as an error, which is why we have to register this callback.
+static void PNGAPI decodingWarning(png_structp png_ptr, png_const_charp warning_msg);
+
+// Called when we have obtained the header information (including the size).
+static void PNGAPI headerAvailable(png_structp png_ptr, png_infop info_ptr);
+
+// Called when a row is ready.
+static void PNGAPI rowAvailable(png_structp png_ptr, png_bytep new_row,
+                                png_uint_32 row_num, int pass);
+
+// Called when we have completely finished decoding the image.
+static void PNGAPI pngComplete(png_structp png_ptr, png_infop info_ptr);
+
+class PNGImageReader
+{
+public:
+    PNGImageReader(PNGImageDecoder* decoder)
+    : m_readOffset(0), m_decodingSizeOnly(false), m_interlaceBuffer(0), m_hasAlpha(0)
+    {
+        m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, decodingFailed, decodingWarning);
+        m_info = png_create_info_struct(m_png);
+        png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete);
+    }
+
+    ~PNGImageReader()
+    {
+        close();
+    }
+
+    void close() {
+        if (m_png && m_info)
+            png_destroy_read_struct(&m_png, &m_info, 0);
+        delete []m_interlaceBuffer;
+        m_readOffset = 0;
+    }
+
+    void decode(const Vector<char>& data, bool sizeOnly)
+    {
+        m_decodingSizeOnly = sizeOnly;
+
+        // We need to do the setjmp here. Otherwise bad things will happen
+        if (setjmp(m_png->jmpbuf)) {
+            close();
+            return;
+        }
+
+        // Go ahead and assume we consumed all the data.  If we consume less, the
+        // callback will adjust our read offset accordingly.  Do not attempt to adjust the
+        // offset after png_process_data returns.
+        unsigned offset = m_readOffset;
+        unsigned remaining = data.size() - m_readOffset;
+        m_readOffset = data.size();
+        png_process_data(m_png, m_info, (png_bytep)(data.data()) + offset, remaining);
+    }
+
+    bool decodingSizeOnly() const { return m_decodingSizeOnly; }
+    png_structp pngPtr() const { return m_png; }
+    png_infop infoPtr() const { return m_info; }
+    png_bytep interlaceBuffer() const { return m_interlaceBuffer; }
+    bool hasAlpha() const { return m_hasAlpha; }
+
+    void setReadOffset(unsigned offset) { m_readOffset = offset; }
+    void setHasAlpha(bool b) { m_hasAlpha = b; }
+
+    void createInterlaceBuffer(int size) {
+        m_interlaceBuffer = new png_byte[size];
+    }
+
+private:
+    unsigned m_readOffset;
+    bool m_decodingSizeOnly;
+    png_structp m_png;
+    png_infop m_info;
+    png_bytep m_interlaceBuffer;
+    bool m_hasAlpha;
+};
+
+PNGImageDecoder::PNGImageDecoder()
+: m_reader(0)
+{
+    m_frameBufferCache.resize(1);
+}
+
+PNGImageDecoder::~PNGImageDecoder()
+{
+    delete m_reader;
+}
+
+// Take the data and store it.
+void PNGImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
+{
+    if (m_failed)
+        return;
+
+    // Cache our new data.
+    ImageDecoder::setData(data, allDataReceived);
+
+    // Create the PNG reader.
+    if (!m_reader && !m_failed)
+        m_reader = new PNGImageReader(this);
+}
+
+// Whether or not the size information has been decoded yet.
+bool PNGImageDecoder::isSizeAvailable() const
+{
+    // If we have pending data to decode, send it to the PNG reader now.
+    if (!m_sizeAvailable && m_reader) {
+        if (m_failed)
+            return false;
+
+        // The decoder will go ahead and aggressively consume everything up until the
+        // size is encountered.
+        decode(true);
+    }
+
+    return m_sizeAvailable;
+}
+
+RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index)
+{
+    if (index)
+        return 0;
+
+    RGBA32Buffer& frame = m_frameBufferCache[0];
+    if (frame.status() != RGBA32Buffer::FrameComplete && m_reader)
+        // Decode this frame.
+        decode();
+    return &frame;
+}
+
+// Feed data to the PNG reader.
+void PNGImageDecoder::decode(bool sizeOnly) const
+{
+    if (m_failed)
+        return;
+
+    m_reader->decode(m_data->buffer(), sizeOnly);
+    
+    if (m_failed || (m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) {
+        delete m_reader;
+        m_reader = 0;
+    }
+}
+
+void decodingFailed(png_structp png, png_const_charp errorMsg)
+{
+    static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed();
+    longjmp(png->jmpbuf, 1);
+}
+
+void decodingWarning(png_structp png, png_const_charp warningMsg)
+{
+  // Mozilla did this, so we will too.
+  // Convert a tRNS warning to be an error (documented in bugzilla.mozilla.org bug #251381)
+  if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24))
+      png_error(png, warningMsg);
+}
+
+void headerAvailable(png_structp png, png_infop info)
+{
+    static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable();
+}
+
+void PNGImageDecoder::headerAvailable()
+{
+    png_structp png = reader()->pngPtr();
+    png_infop info = reader()->infoPtr();
+    png_uint_32 width = png->width;
+    png_uint_32 height = png->height;
+    
+    // Protect against large images.
+    if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) {
+        m_failed = true;
+        longjmp(png->jmpbuf, 1);
+        return;
+    }
+    
+    // We can fill in the size now that the header is available.
+    if (!m_sizeAvailable) {
+        m_sizeAvailable = true;
+        m_size = IntSize(width, height);
+    }
+
+    int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
+    png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType,
+                 &interlaceType, &compressionType, &filterType);
+
+    // The options we set here match what Mozilla does.
+
+    // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
+    if (colorType == PNG_COLOR_TYPE_PALETTE ||
+        (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8))
+        png_set_expand(png);
+    
+    png_bytep trns = 0;
+    int trnsCount = 0;
+    if (png_get_valid(png, info, PNG_INFO_tRNS)) {
+        png_get_tRNS(png, info, &trns, &trnsCount, 0);
+        png_set_expand(png);
+    }
+
+    if (bitDepth == 16)
+        png_set_strip_16(png);
+
+    if (colorType == PNG_COLOR_TYPE_GRAY ||
+        colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+        png_set_gray_to_rgb(png);
+
+    // Deal with gamma and keep it under our control.
+    double gamma;
+    if (png_get_gAMA(png, info, &gamma)) {
+        if ((gamma <= 0.0) || (gamma > cMaxGamma)) {
+            gamma = cInverseGamma;
+            png_set_gAMA(png, info, gamma);
+        }
+        png_set_gamma(png, cDefaultGamma, gamma);
+    }
+    else
+        png_set_gamma(png, cDefaultGamma, cInverseGamma);
+
+    // Tell libpng to send us rows for interlaced pngs.
+    if (interlaceType == PNG_INTERLACE_ADAM7)
+        png_set_interlace_handling(png);
+
+    // Update our info now
+    png_read_update_info(png, info);
+    channels = png_get_channels(png, info);
+    assert(channels == 3 || channels == 4);
+
+    reader()->setHasAlpha(channels == 4);
+
+    if (reader()->decodingSizeOnly()) {
+        // If we only needed the size, halt the reader.     
+        reader()->setReadOffset(m_data->size() - png->buffer_size);
+        png->buffer_size = 0;
+    }
+}
+
+void rowAvailable(png_structp png, png_bytep rowBuffer,
+                  png_uint_32 rowIndex, int interlacePass)
+{
+    static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass);
+}
+
+void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
+{
+    // Resize to the width and height of the image.
+    RGBA32Buffer& buffer = m_frameBufferCache[0];
+    if (buffer.status() == RGBA32Buffer::FrameEmpty) {
+        // Let's resize our buffer now to the correct width/height.
+        RGBA32Array& bytes = buffer.bytes();
+        bytes.resize(m_size.width() * m_size.height());
+
+        // Update our status to be partially complete.
+        buffer.setStatus(RGBA32Buffer::FramePartial);
+
+        // For PNGs, the frame always fills the entire image.
+        buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height()));
+
+        if (reader()->pngPtr()->interlaced)
+            reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * m_size.width() * m_size.height());
+    }
+
+    if (rowBuffer == 0)
+        return;
+
+   /* libpng comments (pasted in here to explain what follows)
+    *
+    * this function is called for every row in the image.  If the
+    * image is interlacing, and you turned on the interlace handler,
+    * this function will be called for every row in every pass.
+    * Some of these rows will not be changed from the previous pass.
+    * When the row is not changed, the new_row variable will be NULL.
+    * The rows and passes are called in order, so you don't really
+    * need the row_num and pass, but I'm supplying them because it
+    * may make your life easier.
+    *
+    * For the non-NULL rows of interlaced images, you must call
+    * png_progressive_combine_row() passing in the row and the
+    * old row.  You can call this function for NULL rows (it will
+    * just return) and for non-interlaced images (it just does the
+    * memcpy for you) if it will make the code easier.  Thus, you
+    * can just do this for all cases:
+    *
+    *    png_progressive_combine_row(png_ptr, old_row, new_row);
+    *
+    * where old_row is what was displayed for previous rows.  Note
+    * that the first pass (pass == 0 really) will completely cover
+    * the old row, so the rows do not have to be initialized.  After
+    * the first pass (and only for interlaced images), you will have
+    * to pass the current row, and the function will combine the
+    * old row and the new row.
+    */
+    
+    png_structp png = reader()->pngPtr();
+    bool hasAlpha = reader()->hasAlpha();
+    unsigned colorChannels = hasAlpha ? 4 : 3;
+    png_bytep row;
+    png_bytep interlaceBuffer = reader()->interlaceBuffer();
+    if (interlaceBuffer) {
+        row = interlaceBuffer + (rowIndex * colorChannels * m_size.width());
+        png_progressive_combine_row(png, row, rowBuffer);
+    }
+    else
+        row = rowBuffer;
+
+    // Copy the data into our buffer.
+    int width = m_size.width();
+    unsigned* dst = buffer.bytes().data() + rowIndex * width;
+    bool sawAlpha = false;
+    for (int i = 0; i < width; i++) {
+        unsigned red = *row++;
+        unsigned green = *row++;
+        unsigned blue = *row++;
+        unsigned alpha = (hasAlpha ? *row++ : 255);
+        RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha);
+        if (!sawAlpha && alpha < 255) {
+            sawAlpha = true;
+            buffer.setHasAlpha(true);
+        }
+    }
+
+    buffer.ensureHeight(rowIndex + 1);
+}
+
+void pngComplete(png_structp png, png_infop info)
+{
+    static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete();
+}
+
+void PNGImageDecoder::pngComplete()
+{
+    // Hand back an appropriately sized buffer, even if the image ended up being empty.
+    RGBA32Buffer& buffer = m_frameBufferCache[0];
+    buffer.setStatus(RGBA32Buffer::FrameComplete);
+}
+
+}
+
+#endif // PLATFORM(CAIRO)
diff --git a/WebCore/platform/image-decoders/skia/PNGImageDecoder.h b/WebCore/platform/image-decoders/skia/PNGImageDecoder.h
new file mode 100644 (file)
index 0000000..8c73785
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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. 
+ */
+
+#ifndef PNG_DECODER_H_
+#define PNG_DECODER_H_
+
+#include "ImageDecoder.h"
+
+namespace WebCore {
+
+class PNGImageReader;
+
+// This class decodes the PNG image format.
+class PNGImageDecoder : public ImageDecoder
+{
+public:
+    PNGImageDecoder();
+    ~PNGImageDecoder();
+
+    virtual String filenameExtension() const { return "png"; }
+
+    // Take the data and store it.
+    virtual void setData(SharedBuffer* data, bool allDataReceived);
+
+    // Whether or not the size information has been decoded yet.
+    virtual bool isSizeAvailable() const;
+
+    virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+
+    void decode(bool sizeOnly = false) const;
+
+    PNGImageReader* reader() { return m_reader; }
+
+    // Callbacks from libpng
+    void decodingFailed() { m_failed = true; }
+    void headerAvailable();
+    void rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass);
+    void pngComplete();
+
+private:
+    mutable PNGImageReader* m_reader;
+};
+
+}
+
+#endif