diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/platform/image-decoders | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_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')
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 |