summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/image-decoders
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/image-decoders
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/platform/image-decoders')
-rw-r--r--Source/WebCore/platform/image-decoders/ImageDecoder.cpp303
-rw-r--r--Source/WebCore/platform/image-decoders/ImageDecoder.h396
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp151
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h85
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp737
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h354
-rw-r--r--Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp41
-rw-r--r--Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp105
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp399
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h85
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp925
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageReader.h209
-rw-r--r--Source/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp74
-rw-r--r--Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp315
-rw-r--r--Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h142
-rw-r--r--Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp537
-rw-r--r--Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h70
-rw-r--r--Source/WebCore/platform/image-decoders/openvg/ImageDecoderOpenVG.cpp77
-rw-r--r--Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp434
-rw-r--r--Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h74
-rw-r--r--Source/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp158
-rw-r--r--Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp143
-rw-r--r--Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp125
-rw-r--r--Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h56
-rw-r--r--Source/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp89
25 files changed, 6084 insertions, 0 deletions
diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.cpp b/Source/WebCore/platform/image-decoders/ImageDecoder.cpp
new file mode 100644
index 0000000..8491da9
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/ImageDecoder.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2008-2009 Torch Mobile, Inc.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "ImageDecoder.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "BMPImageDecoder.h"
+#include "GIFImageDecoder.h"
+#include "ICOImageDecoder.h"
+#include "JPEGImageDecoder.h"
+#include "PNGImageDecoder.h"
+#include "WEBPImageDecoder.h"
+#include "SharedBuffer.h"
+
+using namespace std;
+
+namespace WebCore {
+
+static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset)
+{
+ unsigned bytesExtracted = 0;
+ const char* moreData;
+ while (unsigned moreDataLength = sharedBuffer.getSomeData(moreData, offset)) {
+ unsigned bytesToCopy = min(bufferLength - bytesExtracted, moreDataLength);
+ memcpy(buffer + bytesExtracted, moreData, bytesToCopy);
+ bytesExtracted += bytesToCopy;
+ if (bytesExtracted == bufferLength)
+ break;
+ offset += bytesToCopy;
+ }
+ return bytesExtracted;
+}
+
+#if !OS(ANDROID)
+// This method requires BMPImageDecoder, PNGImageDecoder, ICOImageDecoder and
+// JPEGDecoder, which aren't used on Android, and which don't all compile.
+// TODO: Find a better fix.
+ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+{
+ // We need at least 4 bytes to figure out what kind of image we're dealing
+ // with.
+ static const unsigned maxMarkerLength = 4;
+ char contents[maxMarkerLength];
+ unsigned length = copyFromSharedBuffer(contents, maxMarkerLength, data, 0);
+ if (length < maxMarkerLength)
+ return 0;
+
+ // GIFs begin with GIF8(7 or 9).
+ if (strncmp(contents, "GIF8", 4) == 0)
+ return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption);
+
+ // Test for PNG.
+ if (!memcmp(contents, "\x89\x50\x4E\x47", 4))
+ return new PNGImageDecoder(alphaOption, gammaAndColorProfileOption);
+
+ // JPEG
+ if (!memcmp(contents, "\xFF\xD8\xFF", 3))
+ return new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption);
+
+#if USE(WEBP)
+ if (!memcmp(contents, "RIFF", 4)) {
+ static const unsigned webpExtraMarker = 6;
+ static const unsigned webpExtraMarkeroffset = 8;
+ char header[webpExtraMarker];
+ unsigned length = copyFromSharedBuffer(header, webpExtraMarker, data, webpExtraMarkeroffset);
+ if (length >= webpExtraMarker) {
+ if (!memcmp(header, "WEBPVP", webpExtraMarker))
+ return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption);
+ }
+ }
+#endif
+
+ // BMP
+ if (strncmp(contents, "BM", 2) == 0)
+ return new BMPImageDecoder(alphaOption, gammaAndColorProfileOption);
+
+ // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
+ // CURs begin with 2-byte 0 followed by 2-byte 2.
+ if (!memcmp(contents, "\x00\x00\x01\x00", 4) || !memcmp(contents, "\x00\x00\x02\x00", 4))
+ return new ICOImageDecoder(alphaOption, gammaAndColorProfileOption);
+
+ // Give up. We don't know what the heck this is.
+ return 0;
+}
+#endif // !OS(ANDROID)
+
+#if !PLATFORM(SKIA)
+
+RGBA32Buffer::RGBA32Buffer()
+ : m_hasAlpha(false)
+ , m_status(FrameEmpty)
+ , m_duration(0)
+ , m_disposalMethod(DisposeNotSpecified)
+ , m_premultiplyAlpha(true)
+{
+}
+
+RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return *this;
+
+ copyReferenceToBitmapData(other);
+ setRect(other.rect());
+ setStatus(other.status());
+ setDuration(other.duration());
+ setDisposalMethod(other.disposalMethod());
+ setPremultiplyAlpha(other.premultiplyAlpha());
+ return *this;
+}
+
+void RGBA32Buffer::clear()
+{
+ m_backingStore.clear();
+ m_bytes = 0;
+ m_status = FrameEmpty;
+ // NOTE: Do not reset other members here; clearFrameBufferCache() calls this
+ // to free the bitmap data, but other functions like initFrameBuffer() and
+ // frameComplete() may still need to read other metadata out of this frame
+ // later.
+}
+
+void RGBA32Buffer::zeroFill()
+{
+ memset(m_bytes, 0, m_size.width() * m_size.height() * sizeof(PixelData));
+ m_hasAlpha = true;
+}
+
+#if !PLATFORM(CG)
+
+void RGBA32Buffer::copyReferenceToBitmapData(const RGBA32Buffer& other)
+{
+ ASSERT(this != &other);
+ copyBitmapData(other);
+}
+
+bool RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return true;
+
+ m_backingStore = other.m_backingStore;
+ m_bytes = m_backingStore.data();
+ m_size = other.m_size;
+ setHasAlpha(other.m_hasAlpha);
+ return true;
+}
+
+bool RGBA32Buffer::setSize(int newWidth, int newHeight)
+{
+ // NOTE: This has no way to check for allocation failure if the requested
+ // size was too big...
+ m_backingStore.resize(newWidth * newHeight);
+ m_bytes = m_backingStore.data();
+ m_size = IntSize(newWidth, newHeight);
+
+ // Zero the image.
+ zeroFill();
+
+ return true;
+}
+
+#endif
+
+bool RGBA32Buffer::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+void RGBA32Buffer::setHasAlpha(bool alpha)
+{
+ m_hasAlpha = alpha;
+}
+
+void RGBA32Buffer::setColorProfile(const ColorProfile& colorProfile)
+{
+ m_colorProfile = colorProfile;
+}
+
+void RGBA32Buffer::setStatus(FrameStatus status)
+{
+ m_status = status;
+}
+
+int RGBA32Buffer::width() const
+{
+ return m_size.width();
+}
+
+int RGBA32Buffer::height() const
+{
+ return m_size.height();
+}
+
+#endif
+
+namespace {
+
+enum MatchType {
+ Exact,
+ UpperBound,
+ LowerBound
+};
+
+inline void fillScaledValues(Vector<int>& scaledValues, double scaleRate, int length)
+{
+ double inflateRate = 1. / scaleRate;
+ scaledValues.reserveCapacity(static_cast<int>(length * scaleRate + 0.5));
+ for (int scaledIndex = 0; ; ++scaledIndex) {
+ int index = static_cast<int>(scaledIndex * inflateRate + 0.5);
+ if (index >= length)
+ break;
+ scaledValues.append(index);
+ }
+}
+
+template <MatchType type> int getScaledValue(const Vector<int>& scaledValues, int valueToMatch, int searchStart)
+{
+ if (scaledValues.isEmpty())
+ return valueToMatch;
+
+ const int* dataStart = scaledValues.data();
+ const int* dataEnd = dataStart + scaledValues.size();
+ const int* matched = std::lower_bound(dataStart + searchStart, dataEnd, valueToMatch);
+ switch (type) {
+ case Exact:
+ return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : -1;
+ case LowerBound:
+ return matched != dataEnd && *matched == valueToMatch ? matched - dataStart : matched - dataStart - 1;
+ case UpperBound:
+ default:
+ return matched != dataEnd ? matched - dataStart : -1;
+ }
+}
+
+}
+
+void ImageDecoder::prepareScaleDataIfNecessary()
+{
+ m_scaled = false;
+ m_scaledColumns.clear();
+ m_scaledRows.clear();
+
+ int width = size().width();
+ int height = size().height();
+ int numPixels = height * width;
+ if (m_maxNumPixels <= 0 || numPixels <= m_maxNumPixels)
+ return;
+
+ m_scaled = true;
+ double scale = sqrt(m_maxNumPixels / (double)numPixels);
+ fillScaledValues(m_scaledColumns, scale, width);
+ fillScaledValues(m_scaledRows, scale, height);
+}
+
+int ImageDecoder::upperBoundScaledX(int origX, int searchStart)
+{
+ return getScaledValue<UpperBound>(m_scaledColumns, origX, searchStart);
+}
+
+int ImageDecoder::lowerBoundScaledX(int origX, int searchStart)
+{
+ return getScaledValue<LowerBound>(m_scaledColumns, origX, searchStart);
+}
+
+int ImageDecoder::upperBoundScaledY(int origY, int searchStart)
+{
+ return getScaledValue<UpperBound>(m_scaledRows, origY, searchStart);
+}
+
+int ImageDecoder::lowerBoundScaledY(int origY, int searchStart)
+{
+ return getScaledValue<LowerBound>(m_scaledRows, origY, searchStart);
+}
+
+int ImageDecoder::scaledY(int origY, int searchStart)
+{
+ return getScaledValue<Exact>(m_scaledRows, origY, searchStart);
+}
+
+}
diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.h b/Source/WebCore/platform/image-decoders/ImageDecoder.h
new file mode 100644
index 0000000..c3d73c0
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/ImageDecoder.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008-2009 Torch Mobile, Inc.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * 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 ImageDecoder_h
+#define ImageDecoder_h
+
+#include "IntRect.h"
+#include "ImageSource.h"
+#include "PlatformString.h"
+#include "SharedBuffer.h"
+#include <wtf/Assertions.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(SKIA)
+#include "NativeImageSkia.h"
+#include "SkColorPriv.h"
+#elif PLATFORM(QT)
+#include <QPixmap>
+#include <QImage>
+#endif
+
+namespace WebCore {
+
+ // FIXME: Do we want better encapsulation?
+ typedef Vector<char> ColorProfile;
+
+ // The RGBA32Buffer object represents the decoded image data in RGBA32
+ // format. This buffer is what all decoders write a single frame into.
+ // Frames are then instantiated for drawing by being handed this buffer.
+ class RGBA32Buffer {
+ public:
+ enum FrameStatus { FrameEmpty, FramePartial, FrameComplete };
+ enum FrameDisposalMethod {
+ // If you change the numeric values of these, make sure you audit
+ // all users, as some users may cast raw values to/from these
+ // constants.
+ DisposeNotSpecified, // Leave frame in framebuffer
+ DisposeKeep, // Leave frame in framebuffer
+ DisposeOverwriteBgcolor, // Clear frame to transparent
+ DisposeOverwritePrevious, // Clear frame to previous framebuffer
+ // contents
+ };
+#if PLATFORM(SKIA) || PLATFORM(QT)
+ typedef uint32_t PixelData;
+#else
+ typedef unsigned PixelData;
+#endif
+
+ RGBA32Buffer();
+
+ RGBA32Buffer(const RGBA32Buffer& other) { operator=(other); }
+
+ // For backends which refcount their data, this operator doesn't need to
+ // create a new copy of the image data, only increase the ref count.
+ RGBA32Buffer& operator=(const RGBA32Buffer& other);
+
+ // Deletes the pixel data entirely; used by ImageDecoder to save memory
+ // when we no longer need to display a frame and only need its metadata.
+ void clear();
+
+ // Zeroes the pixel data in the buffer, setting it to fully-transparent.
+ void zeroFill();
+
+ // Creates a new copy of the image data in |other|, so the two images
+ // can be modified independently. Returns whether the copy succeeded.
+ bool copyBitmapData(const RGBA32Buffer&);
+
+ // Creates a new reference to the image data in |other|. The two images
+ // share a common backing store.
+ void copyReferenceToBitmapData(const RGBA32Buffer&);
+
+ // Copies the pixel data at [(startX, startY), (endX, startY)) to the
+ // same X-coordinates on each subsequent row up to but not including
+ // endY.
+ void copyRowNTimes(int startX, int endX, int startY, int endY)
+ {
+ ASSERT(startX < width());
+ ASSERT(endX <= width());
+ ASSERT(startY < height());
+ ASSERT(endY <= height());
+ const int rowBytes = (endX - startX) * sizeof(PixelData);
+ const PixelData* const startAddr = getAddr(startX, startY);
+ for (int destY = startY + 1; destY < endY; ++destY)
+ memcpy(getAddr(startX, destY), startAddr, rowBytes);
+ }
+
+#if PLATFORM(ANDROID)
+ NativeImageSkia& bitmap() { return m_bitmap; }
+ const NativeImageSkia& bitmap() const { return m_bitmap; }
+#endif
+
+ // Allocates space for the pixel data. Must be called before any pixels
+ // are written. Will return true on success, false if the memory
+ // allocation fails. Calling this multiple times is undefined and may
+ // leak memory.
+ bool setSize(int newWidth, int newHeight);
+
+ // To be used by ImageSource::createFrameAtIndex(). Returns a pointer
+ // to the underlying native image data. This pointer will be owned by
+ // the BitmapImage and freed in FrameData::clear().
+ NativeImagePtr asNewNativeImage() const;
+
+ bool hasAlpha() const;
+ const IntRect& rect() const { return m_rect; }
+ FrameStatus status() const { return m_status; }
+ unsigned duration() const { return m_duration; }
+ FrameDisposalMethod disposalMethod() const { return m_disposalMethod; }
+ bool premultiplyAlpha() const { return m_premultiplyAlpha; }
+
+ void setHasAlpha(bool alpha);
+ void setColorProfile(const ColorProfile&);
+ void setRect(const IntRect& r) { m_rect = r; }
+ void setStatus(FrameStatus status);
+ void setDuration(unsigned duration) { m_duration = duration; }
+ void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; }
+ void setPremultiplyAlpha(bool premultiplyAlpha) { m_premultiplyAlpha = premultiplyAlpha; }
+
+ inline void setRGBA(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a)
+ {
+ setRGBA(getAddr(x, y), r, g, b, a);
+ }
+
+#if PLATFORM(QT)
+ void setPixmap(const QPixmap& pixmap);
+#endif
+
+ private:
+#if PLATFORM(CG)
+ typedef RetainPtr<CFMutableDataRef> NativeBackingStore;
+#else
+ typedef Vector<PixelData> NativeBackingStore;
+#endif
+
+ int width() const;
+ int height() const;
+
+ inline PixelData* getAddr(int x, int y)
+ {
+#if PLATFORM(SKIA)
+ return m_bitmap.getAddr32(x, y);
+#elif PLATFORM(QT)
+ m_image = m_pixmap.toImage();
+ m_pixmap = QPixmap();
+ return reinterpret_cast_ptr<QRgb*>(m_image.scanLine(y)) + x;
+#else
+ return m_bytes + (y * width()) + x;
+#endif
+ }
+
+ inline void setRGBA(PixelData* dest, unsigned r, unsigned g, unsigned b, unsigned a)
+ {
+ if (m_premultiplyAlpha && !a)
+ *dest = 0;
+ else {
+ if (m_premultiplyAlpha && a < 255) {
+ float alphaPercent = a / 255.0f;
+ r = static_cast<unsigned>(r * alphaPercent);
+ g = static_cast<unsigned>(g * alphaPercent);
+ b = static_cast<unsigned>(b * alphaPercent);
+ }
+#if PLATFORM(ANDROID)
+ *dest = SkPackARGB32(a, r, g, b);
+#else
+ *dest = (a << 24 | r << 16 | g << 8 | b);
+#endif
+ }
+ }
+
+#if PLATFORM(SKIA)
+ NativeImageSkia m_bitmap;
+#elif PLATFORM(QT)
+ mutable QPixmap m_pixmap;
+ mutable QImage m_image;
+ bool m_hasAlpha;
+ IntSize m_size;
+#else
+ NativeBackingStore m_backingStore;
+ PixelData* m_bytes; // The memory is backed by m_backingStore.
+ IntSize m_size; // The size of the buffer. This should be the
+ // same as ImageDecoder::m_size.
+ bool m_hasAlpha; // Whether or not any of the pixels in the buffer
+ // have transparency.
+ ColorProfile m_colorProfile;
+#endif
+ IntRect m_rect; // The rect of the original specified frame within
+ // the overall buffer. This will always just be
+ // the entire buffer except for GIF frames whose
+ // original rect was smaller than the overall
+ // image size.
+ FrameStatus m_status; // Whether or not this frame is completely
+ // finished decoding.
+ unsigned m_duration; // The animation delay.
+ FrameDisposalMethod m_disposalMethod; // What to do with this frame's data when
+ // initializing the next frame.
+ bool m_premultiplyAlpha; // Whether to premultiply alpha into R, G, B
+ // channels; by default it's true.
+ };
+
+ // The ImageDecoder class represents a base class for specific image format
+ // decoders (e.g., GIF, JPG, PNG, ICO) to derive from. All decoders decode
+ // into RGBA32 format and the base class manages the RGBA32 frame cache.
+ //
+ // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write
+ // directly to scaled output buffers by down sampling. Call
+ // setMaxNumPixels() to specify the biggest size that decoded images can
+ // have. Image decoders will deflate those images that are bigger than
+ // m_maxNumPixels. (Not supported by all image decoders yet)
+ class ImageDecoder : public Noncopyable {
+ public:
+ ImageDecoder(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : m_scaled(false)
+ , m_premultiplyAlpha(alphaOption == ImageSource::AlphaPremultiplied)
+ , m_ignoreGammaAndColorProfile(gammaAndColorProfileOption == ImageSource::GammaAndColorProfileIgnored)
+ , m_sizeAvailable(false)
+ , m_maxNumPixels(-1)
+ , m_isAllDataReceived(false)
+ , m_failed(false)
+ {
+ }
+
+ virtual ~ImageDecoder() {}
+
+ // Factory function to create an ImageDecoder. Ports that subclass
+ // ImageDecoder can provide their own implementation of this to avoid
+ // needing to write a dedicated setData() implementation.
+ static ImageDecoder* create(const SharedBuffer& data, ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+
+ // The the filename extension usually associated with an undecoded image
+ // of this type.
+ virtual String filenameExtension() const = 0;
+
+ bool isAllDataReceived() const { return m_isAllDataReceived; }
+
+ virtual void setData(SharedBuffer* data, bool allDataReceived)
+ {
+ if (m_failed)
+ return;
+ m_data = data;
+ m_isAllDataReceived = allDataReceived;
+ }
+
+ // Whether or not the size information has been decoded yet. This
+ // default implementation just returns true if the size has been set and
+ // we have not seen a failure. Decoders may want to override this to
+ // lazily decode enough of the image to get the size.
+ virtual bool isSizeAvailable()
+ {
+ return !m_failed && m_sizeAvailable;
+ }
+
+ // Returns the size of the image.
+ virtual IntSize size() const
+ {
+ return m_size;
+ }
+
+ IntSize scaledSize() const
+ {
+ return m_scaled ? IntSize(m_scaledColumns.size(), m_scaledRows.size()) : size();
+ }
+
+ // Returns the size of frame |index|. This will only differ from size()
+ // for formats where different frames are different sizes (namely ICO,
+ // where each frame represents a different icon within the master file).
+ // Notably, this does not return different sizes for different GIF
+ // frames, since while these may be stored as smaller rectangles, during
+ // decoding they are composited to create a full-size frame.
+ virtual IntSize frameSizeAtIndex(size_t) const
+ {
+ return size();
+ }
+
+ // Called by the image decoders to set their decoded size, this also
+ // checks the size for validity. It will return true if the size was
+ // set, or false if there is an error. On error, the m_failed flag will
+ // be set and the caller should immediately stop decoding.
+ virtual bool setSize(unsigned width, unsigned height)
+ {
+ if (isOverSize(width, height))
+ return setFailed();
+ m_size = IntSize(width, height);
+ m_sizeAvailable = true;
+ return true;
+ }
+
+ // The total number of frames for the image. Classes that support
+ // multiple frames will scan the image data for the answer if they need
+ // to (without necessarily decoding all of the individual frames).
+ virtual size_t frameCount() { return 1; }
+
+ // The number of repetitions to perform for an animation loop.
+ virtual int repetitionCount() const { return cAnimationNone; }
+
+ // Called to obtain the RGBA32Buffer full of decoded data for rendering.
+ // The decoder plugin will decode as much of the frame as it can before
+ // handing back the buffer.
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t) = 0;
+
+ // Whether or not the underlying image format even supports alpha
+ // transparency.
+ virtual bool supportsAlpha() const { return true; }
+
+ void setIgnoreGammaAndColorProfile(bool flag) { m_ignoreGammaAndColorProfile = flag; }
+
+ // Whether or not the gamma and color profile are applied.
+ bool ignoresGammaAndColorProfile() const { return m_ignoreGammaAndColorProfile; }
+
+ // Sets the "decode failure" flag. For caller convenience (since so
+ // many callers want to return false after calling this), returns false
+ // to enable easy tailcalling. Subclasses may override this to also
+ // clean up any local data.
+ virtual bool setFailed()
+ {
+ m_failed = true;
+ return false;
+ }
+
+ bool failed() const { return m_failed; }
+
+ // Wipe out frames in the frame buffer cache before |clearBeforeFrame|,
+ // assuming this can be done without breaking decoding. Different
+ // decoders place different restrictions on what frames are safe to
+ // destroy, so this is left to them to implement.
+ // For convenience's sake, we provide a default (empty) implementation,
+ // since in practice only GIFs will ever use this.
+ virtual void clearFrameBufferCache(size_t clearBeforeFrame) { }
+
+#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
+ void setMaxNumPixels(int m) { m_maxNumPixels = m; }
+#endif
+
+ protected:
+ void prepareScaleDataIfNecessary();
+ int upperBoundScaledX(int origX, int searchStart = 0);
+ int lowerBoundScaledX(int origX, int searchStart = 0);
+ int upperBoundScaledY(int origY, int searchStart = 0);
+ int lowerBoundScaledY(int origY, int searchStart = 0);
+ int scaledY(int origY, int searchStart = 0);
+
+ RefPtr<SharedBuffer> m_data; // The encoded data.
+ Vector<RGBA32Buffer> m_frameBufferCache;
+ ColorProfile m_colorProfile;
+ bool m_scaled;
+ Vector<int> m_scaledColumns;
+ Vector<int> m_scaledRows;
+ bool m_premultiplyAlpha;
+ bool m_ignoreGammaAndColorProfile;
+
+ private:
+ // Some code paths compute the size of the image as "width * height * 4"
+ // and return it as a (signed) int. Avoid overflow.
+ static bool isOverSize(unsigned width, unsigned height)
+ {
+ // width * height must not exceed (2 ^ 29) - 1, so that we don't
+ // overflow when we multiply by 4.
+ unsigned long long total_size = static_cast<unsigned long long>(width)
+ * static_cast<unsigned long long>(height);
+ return total_size > ((1 << 29) - 1);
+ }
+
+ IntSize m_size;
+ bool m_sizeAvailable;
+ int m_maxNumPixels;
+ bool m_isAllDataReceived;
+ bool m_failed;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp
new file mode 100644
index 0000000..220a1ed
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2008, 2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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 "BMPImageDecoder.h"
+
+#include "BMPImageReader.h"
+
+namespace WebCore {
+
+// Number of bits in .BMP used to store the file header (doesn't match
+// "sizeof(BMPImageDecoder::BitmapFileHeader)" since we omit some fields and
+// don't pack).
+static const size_t sizeOfFileHeader = 14;
+
+BMPImageDecoder::BMPImageDecoder(ImageSource::AlphaOption alphaOption,
+ ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : ImageDecoder(alphaOption, gammaAndColorProfileOption)
+ , m_decodedOffset(0)
+{
+}
+
+void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
+{
+ if (failed())
+ return;
+
+ ImageDecoder::setData(data, allDataReceived);
+ if (m_reader)
+ m_reader->setData(data);
+}
+
+bool BMPImageDecoder::isSizeAvailable()
+{
+ if (!ImageDecoder::isSizeAvailable())
+ decode(true);
+
+ return ImageDecoder::isSizeAvailable();
+}
+
+RGBA32Buffer* BMPImageDecoder::frameBufferAtIndex(size_t index)
+{
+ if (index)
+ return 0;
+
+ if (m_frameBufferCache.isEmpty()) {
+ m_frameBufferCache.resize(1);
+ m_frameBufferCache.first().setPremultiplyAlpha(m_premultiplyAlpha);
+ }
+
+ RGBA32Buffer* buffer = &m_frameBufferCache.first();
+ if (buffer->status() != RGBA32Buffer::FrameComplete)
+ decode(false);
+ return buffer;
+}
+
+bool BMPImageDecoder::setFailed()
+{
+ m_reader.clear();
+ return ImageDecoder::setFailed();
+}
+
+void BMPImageDecoder::decode(bool onlySize)
+{
+ if (failed())
+ return;
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if (!decodeHelper(onlySize) && isAllDataReceived())
+ setFailed();
+ // If we're done decoding the image, we don't need the BMPImageReader
+ // anymore. (If we failed, |m_reader| has already been cleared.)
+ else if (!m_frameBufferCache.isEmpty() && (m_frameBufferCache.first().status() == RGBA32Buffer::FrameComplete))
+ m_reader.clear();
+}
+
+bool BMPImageDecoder::decodeHelper(bool onlySize)
+{
+ size_t imgDataOffset = 0;
+ if ((m_decodedOffset < sizeOfFileHeader) && !processFileHeader(&imgDataOffset))
+ return false;
+
+ if (!m_reader) {
+ m_reader.set(new BMPImageReader(this, m_decodedOffset, imgDataOffset, false));
+ m_reader->setData(m_data.get());
+ }
+
+ if (!m_frameBufferCache.isEmpty())
+ m_reader->setBuffer(&m_frameBufferCache.first());
+
+ return m_reader->decodeBMP(onlySize);
+}
+
+bool BMPImageDecoder::processFileHeader(size_t* imgDataOffset)
+{
+ ASSERT(imgDataOffset);
+
+ // Read file header.
+ ASSERT(!m_decodedOffset);
+ if (m_data->size() < sizeOfFileHeader)
+ return false;
+ const uint16_t fileType = (m_data->data()[0] << 8) | static_cast<uint8_t>(m_data->data()[1]);
+ *imgDataOffset = readUint32(10);
+ m_decodedOffset = sizeOfFileHeader;
+
+ // See if this is a bitmap filetype we understand.
+ enum {
+ BMAP = 0x424D, // "BM"
+ // The following additional OS/2 2.x header values (see
+ // http://www.fileformat.info/format/os2bmp/egff.htm ) aren't widely
+ // decoded, and are unlikely to be in much use.
+ /*
+ ICON = 0x4943, // "IC"
+ POINTER = 0x5054, // "PT"
+ COLORICON = 0x4349, // "CI"
+ COLORPOINTER = 0x4350, // "CP"
+ BITMAPARRAY = 0x4241, // "BA"
+ */
+ };
+ return (fileType == BMAP) || setFailed();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h
new file mode 100644
index 0000000..5f4ed82
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2008, 2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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 BMPImageDecoder_h
+#define BMPImageDecoder_h
+
+#include "BMPImageReader.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+ // This class decodes the BMP image format.
+ class BMPImageDecoder : public ImageDecoder {
+ public:
+ BMPImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+
+ // ImageDecoder
+ virtual String filenameExtension() const { return "bmp"; }
+ virtual void setData(SharedBuffer*, bool allDataReceived);
+ virtual bool isSizeAvailable();
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+ // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid
+ // accessing deleted memory, especially when calling this from inside
+ // BMPImageReader!
+ virtual bool setFailed();
+
+ private:
+ inline uint32_t readUint32(int offset) const
+ {
+ return BMPImageReader::readUint32(m_data.get(), m_decodedOffset + offset);
+ }
+
+ // Decodes the image. If |onlySize| is true, stops decoding after
+ // calculating the image size. If decoding fails but there is no more
+ // data coming, sets the "decode failure" flag.
+ void decode(bool onlySize);
+
+ // Decodes the image. If |onlySize| is true, stops decoding after
+ // calculating the image size. Returns whether decoding succeeded.
+ bool decodeHelper(bool onlySize);
+
+ // Processes the file header at the beginning of the data. Sets
+ // |*imgDataOffset| based on the header contents. Returns true if the
+ // file header could be decoded.
+ bool processFileHeader(size_t* imgDataOffset);
+
+ // An index into |m_data| representing how much we've already decoded.
+ // Note that this only tracks data _this_ class decodes; once the
+ // BMPImageReader takes over this will not be updated further.
+ size_t m_decodedOffset;
+
+ // The reader used to do most of the BMP decoding.
+ OwnPtr<BMPImageReader> m_reader;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp
new file mode 100644
index 0000000..93bedf3
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 2008, 2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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 "BMPImageReader.h"
+
+namespace WebCore {
+
+BMPImageReader::BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask)
+ : m_parent(parent)
+ , m_buffer(0)
+ , m_decodedOffset(decodedAndHeaderOffset)
+ , m_headerOffset(decodedAndHeaderOffset)
+ , m_imgDataOffset(imgDataOffset)
+ , m_isOS21x(false)
+ , m_isOS22x(false)
+ , m_isTopDown(false)
+ , m_needToProcessBitmasks(false)
+ , m_needToProcessColorTable(false)
+ , m_tableSizeInBytes(0)
+ , m_seenNonZeroAlphaPixel(false)
+ , m_seenZeroAlphaPixel(false)
+ , m_andMaskState(usesAndMask ? NotYetDecoded : None)
+{
+ // Clue-in decodeBMP() that we need to detect the correct info header size.
+ memset(&m_infoHeader, 0, sizeof(m_infoHeader));
+}
+
+bool BMPImageReader::decodeBMP(bool onlySize)
+{
+ // Calculate size of info header.
+ if (!m_infoHeader.biSize && !readInfoHeaderSize())
+ return false;
+
+ // Read and process info header.
+ if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) && !processInfoHeader())
+ return false;
+
+ // processInfoHeader() set the size, so if that's all we needed, we're done.
+ if (onlySize)
+ return true;
+
+ // Read and process the bitmasks, if needed.
+ if (m_needToProcessBitmasks && !processBitmasks())
+ return false;
+
+ // Read and process the color table, if needed.
+ if (m_needToProcessColorTable && !processColorTable())
+ return false;
+
+ // Initialize the framebuffer if needed.
+ ASSERT(m_buffer); // Parent should set this before asking us to decode!
+ if (m_buffer->status() == RGBA32Buffer::FrameEmpty) {
+ if (!m_buffer->setSize(m_parent->size().width(), m_parent->size().height()))
+ return m_parent->setFailed(); // Unable to allocate.
+ m_buffer->setStatus(RGBA32Buffer::FramePartial);
+ // setSize() calls eraseARGB(), which resets the alpha flag, so we force
+ // it back to false here. We'll set it true below in all cases where
+ // these 0s could actually show through.
+ m_buffer->setHasAlpha(false);
+
+ // For BMPs, the frame always fills the entire image.
+ m_buffer->setRect(IntRect(IntPoint(), m_parent->size()));
+
+ if (!m_isTopDown)
+ m_coord.setY(m_parent->size().height() - 1);
+ }
+
+ // Decode the data.
+ if ((m_andMaskState != Decoding) && !pastEndOfImage(0)) {
+ if ((m_infoHeader.biCompression != RLE4) && (m_infoHeader.biCompression != RLE8) && (m_infoHeader.biCompression != RLE24)) {
+ const ProcessingResult result = processNonRLEData(false, 0);
+ if (result != Success)
+ return (result == Failure) ? m_parent->setFailed() : false;
+ } else if (!processRLEData())
+ return false;
+ }
+
+ // If the image has an AND mask and there was no alpha data, process the
+ // mask.
+ if ((m_andMaskState == NotYetDecoded) && !m_buffer->hasAlpha()) {
+ // Reset decoding coordinates to start of image.
+ m_coord.setX(0);
+ m_coord.setY(m_isTopDown ? 0 : (m_parent->size().height() - 1));
+
+ // The AND mask is stored as 1-bit data.
+ m_infoHeader.biBitCount = 1;
+
+ m_andMaskState = Decoding;
+ }
+ if (m_andMaskState == Decoding) {
+ const ProcessingResult result = processNonRLEData(false, 0);
+ if (result != Success)
+ return (result == Failure) ? m_parent->setFailed() : false;
+ }
+
+ // Done!
+ m_buffer->setStatus(RGBA32Buffer::FrameComplete);
+ return true;
+}
+
+bool BMPImageReader::readInfoHeaderSize()
+{
+ // Get size of info header.
+ ASSERT(m_decodedOffset == m_headerOffset);
+ if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < 4))
+ return false;
+ m_infoHeader.biSize = readUint32(0);
+ // Don't increment m_decodedOffset here, it just makes the code in
+ // processInfoHeader() more confusing.
+
+ // Don't allow the header to overflow (which would be harmless here, but
+ // problematic or at least confusing in other places), or to overrun the
+ // image data.
+ if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize))))
+ return m_parent->setFailed();
+
+ // See if this is a header size we understand:
+ // OS/2 1.x: 12
+ if (m_infoHeader.biSize == 12)
+ m_isOS21x = true;
+ // Windows V3: 40
+ else if ((m_infoHeader.biSize == 40) || isWindowsV4Plus())
+ ;
+ // OS/2 2.x: any multiple of 4 between 16 and 64, inclusive, or 42 or 46
+ else if ((m_infoHeader.biSize >= 16) && (m_infoHeader.biSize <= 64) && (!(m_infoHeader.biSize & 3) || (m_infoHeader.biSize == 42) || (m_infoHeader.biSize == 46)))
+ m_isOS22x = true;
+ else
+ return m_parent->setFailed();
+
+ return true;
+}
+
+bool BMPImageReader::processInfoHeader()
+{
+ // Read info header.
+ ASSERT(m_decodedOffset == m_headerOffset);
+ if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) || !readInfoHeader())
+ return false;
+ m_decodedOffset += m_infoHeader.biSize;
+
+ // Sanity-check header values.
+ if (!isInfoHeaderValid())
+ return m_parent->setFailed();
+
+ // Set our size.
+ if (!m_parent->setSize(m_infoHeader.biWidth, m_infoHeader.biHeight))
+ return false;
+
+ // For paletted images, bitmaps can set biClrUsed to 0 to mean "all
+ // colors", so set it to the maximum number of colors for this bit depth.
+ // Also do this for bitmaps that put too large a value here.
+ if (m_infoHeader.biBitCount < 16) {
+ const uint32_t maxColors = static_cast<uint32_t>(1) << m_infoHeader.biBitCount;
+ if (!m_infoHeader.biClrUsed || (m_infoHeader.biClrUsed > maxColors))
+ m_infoHeader.biClrUsed = maxColors;
+ }
+
+ // For any bitmaps that set their BitCount to the wrong value, reset the
+ // counts now that we've calculated the number of necessary colors, since
+ // other code relies on this value being correct.
+ if (m_infoHeader.biCompression == RLE8)
+ m_infoHeader.biBitCount = 8;
+ else if (m_infoHeader.biCompression == RLE4)
+ m_infoHeader.biBitCount = 4;
+
+ // Tell caller what still needs to be processed.
+ if (m_infoHeader.biBitCount >= 16)
+ m_needToProcessBitmasks = true;
+ else if (m_infoHeader.biBitCount)
+ m_needToProcessColorTable = true;
+
+ return true;
+}
+
+bool BMPImageReader::readInfoHeader()
+{
+ // Pre-initialize some fields that not all headers set.
+ m_infoHeader.biCompression = RGB;
+ m_infoHeader.biClrUsed = 0;
+
+ if (m_isOS21x) {
+ m_infoHeader.biWidth = readUint16(4);
+ m_infoHeader.biHeight = readUint16(6);
+ ASSERT(m_andMaskState == None); // ICO is a Windows format, not OS/2!
+ m_infoHeader.biBitCount = readUint16(10);
+ return true;
+ }
+
+ m_infoHeader.biWidth = readUint32(4);
+ m_infoHeader.biHeight = readUint32(8);
+ if (m_andMaskState != None)
+ m_infoHeader.biHeight /= 2;
+ m_infoHeader.biBitCount = readUint16(14);
+
+ // Read compression type, if present.
+ if (m_infoHeader.biSize >= 20) {
+ uint32_t biCompression = readUint32(16);
+
+ // Detect OS/2 2.x-specific compression types.
+ if ((biCompression == 3) && (m_infoHeader.biBitCount == 1)) {
+ m_infoHeader.biCompression = HUFFMAN1D;
+ m_isOS22x = true;
+ } else if ((biCompression == 4) && (m_infoHeader.biBitCount == 24)) {
+ m_infoHeader.biCompression = RLE24;
+ m_isOS22x = true;
+ } else if (biCompression > 5)
+ return m_parent->setFailed(); // Some type we don't understand.
+ else
+ m_infoHeader.biCompression = static_cast<CompressionType>(biCompression);
+ }
+
+ // Read colors used, if present.
+ if (m_infoHeader.biSize >= 36)
+ m_infoHeader.biClrUsed = readUint32(32);
+
+ // Windows V4+ can safely read the four bitmasks from 40-56 bytes in, so do
+ // that here. If the bit depth is less than 16, these values will be
+ // ignored by the image data decoders. If the bit depth is at least 16 but
+ // the compression format isn't BITFIELDS, these values will be ignored and
+ // overwritten* in processBitmasks().
+ // NOTE: We allow alpha here. Microsoft doesn't really document this well,
+ // but some BMPs appear to use it.
+ //
+ // For non-Windows V4+, m_bitMasks[] et. al will be initialized later
+ // during processBitmasks().
+ //
+ // *Except the alpha channel. Bizarrely, some RGB bitmaps expect decoders
+ // to pay attention to the alpha mask here, so there's a special case in
+ // processBitmasks() that doesn't always overwrite that value.
+ if (isWindowsV4Plus()) {
+ m_bitMasks[0] = readUint32(40);
+ m_bitMasks[1] = readUint32(44);
+ m_bitMasks[2] = readUint32(48);
+ m_bitMasks[3] = readUint32(52);
+ }
+
+ // Detect top-down BMPs.
+ if (m_infoHeader.biHeight < 0) {
+ m_isTopDown = true;
+ m_infoHeader.biHeight = -m_infoHeader.biHeight;
+ }
+
+ return true;
+}
+
+bool BMPImageReader::isInfoHeaderValid() const
+{
+ // Non-positive widths/heights are invalid. (We've already flipped the
+ // sign of the height for top-down bitmaps.)
+ if ((m_infoHeader.biWidth <= 0) || !m_infoHeader.biHeight)
+ return false;
+
+ // Only Windows V3+ has top-down bitmaps.
+ if (m_isTopDown && (m_isOS21x || m_isOS22x))
+ return false;
+
+ // Only bit depths of 1, 4, 8, or 24 are universally supported.
+ if ((m_infoHeader.biBitCount != 1) && (m_infoHeader.biBitCount != 4) && (m_infoHeader.biBitCount != 8) && (m_infoHeader.biBitCount != 24)) {
+ // Windows V3+ additionally supports bit depths of 0 (for embedded
+ // JPEG/PNG images), 16, and 32.
+ if (m_isOS21x || m_isOS22x || (m_infoHeader.biBitCount && (m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32)))
+ return false;
+ }
+
+ // Each compression type is only valid with certain bit depths (except RGB,
+ // which can be used with any bit depth). Also, some formats do not
+ // some compression types.
+ switch (m_infoHeader.biCompression) {
+ case RGB:
+ if (!m_infoHeader.biBitCount)
+ return false;
+ break;
+
+ case RLE8:
+ // Supposedly there are undocumented formats like "BitCount = 1,
+ // Compression = RLE4" (which means "4 bit, but with a 2-color table"),
+ // so also allow the paletted RLE compression types to have too low a
+ // bit count; we'll correct this later.
+ if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 8))
+ return false;
+ break;
+
+ case RLE4:
+ // See comments in RLE8.
+ if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 4))
+ return false;
+ break;
+
+ case BITFIELDS:
+ // Only valid for Windows V3+.
+ if (m_isOS21x || m_isOS22x || ((m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32)))
+ return false;
+ break;
+
+ case JPEG:
+ case PNG:
+ // Only valid for Windows V3+.
+ if (m_isOS21x || m_isOS22x || m_infoHeader.biBitCount)
+ return false;
+ break;
+
+ case HUFFMAN1D:
+ // Only valid for OS/2 2.x.
+ if (!m_isOS22x || (m_infoHeader.biBitCount != 1))
+ return false;
+ break;
+
+ case RLE24:
+ // Only valid for OS/2 2.x.
+ if (!m_isOS22x || (m_infoHeader.biBitCount != 24))
+ return false;
+ break;
+
+ default:
+ // Some type we don't understand. This should have been caught in
+ // readInfoHeader().
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+
+ // Top-down bitmaps cannot be compressed; they must be RGB or BITFIELDS.
+ if (m_isTopDown && (m_infoHeader.biCompression != RGB) && (m_infoHeader.biCompression != BITFIELDS))
+ return false;
+
+ // Reject the following valid bitmap types that we don't currently bother
+ // decoding. Few other people decode these either, they're unlikely to be
+ // in much use.
+ // TODO(pkasting): Consider supporting these someday.
+ // * Bitmaps larger than 2^16 pixels in either dimension (Windows
+ // probably doesn't draw these well anyway, and the decoded data would
+ // take a lot of memory).
+ if ((m_infoHeader.biWidth >= (1 << 16)) || (m_infoHeader.biHeight >= (1 << 16)))
+ return false;
+ // * Windows V3+ JPEG-in-BMP and PNG-in-BMP bitmaps (supposedly not found
+ // in the wild, only used to send data to printers?).
+ if ((m_infoHeader.biCompression == JPEG) || (m_infoHeader.biCompression == PNG))
+ return false;
+ // * OS/2 2.x Huffman-encoded monochrome bitmaps (see
+ // http://www.fileformat.info/mirror/egff/ch09_05.htm , re: "G31D"
+ // algorithm).
+ if (m_infoHeader.biCompression == HUFFMAN1D)
+ return false;
+
+ return true;
+}
+
+bool BMPImageReader::processBitmasks()
+{
+ // Create m_bitMasks[] values.
+ if (m_infoHeader.biCompression != BITFIELDS) {
+ // The format doesn't actually use bitmasks. To simplify the decode
+ // logic later, create bitmasks for the RGB data. For Windows V4+,
+ // this overwrites the masks we read from the header, which are
+ // supposed to be ignored in non-BITFIELDS cases.
+ // 16 bits: MSB <- xRRRRRGG GGGBBBBB -> LSB
+ // 24/32 bits: MSB <- [AAAAAAAA] RRRRRRRR GGGGGGGG BBBBBBBB -> LSB
+ const int numBits = (m_infoHeader.biBitCount == 16) ? 5 : 8;
+ for (int i = 0; i <= 2; ++i)
+ m_bitMasks[i] = ((static_cast<uint32_t>(1) << (numBits * (3 - i))) - 1) ^ ((static_cast<uint32_t>(1) << (numBits * (2 - i))) - 1);
+
+ // For Windows V4+ 32-bit RGB, don't overwrite the alpha mask from the
+ // header (see note in readInfoHeader()).
+ if (m_infoHeader.biBitCount < 32)
+ m_bitMasks[3] = 0;
+ else if (!isWindowsV4Plus())
+ m_bitMasks[3] = static_cast<uint32_t>(0xff000000);
+ } else if (!isWindowsV4Plus()) {
+ // For Windows V4+ BITFIELDS mode bitmaps, this was already done when
+ // we read the info header.
+
+ // Fail if we don't have enough file space for the bitmasks.
+ static const size_t SIZEOF_BITMASKS = 12;
+ if (((m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS) < (m_headerOffset + m_infoHeader.biSize)) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS))))
+ return m_parent->setFailed();
+
+ // Read bitmasks.
+ if ((m_data->size() - m_decodedOffset) < SIZEOF_BITMASKS)
+ return false;
+ m_bitMasks[0] = readUint32(0);
+ m_bitMasks[1] = readUint32(4);
+ m_bitMasks[2] = readUint32(8);
+ // No alpha in anything other than Windows V4+.
+ m_bitMasks[3] = 0;
+
+ m_decodedOffset += SIZEOF_BITMASKS;
+ }
+
+ // We've now decoded all the non-image data we care about. Skip anything
+ // else before the actual raster data.
+ if (m_imgDataOffset)
+ m_decodedOffset = m_imgDataOffset;
+ m_needToProcessBitmasks = false;
+
+ // Check masks and set shift values.
+ for (int i = 0; i < 4; ++i) {
+ // Trim the mask to the allowed bit depth. Some Windows V4+ BMPs
+ // specify a bogus alpha channel in bits that don't exist in the pixel
+ // data (for example, bits 25-31 in a 24-bit RGB format).
+ if (m_infoHeader.biBitCount < 32)
+ m_bitMasks[i] &= ((static_cast<uint32_t>(1) << m_infoHeader.biBitCount) - 1);
+
+ // For empty masks (common on the alpha channel, especially after the
+ // trimming above), quickly clear the shifts and continue, to avoid an
+ // infinite loop in the counting code below.
+ uint32_t tempMask = m_bitMasks[i];
+ if (!tempMask) {
+ m_bitShiftsRight[i] = m_bitShiftsLeft[i] = 0;
+ continue;
+ }
+
+ // Make sure bitmask does not overlap any other bitmasks.
+ for (int j = 0; j < i; ++j) {
+ if (tempMask & m_bitMasks[j])
+ return m_parent->setFailed();
+ }
+
+ // Count offset into pixel data.
+ for (m_bitShiftsRight[i] = 0; !(tempMask & 1); tempMask >>= 1)
+ ++m_bitShiftsRight[i];
+
+ // Count size of mask.
+ for (m_bitShiftsLeft[i] = 8; tempMask & 1; tempMask >>= 1)
+ --m_bitShiftsLeft[i];
+
+ // Make sure bitmask is contiguous.
+ if (tempMask)
+ return m_parent->setFailed();
+
+ // Since RGBABuffer tops out at 8 bits per channel, adjust the shift
+ // amounts to use the most significant 8 bits of the channel.
+ if (m_bitShiftsLeft[i] < 0) {
+ m_bitShiftsRight[i] -= m_bitShiftsLeft[i];
+ m_bitShiftsLeft[i] = 0;
+ }
+ }
+
+ return true;
+}
+
+bool BMPImageReader::processColorTable()
+{
+ m_tableSizeInBytes = m_infoHeader.biClrUsed * (m_isOS21x ? 3 : 4);
+
+ // Fail if we don't have enough file space for the color table.
+ if (((m_headerOffset + m_infoHeader.biSize + m_tableSizeInBytes) < (m_headerOffset + m_infoHeader.biSize)) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize + m_tableSizeInBytes))))
+ return m_parent->setFailed();
+
+ // Read color table.
+ if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < m_tableSizeInBytes))
+ return false;
+ m_colorTable.resize(m_infoHeader.biClrUsed);
+ for (size_t i = 0; i < m_infoHeader.biClrUsed; ++i) {
+ m_colorTable[i].rgbBlue = m_data->data()[m_decodedOffset++];
+ m_colorTable[i].rgbGreen = m_data->data()[m_decodedOffset++];
+ m_colorTable[i].rgbRed = m_data->data()[m_decodedOffset++];
+ // Skip padding byte (not present on OS/2 1.x).
+ if (!m_isOS21x)
+ ++m_decodedOffset;
+ }
+
+ // We've now decoded all the non-image data we care about. Skip anything
+ // else before the actual raster data.
+ if (m_imgDataOffset)
+ m_decodedOffset = m_imgDataOffset;
+ m_needToProcessColorTable = false;
+
+ return true;
+}
+
+bool BMPImageReader::processRLEData()
+{
+ if (m_decodedOffset > m_data->size())
+ return false;
+
+ // RLE decoding is poorly specified. Two main problems:
+ // (1) Are EOL markers necessary? What happens when we have too many
+ // pixels for one row?
+ // http://www.fileformat.info/format/bmp/egff.htm says extra pixels
+ // should wrap to the next line. Real BMPs I've encountered seem to
+ // instead expect extra pixels to be ignored until the EOL marker is
+ // seen, although this has only happened in a few cases and I suspect
+ // those BMPs may be invalid. So we only change lines on EOL (or Delta
+ // with dy > 0), and fail in most cases when pixels extend past the end
+ // of the line.
+ // (2) When Delta, EOL, or EOF are seen, what happens to the "skipped"
+ // pixels?
+ // http://www.daubnet.com/formats/BMP.html says these should be filled
+ // with color 0. However, the "do nothing" and "don't care" comments
+ // of other references suggest leaving these alone, i.e. letting them
+ // be transparent to the background behind the image. This seems to
+ // match how MSPAINT treats BMPs, so we do that. Note that when we
+ // actually skip pixels for a case like this, we need to note on the
+ // framebuffer that we have alpha.
+
+ // Impossible to decode row-at-a-time, so just do things as a stream of
+ // bytes.
+ while (true) {
+ // Every entry takes at least two bytes; bail if there isn't enough
+ // data.
+ if ((m_data->size() - m_decodedOffset) < 2)
+ return false;
+
+ // For every entry except EOF, we'd better not have reached the end of
+ // the image.
+ const uint8_t count = m_data->data()[m_decodedOffset];
+ const uint8_t code = m_data->data()[m_decodedOffset + 1];
+ if ((count || (code != 1)) && pastEndOfImage(0))
+ return m_parent->setFailed();
+
+ // Decode.
+ if (!count) {
+ switch (code) {
+ case 0: // Magic token: EOL
+ // Skip any remaining pixels in this row.
+ if (m_coord.x() < m_parent->size().width())
+ m_buffer->setHasAlpha(true);
+ moveBufferToNextRow();
+
+ m_decodedOffset += 2;
+ break;
+
+ case 1: // Magic token: EOF
+ // Skip any remaining pixels in the image.
+ if ((m_coord.x() < m_parent->size().width()) || (m_isTopDown ? (m_coord.y() < (m_parent->size().height() - 1)) : (m_coord.y() > 0)))
+ m_buffer->setHasAlpha(true);
+ return true;
+
+ case 2: { // Magic token: Delta
+ // The next two bytes specify dx and dy. Bail if there isn't
+ // enough data.
+ if ((m_data->size() - m_decodedOffset) < 4)
+ return false;
+
+ // Fail if this takes us past the end of the desired row or
+ // past the end of the image.
+ const uint8_t dx = m_data->data()[m_decodedOffset + 2];
+ const uint8_t dy = m_data->data()[m_decodedOffset + 3];
+ if (dx || dy)
+ m_buffer->setHasAlpha(true);
+ if (((m_coord.x() + dx) > m_parent->size().width()) || pastEndOfImage(dy))
+ return m_parent->setFailed();
+
+ // Skip intervening pixels.
+ m_coord.move(dx, m_isTopDown ? dy : -dy);
+
+ m_decodedOffset += 4;
+ break;
+ }
+
+ default: { // Absolute mode
+ // |code| pixels specified as in BI_RGB, zero-padded at the end
+ // to a multiple of 16 bits.
+ // Because processNonRLEData() expects m_decodedOffset to
+ // point to the beginning of the pixel data, bump it past
+ // the escape bytes and then reset if decoding failed.
+ m_decodedOffset += 2;
+ const ProcessingResult result = processNonRLEData(true, code);
+ if (result == Failure)
+ return m_parent->setFailed();
+ if (result == InsufficientData) {
+ m_decodedOffset -= 2;
+ return false;
+ }
+ break;
+ }
+ }
+ } else { // Encoded mode
+ // The following color data is repeated for |count| total pixels.
+ // Strangely, some BMPs seem to specify excessively large counts
+ // here; ignore pixels past the end of the row.
+ const int endX = std::min(m_coord.x() + count, m_parent->size().width());
+
+ if (m_infoHeader.biCompression == RLE24) {
+ // Bail if there isn't enough data.
+ if ((m_data->size() - m_decodedOffset) < 4)
+ return false;
+
+ // One BGR triple that we copy |count| times.
+ fillRGBA(endX, m_data->data()[m_decodedOffset + 3], m_data->data()[m_decodedOffset + 2], code, 0xff);
+ m_decodedOffset += 4;
+ } else {
+ // RLE8 has one color index that gets repeated; RLE4 has two
+ // color indexes in the upper and lower 4 bits of the byte,
+ // which are alternated.
+ size_t colorIndexes[2] = {code, code};
+ if (m_infoHeader.biCompression == RLE4) {
+ colorIndexes[0] = (colorIndexes[0] >> 4) & 0xf;
+ colorIndexes[1] &= 0xf;
+ }
+ if ((colorIndexes[0] >= m_infoHeader.biClrUsed) || (colorIndexes[1] >= m_infoHeader.biClrUsed))
+ return m_parent->setFailed();
+ for (int which = 0; m_coord.x() < endX; ) {
+ setI(colorIndexes[which]);
+ which = !which;
+ }
+
+ m_decodedOffset += 2;
+ }
+ }
+ }
+}
+
+BMPImageReader::ProcessingResult BMPImageReader::processNonRLEData(bool inRLE, int numPixels)
+{
+ if (m_decodedOffset > m_data->size())
+ return InsufficientData;
+
+ if (!inRLE)
+ numPixels = m_parent->size().width();
+
+ // Fail if we're being asked to decode more pixels than remain in the row.
+ const int endX = m_coord.x() + numPixels;
+ if (endX > m_parent->size().width())
+ return Failure;
+
+ // Determine how many bytes of data the requested number of pixels
+ // requires.
+ const size_t pixelsPerByte = 8 / m_infoHeader.biBitCount;
+ const size_t bytesPerPixel = m_infoHeader.biBitCount / 8;
+ const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) : (numPixels * bytesPerPixel);
+ // RLE runs are zero-padded at the end to a multiple of 16 bits. Non-RLE
+ // data is in rows and is zero-padded to a multiple of 32 bits.
+ const size_t alignBits = inRLE ? 1 : 3;
+ const size_t paddedNumBytes = (unpaddedNumBytes + alignBits) & ~alignBits;
+
+ // Decode as many rows as we can. (For RLE, where we only want to decode
+ // one row, we've already checked that this condition is true.)
+ while (!pastEndOfImage(0)) {
+ // Bail if we don't have enough data for the desired number of pixels.
+ if ((m_data->size() - m_decodedOffset) < paddedNumBytes)
+ return InsufficientData;
+
+ if (m_infoHeader.biBitCount < 16) {
+ // Paletted data. Pixels are stored little-endian within bytes.
+ // Decode pixels one byte at a time, left to right (so, starting at
+ // the most significant bits in the byte).
+ const uint8_t mask = (1 << m_infoHeader.biBitCount) - 1;
+ for (size_t byte = 0; byte < unpaddedNumBytes; ++byte) {
+ uint8_t pixelData = m_data->data()[m_decodedOffset + byte];
+ for (size_t pixel = 0; (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) {
+ const size_t colorIndex = (pixelData >> (8 - m_infoHeader.biBitCount)) & mask;
+ if (m_andMaskState == Decoding) {
+ // There's no way to accurately represent an AND + XOR
+ // operation as an RGBA image, so where the AND values
+ // are 1, we simply set the framebuffer pixels to fully
+ // transparent, on the assumption that most ICOs on the
+ // web will not be doing a lot of inverting.
+ if (colorIndex) {
+ setRGBA(0, 0, 0, 0);
+ m_buffer->setHasAlpha(true);
+ } else
+ m_coord.move(1, 0);
+ } else {
+ if (colorIndex >= m_infoHeader.biClrUsed)
+ return Failure;
+ setI(colorIndex);
+ }
+ pixelData <<= m_infoHeader.biBitCount;
+ }
+ }
+ } else {
+ // RGB data. Decode pixels one at a time, left to right.
+ while (m_coord.x() < endX) {
+ const uint32_t pixel = readCurrentPixel(bytesPerPixel);
+
+ // Some BMPs specify an alpha channel but don't actually use it
+ // (it contains all 0s). To avoid displaying these images as
+ // fully-transparent, decode as if images are fully opaque
+ // until we actually see a non-zero alpha value; at that point,
+ // reset any previously-decoded pixels to fully transparent and
+ // continue decoding based on the real alpha channel values.
+ // As an optimization, avoid setting "hasAlpha" to true for
+ // images where all alpha values are 255; opaque images are
+ // faster to draw.
+ int alpha = getAlpha(pixel);
+ if (!m_seenNonZeroAlphaPixel && !alpha) {
+ m_seenZeroAlphaPixel = true;
+ alpha = 255;
+ } else {
+ m_seenNonZeroAlphaPixel = true;
+ if (m_seenZeroAlphaPixel) {
+ m_buffer->zeroFill();
+ m_seenZeroAlphaPixel = false;
+ } else if (alpha != 255)
+ m_buffer->setHasAlpha(true);
+ }
+
+ setRGBA(getComponent(pixel, 0), getComponent(pixel, 1),
+ getComponent(pixel, 2), alpha);
+ }
+ }
+
+ // Success, keep going.
+ m_decodedOffset += paddedNumBytes;
+ if (inRLE)
+ return Success;
+ moveBufferToNextRow();
+ }
+
+ // Finished decoding whole image.
+ return Success;
+}
+
+void BMPImageReader::moveBufferToNextRow()
+{
+ m_coord.move(-m_coord.x(), m_isTopDown ? 1 : -1);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h
new file mode 100644
index 0000000..0a6dc84
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2008, 2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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 BMPImageReader_h
+#define BMPImageReader_h
+
+#include <stdint.h>
+#include "ImageDecoder.h"
+
+namespace WebCore {
+
+ // This class decodes a BMP image. It is used in the BMP and ICO decoders,
+ // which wrap it in the appropriate code to read file headers, etc.
+ class BMPImageReader {
+ public:
+ // Read a value from |data[offset]|, converting from little to native
+ // endianness.
+ static inline uint16_t readUint16(SharedBuffer* data, int offset)
+ {
+ uint16_t result;
+ memcpy(&result, &data->data()[offset], 2);
+ #if CPU(BIG_ENDIAN)
+ result = ((result & 0xff) << 8) | ((result & 0xff00) >> 8);
+ #endif
+ return result;
+ }
+
+ static inline uint32_t readUint32(SharedBuffer* data, int offset)
+ {
+ uint32_t result;
+ memcpy(&result, &data->data()[offset], 4);
+ #if CPU(BIG_ENDIAN)
+ result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) | ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24);
+ #endif
+ return result;
+ }
+
+ // |parent| is the decoder that owns us.
+ // |startOffset| points to the start of the BMP within the file.
+ // |buffer| points at an empty RGBA32Buffer that we'll initialize and
+ // fill with decoded data.
+ BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask);
+
+ void setBuffer(RGBA32Buffer* buffer) { m_buffer = buffer; }
+ void setData(SharedBuffer* data) { m_data = data; }
+
+ // Does the actual decoding. If |onlySize| is true, decoding only
+ // progresses as far as necessary to get the image size. Returns
+ // whether decoding succeeded.
+ bool decodeBMP(bool onlySize);
+
+ private:
+ // The various BMP compression types. We don't currently decode all
+ // these.
+ enum CompressionType {
+ // Universal types
+ RGB = 0,
+ RLE8 = 1,
+ RLE4 = 2,
+ // Windows V3+ only
+ BITFIELDS = 3,
+ JPEG = 4,
+ PNG = 5,
+ // OS/2 2.x-only
+ HUFFMAN1D, // Stored in file as 3
+ RLE24, // Stored in file as 4
+ };
+ enum AndMaskState {
+ None,
+ NotYetDecoded,
+ Decoding,
+ };
+ enum ProcessingResult {
+ Success,
+ Failure,
+ InsufficientData,
+ };
+
+ // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE
+ // structs, but with unnecessary entries removed.
+ struct BitmapInfoHeader {
+ uint32_t biSize;
+ int32_t biWidth;
+ int32_t biHeight;
+ uint16_t biBitCount;
+ CompressionType biCompression;
+ uint32_t biClrUsed;
+ };
+ struct RGBTriple {
+ uint8_t rgbBlue;
+ uint8_t rgbGreen;
+ uint8_t rgbRed;
+ };
+
+ inline uint16_t readUint16(int offset) const
+ {
+ return readUint16(m_data.get(), m_decodedOffset + offset);
+ }
+
+ inline uint32_t readUint32(int offset) const
+ {
+ return readUint32(m_data.get(), m_decodedOffset + offset);
+ }
+
+ // Determines the size of the BMP info header. Returns true if the size
+ // is valid.
+ bool readInfoHeaderSize();
+
+ // Processes the BMP info header. Returns true if the info header could
+ // be decoded.
+ bool processInfoHeader();
+
+ // Helper function for processInfoHeader() which does the actual reading
+ // of header values from the byte stream. Returns false on error.
+ bool readInfoHeader();
+
+ // Returns true if this is a Windows V4+ BMP.
+ inline bool isWindowsV4Plus() const
+ {
+ // Windows V4 info header is 108 bytes. V5 is 124 bytes.
+ return (m_infoHeader.biSize == 108) || (m_infoHeader.biSize == 124);
+ }
+
+ // Returns false if consistency errors are found in the info header.
+ bool isInfoHeaderValid() const;
+
+ // For BI_BITFIELDS images, initializes the m_bitMasks[] and
+ // m_bitOffsets[] arrays. processInfoHeader() will initialize these for
+ // other compression types where needed.
+ bool processBitmasks();
+
+ // For paletted images, allocates and initializes the m_colorTable[]
+ // array.
+ bool processColorTable();
+
+ // Processes an RLE-encoded image. Returns true if the entire image was
+ // decoded.
+ bool processRLEData();
+
+ // Processes a set of non-RLE-compressed pixels. Two cases:
+ // * inRLE = true: the data is inside an RLE-encoded bitmap. Tries to
+ // process |numPixels| pixels on the current row.
+ // * inRLE = false: the data is inside a non-RLE-encoded bitmap.
+ // |numPixels| is ignored. Expects |m_coord| to point at the
+ // beginning of the next row to be decoded. Tries to process as
+ // many complete rows as possible. Returns InsufficientData if
+ // there wasn't enough data to decode the whole image.
+ //
+ // This function returns a ProcessingResult instead of a bool so that it
+ // can avoid calling m_parent->setFailed(), which could lead to memory
+ // corruption since that will delete |this| but some callers still want
+ // to access member variables after this returns.
+ ProcessingResult processNonRLEData(bool inRLE, int numPixels);
+
+ // Returns true if the current y-coordinate plus |numRows| would be past
+ // the end of the image. Here "plus" means "toward the end of the
+ // image", so downwards for m_isTopDown images and upwards otherwise.
+ inline bool pastEndOfImage(int numRows)
+ {
+ return m_isTopDown ? ((m_coord.y() + numRows) >= m_parent->size().height()) : ((m_coord.y() - numRows) < 0);
+ }
+
+ // Returns the pixel data for the current X coordinate in a uint32_t.
+ // Assumes m_decodedOffset has been set to the beginning of the current
+ // row.
+ // NOTE: Only as many bytes of the return value as are needed to hold
+ // the pixel data will actually be set.
+ inline uint32_t readCurrentPixel(int bytesPerPixel) const
+ {
+ const int offset = m_coord.x() * bytesPerPixel;
+ switch (bytesPerPixel) {
+ case 2:
+ return readUint16(offset);
+
+ case 3: {
+ // It doesn't matter that we never set the most significant byte
+ // of the return value here in little-endian mode, the caller
+ // won't read it.
+ uint32_t pixel;
+ memcpy(&pixel, &m_data->data()[m_decodedOffset + offset], 3);
+ #if CPU(BIG_ENDIAN)
+ pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) | ((pixel & 0xff000000) >> 24);
+ #endif
+ return pixel;
+ }
+
+ case 4:
+ return readUint32(offset);
+
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ }
+
+ // Returns the value of the desired component (0, 1, 2, 3 == R, G, B, A)
+ // in the given pixel data.
+ inline unsigned getComponent(uint32_t pixel, int component) const
+ {
+ return ((pixel & m_bitMasks[component]) >> m_bitShiftsRight[component]) << m_bitShiftsLeft[component];
+ }
+
+ inline unsigned getAlpha(uint32_t pixel) const
+ {
+ // For images without alpha, return alpha of 0xff.
+ return m_bitMasks[3] ? getComponent(pixel, 3) : 0xff;
+ }
+
+ // Sets the current pixel to the color given by |colorIndex|. This also
+ // increments the relevant local variables to move the current pixel
+ // right by one.
+ inline void setI(size_t colorIndex)
+ {
+ setRGBA(m_colorTable[colorIndex].rgbRed, m_colorTable[colorIndex].rgbGreen, m_colorTable[colorIndex].rgbBlue, 0xff);
+ }
+
+ // Like setI(), but with the individual component values specified.
+ inline void setRGBA(unsigned red,
+ unsigned green,
+ unsigned blue,
+ unsigned alpha)
+ {
+ m_buffer->setRGBA(m_coord.x(), m_coord.y(), red, green, blue, alpha);
+ m_coord.move(1, 0);
+ }
+
+ // Fills pixels from the current X-coordinate up to, but not including,
+ // |endCoord| with the color given by the individual components. This
+ // also increments the relevant local variables to move the current
+ // pixel right to |endCoord|.
+ inline void fillRGBA(int endCoord,
+ unsigned red,
+ unsigned green,
+ unsigned blue,
+ unsigned alpha)
+ {
+ while (m_coord.x() < endCoord)
+ setRGBA(red, green, blue, alpha);
+ }
+
+ // Resets the relevant local variables to start drawing at the left edge
+ // of the "next" row, where "next" is above or below the current row
+ // depending on the value of |m_isTopDown|.
+ void moveBufferToNextRow();
+
+ // The decoder that owns us.
+ ImageDecoder* m_parent;
+
+ // The destination for the pixel data.
+ RGBA32Buffer* m_buffer;
+
+ // The file to decode.
+ RefPtr<SharedBuffer> m_data;
+
+ // An index into |m_data| representing how much we've already decoded.
+ size_t m_decodedOffset;
+
+ // The file offset at which the BMP info header starts.
+ size_t m_headerOffset;
+
+ // The file offset at which the actual image bits start. When decoding
+ // ICO files, this is set to 0, since it's not stored anywhere in a
+ // header; the reader functions expect the image data to start
+ // immediately after the header and (if necessary) color table.
+ size_t m_imgDataOffset;
+
+ // The BMP info header.
+ BitmapInfoHeader m_infoHeader;
+
+ // True if this is an OS/2 1.x (aka Windows 2.x) BMP. The struct
+ // layouts for this type of BMP are slightly different from the later,
+ // more common formats.
+ bool m_isOS21x;
+
+ // True if this is an OS/2 2.x BMP. The meanings of compression types 3
+ // and 4 for this type of BMP differ from Windows V3+ BMPs.
+ //
+ // This will be falsely negative in some cases, but only ones where the
+ // way we misinterpret the data is irrelevant.
+ bool m_isOS22x;
+
+ // True if the BMP is not vertically flipped, that is, the first line of
+ // raster data in the file is the top line of the image.
+ bool m_isTopDown;
+
+ // These flags get set to false as we finish each processing stage.
+ bool m_needToProcessBitmasks;
+ bool m_needToProcessColorTable;
+
+ // Masks/offsets for the color values for non-palette formats. These
+ // are bitwise, with array entries 0, 1, 2, 3 corresponding to R, G, B,
+ // A.
+ //
+ // The right/left shift values are meant to be applied after the masks.
+ // We need to right shift to compensate for the bitfields' offsets into
+ // the 32 bits of pixel data, and left shift to scale the color values
+ // up for fields with less than 8 bits of precision. Sadly, we can't
+ // just combine these into one shift value because the net shift amount
+ // could go either direction. (If only "<< -x" were equivalent to
+ // ">> x"...)
+ uint32_t m_bitMasks[4];
+ int m_bitShiftsRight[4];
+ int m_bitShiftsLeft[4];
+
+ // The color palette, for paletted formats.
+ size_t m_tableSizeInBytes;
+ Vector<RGBTriple> m_colorTable;
+
+ // The coordinate to which we've decoded the image.
+ IntPoint m_coord;
+
+ // Variables that track whether we've seen pixels with alpha values != 0
+ // and == 0, respectively. See comments in processNonRLEData() on how
+ // these are used.
+ bool m_seenNonZeroAlphaPixel;
+ bool m_seenZeroAlphaPixel;
+
+ // ICOs store a 1bpp "mask" immediately after the main bitmap image data
+ // (and, confusingly, add its height to the biHeight value in the info
+ // header, thus doubling it). This variable tracks whether we have such
+ // a mask and if we've started decoding it yet.
+ AndMaskState m_andMaskState;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp b/Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp
new file mode 100644
index 0000000..d741882
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "ImageDecoder.h"
+
+#include <cairo.h>
+
+namespace WebCore {
+
+NativeImagePtr RGBA32Buffer::asNewNativeImage() const
+{
+ return cairo_image_surface_create_for_data(
+ reinterpret_cast<unsigned char*>(const_cast<PixelData*>(
+ m_bytes)), CAIRO_FORMAT_ARGB32, width(), height(),
+ width() * sizeof(PixelData));
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp b/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp
new file mode 100644
index 0000000..03b4c8f
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * 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 "ImageDecoder.h"
+
+#include "GraphicsContextCG.h"
+
+#include <CoreGraphics/CGColorSpace.h>
+#include <CoreGraphics/CGImage.h>
+
+namespace WebCore {
+
+static RGBA32Buffer::PixelData* getPtrAsPixelData(CFMutableDataRef data)
+{
+ return data ? reinterpret_cast<RGBA32Buffer::PixelData*>(CFDataGetMutableBytePtr(data)) : 0;
+}
+
+void RGBA32Buffer::copyReferenceToBitmapData(const RGBA32Buffer& other)
+{
+ ASSERT(this != &other);
+ m_backingStore = other.m_backingStore;
+ m_bytes = getPtrAsPixelData(m_backingStore.get());
+ // FIXME: The rest of this function seems redundant with RGBA32Buffer::copyBitmapData.
+ m_size = other.m_size;
+ setHasAlpha(other.m_hasAlpha);
+}
+
+bool RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return true;
+
+ m_backingStore.adoptCF(CFDataCreateMutableCopy(kCFAllocatorDefault, 0, other.m_backingStore.get()));
+ m_bytes = getPtrAsPixelData(m_backingStore.get());
+ m_size = other.m_size;
+ setHasAlpha(other.m_hasAlpha);
+ return true;
+}
+
+bool RGBA32Buffer::setSize(int newWidth, int newHeight)
+{
+ ASSERT(!m_backingStore);
+ size_t backingStoreSize = newWidth * newHeight * sizeof(PixelData);
+ CFMutableDataRef backingStoreRef = CFDataCreateMutable(kCFAllocatorDefault, backingStoreSize);
+ if (!backingStoreRef)
+ return false;
+ m_backingStore.adoptCF(backingStoreRef);
+ CFDataSetLength(backingStoreRef, backingStoreSize);
+ m_bytes = reinterpret_cast<PixelData*>(CFDataGetMutableBytePtr(m_backingStore.get()));
+ m_size = IntSize(newWidth, newHeight);
+
+ zeroFill();
+ return true;
+}
+
+static CGColorSpaceRef createColorSpace(const ColorProfile& colorProfile)
+{
+ if (colorProfile.isEmpty())
+ return CGColorSpaceCreateDeviceRGB();
+
+ RetainPtr<CFDataRef> data(AdoptCF, CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(colorProfile.data()), colorProfile.size()));
+#if !defined(TARGETING_TIGER) && !defined(TARGETING_LEOPARD)
+ return CGColorSpaceCreateWithICCProfile(data.get());
+#else
+ RetainPtr<CGDataProviderRef> profileDataProvider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
+ CGFloat ranges[] = {0.0, 255.0, 0.0, 255.0, 0.0, 255.0};
+ return CGColorSpaceCreateICCBased(3, ranges, profileDataProvider.get(), deviceRGBColorSpaceRef());
+#endif
+}
+
+NativeImagePtr RGBA32Buffer::asNewNativeImage() const
+{
+ RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, createColorSpace(m_colorProfile));
+ RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(m_backingStore.get()));
+
+ CGImageAlphaInfo alphaInfo = m_premultiplyAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaFirst;
+
+ return CGImageCreate(width(), height(), 8, 32, width() * sizeof(PixelData), colorSpace.get(),
+ alphaInfo | kCGBitmapByteOrder32Host, dataProvider.get(), 0, /*shouldInterpolate=*/true, kCGRenderingIntentDefault);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
new file mode 100644
index 0000000..e92f264
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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"
+
+namespace WebCore {
+
+GIFImageDecoder::GIFImageDecoder(ImageSource::AlphaOption alphaOption,
+ ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : ImageDecoder(alphaOption, gammaAndColorProfileOption)
+ , m_alreadyScannedThisDataForFrameCount(true)
+ , m_repetitionCount(cAnimationLoopOnce)
+ , m_readOffset(0)
+{
+}
+
+GIFImageDecoder::~GIFImageDecoder()
+{
+}
+
+void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
+{
+ if (failed())
+ return;
+
+ ImageDecoder::setData(data, allDataReceived);
+
+ // We need to rescan the frame count, as the new data may have changed it.
+ m_alreadyScannedThisDataForFrameCount = false;
+}
+
+bool GIFImageDecoder::isSizeAvailable()
+{
+ if (!ImageDecoder::isSizeAvailable())
+ decode(0, GIFSizeQuery);
+
+ return ImageDecoder::isSizeAvailable();
+}
+
+bool GIFImageDecoder::setSize(int width, int height)
+{
+ if (ImageDecoder::isSizeAvailable() && size().width() == width && size().height() == height)
+ return true;
+
+ if (!ImageDecoder::setSize(width, height))
+ return false;
+
+ prepareScaleDataIfNecessary();
+ return true;
+}
+
+size_t GIFImageDecoder::frameCount()
+{
+ if (!m_alreadyScannedThisDataForFrameCount) {
+ // 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).
+ GIFImageReader reader(0);
+ reader.read((const unsigned char*)m_data->data(), m_data->size(), GIFFrameCountQuery, static_cast<unsigned>(-1));
+ m_alreadyScannedThisDataForFrameCount = true;
+ m_frameBufferCache.resize(reader.images_count);
+ for (int i = 0; i < reader.images_count; ++i)
+ m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha);
+ }
+
+ return m_frameBufferCache.size();
+}
+
+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. If the reader hasn't
+ // seen a loop count yet, it will return cLoopCountNotSeen, in which case we
+ // should default to looping once (the initial value for
+ // |m_repetitionCount|).
+ //
+ // There are two additional wrinkles here. First, ImageSource::clear() may
+ // destroy the reader, making the result from the reader _less_
+ // authoritative on future calls if the recreated reader hasn't seen the
+ // loop count. We don't need to special-case this because in this case the
+ // new reader will once again return cLoopCountNotSeen, and we won't
+ // overwrite the cached correct value.
+ //
+ // Second, a GIF might never set a loop count at all, in which case we
+ // should continue to treat it as a "loop once" animation. We don't need
+ // special code here either, because in this case we'll never change
+ // |m_repetitionCount| from its default value.
+ if (m_reader && (m_reader->loop_count != cLoopCountNotSeen))
+ m_repetitionCount = m_reader->loop_count;
+ return m_repetitionCount;
+}
+
+RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index)
+{
+ if (index >= frameCount())
+ return 0;
+
+ RGBA32Buffer& frame = m_frameBufferCache[index];
+ if (frame.status() != RGBA32Buffer::FrameComplete)
+ decode(index + 1, GIFFullQuery);
+ return &frame;
+}
+
+bool GIFImageDecoder::setFailed()
+{
+ m_reader.clear();
+ return ImageDecoder::setFailed();
+}
+
+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);
+
+ // We need to preserve frames such that:
+ // * We don't clear |end|
+ // * We don't clear the frame we're currently decoding
+ // * We don't clear any frame from which a future initFrameBuffer() call
+ // will copy bitmap data
+ // All other frames can be cleared. Because of the constraints on when
+ // ImageSource::clear() can be called (see ImageSource.h), we're guaranteed
+ // not to have non-empty frames after the frame we're currently decoding.
+ // So, scan backwards from |end| as follows:
+ // * If the frame is empty, we're still past any frames we care about.
+ // * If the frame is complete, but is DisposeOverwritePrevious, we'll
+ // skip over it in future initFrameBuffer() calls. We can clear it
+ // unless it's |end|, and keep scanning. For any other disposal method,
+ // stop scanning, as we've found the frame initFrameBuffer() will need
+ // next.
+ // * If the frame is partial, we're decoding it, so don't clear it; if it
+ // has a disposal method other than DisposeOverwritePrevious, stop
+ // scanning, as we'll only need this frame when decoding the next one.
+ Vector<RGBA32Buffer>::iterator i(end);
+ for (; (i != m_frameBufferCache.begin()) && ((i->status() == RGBA32Buffer::FrameEmpty) || (i->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)); --i) {
+ if ((i->status() == RGBA32Buffer::FrameComplete) && (i != end))
+ i->clear();
+ }
+
+ // Now |i| holds the last frame we need to preserve; clear prior frames.
+ for (Vector<RGBA32Buffer>::iterator j(m_frameBufferCache.begin()); j != i; ++j) {
+ ASSERT(j->status() != RGBA32Buffer::FramePartial);
+ if (j->status() != RGBA32Buffer::FrameEmpty)
+ j->clear();
+ }
+}
+
+void GIFImageDecoder::decodingHalted(unsigned bytesLeft)
+{
+ m_readOffset = m_data->size() - bytesLeft;
+}
+
+bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels)
+{
+ const GIFFrameReader* frameReader = m_reader->frame_reader;
+ // The pixel data and coordinates supplied to us are relative to the frame's
+ // origin within the entire image size, i.e.
+ // (frameReader->x_offset, frameReader->y_offset). There is no guarantee
+ // that (rowEnd - rowBuffer) == (size().width() - frameReader->x_offset), so
+ // we must ensure we don't run off the end of either the source data or the
+ // row's X-coordinates.
+ int xBegin = upperBoundScaledX(frameReader->x_offset);
+ int yBegin = upperBoundScaledY(frameReader->y_offset + rowNumber);
+ int xEnd = lowerBoundScaledX(std::min(static_cast<int>(frameReader->x_offset + (rowEnd - rowBuffer)), size().width()) - 1, xBegin + 1) + 1;
+ int yEnd = lowerBoundScaledY(std::min(static_cast<int>(frameReader->y_offset + rowNumber + repeatCount), size().height()) - 1, yBegin + 1) + 1;
+ if (!rowBuffer || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))
+ return true;
+
+ // Get the colormap.
+ const unsigned char* colorMap;
+ unsigned colorMapSize;
+ if (frameReader->is_local_colormap_defined) {
+ colorMap = frameReader->local_colormap;
+ colorMapSize = (unsigned)frameReader->local_colormap_size;
+ } else {
+ colorMap = m_reader->global_colormap;
+ colorMapSize = m_reader->global_colormap_size;
+ }
+ if (!colorMap)
+ return true;
+
+ // Initialize the frame if necessary.
+ RGBA32Buffer& buffer = m_frameBufferCache[frameIndex];
+ if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex))
+ return false;
+
+ // Write one row's worth of data into the frame.
+ for (int x = xBegin; x < xEnd; ++x) {
+ const unsigned char sourceValue = *(rowBuffer + (m_scaled ? m_scaledColumns[x] : x) - frameReader->x_offset);
+ if ((!frameReader->is_transparent || (sourceValue != frameReader->tpixel)) && (sourceValue < colorMapSize)) {
+ const size_t colorIndex = static_cast<size_t>(sourceValue) * 3;
+ buffer.setRGBA(x, yBegin, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255);
+ } else {
+ m_currentBufferSawAlpha = true;
+ // We may or may not need to write transparent pixels to the buffer.
+ // 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)
+ buffer.setRGBA(x, yBegin, 0, 0, 0, 0);
+ }
+ }
+
+ // Tell the frame to copy the row data if need be.
+ if (repeatCount > 1)
+ buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd);
+
+ return true;
+}
+
+bool 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))
+ return false; // initFrameBuffer() has already called setFailed().
+
+ 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(), scaledSize())))
+ buffer.setHasAlpha(false);
+ else if (frameIndex) {
+ // 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 && (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);
+ }
+ }
+
+ return true;
+}
+
+void GIFImageDecoder::gifComplete()
+{
+ // Cache the repetition count, which is now as authoritative as it's ever
+ // going to be.
+ repetitionCount();
+
+ m_reader.clear();
+}
+
+void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query)
+{
+ if (failed())
+ return;
+
+ if (!m_reader)
+ m_reader.set(new GIFImageReader(this));
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if (!m_reader->read((const unsigned char*)m_data->data() + m_readOffset, m_data->size() - m_readOffset, query, haltAtFrame) && isAllDataReceived())
+ setFailed();
+}
+
+bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
+{
+ // Initialize the frame rect in our buffer.
+ const GIFFrameReader* frameReader = m_reader->frame_reader;
+ IntRect frameRect(frameReader->x_offset, frameReader->y_offset, frameReader->width, frameReader->height);
+
+ // Make sure the frameRect doesn't extend outside the buffer.
+ if (frameRect.right() > size().width())
+ frameRect.setWidth(size().width() - frameReader->x_offset);
+ if (frameRect.bottom() > size().height())
+ frameRect.setHeight(size().height() - frameReader->y_offset);
+
+ RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex];
+ int left = upperBoundScaledX(frameRect.x());
+ int right = lowerBoundScaledX(frameRect.right(), left);
+ int top = upperBoundScaledY(frameRect.y());
+ int bottom = lowerBoundScaledY(frameRect.bottom(), top);
+ buffer->setRect(IntRect(left, top, right - left, bottom - top));
+
+ if (!frameIndex) {
+ // This is the first frame, so we're not relying on any previous data.
+ if (!buffer->setSize(scaledSize().width(), scaledSize().height()))
+ return setFailed();
+ } else {
+ // The starting state for this frame depends on the previous frame's
+ // disposal method.
+ //
+ // Frames that use the DisposeOverwritePrevious method are effectively
+ // no-ops in terms of changing the starting state of a frame compared to
+ // the starting state of the previous frame, so skip over them. (If the
+ // first frame specifies this method, it will get treated like
+ // DisposeOverwriteBgcolor below and reset to a completely empty image.)
+ const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex];
+ RGBA32Buffer::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod();
+ while (frameIndex && (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) {
+ prevBuffer = &m_frameBufferCache[--frameIndex];
+ prevMethod = prevBuffer->disposalMethod();
+ }
+ ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete);
+
+ if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || (prevMethod == RGBA32Buffer::DisposeKeep)) {
+ // Preserve the last frame as the starting state for this frame.
+ if (!buffer->copyBitmapData(*prevBuffer))
+ return setFailed();
+ } 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();
+ const IntSize& bufferSize = scaledSize();
+ if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) {
+ // Clearing the first frame, or a frame the size of the whole
+ // image, results in a completely empty image.
+ if (!buffer->setSize(bufferSize.width(), bufferSize.height()))
+ return setFailed();
+ } else {
+ // Copy the whole previous buffer, then clear just its frame.
+ if (!buffer->copyBitmapData(*prevBuffer))
+ return setFailed();
+ for (int y = prevRect.y(); y < prevRect.bottom(); ++y) {
+ for (int x = prevRect.x(); x < prevRect.right(); ++x)
+ buffer->setRGBA(x, y, 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;
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h
new file mode 100644
index 0000000..64240d4
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h
@@ -0,0 +1,85 @@
+/*
+ * 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 GIFImageDecoder_h
+#define GIFImageDecoder_h
+
+#include "ImageDecoder.h"
+#include <wtf/OwnPtr.h>
+
+struct GIFImageReader;
+
+namespace WebCore {
+
+ // This class decodes the GIF image format.
+ class GIFImageDecoder : public ImageDecoder {
+ public:
+ GIFImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+ virtual ~GIFImageDecoder();
+
+ enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery };
+
+ // ImageDecoder
+ virtual String filenameExtension() const { return "gif"; }
+ virtual void setData(SharedBuffer* data, bool allDataReceived);
+ virtual bool isSizeAvailable();
+ virtual bool setSize(int width, int height);
+ virtual size_t frameCount();
+ virtual int repetitionCount() const;
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+ // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid
+ // accessing deleted memory, especially when calling this from inside
+ // GIFImageReader!
+ virtual bool setFailed();
+ virtual void clearFrameBufferCache(size_t clearBeforeFrame);
+
+ // Callbacks from the GIF reader.
+ void decodingHalted(unsigned bytesLeft);
+ bool haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels);
+ bool frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod);
+ void gifComplete();
+
+ private:
+ // If the query is GIFFullQuery, decodes the image up to (but not
+ // including) |haltAtFrame|. Otherwise, decodes as much as is needed to
+ // answer the query, ignoring bitmap data. If decoding fails but there
+ // is no more data coming, sets the "decode failure" flag.
+ void decode(unsigned haltAtFrame, GIFQuery);
+
+ // Called to initialize the frame buffer with the given index, based on
+ // the previous frame's disposal method. Returns true on success. On
+ // failure, this will mark the image as failed.
+ bool initFrameBuffer(unsigned frameIndex);
+
+ bool m_alreadyScannedThisDataForFrameCount;
+ bool m_currentBufferSawAlpha;
+ mutable int m_repetitionCount;
+ OwnPtr<GIFImageReader> m_reader;
+ unsigned m_readOffset;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp
new file mode 100644
index 0000000..1e033a3
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp
@@ -0,0 +1,925 @@
+/* -*- 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"
+#include "ImageSource.h"
+
+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.
+bool 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 true;
+
+ // 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))
+ return false;
+
+ 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));
+ }
+
+ return true;
+}
+
+//******************************************************************************
+/* Perform Lempel-Ziv-Welch decoding */
+bool GIFImageReader::do_lzw(const unsigned char *q)
+{
+ GIFFrameReader* gs = frame_reader;
+ if (!gs)
+ return true;
+
+ 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 true;
+
+#define OUTPUT_ROW \
+ PR_BEGIN_MACRO \
+ if (!output_row()) \
+ return false; \
+ 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)
+ return true;
+ return clientptr ? clientptr->setFailed() : false;
+ }
+
+ 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 clientptr ? clientptr->setFailed() : false;
+ }
+
+ while (code >= clear_code)
+ {
+ if (code >= MAX_BITS || code == prefix[code])
+ return clientptr ? clientptr->setFailed() : false;
+
+ // Even though suffix[] only holds characters through suffix[avail - 1],
+ // allowing code >= avail here lets us be more tolerant of malformed
+ // data. As long as code < MAX_BITS, the only risk is a garbled image,
+ // which is no worse than refusing to display it.
+ *stackp++ = suffix[code];
+ code = prefix[code];
+
+ if (stackp == stack + MAX_BITS)
+ return clientptr ? clientptr->setFailed() : false;
+ }
+
+ *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 true;
+}
+
+
+/******************************************************************************/
+/*
+ * 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 false;
+ }
+ // 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))
+ return false; // If do_lzw() encountered an error, it has already called
+ // clientptr->setFailed().
+ GETN(1, gif_sub_block);
+ break;
+
+ case gif_lzw_start:
+ {
+ /* Initialize LZW parser/decoder */
+ int datasize = *q;
+ // Since we use a codesize of 1 more than the datasize, we need to ensure
+ // that our datasize is strictly less than the MAX_LZW_BITS value (12).
+ // This sets the largest possible codemask correctly at 4095.
+ if (datasize >= MAX_LZW_BITS)
+ return clientptr ? clientptr->setFailed() : false;
+ int clear_code = 1 << datasize;
+ if (clear_code >= MAX_BITS)
+ return clientptr ? clientptr->setFailed() : false;
+
+ 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];
+ // Clearing the whole suffix table lets us be more tolerant of bad data.
+ memset(frame_reader->suffix, 0, MAX_BITS);
+ for (int i = 0; i < frame_reader->clear_code; i++)
+ frame_reader->suffix[i] = i;
+
+ 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
+ return clientptr ? clientptr->setFailed() : false;
+ 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->setSize(screen_width, screen_height))
+ return false;
+
+ 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 */
+ GETN(0, 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 != ',')
+ return clientptr ? clientptr->setFailed() : false;
+
+ 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);
+
+ /* Zero loop count is infinite animation loop request */
+ if (loop_count == 0)
+ loop_count = WebCore::cAnimationLoopInfinite;
+
+ 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 {
+ // 0,3-7 are yet to be defined netscape extension codes
+ return clientptr ? clientptr->setFailed() : false;
+ }
+
+ 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->setSize(screen_width, screen_height))
+ return false;
+ }
+
+ /* 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)
+ return clientptr ? clientptr->setFailed() : false;
+ }
+
+ 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)
+ return clientptr ? clientptr->setFailed() : false;
+ 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)
+ return clientptr ? clientptr->setFailed() : false;
+ }
+
+ /* 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))
+ return false; // frameComplete() has already called
+ // clientptr->setFailed().
+
+ /* 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;
+
+ // 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 false;
+}
diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h
new file mode 100644
index 0000000..be5be19
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h
@@ -0,0 +1,209 @@
+/* -*- 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 GIFImageReader_h
+#define GIFImageReader_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_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. */
+ unsigned 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() {
+ 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:
+ bool output_row();
+ bool do_lzw(const unsigned char *q);
+};
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp b/Source/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp
new file mode 100644
index 0000000..47302f7
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2010 Stephan Aßmus, <superstippi@gmx.de>
+ *
+ * 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 "ImageDecoder.h"
+
+#include <Bitmap.h>
+
+namespace WebCore {
+
+NativeImagePtr RGBA32Buffer::asNewNativeImage() const
+{
+ int bytesPerRow = width() * sizeof(PixelData);
+ OwnPtr<BBitmap> bitmap(new BBitmap(BRect(0, 0, width() - 1, height() - 1), 0, B_RGBA32, bytesPerRow));
+
+ const uint8* source = reinterpret_cast<const uint8*>(m_bytes);
+ uint8* destination = reinterpret_cast<uint8*>(bitmap->Bits());
+ int h = height();
+ int w = width();
+ for (int y = 0; y < h; y++) {
+#if 0
+// FIXME: Enable this conversion once Haiku has B_RGBA32P[remultiplied]...
+ memcpy(dst, source, bytesPerRow);
+#else
+ const uint8* sourceHandle = source;
+ uint8* destinationHandle = destination;
+ for (int x = 0; x < w; x++) {
+ if (sourceHandle[3] == 255 || !sourceHandle[3]) {
+ destinationHandle[0] = sourceHandle[0];
+ destinationHandle[1] = sourceHandle[1];
+ destinationHandle[2] = sourceHandle[2];
+ destinationHandle[3] = sourceHandle[3];
+ } else {
+ destinationHandle[0] = static_cast<uint16>(sourceHandle[0]) * 255 / sourceHandle[3];
+ destinationHandle[1] = static_cast<uint16>(sourceHandle[1]) * 255 / sourceHandle[3];
+ destinationHandle[2] = static_cast<uint16>(sourceHandle[2]) * 255 / sourceHandle[3];
+ destinationHandle[3] = sourceHandle[3];
+ }
+ destinationHandle += 4;
+ sourceHandle += 4;
+ }
+#endif
+ destination += bytesPerRow;
+ source += bytesPerRow;
+ }
+
+ return bitmap.release();
+}
+
+} // namespace WebCore
+
diff --git a/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp
new file mode 100644
index 0000000..92a7dcf
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2008, 2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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 "ICOImageDecoder.h"
+
+#include <algorithm>
+
+#include "BMPImageReader.h"
+#include "PNGImageDecoder.h"
+
+namespace WebCore {
+
+// Number of bits in .ICO/.CUR used to store the directory and its entries,
+// respectively (doesn't match sizeof values for member structs since we omit
+// some fields).
+static const size_t sizeOfDirectory = 6;
+static const size_t sizeOfDirEntry = 16;
+
+ICOImageDecoder::ICOImageDecoder(ImageSource::AlphaOption alphaOption,
+ ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : ImageDecoder(alphaOption, gammaAndColorProfileOption)
+ , m_decodedOffset(0)
+{
+}
+
+ICOImageDecoder::~ICOImageDecoder()
+{
+}
+
+void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived)
+{
+ if (failed())
+ return;
+
+ ImageDecoder::setData(data, allDataReceived);
+
+ for (BMPReaders::iterator i(m_bmpReaders.begin()); i != m_bmpReaders.end(); ++i) {
+ if (*i)
+ (*i)->setData(data);
+ }
+ for (size_t i = 0; i < m_pngDecoders.size(); ++i)
+ setDataForPNGDecoderAtIndex(i);
+}
+
+bool ICOImageDecoder::isSizeAvailable()
+{
+ if (!ImageDecoder::isSizeAvailable())
+ decode(0, true);
+
+ return ImageDecoder::isSizeAvailable();
+}
+
+IntSize ICOImageDecoder::size() const
+{
+ return m_frameSize.isEmpty() ? ImageDecoder::size() : m_frameSize;
+}
+
+IntSize ICOImageDecoder::frameSizeAtIndex(size_t index) const
+{
+ return (index && (index < m_dirEntries.size())) ? m_dirEntries[index].m_size : size();
+}
+
+bool ICOImageDecoder::setSize(unsigned width, unsigned height)
+{
+ // The size calculated inside the BMPImageReader had better match the one in
+ // the icon directory.
+ return m_frameSize.isEmpty() ? ImageDecoder::setSize(width, height) : ((IntSize(width, height) == m_frameSize) || setFailed());
+}
+
+size_t ICOImageDecoder::frameCount()
+{
+ decode(0, true);
+ if (m_frameBufferCache.isEmpty()) {
+ m_frameBufferCache.resize(m_dirEntries.size());
+ for (size_t i = 0; i < m_dirEntries.size(); ++i)
+ m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha);
+ }
+ // CAUTION: We must not resize m_frameBufferCache again after this, as
+ // decodeAtIndex() may give a BMPImageReader a pointer to one of the
+ // entries.
+ return m_frameBufferCache.size();
+}
+
+RGBA32Buffer* ICOImageDecoder::frameBufferAtIndex(size_t index)
+{
+ // Ensure |index| is valid.
+ if (index >= frameCount())
+ return 0;
+
+ RGBA32Buffer* buffer = &m_frameBufferCache[index];
+ if (buffer->status() != RGBA32Buffer::FrameComplete)
+ decode(index, false);
+ return buffer;
+}
+
+bool ICOImageDecoder::setFailed()
+{
+ m_bmpReaders.clear();
+ m_pngDecoders.clear();
+ return ImageDecoder::setFailed();
+}
+
+// static
+bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a, const IconDirectoryEntry& b)
+{
+ // Larger icons are better. After that, higher bit-depth icons are better.
+ const int aEntryArea = a.m_size.width() * a.m_size.height();
+ const int bEntryArea = b.m_size.width() * b.m_size.height();
+ return (aEntryArea == bEntryArea) ? (a.m_bitCount > b.m_bitCount) : (aEntryArea > bEntryArea);
+}
+
+void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t index)
+{
+ if (!m_pngDecoders[index])
+ return;
+
+ const IconDirectoryEntry& dirEntry = m_dirEntries[index];
+ // Copy out PNG data to a separate vector and send to the PNG decoder.
+ // FIXME: Save this copy by making the PNG decoder able to take an
+ // optional offset.
+ RefPtr<SharedBuffer> pngData(SharedBuffer::create(&m_data->data()[dirEntry.m_imageOffset], m_data->size() - dirEntry.m_imageOffset));
+ m_pngDecoders[index]->setData(pngData.get(), isAllDataReceived());
+}
+
+void ICOImageDecoder::decode(size_t index, bool onlySize)
+{
+ if (failed())
+ return;
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if ((!decodeDirectory() || (!onlySize && !decodeAtIndex(index))) && isAllDataReceived())
+ setFailed();
+ // If we're done decoding this frame, we don't need the BMPImageReader or
+ // PNGImageDecoder anymore. (If we failed, these have already been
+ // cleared.)
+ else if ((m_frameBufferCache.size() > index) && (m_frameBufferCache[index].status() == RGBA32Buffer::FrameComplete)) {
+ m_bmpReaders[index].clear();
+ m_pngDecoders[index].clear();
+ }
+}
+
+bool ICOImageDecoder::decodeDirectory()
+{
+ // Read and process directory.
+ if ((m_decodedOffset < sizeOfDirectory) && !processDirectory())
+ return false;
+
+ // Read and process directory entries.
+ return (m_decodedOffset >= (sizeOfDirectory + (m_dirEntries.size() * sizeOfDirEntry))) || processDirectoryEntries();
+}
+
+bool ICOImageDecoder::decodeAtIndex(size_t index)
+{
+ ASSERT(index < m_dirEntries.size());
+ const IconDirectoryEntry& dirEntry = m_dirEntries[index];
+ const ImageType imageType = imageTypeAtIndex(index);
+ if (imageType == Unknown)
+ return false; // Not enough data to determine image type yet.
+
+ if (imageType == BMP) {
+ if (!m_bmpReaders[index]) {
+ // We need to have already sized m_frameBufferCache before this, and
+ // we must not resize it again later (see caution in frameCount()).
+ ASSERT(m_frameBufferCache.size() == m_dirEntries.size());
+ m_bmpReaders[index].set(new BMPImageReader(this, dirEntry.m_imageOffset, 0, true));
+ m_bmpReaders[index]->setData(m_data.get());
+ m_bmpReaders[index]->setBuffer(&m_frameBufferCache[index]);
+ }
+ m_frameSize = dirEntry.m_size;
+ bool result = m_bmpReaders[index]->decodeBMP(false);
+ m_frameSize = IntSize();
+ return result;
+ }
+
+ if (!m_pngDecoders[index]) {
+ m_pngDecoders[index].set(
+ new PNGImageDecoder(m_premultiplyAlpha ? ImageSource::AlphaPremultiplied : ImageSource::AlphaNotPremultiplied,
+ m_ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied));
+ setDataForPNGDecoderAtIndex(index);
+ }
+ // Fail if the size the PNGImageDecoder calculated does not match the size
+ // in the directory.
+ if (m_pngDecoders[index]->isSizeAvailable() && (m_pngDecoders[index]->size() != dirEntry.m_size))
+ return setFailed();
+ m_frameBufferCache[index] = *m_pngDecoders[index]->frameBufferAtIndex(0);
+ return !m_pngDecoders[index]->failed() || setFailed();
+}
+
+bool ICOImageDecoder::processDirectory()
+{
+ // Read directory.
+ ASSERT(!m_decodedOffset);
+ if (m_data->size() < sizeOfDirectory)
+ return false;
+ const uint16_t fileType = readUint16(2);
+ const uint16_t idCount = readUint16(4);
+ m_decodedOffset = sizeOfDirectory;
+
+ // See if this is an icon filetype we understand, and make sure we have at
+ // least one entry in the directory.
+ enum {
+ ICON = 1,
+ CURSOR = 2,
+ };
+ if (((fileType != ICON) && (fileType != CURSOR)) || (!idCount))
+ return setFailed();
+
+ // Enlarge member vectors to hold all the entries.
+ m_dirEntries.resize(idCount);
+ m_bmpReaders.resize(idCount);
+ m_pngDecoders.resize(idCount);
+ return true;
+}
+
+bool ICOImageDecoder::processDirectoryEntries()
+{
+ // Read directory entries.
+ ASSERT(m_decodedOffset == sizeOfDirectory);
+ if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < (m_dirEntries.size() * sizeOfDirEntry)))
+ return false;
+ for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); i != m_dirEntries.end(); ++i)
+ *i = readDirectoryEntry(); // Updates m_decodedOffset.
+
+ // Make sure the specified image offsets are past the end of the directory
+ // entries.
+ for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); i != m_dirEntries.end(); ++i) {
+ if (i->m_imageOffset < m_decodedOffset)
+ return setFailed();
+ }
+
+ // Arrange frames in decreasing quality order.
+ std::sort(m_dirEntries.begin(), m_dirEntries.end(), compareEntries);
+
+ // The image size is the size of the largest entry.
+ const IconDirectoryEntry& dirEntry = m_dirEntries.first();
+ // Technically, this next call shouldn't be able to fail, since the width
+ // and height here are each <= 256, and |m_frameSize| is empty.
+ return setSize(dirEntry.m_size.width(), dirEntry.m_size.height());
+}
+
+ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry()
+{
+ // Read icon data.
+ // The casts to uint8_t in the next few lines are because that's the on-disk
+ // type of the width and height values. Storing them in ints (instead of
+ // matching uint8_ts) is so we can record dimensions of size 256 (which is
+ // what a zero byte really means).
+ int width = static_cast<uint8_t>(m_data->data()[m_decodedOffset]);
+ if (!width)
+ width = 256;
+ int height = static_cast<uint8_t>(m_data->data()[m_decodedOffset + 1]);
+ if (!height)
+ height = 256;
+ IconDirectoryEntry entry;
+ entry.m_size = IntSize(width, height);
+ entry.m_bitCount = readUint16(6);
+ entry.m_imageOffset = readUint32(12);
+
+ // Some icons don't have a bit depth, only a color count. Convert the
+ // color count to the minimum necessary bit depth. It doesn't matter if
+ // this isn't quite what the bitmap info header says later, as we only use
+ // this value to determine which icon entry is best.
+ if (!entry.m_bitCount) {
+ int colorCount = static_cast<uint8_t>(m_data->data()[m_decodedOffset + 2]);
+ if (!colorCount)
+ colorCount = 256; // Vague in the spec, needed by real-world icons.
+ for (--colorCount; colorCount; colorCount >>= 1)
+ ++entry.m_bitCount;
+ }
+
+ m_decodedOffset += sizeOfDirEntry;
+ return entry;
+}
+
+ICOImageDecoder::ImageType ICOImageDecoder::imageTypeAtIndex(size_t index)
+{
+ // Check if this entry is a BMP or a PNG; we need 4 bytes to check the magic
+ // number.
+ ASSERT(index < m_dirEntries.size());
+ const uint32_t imageOffset = m_dirEntries[index].m_imageOffset;
+ if ((imageOffset > m_data->size()) || ((m_data->size() - imageOffset) < 4))
+ return Unknown;
+ return strncmp(&m_data->data()[imageOffset], "\x89PNG", 4) ? BMP : PNG;
+}
+
+}
diff --git a/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h
new file mode 100644
index 0000000..c2af6a3
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2008, 2009, Google 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:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT
+ * OWNER 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 ICOImageDecoder_h
+#define ICOImageDecoder_h
+
+#include "BMPImageReader.h"
+
+namespace WebCore {
+
+ class PNGImageDecoder;
+
+ // This class decodes the ICO and CUR image formats.
+ class ICOImageDecoder : public ImageDecoder {
+ public:
+ ICOImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+ virtual ~ICOImageDecoder();
+
+ // ImageDecoder
+ virtual String filenameExtension() const { return "ico"; }
+ virtual void setData(SharedBuffer*, bool allDataReceived);
+ virtual bool isSizeAvailable();
+ virtual IntSize size() const;
+ virtual IntSize frameSizeAtIndex(size_t) const;
+ virtual bool setSize(unsigned width, unsigned height);
+ virtual size_t frameCount();
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t);
+ // CAUTION: setFailed() deletes all readers and decoders. Be careful to
+ // avoid accessing deleted memory, especially when calling this from
+ // inside BMPImageReader!
+ virtual bool setFailed();
+
+ private:
+ enum ImageType {
+ Unknown,
+ BMP,
+ PNG,
+ };
+
+ struct IconDirectoryEntry {
+ IntSize m_size;
+ uint16_t m_bitCount;
+ uint32_t m_imageOffset;
+ };
+
+ // Returns true if |a| is a preferable icon entry to |b|.
+ // Larger sizes, or greater bitdepths at the same size, are preferable.
+ static bool compareEntries(const IconDirectoryEntry& a, const IconDirectoryEntry& b);
+
+ inline uint16_t readUint16(int offset) const
+ {
+ return BMPImageReader::readUint16(m_data.get(), m_decodedOffset + offset);
+ }
+
+ inline uint32_t readUint32(int offset) const
+ {
+ return BMPImageReader::readUint32(m_data.get(), m_decodedOffset + offset);
+ }
+
+ // If the desired PNGImageDecoder exists, gives it the appropriate data.
+ void setDataForPNGDecoderAtIndex(size_t);
+
+ // Decodes the entry at |index|. If |onlySize| is true, stops decoding
+ // after calculating the image size. If decoding fails but there is no
+ // more data coming, sets the "decode failure" flag.
+ void decode(size_t index, bool onlySize);
+
+ // Decodes the directory and directory entries at the beginning of the
+ // data, and initializes members. Returns true if all decoding
+ // succeeded. Once this returns true, all entries' sizes are known.
+ bool decodeDirectory();
+
+ // Decodes the specified entry.
+ bool decodeAtIndex(size_t);
+
+ // Processes the ICONDIR at the beginning of the data. Returns true if
+ // the directory could be decoded.
+ bool processDirectory();
+
+ // Processes the ICONDIRENTRY records after the directory. Keeps the
+ // "best" entry as the one we'll decode. Returns true if the entries
+ // could be decoded.
+ bool processDirectoryEntries();
+
+ // Reads and returns a directory entry from the current offset into
+ // |data|.
+ IconDirectoryEntry readDirectoryEntry();
+
+ // Determines whether the desired entry is a BMP or PNG. Returns true
+ // if the type could be determined.
+ ImageType imageTypeAtIndex(size_t);
+
+ // An index into |m_data| representing how much we've already decoded.
+ // Note that this only tracks data _this_ class decodes; once the
+ // BMPImageReader takes over this will not be updated further.
+ size_t m_decodedOffset;
+
+ // The headers for the ICO.
+ typedef Vector<IconDirectoryEntry> IconDirectoryEntries;
+ IconDirectoryEntries m_dirEntries;
+
+ // The image decoders for the various frames.
+ typedef Vector<OwnPtr<BMPImageReader> > BMPReaders;
+ BMPReaders m_bmpReaders;
+ typedef Vector<OwnPtr<PNGImageDecoder> > PNGDecoders;
+ PNGDecoders m_pngDecoders;
+
+ // Valid only while a BMPImageReader is decoding, this holds the size
+ // for the particular entry being decoded.
+ IntSize m_frameSize;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
new file mode 100644
index 0000000..632d428
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 2001-6 mozilla.org
+ *
+ * Other contributors:
+ * Stuart Parmenter <stuart@mozilla.com>
+ *
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ *
+ * 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 <stdio.h> // Needed by jpeglib.h for FILE.
+
+#if OS(WINCE) || PLATFORM(BREWMP_SIMULATOR)
+// Remove warning: 'FAR' macro redefinition
+#undef FAR
+
+// jmorecfg.h in libjpeg checks for XMD_H with the comment: "X11/xmd.h correctly defines INT32"
+// fix INT32 redefinition error by pretending we are X11/xmd.h
+#define XMD_H
+#endif
+
+extern "C" {
+
+#include "jpeglib.h"
+
+#if USE(ICCJPEG)
+#include "iccjpeg.h"
+#endif
+
+}
+
+#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_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;
+};
+
+static ColorProfile readColorProfile(jpeg_decompress_struct* info)
+{
+#if USE(ICCJPEG)
+ JOCTET* profile;
+ unsigned int profileLength;
+
+ if (!read_icc_profile(info, &profile, &profileLength))
+ return ColorProfile();
+
+ ColorProfile colorProfile;
+ colorProfile.append(reinterpret_cast<char*>(profile), profileLength);
+ free(profile);
+ return colorProfile;
+#else
+ return ColorProfile();
+#endif
+}
+
+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;
+
+ // Enable these markers for the ICC color profile.
+ // Apparently there are 16 of these markers. I don't see anywhere in the header with this constant.
+ for (unsigned i = 0; i < 0xF; ++i)
+ jpeg_save_markers(&m_info, JPEG_APP0 + i, 0xFFFF);
+ }
+
+ ~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 numBytes)
+ {
+ decoder_source_mgr* src = (decoder_source_mgr*)m_info.src;
+ long bytesToSkip = std::min(numBytes, (long)src->pub.bytes_in_buffer);
+ src->pub.bytes_in_buffer -= (size_t)bytesToSkip;
+ src->pub.next_input_byte += bytesToSkip;
+
+ m_bytesToSkip = std::max(numBytes - bytesToSkip, static_cast<long>(0));
+ }
+
+ bool decode(const Vector<char>& data, bool onlySize)
+ {
+ m_decodingSizeOnly = onlySize;
+
+ 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))
+ return m_decoder->setFailed();
+
+ switch (m_state) {
+ case JPEG_HEADER:
+ // Read file parameters with jpeg_read_header().
+ if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED)
+ return false; // 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_YCbCr:
+ // Grayscale images get "upsampled" by libjpeg. If we use
+ // their color profile, CoreGraphics will "upsample" them
+ // again, resulting in horizontal distortions.
+ m_decoder->setIgnoreGammaAndColorProfile(true);
+ // Note fall-through!
+ case JCS_RGB:
+ m_info.out_color_space = JCS_RGB;
+ break;
+ case JCS_CMYK:
+ case JCS_YCCK:
+ // jpeglib cannot convert these to rgb, but it can convert ycck
+ // to cmyk.
+ m_info.out_color_space = JCS_CMYK;
+ break;
+ default:
+ return m_decoder->setFailed();
+ }
+
+ // 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().
+ m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, JPOOL_IMAGE, m_info.output_width * 4, 1);
+
+ m_state = JPEG_START_DECOMPRESS;
+
+ // We can fill in the size now that the header is available.
+ if (!m_decoder->setSize(m_info.image_width, m_info.image_height))
+ return false;
+
+ if (!m_decoder->ignoresGammaAndColorProfile())
+ m_decoder->setColorProfile(readColorProfile(info()));
+
+ 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;
+ }
+ // FALL THROUGH
+
+ 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 false; // I/O suspension.
+
+ // If this is a progressive JPEG ...
+ m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
+ // FALL THROUGH
+
+ case JPEG_DECOMPRESS_SEQUENTIAL:
+ if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) {
+
+ if (!m_decoder->outputScanlines())
+ return false; // I/O suspension.
+
+ // If we've completed image output...
+ ASSERT(m_info.output_scanline == m_info.output_height);
+ m_state = JPEG_DONE;
+ }
+ // FALL THROUGH
+
+ 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) {
+ 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 && (scan > 1) && (status != JPEG_REACHED_EOI))
+ --scan;
+
+ if (!jpeg_start_output(&m_info, scan))
+ return false; // I/O suspension.
+ }
+
+ if (m_info.output_scanline == 0xffffff)
+ m_info.output_scanline = 0;
+
+ if (!m_decoder->outputScanlines()) {
+ if (!m_info.output_scanline)
+ // 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 false; // I/O suspension.
+ }
+
+ if (m_info.output_scanline == m_info.output_height) {
+ if (!jpeg_finish_output(&m_info))
+ return false; // 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;
+ }
+ // FALL THROUGH
+
+ case JPEG_DONE:
+ // Finish decompression.
+ return jpeg_finish_decompress(&m_info);
+
+ case JPEG_ERROR:
+ // We can get here if the constructor failed.
+ return m_decoder->setFailed();
+ }
+
+ 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(ImageSource::AlphaOption alphaOption,
+ ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : ImageDecoder(alphaOption, gammaAndColorProfileOption)
+{
+}
+
+JPEGImageDecoder::~JPEGImageDecoder()
+{
+}
+
+bool JPEGImageDecoder::isSizeAvailable()
+{
+ if (!ImageDecoder::isSizeAvailable())
+ decode(true);
+
+ return ImageDecoder::isSizeAvailable();
+}
+
+bool JPEGImageDecoder::setSize(unsigned width, unsigned height)
+{
+ if (!ImageDecoder::setSize(width, height))
+ return false;
+
+ prepareScaleDataIfNecessary();
+ return true;
+}
+
+RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index)
+{
+ if (index)
+ return 0;
+
+ if (m_frameBufferCache.isEmpty()) {
+ m_frameBufferCache.resize(1);
+ m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha);
+ }
+
+ RGBA32Buffer& frame = m_frameBufferCache[0];
+ if (frame.status() != RGBA32Buffer::FrameComplete)
+ decode(false);
+ return &frame;
+}
+
+bool JPEGImageDecoder::setFailed()
+{
+ m_reader.clear();
+ return ImageDecoder::setFailed();
+}
+
+bool JPEGImageDecoder::outputScanlines()
+{
+ if (m_frameBufferCache.isEmpty())
+ return false;
+
+ // Initialize the framebuffer if needed.
+ RGBA32Buffer& buffer = m_frameBufferCache[0];
+ if (buffer.status() == RGBA32Buffer::FrameEmpty) {
+ if (!buffer.setSize(scaledSize().width(), scaledSize().height()))
+ return setFailed();
+ buffer.setStatus(RGBA32Buffer::FramePartial);
+ buffer.setHasAlpha(false);
+ buffer.setColorProfile(m_colorProfile);
+
+ // For JPEGs, the frame always fills the entire image.
+ buffer.setRect(IntRect(IntPoint(), size()));
+ }
+
+ jpeg_decompress_struct* info = m_reader->info();
+ JSAMPARRAY samples = m_reader->samples();
+
+ while (info->output_scanline < info->output_height) {
+ // jpeg_read_scanlines will increase the scanline counter, so we
+ // save the scanline before calling it.
+ int sourceY = info->output_scanline;
+ /* Request one scanline. Returns 0 or 1 scanlines. */
+ if (jpeg_read_scanlines(info, samples, 1) != 1)
+ return false;
+
+ int destY = scaledY(sourceY);
+ if (destY < 0)
+ continue;
+ int width = m_scaled ? m_scaledColumns.size() : info->output_width;
+ for (int x = 0; x < width; ++x) {
+ JSAMPLE* jsample = *samples + (m_scaled ? m_scaledColumns[x] : x) * ((info->out_color_space == JCS_RGB) ? 3 : 4);
+ if (info->out_color_space == JCS_RGB)
+ buffer.setRGBA(x, destY, jsample[0], jsample[1], jsample[2], 0xFF);
+ else if (info->out_color_space == JCS_CMYK) {
+ // Source is 'Inverted CMYK', output is RGB.
+ // See: http://www.easyrgb.com/math.php?MATH=M12#text12
+ // Or: http://www.ilkeratalay.com/colorspacesfaq.php#rgb
+ // From CMYK to CMY:
+ // X = X * (1 - K ) + K [for X = C, M, or Y]
+ // Thus, from Inverted CMYK to CMY is:
+ // X = (1-iX) * (1 - (1-iK)) + (1-iK) => 1 - iX*iK
+ // From CMY (0..1) to RGB (0..1):
+ // R = 1 - C => 1 - (1 - iC*iK) => iC*iK [G and B similar]
+ unsigned k = jsample[3];
+ buffer.setRGBA(x, destY, jsample[0] * k / 255, jsample[1] * k / 255, jsample[2] * k / 255, 0xFF);
+ } else {
+ ASSERT_NOT_REACHED();
+ return setFailed();
+ }
+ }
+ }
+
+ return true;
+}
+
+void JPEGImageDecoder::jpegComplete()
+{
+ if (m_frameBufferCache.isEmpty())
+ return;
+
+ // Hand back an appropriately sized buffer, even if the image ended up being
+ // empty.
+ m_frameBufferCache[0].setStatus(RGBA32Buffer::FrameComplete);
+}
+
+void JPEGImageDecoder::decode(bool onlySize)
+{
+ if (failed())
+ return;
+
+ if (!m_reader)
+ m_reader.set(new JPEGImageReader(this));
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if (!m_reader->decode(m_data->buffer(), onlySize) && isAllDataReceived())
+ setFailed();
+ // If we're done decoding the image, we don't need the JPEGImageReader
+ // anymore. (If we failed, |m_reader| has already been cleared.)
+ else if (!m_frameBufferCache.isEmpty() && (m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete))
+ m_reader.clear();
+}
+
+}
diff --git a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h
new file mode 100644
index 0000000..63f29ab
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008-2009 Torch Mobile, Inc.
+ *
+ * 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 JPEGImageDecoder_h
+#define JPEGImageDecoder_h
+
+#include "ImageDecoder.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+ class JPEGImageReader;
+
+ // This class decodes the JPEG image format.
+ class JPEGImageDecoder : public ImageDecoder {
+ public:
+ JPEGImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+ virtual ~JPEGImageDecoder();
+
+ // ImageDecoder
+ virtual String filenameExtension() const { return "jpg"; }
+ virtual bool isSizeAvailable();
+ virtual bool setSize(unsigned width, unsigned height);
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+ virtual bool supportsAlpha() const { return false; }
+ // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid
+ // accessing deleted memory, especially when calling this from inside
+ // JPEGImageReader!
+ virtual bool setFailed();
+
+ bool outputScanlines();
+ void jpegComplete();
+
+ void setColorProfile(const ColorProfile& colorProfile) { m_colorProfile = colorProfile; }
+
+ private:
+ // Decodes the image. If |onlySize| is true, stops decoding after
+ // calculating the image size. If decoding fails but there is no more
+ // data coming, sets the "decode failure" flag.
+ void decode(bool onlySize);
+
+ OwnPtr<JPEGImageReader> m_reader;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/openvg/ImageDecoderOpenVG.cpp b/Source/WebCore/platform/image-decoders/openvg/ImageDecoderOpenVG.cpp
new file mode 100644
index 0000000..061c5ab
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/openvg/ImageDecoderOpenVG.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ImageDecoder.h"
+
+#include "IntRect.h"
+#include "IntSize.h"
+#include "SurfaceOpenVG.h"
+#include "TiledImageOpenVG.h"
+#include "VGUtils.h"
+
+#if PLATFORM(EGL)
+#include "EGLDisplayOpenVG.h"
+#endif
+
+#include <openvg.h>
+
+namespace WebCore {
+
+NativeImagePtr RGBA32Buffer::asNewNativeImage() const
+{
+ static const VGImageFormat bufferFormat = VG_sARGB_8888_PRE;
+ // Save memory by using 16-bit images for fully opaque images.
+ const VGImageFormat imageFormat = hasAlpha() ? bufferFormat : VG_sRGB_565;
+
+#if PLATFORM(EGL)
+ EGLDisplayOpenVG::current()->sharedPlatformSurface()->makeCurrent();
+#endif
+
+ const IntSize vgMaxImageSize(vgGeti(VG_MAX_IMAGE_WIDTH), vgGeti(VG_MAX_IMAGE_HEIGHT));
+ ASSERT_VG_NO_ERROR();
+
+ TiledImageOpenVG* tiledImage = new TiledImageOpenVG(IntSize(width(), height()), vgMaxImageSize);
+
+ const int numColumns = tiledImage->numColumns();
+ const int numRows = tiledImage->numRows();
+
+ for (int yIndex = 0; yIndex < numRows; ++yIndex) {
+ for (int xIndex = 0; xIndex < numColumns; ++xIndex) {
+ IntRect tileRect = tiledImage->tileRect(xIndex, yIndex);
+ VGImage image = vgCreateImage(imageFormat,
+ tileRect.width(), tileRect.height(), VG_IMAGE_QUALITY_FASTER);
+ ASSERT_VG_NO_ERROR();
+
+ PixelData* pixelData = const_cast<PixelData*>(m_bytes);
+ pixelData += (tileRect.y() * width()) + tileRect.x();
+
+ vgImageSubData(image, reinterpret_cast<unsigned char*>(pixelData),
+ width() * sizeof(PixelData), bufferFormat,
+ 0, 0, tileRect.width(), tileRect.height());
+ ASSERT_VG_NO_ERROR();
+
+ tiledImage->setTile(xIndex, yIndex, image);
+ }
+ }
+
+ return tiledImage;
+}
+
+}
diff --git a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp
new file mode 100644
index 0000000..3fe4d3c
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2007-2009 Torch Mobile, Inc.
+ * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
+ *
+ * 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"
+
+#if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4))
+#define JMPBUF(png_ptr) png_jmpbuf(png_ptr)
+#else
+#define JMPBUF(png_ptr) png_ptr->jmpbuf
+#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 unsigned long cMaxPNGSize = 1000000UL;
+
+// Called if the decoding of the image fails.
+static void PNGAPI decodingFailed(png_structp png, png_const_charp)
+{
+ longjmp(JMPBUF(png), 1);
+}
+
+// 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, png_const_charp warningMsg)
+{
+ // Mozilla did this, so we will too.
+ // Convert a tRNS warning to be an error (see
+ // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 )
+ if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24))
+ png_error(png, warningMsg);
+}
+
+// Called when we have obtained the header information (including the size).
+static void PNGAPI headerAvailable(png_structp png, png_infop)
+{
+ static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable();
+}
+
+// Called when a row is ready.
+static void PNGAPI 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);
+}
+
+// Called when we have completely finished decoding the image.
+static void PNGAPI pngComplete(png_structp png, png_infop)
+{
+ static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete();
+}
+
+class PNGImageReader
+{
+public:
+ PNGImageReader(PNGImageDecoder* decoder)
+ : m_readOffset(0)
+ , m_decodingSizeOnly(false)
+ , m_interlaceBuffer(0)
+ , m_hasAlpha(false)
+ , m_currentBufferSize(0)
+ {
+ m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 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)
+ // This will zero the pointers.
+ png_destroy_read_struct(&m_png, &m_info, 0);
+ delete[] m_interlaceBuffer;
+ m_interlaceBuffer = 0;
+ m_readOffset = 0;
+ }
+
+ unsigned currentBufferSize() const { return m_currentBufferSize; }
+
+ bool decode(const SharedBuffer& data, bool sizeOnly)
+ {
+ m_decodingSizeOnly = sizeOnly;
+ PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png));
+
+ // We need to do the setjmp here. Otherwise bad things will happen.
+ if (setjmp(JMPBUF(m_png)))
+ return decoder->setFailed();
+
+ const char* segment;
+ while (unsigned segmentLength = data.getSomeData(segment, m_readOffset)) {
+ m_readOffset += segmentLength;
+ m_currentBufferSize = m_readOffset;
+ png_process_data(m_png, m_info, reinterpret_cast<png_bytep>(const_cast<char*>(segment)), segmentLength);
+ // We explicitly specify the superclass isSizeAvailable() because we
+ // merely want to check if we've managed to set the size, not
+ // (recursively) trigger additional decoding if we haven't.
+ if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isComplete())
+ return true;
+ }
+ return false;
+ }
+
+ 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;
+ unsigned m_currentBufferSize;
+};
+
+PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption,
+ ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : ImageDecoder(alphaOption, gammaAndColorProfileOption)
+ , m_doNothingOnFailure(false)
+{
+}
+
+PNGImageDecoder::~PNGImageDecoder()
+{
+}
+
+bool PNGImageDecoder::isSizeAvailable()
+{
+ if (!ImageDecoder::isSizeAvailable())
+ decode(true);
+
+ return ImageDecoder::isSizeAvailable();
+}
+
+bool PNGImageDecoder::setSize(unsigned width, unsigned height)
+{
+ if (!ImageDecoder::setSize(width, height))
+ return false;
+
+ prepareScaleDataIfNecessary();
+ return true;
+}
+
+RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index)
+{
+ if (index)
+ return 0;
+
+ if (m_frameBufferCache.isEmpty()) {
+ m_frameBufferCache.resize(1);
+ m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha);
+ }
+
+ RGBA32Buffer& frame = m_frameBufferCache[0];
+ if (frame.status() != RGBA32Buffer::FrameComplete)
+ decode(false);
+ return &frame;
+}
+
+bool PNGImageDecoder::setFailed()
+{
+ if (m_doNothingOnFailure)
+ return false;
+ m_reader.clear();
+ return ImageDecoder::setFailed();
+}
+
+static ColorProfile readColorProfile(png_structp png, png_infop info)
+{
+#ifdef PNG_iCCP_SUPPORTED
+ char* profileName;
+ int compressionType;
+ char* profile;
+ png_uint_32 profileLength;
+ if (png_get_iCCP(png, info, &profileName, &compressionType, &profile, &profileLength)) {
+ ColorProfile colorProfile;
+ colorProfile.append(profile, profileLength);
+ return colorProfile;
+ }
+#endif
+ return ColorProfile();
+}
+
+void PNGImageDecoder::headerAvailable()
+{
+ png_structp png = m_reader->pngPtr();
+ png_infop info = m_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) {
+ longjmp(JMPBUF(png), 1);
+ return;
+ }
+
+ // We can fill in the size now that the header is available. Avoid memory
+ // corruption issues by neutering setFailed() during this call; if we don't
+ // do this, failures will cause |m_reader| to be deleted, and our jmpbuf
+ // will cease to exist. Note that we'll still properly set the failure flag
+ // in this case as soon as we longjmp().
+ m_doNothingOnFailure = true;
+ bool result = setSize(width, height);
+ m_doNothingOnFailure = false;
+ if (!result) {
+ longjmp(JMPBUF(png), 1);
+ return;
+ }
+
+ int bitDepth, colorType, interlaceType, compressionType, filterType, channels;
+ png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, &compressionType, &filterType);
+
+ if ((colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) && !m_ignoreGammaAndColorProfile) {
+ // We currently support color profiles only for RGB and RGBA PNGs. Supporting
+ // color profiles for gray-scale images is slightly tricky, at least using the
+ // CoreGraphics ICC library, because we expand gray-scale images to RGB but we
+ // don't similarly transform the color profile. We'd either need to transform
+ // the color profile or we'd need to decode into a gray-scale image buffer and
+ // hand that to CoreGraphics.
+ m_colorProfile = readColorProfile(png, info);
+ }
+
+ // 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 (!m_ignoreGammaAndColorProfile && 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);
+
+ m_reader->setHasAlpha(channels == 4);
+
+ if (m_reader->decodingSizeOnly()) {
+ // If we only needed the size, halt the reader.
+ m_reader->setReadOffset(m_reader->currentBufferSize() - png->buffer_size);
+ png->buffer_size = 0;
+ }
+}
+
+void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass)
+{
+ if (m_frameBufferCache.isEmpty())
+ return;
+
+ // Initialize the framebuffer if needed.
+ RGBA32Buffer& buffer = m_frameBufferCache[0];
+ if (buffer.status() == RGBA32Buffer::FrameEmpty) {
+ if (!buffer.setSize(scaledSize().width(), scaledSize().height())) {
+ longjmp(JMPBUF(m_reader->pngPtr()), 1);
+ return;
+ }
+ buffer.setStatus(RGBA32Buffer::FramePartial);
+ buffer.setHasAlpha(false);
+ buffer.setColorProfile(m_colorProfile);
+
+ // For PNGs, the frame always fills the entire image.
+ buffer.setRect(IntRect(IntPoint(), size()));
+
+ if (m_reader->pngPtr()->interlaced)
+ m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height());
+ }
+
+ if (!rowBuffer)
+ 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 = m_reader->pngPtr();
+ bool hasAlpha = m_reader->hasAlpha();
+ unsigned colorChannels = hasAlpha ? 4 : 3;
+ png_bytep row;
+ png_bytep interlaceBuffer = m_reader->interlaceBuffer();
+ if (interlaceBuffer) {
+ row = interlaceBuffer + (rowIndex * colorChannels * size().width());
+ png_progressive_combine_row(png, row, rowBuffer);
+ } else
+ row = rowBuffer;
+
+ // Copy the data into our buffer.
+ int width = scaledSize().width();
+ int destY = scaledY(rowIndex);
+
+ // Check that the row is within the image bounds. LibPNG may supply an extra row.
+ if (destY < 0 || destY >= scaledSize().height())
+ return;
+ bool nonTrivialAlpha = false;
+ for (int x = 0; x < width; ++x) {
+ png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels;
+ unsigned alpha = hasAlpha ? pixel[3] : 255;
+ buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha);
+ nonTrivialAlpha |= alpha < 255;
+ }
+ if (nonTrivialAlpha && !buffer.hasAlpha())
+ buffer.setHasAlpha(nonTrivialAlpha);
+}
+
+void PNGImageDecoder::pngComplete()
+{
+ if (!m_frameBufferCache.isEmpty())
+ m_frameBufferCache.first().setStatus(RGBA32Buffer::FrameComplete);
+}
+
+void PNGImageDecoder::decode(bool onlySize)
+{
+ if (failed())
+ return;
+
+ if (!m_reader)
+ m_reader.set(new PNGImageReader(this));
+
+ // If we couldn't decode the image but we've received all the data, decoding
+ // has failed.
+ if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived())
+ setFailed();
+ // If we're done decoding the image, we don't need the PNGImageReader
+ // anymore. (If we failed, |m_reader| has already been cleared.)
+ else if (isComplete())
+ m_reader.clear();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h
new file mode 100644
index 0000000..1e8902f
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h
@@ -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 PNGImageDecoder_h
+#define PNGImageDecoder_h
+
+#include "ImageDecoder.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+ class PNGImageReader;
+
+ // This class decodes the PNG image format.
+ class PNGImageDecoder : public ImageDecoder {
+ public:
+ PNGImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+ virtual ~PNGImageDecoder();
+
+ // ImageDecoder
+ virtual String filenameExtension() const { return "png"; }
+ virtual bool isSizeAvailable();
+ virtual bool setSize(unsigned width, unsigned height);
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+ // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid
+ // accessing deleted memory, especially when calling this from inside
+ // PNGImageReader!
+ virtual bool setFailed();
+
+ // Callbacks from libpng
+ void headerAvailable();
+ void rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass);
+ void pngComplete();
+
+ bool isComplete() const
+ {
+ return !m_frameBufferCache.isEmpty() && (m_frameBufferCache.first().status() == RGBA32Buffer::FrameComplete);
+ }
+
+ private:
+ // Decodes the image. If |onlySize| is true, stops decoding after
+ // calculating the image size. If decoding fails but there is no more
+ // data coming, sets the "decode failure" flag.
+ void decode(bool onlySize);
+
+ OwnPtr<PNGImageReader> m_reader;
+ bool m_doNothingOnFailure;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp b/Source/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp
new file mode 100644
index 0000000..998234f
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Google, Inc.
+ * Copyright (C) 2009 Holger Hans Peter Freyther
+ *
+ * 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 "ImageDecoder.h"
+
+#include "NotImplemented.h"
+
+#include <QPixmap>
+#include <stdio.h>
+
+namespace WebCore {
+
+RGBA32Buffer::RGBA32Buffer()
+ : m_hasAlpha(false)
+ , m_size()
+ , m_status(FrameEmpty)
+ , m_duration(0)
+ , m_disposalMethod(DisposeNotSpecified)
+{
+}
+
+RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return *this;
+
+ copyBitmapData(other);
+ setRect(other.rect());
+ setStatus(other.status());
+ setDuration(other.duration());
+ setDisposalMethod(other.disposalMethod());
+ return *this;
+}
+
+void RGBA32Buffer::clear()
+{
+ m_pixmap = QPixmap();
+ m_image = QImage();
+ m_status = FrameEmpty;
+ // NOTE: Do not reset other members here; clearFrameBufferCache()
+ // calls this to free the bitmap data, but other functions like
+ // initFrameBuffer() and frameComplete() may still need to read
+ // other metadata out of this frame later.
+}
+
+void RGBA32Buffer::zeroFill()
+{
+ if (m_pixmap.isNull() && !m_image.isNull()) {
+ m_pixmap = QPixmap(m_image.width(), m_image.height());
+ m_image = QImage();
+ }
+ m_pixmap.fill(QColor(0, 0, 0, 0));
+}
+
+bool RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return true;
+
+ m_image = other.m_image;
+ m_pixmap = other.m_pixmap;
+ m_size = other.m_size;
+ m_hasAlpha = other.m_hasAlpha;
+ return true;
+}
+
+bool RGBA32Buffer::setSize(int newWidth, int newHeight)
+{
+ // This function should only be called once, it will leak memory
+ // otherwise.
+ ASSERT(width() == 0 && height() == 0);
+
+ m_size = IntSize(newWidth, newHeight);
+ m_image = QImage();
+ m_pixmap = QPixmap(newWidth, newHeight);
+ if (m_pixmap.isNull())
+ return false;
+
+ // Zero the image.
+ zeroFill();
+
+ return true;
+}
+
+QPixmap* RGBA32Buffer::asNewNativeImage() const
+{
+ if (m_pixmap.isNull() && !m_image.isNull()) {
+ m_pixmap = QPixmap::fromImage(m_image);
+ m_image = QImage();
+ }
+ return new QPixmap(m_pixmap);
+}
+
+bool RGBA32Buffer::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+void RGBA32Buffer::setHasAlpha(bool alpha)
+{
+ m_hasAlpha = alpha;
+}
+
+void RGBA32Buffer::setColorProfile(const ColorProfile& colorProfile)
+{
+ notImplemented();
+}
+
+void RGBA32Buffer::setStatus(FrameStatus status)
+{
+ m_status = status;
+}
+
+// The image must not have format 8888 pre multiplied...
+void RGBA32Buffer::setPixmap(const QPixmap& pixmap)
+{
+ m_pixmap = pixmap;
+ m_image = QImage();
+ m_size = pixmap.size();
+ m_hasAlpha = pixmap.hasAlphaChannel();
+}
+
+int RGBA32Buffer::width() const
+{
+ return m_size.width();
+}
+
+int RGBA32Buffer::height() const
+{
+ return m_size.height();
+}
+
+}
diff --git a/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp b/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp
new file mode 100644
index 0000000..50e2106
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Google, Inc.
+ *
+ * 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 "ImageDecoder.h"
+#if PLATFORM(ANDROID)
+#include "SkBitmapRef.h"
+#endif
+
+#include "NotImplemented.h"
+
+namespace WebCore {
+
+RGBA32Buffer::RGBA32Buffer()
+ : m_status(FrameEmpty)
+ , m_duration(0)
+ , m_disposalMethod(DisposeNotSpecified)
+ , m_premultiplyAlpha(true)
+{
+}
+
+RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return *this;
+
+ m_bitmap = other.m_bitmap;
+ // Keep the pixels locked since we will be writing directly into the
+ // bitmap throughout this object's lifetime.
+ m_bitmap.lockPixels();
+ setRect(other.rect());
+ setStatus(other.status());
+ setDuration(other.duration());
+ setDisposalMethod(other.disposalMethod());
+ setPremultiplyAlpha(other.premultiplyAlpha());
+ return *this;
+}
+
+void RGBA32Buffer::clear()
+{
+ m_bitmap.reset();
+ m_status = FrameEmpty;
+ // NOTE: Do not reset other members here; clearFrameBufferCache()
+ // calls this to free the bitmap data, but other functions like
+ // initFrameBuffer() and frameComplete() may still need to read
+ // other metadata out of this frame later.
+}
+
+void RGBA32Buffer::zeroFill()
+{
+ m_bitmap.eraseARGB(0, 0, 0, 0);
+}
+
+bool RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other)
+{
+ if (this == &other)
+ return true;
+
+ m_bitmap.reset();
+ const NativeImageSkia& otherBitmap = other.m_bitmap;
+ return otherBitmap.copyTo(&m_bitmap, otherBitmap.config());
+}
+
+bool RGBA32Buffer::setSize(int newWidth, int newHeight)
+{
+ // This function should only be called once, it will leak memory
+ // otherwise.
+ ASSERT(width() == 0 && height() == 0);
+ m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, newWidth, newHeight);
+ if (!m_bitmap.allocPixels())
+ return false;
+
+ // Zero the image.
+ zeroFill();
+
+ return true;
+}
+
+NativeImagePtr RGBA32Buffer::asNewNativeImage() const
+{
+#if PLATFORM(ANDROID)
+ return new SkBitmapRef(m_bitmap);
+#else
+ return new NativeImageSkia(m_bitmap);
+#endif
+}
+
+bool RGBA32Buffer::hasAlpha() const
+{
+ return !m_bitmap.isOpaque();
+}
+
+void RGBA32Buffer::setHasAlpha(bool alpha)
+{
+ m_bitmap.setIsOpaque(!alpha);
+}
+
+void RGBA32Buffer::setColorProfile(const ColorProfile& colorProfile)
+{
+ notImplemented();
+}
+
+void RGBA32Buffer::setStatus(FrameStatus status)
+{
+ m_status = status;
+ if (m_status == FrameComplete)
+ m_bitmap.setDataComplete(); // Tell the bitmap it's done.
+}
+
+int RGBA32Buffer::width() const
+{
+ return m_bitmap.width();
+}
+
+int RGBA32Buffer::height() const
+{
+ return m_bitmap.height();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp
new file mode 100644
index 0000000..a988d9c
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Google 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "WEBPImageDecoder.h"
+
+#if USE(WEBP)
+
+#include "webp/decode.h"
+
+namespace WebCore {
+
+WEBPImageDecoder::WEBPImageDecoder(ImageSource::AlphaOption alphaOption,
+ ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+ : ImageDecoder(alphaOption, gammaAndColorProfileOption)
+{
+}
+
+WEBPImageDecoder::~WEBPImageDecoder()
+{
+}
+
+bool WEBPImageDecoder::isSizeAvailable()
+{
+ if (!ImageDecoder::isSizeAvailable())
+ decode(true);
+
+ return ImageDecoder::isSizeAvailable();
+}
+
+RGBA32Buffer* WEBPImageDecoder::frameBufferAtIndex(size_t index)
+{
+ if (index)
+ return 0;
+
+ if (m_frameBufferCache.isEmpty()) {
+ m_frameBufferCache.resize(1);
+ m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha);
+ }
+
+ RGBA32Buffer& frame = m_frameBufferCache[0];
+ if (frame.status() != RGBA32Buffer::FrameComplete)
+ decode(false);
+ return &frame;
+}
+
+
+bool WEBPImageDecoder::decode(bool onlySize)
+{
+ // Minimum number of bytes needed to ensure one can parse size information.
+ static const size_t sizeOfHeader = 30;
+ // Number of bytes per pixel.
+ static const int bytesPerPixel = 3;
+
+ if (failed())
+ return false;
+ const size_t dataSize = m_data->buffer().size();
+ const uint8_t* dataBytes =
+ reinterpret_cast<const uint8_t*>(m_data->buffer().data());
+ int width, height;
+ if (dataSize < sizeOfHeader)
+ return true;
+ if (!WebPGetInfo(dataBytes, dataSize, &width, &height))
+ return setFailed();
+ if (!ImageDecoder::isSizeAvailable() && !setSize(width, height))
+ return setFailed();
+ if (onlySize)
+ return true;
+
+ // FIXME: Add support for progressive decoding.
+ if (!isAllDataReceived())
+ return true;
+ ASSERT(!m_frameBufferCache.isEmpty());
+ RGBA32Buffer& buffer = m_frameBufferCache[0];
+ if (buffer.status() == RGBA32Buffer::FrameEmpty) {
+ ASSERT(width == size().width());
+ ASSERT(height == size().height());
+ if (!buffer.setSize(width, height))
+ return setFailed();
+ }
+ const int stride = width * bytesPerPixel;
+ Vector<uint8_t> rgb;
+ rgb.resize(height * stride);
+ if (!WebPDecodeBGRInto(dataBytes, dataSize, rgb.data(), rgb.size(), stride))
+ return setFailed();
+ // FIXME: remove this data copy.
+ for (int y = 0; y < height; ++y) {
+ const uint8_t* const src = &rgb[y * stride];
+ for (int x = 0; x < width; ++x)
+ buffer.setRGBA(x, y, src[bytesPerPixel * x + 2], src[bytesPerPixel * x + 1], src[bytesPerPixel * x + 0], 0xff);
+ }
+ buffer.setStatus(RGBA32Buffer::FrameComplete);
+ buffer.setHasAlpha(false);
+ buffer.setRect(IntRect(IntPoint(), size()));
+ return true;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h
new file mode 100644
index 0000000..6cf8870
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 Google 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 WEBPImageDecoder_h
+#define WEBPImageDecoder_h
+
+#include "ImageDecoder.h"
+
+#if USE(WEBP)
+
+namespace WebCore {
+
+class WEBPImageDecoder : public ImageDecoder {
+public:
+ WEBPImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption);
+ virtual ~WEBPImageDecoder();
+ virtual String filenameExtension() const { return "vp8"; }
+ virtual bool isSizeAvailable();
+ virtual RGBA32Buffer* frameBufferAtIndex(size_t index);
+ virtual bool supportsAlpha() const { return false; }
+
+private:
+ // Returns false in case of decoding failure.
+ bool decode(bool onlySize);
+};
+
+} // namespace WebCore
+
+#endif
+
+#endif
diff --git a/Source/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp b/Source/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp
new file mode 100644
index 0000000..966eb90
--- /dev/null
+++ b/Source/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "ImageDecoder.h"
+
+// FIXME: Are all these needed?
+#include <wx/defs.h>
+#include <wx/bitmap.h>
+#if USE(WXGC)
+#include <wx/graphics.h>
+#endif
+#include <wx/image.h>
+#include <wx/rawbmp.h>
+
+namespace WebCore {
+
+NativeImagePtr RGBA32Buffer::asNewNativeImage() const
+{
+ wxBitmap* bmp = new wxBitmap(width(), height(), 32);
+
+ {
+ typedef wxPixelData<wxBitmap, wxAlphaPixelFormat> WxPixelData;
+ WxPixelData data(*bmp);
+
+ // NB: It appears that the data is in BGRA format instead of RGBA format.
+ // This code works properly on both ppc and intel, meaning the issue is
+ // likely not an issue of byte order getting mixed up on different archs.
+ const unsigned char* bytes = (const unsigned char*)m_bytes;
+ int rowCounter = 0;
+ long pixelCounter = 0;
+ WxPixelData::Iterator p(data);
+ WxPixelData::Iterator rowStart = p;
+ for (size_t i = 0; i < m_size.width() * m_size.height() * sizeof(PixelData); i += sizeof(PixelData)) {
+ p.Red() = bytes[i + 2];
+ p.Green() = bytes[i + 1];
+ p.Blue() = bytes[i + 0];
+ p.Alpha() = bytes[i + 3];
+
+ p++;
+
+ pixelCounter++;
+ if ((pixelCounter % width()) == 0) {
+ rowCounter++;
+ p = rowStart;
+ p.MoveTo(data, 0, rowCounter);
+ }
+ }
+#if !wxCHECK_VERSION(2,9,0)
+ bmp->UseAlpha();
+#endif
+ } // ensure that WxPixelData is destroyed as it unlocks the bitmap data in
+ // its dtor and we can't access it (notably in CreateBitmap() below)
+ // before this is done
+
+ ASSERT(bmp->IsOk());
+
+#if USE(WXGC)
+ wxGraphicsBitmap* bitmap = new wxGraphicsBitmap(wxGraphicsRenderer::GetDefaultRenderer()->CreateBitmap(*bmp));
+ delete bmp;
+ return bitmap;
+#else
+ return bmp;
+#endif
+}
+
+} // namespace WebCore