diff options
Diffstat (limited to 'WebCore/platform/image-decoders')
35 files changed, 2203 insertions, 4676 deletions
diff --git a/WebCore/platform/image-decoders/ImageDecoder.h b/WebCore/platform/image-decoders/ImageDecoder.h index ca27b37..ffc7d9e 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.h +++ b/WebCore/platform/image-decoders/ImageDecoder.h @@ -30,18 +30,22 @@ #include "ImageSource.h" #include "PlatformString.h" #include "SharedBuffer.h" +#include <wtf/Assertions.h> +#include <wtf/RefPtr.h> #include <wtf/Vector.h> -namespace WebCore { +#if PLATFORM(SKIA) +#include "NativeImageSkia.h" +#include "SkBitmap.h" +#endif - typedef Vector<unsigned> RGBA32Array; +namespace WebCore { // 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. @@ -50,46 +54,101 @@ namespace WebCore { DisposeOverwriteBgcolor, // Clear frame to transparent DisposeOverwritePrevious, // Clear frame to previous framebuffer contents }; +#if PLATFORM(SKIA) + typedef uint32_t PixelData; +#else + typedef unsigned PixelData; +#endif + + RGBA32Buffer(); + + // For backends which refcount their data, this constructor doesn't need + // to create a new copy of the image data, only increase the ref count. + // + // This exists because ImageDecoder keeps a Vector<RGBA32Buffer>, and + // Vector requires this constructor. + RGBA32Buffer(const RGBA32Buffer& other) + { + operator=(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. + void copyBitmapData(const RGBA32Buffer& other); - RGBA32Buffer() - : m_height(0) - , m_status(FrameEmpty) - , m_duration(0) - , m_disposalMethod(DisposeNotSpecified) - , m_hasAlpha(false) + // 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) { - } - - void clear() { - m_bytes.clear(); - 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. + 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); } - const RGBA32Array& bytes() const { return m_bytes; } - RGBA32Array& bytes() { return m_bytes; } + // 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; } - unsigned height() const { return m_height; } FrameStatus status() const { return m_status; } unsigned duration() const { return m_duration; } FrameDisposalMethod disposalMethod() const { return m_disposalMethod; } - bool hasAlpha() const { return m_hasAlpha; } + void setHasAlpha(bool alpha); void setRect(const IntRect& r) { m_rect = r; } - void ensureHeight(unsigned rowIndex) { if (rowIndex > m_height) m_height = rowIndex; } - void setStatus(FrameStatus s) { m_status = s; } + void setStatus(FrameStatus status); void setDuration(unsigned duration) { m_duration = duration; } void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; } - void setHasAlpha(bool alpha) { m_hasAlpha = alpha; } - static void setRGBA(unsigned& pos, unsigned r, unsigned g, unsigned b, unsigned a) + inline void setRGBA(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a) + { + setRGBA(getAddr(x, y), r, g, b, a); + } + + private: + RGBA32Buffer& operator=(const RGBA32Buffer& other); + + int width() const; + int height() const; + + inline PixelData* getAddr(int x, int y) + { +#if PLATFORM(CAIRO) || PLATFORM(WX) + return m_bytes.data() + (y * width()) + x; +#elif PLATFORM(SKIA) + return m_bitmap.getAddr32(x, y); +#else + ASSERT_NOT_REACHED(); + return 0; +#endif + } + + inline void setRGBA(PixelData* dest, unsigned r, unsigned g, unsigned b, unsigned a) { // We store this data pre-multiplied. if (a == 0) - pos = 0; + *dest = 0; else { if (a < 255) { float alphaPercent = a / 255.0f; @@ -97,20 +156,25 @@ namespace WebCore { g = static_cast<unsigned>(g * alphaPercent); b = static_cast<unsigned>(b * alphaPercent); } - pos = (a << 24 | r << 16 | g << 8 | b); + *dest = (a << 24 | r << 16 | g << 8 | b); } } - private: - RGBA32Array m_bytes; - 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. - unsigned m_height; // The height (the number of rows we've fully decoded). +#if PLATFORM(CAIRO) || PLATFORM(WX) + Vector<PixelData> m_bytes; + 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. +#elif PLATFORM(SKIA) + NativeImageSkia m_bitmap; +#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_hasAlpha; // Whether or not any of the pixels in the buffer have transparency. + unsigned m_duration; // The animation delay. + FrameDisposalMethod m_disposalMethod; + // What to do with this frame's data when initializing the next frame. }; // The ImageDecoder class represents a base class for specific image format decoders @@ -119,11 +183,11 @@ namespace WebCore { class ImageDecoder { public: ImageDecoder() - : m_sizeAvailable(false) - , m_failed(false) + : m_failed(false) + , m_sizeAvailable(false) { } - + virtual ~ImageDecoder() {} // The the filename extension usually associated with an undecoded image of this type. @@ -132,16 +196,53 @@ namespace WebCore { // All specific decoder plugins must do something with the data they are given. virtual void setData(SharedBuffer* data, bool allDataReceived) { m_data = data; } - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const = 0; + // 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 + { + // Requesting the size of an invalid bitmap is meaningless. + ASSERT(!m_failed); + return m_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(); + } - // Requests the size. - virtual IntSize size() const { return m_size; } + // Called by the image decoders to set their decoded size, this also check + // 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)) { + m_failed = true; + return false; + } + 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 int frameCount() { return 1; } + virtual size_t frameCount() { return 1; } // The number of repetitions to perform for an animation loop. virtual int repetitionCount() const { return cAnimationNone; } @@ -149,7 +250,7 @@ namespace WebCore { // 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 index) = 0; + virtual RGBA32Buffer* frameBufferAtIndex(size_t) = 0; // Whether or not the underlying image format even supports alpha transparency. virtual bool supportsAlpha() const { return true; } @@ -168,9 +269,22 @@ namespace WebCore { protected: RefPtr<SharedBuffer> m_data; // The encoded data. Vector<RGBA32Buffer> m_frameBufferCache; - bool m_sizeAvailable; - mutable bool m_failed; + bool m_failed; + + 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; }; } // namespace WebCore diff --git a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp index 3b90bdc..de0690f 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp +++ b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp @@ -1,42 +1,148 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * + * 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: - * 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 + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #include "config.h" #include "BMPImageDecoder.h" -namespace WebCore +#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() + : ImageDecoder() + , m_allDataReceived(false) + , m_decodedOffset(0) { +} -bool BMPImageDecoder::isSizeAvailable() const +void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) { - return false; + if (failed()) + return; + + ImageDecoder::setData(data, allDataReceived); + m_allDataReceived = allDataReceived; + if (m_reader) + m_reader->setData(data); } - + +bool BMPImageDecoder::isSizeAvailable() +{ + if (!ImageDecoder::isSizeAvailable() && !failed()) + decodeWithCheckForDataEnded(true); + + return ImageDecoder::isSizeAvailable(); +} + RGBA32Buffer* BMPImageDecoder::frameBufferAtIndex(size_t index) { - return 0; + if (index) + return 0; + + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.resize(1); + + RGBA32Buffer* buffer = &m_frameBufferCache.first(); + if (buffer->status() != RGBA32Buffer::FrameComplete && !failed()) + decodeWithCheckForDataEnded(false); + return buffer; +} + +void BMPImageDecoder::decodeWithCheckForDataEnded(bool onlySize) +{ + if (failed()) + return; + + // If we couldn't decode the image but we've received all the data, decoding + // has failed. + if (!decode(onlySize) && m_allDataReceived) + setFailed(); +} + +bool BMPImageDecoder::decode(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" + */ + }; + if (fileType != BMAP) { + setFailed(); + return false; + } + + return true; } } // namespace WebCore diff --git a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h index a2d4b25..c793585 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h +++ b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h @@ -1,46 +1,85 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * + * 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: - * 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 + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef BMPImageDecoder_h #define BMPImageDecoder_h -#include "ImageDecoder.h" +#include "BMPImageReader.h" +#include <wtf/OwnPtr.h> namespace WebCore { - class BMPImageReader; - // This class decodes the BMP image format. class BMPImageDecoder : public ImageDecoder { public: + BMPImageDecoder(); + + // ImageDecoder virtual String filenameExtension() const { return "bmp"; } + virtual void setData(SharedBuffer*, bool allDataReceived); + virtual bool isSizeAvailable(); + virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; + private: + inline uint32_t readUint32(int offset) const + { + return BMPImageReader::readUint32(m_data.get(), + m_decodedOffset + offset); + } - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); + // 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 decodeWithCheckForDataEnded(bool onlySize); + + // Decodes the image. If |onlySize| is true, stops decoding after + // calculating the image size. Returns whether decoding succeeded. + // NOTE: Used internally by decodeWithCheckForDataEnded(). Other people + // should not call this. + bool decode(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); + + // True if we've seen all the data. + bool m_allDataReceived; + + // 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 diff --git a/WebCore/platform/image-decoders/skia/BMPImageReader.cpp b/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp index 829b107..1936d81 100644 --- a/WebCore/platform/image-decoders/skia/BMPImageReader.cpp +++ b/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp @@ -33,11 +33,15 @@ namespace WebCore { -BMPImageReader::BMPImageReader() - : m_decodedOffset(0) - , m_headerOffset(0) - , m_imgDataOffset(0) - , m_andMaskState(None) +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) @@ -46,88 +50,52 @@ BMPImageReader::BMPImageReader() , m_tableSizeInBytes(0) , m_seenNonZeroAlphaPixel(false) , m_seenZeroAlphaPixel(false) + , m_andMaskState(usesAndMask ? NotYetDecoded : None) { - m_frameBufferCache.resize(1); - // Clue-in decodeBMP() that we need to detect the correct info header size. memset(&m_infoHeader, 0, sizeof(m_infoHeader)); } -void BMPImageReader::setData(SharedBuffer* data, bool allDataReceived) -{ - ImageDecoder::setData(data, allDataReceived); - - // NOTE: This function intentionally uses frameBufferAtIndex() instead of - // checking m_frameBufferCache.first() directly, so that it will do the - // right thing for ICOImageDecoder, which needs to override this accessor - // to support ICOs which contain PNGs. - - // Return quickly when we can't do any more work. - if (m_failed || data->isEmpty() - || (frameBufferAtIndex(0)->status() == RGBA32Buffer::FrameComplete)) - return; - - // Decode as much as we can. This assumes |data| starts at the beginning - // of the image data, rather than containing just the latest chunk. - decodeImage(data); - if (m_failed) { - // Handle failure before getting the framebuffer below. - m_colorTable.clear(); - return; - } - - // If we got all the data but couldn't finish decoding, fail. - const bool finished = - (frameBufferAtIndex(0)->status() == RGBA32Buffer::FrameComplete); - if (allDataReceived && !finished) - m_failed = true; - - // Release the color table when we no longer need it. - if (finished || m_failed) - m_colorTable.clear(); -} - -RGBA32Buffer* BMPImageReader::frameBufferAtIndex(size_t index) -{ - return index ? 0 : &m_frameBufferCache.first(); -} - -void BMPImageReader::decodeBMP(SharedBuffer* data) +bool BMPImageReader::decodeBMP(bool onlySize) { // Calculate size of info header. - if (!m_infoHeader.biSize && !getInfoHeaderSize(data)) - return; + if (!m_infoHeader.biSize && !readInfoHeaderSize()) + return false; // Read and process info header. if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) - && !processInfoHeader(data)) - return; + && !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(data)) - return; + if (m_needToProcessBitmasks && !processBitmasks()) + return false; // Read and process the color table, if needed. - if (m_needToProcessColorTable && !processColorTable(data)) - return; - - // Initialize frame buffer state, if needed. - if (m_frameBufferCache.first().status() == RGBA32Buffer::FrameEmpty) { - m_frameBufferCache.first().setRect(IntRect(IntPoint(), size())); - m_frameBufferCache.first().setStatus(RGBA32Buffer::FramePartial); - if (!m_frameBufferCache.first().setSize(m_infoHeader.biWidth, - m_infoHeader.biHeight)) { - // Unable to allocate. - m_failed = true; - return; - } + 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 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_frameBufferCache.first().setHasAlpha(false); + 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(size().height() - 1); + m_coord.setY(m_parent->size().height() - 1); } // Decode the data. @@ -135,40 +103,40 @@ void BMPImageReader::decodeBMP(SharedBuffer* data) if ((m_infoHeader.biCompression == RLE4) || (m_infoHeader.biCompression == RLE8) || (m_infoHeader.biCompression == RLE24)) { - if (!processRLEData(data)) - return; - } else if (!processNonRLEData(data, false, 0)) - return; + if (!processRLEData()) + return false; + } else if (!processNonRLEData(false, 0)) + return false; } // If the image has an AND mask and there was no alpha data, process the // mask. - if ((m_andMaskState == NotYetDecoded) - && !m_frameBufferCache.first().hasAlpha()) { + if ((m_andMaskState == NotYetDecoded) && !m_buffer->hasAlpha()) { // Reset decoding coordinates to start of image. m_coord.setX(0); - m_coord.setY(m_isTopDown ? 0 : (size().height() - 1)); + 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) && !processNonRLEData(data, false, 0)) - return; + if ((m_andMaskState == Decoding) && !processNonRLEData(false, 0)) + return false; // Done! - m_frameBufferCache.first().setStatus(RGBA32Buffer::FrameComplete); + m_buffer->setStatus(RGBA32Buffer::FrameComplete); + return true; } -bool BMPImageReader::getInfoHeaderSize(SharedBuffer* data) +bool BMPImageReader::readInfoHeaderSize() { // Get size of info header. ASSERT(m_decodedOffset == m_headerOffset); - if ((m_decodedOffset > data->size()) - || ((data->size() - m_decodedOffset) < 4)) + if ((m_decodedOffset > m_data->size()) + || ((m_data->size() - m_decodedOffset) < 4)) return false; - m_infoHeader.biSize = readUint32(data, 0); + m_infoHeader.biSize = readUint32(0); // Don't increment m_decodedOffset here, it just makes the code in // processInfoHeader() more confusing. @@ -177,10 +145,8 @@ bool BMPImageReader::getInfoHeaderSize(SharedBuffer* data) // image data. if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) || (m_imgDataOffset - && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize)))) { - m_failed = true; - return false; - } + && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize)))) + return setFailed(); // See if this is a header size we understand: // OS/2 1.x: 12 @@ -195,32 +161,28 @@ bool BMPImageReader::getInfoHeaderSize(SharedBuffer* data) || (m_infoHeader.biSize == 46))) m_isOS22x = true; else - m_failed = true; + return setFailed(); - return !m_failed; + return true; } -bool BMPImageReader::processInfoHeader(SharedBuffer* data) +bool BMPImageReader::processInfoHeader() { // Read info header. ASSERT(m_decodedOffset == m_headerOffset); - if ((m_decodedOffset > data->size()) - || ((data->size() - m_decodedOffset) < m_infoHeader.biSize) - || !readInfoHeader(data)) + 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()) { - m_failed = true; - return false; - } + if (!isInfoHeaderValid()) + return setFailed(); - // Make our size available to the caller. - if (!setSize(m_infoHeader.biWidth, m_infoHeader.biHeight)) { - m_failed = true; - return false; - } + // Set our size. + if (!m_parent->setSize(m_infoHeader.biWidth, m_infoHeader.biHeight)) + return setFailed(); // 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. @@ -250,29 +212,29 @@ bool BMPImageReader::processInfoHeader(SharedBuffer* data) return true; } -bool BMPImageReader::readInfoHeader(SharedBuffer* data) +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(data, 4); - m_infoHeader.biHeight = readUint16(data, 6); + 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(data, 10); + m_infoHeader.biBitCount = readUint16(10); return true; } - m_infoHeader.biWidth = readUint32(data, 4); - m_infoHeader.biHeight = readUint32(data, 8); + m_infoHeader.biWidth = readUint32(4); + m_infoHeader.biHeight = readUint32(8); if (m_andMaskState != None) m_infoHeader.biHeight /= 2; - m_infoHeader.biBitCount = readUint16(data, 14); + m_infoHeader.biBitCount = readUint16(14); // Read compression type, if present. if (m_infoHeader.biSize >= 20) { - uint32_t biCompression = readUint32(data, 16); + uint32_t biCompression = readUint32(16); // Detect OS/2 2.x-specific compression types. if ((biCompression == 3) && (m_infoHeader.biBitCount == 1)) { @@ -281,17 +243,16 @@ bool BMPImageReader::readInfoHeader(SharedBuffer* data) } else if ((biCompression == 4) && (m_infoHeader.biBitCount == 24)) { m_infoHeader.biCompression = RLE24; m_isOS22x = true; - } else if (biCompression > 5) { - // Some type we don't understand. - m_failed = true; - return false; - } else - m_infoHeader.biCompression = static_cast<CompressionType>(biCompression); + } else if (biCompression > 5) + return 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(data, 32); + 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 @@ -308,10 +269,10 @@ bool BMPImageReader::readInfoHeader(SharedBuffer* data) // 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(data, 40); - m_bitMasks[1] = readUint32(data, 44); - m_bitMasks[2] = readUint32(data, 48); - m_bitMasks[3] = readUint32(data, 52); + m_bitMasks[0] = readUint32(40); + m_bitMasks[1] = readUint32(44); + m_bitMasks[2] = readUint32(48); + m_bitMasks[3] = readUint32(52); } // Detect top-down BMPs. @@ -441,7 +402,7 @@ bool BMPImageReader::isInfoHeaderValid() const return true; } -bool BMPImageReader::processBitmasks(SharedBuffer* data) +bool BMPImageReader::processBitmasks() { // Create m_bitMasks[] values. if (m_infoHeader.biCompression != BITFIELDS) { @@ -469,19 +430,19 @@ bool BMPImageReader::processBitmasks(SharedBuffer* data) // we read the info header. // Fail if we don't have enough file space for the bitmasks. - static const int 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)))) { - m_failed = true; - return false; - } + 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 setFailed(); // Read bitmasks. - if ((data->size() - m_decodedOffset) < SIZEOF_BITMASKS) + if ((m_data->size() - m_decodedOffset) < SIZEOF_BITMASKS) return false; - m_bitMasks[0] = readUint32(data, 0); - m_bitMasks[1] = readUint32(data, 4); - m_bitMasks[2] = readUint32(data, 8); + 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; @@ -500,7 +461,8 @@ bool BMPImageReader::processBitmasks(SharedBuffer* data) // 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); + 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 @@ -513,10 +475,8 @@ bool BMPImageReader::processBitmasks(SharedBuffer* data) // Make sure bitmask does not overlap any other bitmasks. for (int j = 0; j < i; ++j) { - if (tempMask & m_bitMasks[j]) { - m_failed = true; - return false; - } + if (tempMask & m_bitMasks[j]) + return setFailed(); } // Count offset into pixel data. @@ -528,10 +488,8 @@ bool BMPImageReader::processBitmasks(SharedBuffer* data) --m_bitShiftsLeft[i]; // Make sure bitmask is contiguous. - if (tempMask) { - m_failed = true; - return false; - } + if (tempMask) + return setFailed(); // Since RGBABuffer tops out at 8 bits per channel, adjust the shift // amounts to use the most significant 8 bits of the channel. @@ -544,26 +502,26 @@ bool BMPImageReader::processBitmasks(SharedBuffer* data) return true; } -bool BMPImageReader::processColorTable(SharedBuffer* data) +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)))) { - m_failed = true; - return false; - } + 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 setFailed(); // Read color table. - if ((m_decodedOffset > data->size()) - || ((data->size() - m_decodedOffset) < m_tableSizeInBytes)) + 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 = data->data()[m_decodedOffset++]; - m_colorTable[i].rgbGreen = data->data()[m_decodedOffset++]; - m_colorTable[i].rgbRed = data->data()[m_decodedOffset++]; + 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; @@ -578,9 +536,9 @@ bool BMPImageReader::processColorTable(SharedBuffer* data) return true; } -bool BMPImageReader::processRLEData(SharedBuffer* data) +bool BMPImageReader::processRLEData() { - if (m_decodedOffset > data->size()) + if (m_decodedOffset > m_data->size()) return false; // RLE decoding is poorly specified. Two main problems: @@ -608,25 +566,23 @@ bool BMPImageReader::processRLEData(SharedBuffer* data) while (true) { // Every entry takes at least two bytes; bail if there isn't enough // data. - if ((data->size() - m_decodedOffset) < 2) + 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 = data->data()[m_decodedOffset]; - const uint8_t code = data->data()[m_decodedOffset + 1]; - if (((count != 0) || (code != 1)) && pastEndOfImage(0)) { - m_failed = true; - return false; - } + const uint8_t count = m_data->data()[m_decodedOffset]; + const uint8_t code = m_data->data()[m_decodedOffset + 1]; + if (((count != 0) || (code != 1)) && pastEndOfImage(0)) + return setFailed(); // Decode. if (count == 0) { switch (code) { case 0: // Magic token: EOL // Skip any remaining pixels in this row. - if (m_coord.x() < size().width()) - m_frameBufferCache.first().setHasAlpha(true); + if (m_coord.x() < m_parent->size().width()) + m_buffer->setHasAlpha(true); moveBufferToNextRow(); m_decodedOffset += 2; @@ -634,28 +590,28 @@ bool BMPImageReader::processRLEData(SharedBuffer* data) case 1: // Magic token: EOF // Skip any remaining pixels in the image. - if ((m_coord.x() < size().width()) - || (m_isTopDown ? (m_coord.y() < (size().height() - 1)) : (m_coord.y() > 0))) - m_frameBufferCache.first().setHasAlpha(true); + 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 ((data->size() - m_decodedOffset) < 4) + 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 = data->data()[m_decodedOffset + 2]; - const uint8_t dy = data->data()[m_decodedOffset + 3]; + const uint8_t dx = m_data->data()[m_decodedOffset + 2]; + const uint8_t dy = m_data->data()[m_decodedOffset + 3]; if ((dx != 0) || (dy != 0)) - m_frameBufferCache.first().setHasAlpha(true); - if (((m_coord.x() + dx) > size().width()) || - pastEndOfImage(dy)) { - m_failed = true; - return false; - } + m_buffer->setHasAlpha(true); + if (((m_coord.x() + dx) > m_parent->size().width()) || + pastEndOfImage(dy)) + return setFailed(); // Skip intervening pixels. m_coord.move(dx, m_isTopDown ? dy : -dy); @@ -671,7 +627,7 @@ bool BMPImageReader::processRLEData(SharedBuffer* data) // point to the beginning of the pixel data, bump it past // the escape bytes and then reset if decoding failed. m_decodedOffset += 2; - if (!processNonRLEData(data, true, code)) { + if (!processNonRLEData(true, code)) { m_decodedOffset -= 2; return false; } @@ -681,16 +637,17 @@ bool BMPImageReader::processRLEData(SharedBuffer* data) // 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, size().width()); + 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 ((data->size() - m_decodedOffset) < 4) + if ((m_data->size() - m_decodedOffset) < 4) return false; // One BGR triple that we copy |count| times. - fillRGBA(endX, data->data()[m_decodedOffset + 3], - data->data()[m_decodedOffset + 2], code, 0xff); + 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 @@ -702,10 +659,8 @@ bool BMPImageReader::processRLEData(SharedBuffer* data) colorIndexes[1] &= 0xf; } if ((colorIndexes[0] >= m_infoHeader.biClrUsed) - || (colorIndexes[1] >= m_infoHeader.biClrUsed)) { - m_failed = true; - return false; - } + || (colorIndexes[1] >= m_infoHeader.biClrUsed)) + return setFailed(); for (int which = 0; m_coord.x() < endX; ) { setI(colorIndexes[which]); which = !which; @@ -717,20 +672,18 @@ bool BMPImageReader::processRLEData(SharedBuffer* data) } } -bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPixels) +bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) { - if (m_decodedOffset > data->size()) + if (m_decodedOffset > m_data->size()) return false; if (!inRLE) - numPixels = size().width(); + 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 > size().width()) { - m_failed = true; - return false; - } + if (endX > m_parent->size().width()) + return setFailed(); // Determine how many bytes of data the requested number of pixels // requires. @@ -748,7 +701,7 @@ bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPi // 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 ((data->size() - m_decodedOffset) < paddedNumBytes) + if ((m_data->size() - m_decodedOffset) < paddedNumBytes) return false; if (m_infoHeader.biBitCount < 16) { @@ -757,8 +710,9 @@ bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPi // 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 = data->data()[m_decodedOffset + byte]; - for (size_t pixel = 0; (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) { + 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) { @@ -769,14 +723,12 @@ bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPi // web will not be doing a lot of inverting. if (colorIndex) { setRGBA(0, 0, 0, 0); - m_frameBufferCache.first().setHasAlpha(true); + m_buffer->setHasAlpha(true); } else m_coord.move(1, 0); } else { - if (colorIndex >= m_infoHeader.biClrUsed) { - m_failed = true; - return false; - } + if (colorIndex >= m_infoHeader.biClrUsed) + return setFailed(); setI(colorIndex); } pixelData <<= m_infoHeader.biBitCount; @@ -785,7 +737,7 @@ bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPi } else { // RGB data. Decode pixels one at a time, left to right. while (m_coord.x() < endX) { - const uint32_t pixel = readCurrentPixel(data, bytesPerPixel); + 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 @@ -803,12 +755,10 @@ bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPi } else { m_seenNonZeroAlphaPixel = true; if (m_seenZeroAlphaPixel) { - // The eraseARGB() call here also sets "hasAlpha" true. - m_frameBufferCache.first().bitmap().eraseARGB(0, 0, 0, - 0); + m_buffer->zeroFill(); m_seenZeroAlphaPixel = false; } else if (alpha != 255) - m_frameBufferCache.first().setHasAlpha(true); + m_buffer->setHasAlpha(true); } setRGBA(getComponent(pixel, 0), getComponent(pixel, 1), @@ -832,4 +782,11 @@ void BMPImageReader::moveBufferToNextRow() m_coord.move(-m_coord.x(), m_isTopDown ? 1 : -1); } +bool BMPImageReader::setFailed() +{ + m_parent->setFailed(); + m_colorTable.clear(); + return false; +} + } // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/BMPImageReader.h b/WebCore/platform/image-decoders/bmp/BMPImageReader.h index 0edf7b0..1271172 100644 --- a/WebCore/platform/image-decoders/skia/BMPImageReader.h +++ b/WebCore/platform/image-decoders/bmp/BMPImageReader.h @@ -36,46 +36,26 @@ namespace WebCore { - // This class decodes a BMP image. It is used as a base for the BMP and ICO - // decoders, which wrap it in the appropriate code to read file headers, etc. - class BMPImageReader : public ImageDecoder { + // 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: - BMPImageReader(); - - // Does the actual decoding. |data| starts at the beginning of the file, - // but may be incomplete. - virtual void decodeImage(SharedBuffer* data) = 0; - - // ImageDecoder - virtual void setData(SharedBuffer* data, bool allDataReceived); - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - - protected: - enum AndMaskState { - None, - NotYetDecoded, - Decoding, - }; - - // Decodes a single BMP, starting with an info header. - void decodeBMP(SharedBuffer* data); - - // Read a value from |data[m_decodedOffset + additionalOffset]|, converting - // from little to native endianness. - inline uint16_t readUint16(SharedBuffer* data, int additionalOffset) const + // 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()[m_decodedOffset + additionalOffset], 2); + memcpy(&result, &data->data()[offset], 2); #if PLATFORM(BIG_ENDIAN) result = ((result & 0xff) << 8) | ((result & 0xff00) >> 8); #endif return result; } - inline uint32_t readUint32(SharedBuffer* data, int additionalOffset) const + static inline uint32_t readUint32(SharedBuffer* data, int offset) { uint32_t result; - memcpy(&result, &data->data()[m_decodedOffset + additionalOffset], 4); + memcpy(&result, &data->data()[offset], 4); #if PLATFORM(BIG_ENDIAN) result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) | ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24); @@ -83,26 +63,26 @@ namespace WebCore { return result; } - // 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; + // |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); - // 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; + void setBuffer(RGBA32Buffer* buffer) { m_buffer = buffer; } + void setData(SharedBuffer* data) { m_data = data; } - // 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; + // 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. + // The various BMP compression types. We don't currently decode all + // these. enum CompressionType { // Universal types RGB = 0, @@ -116,9 +96,14 @@ namespace WebCore { HUFFMAN1D, // Stored in file as 3 RLE24, // Stored in file as 4 }; + enum AndMaskState { + None, + NotYetDecoded, + Decoding, + }; - // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE structs, - // but with unnecessary entries removed. + // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE + // structs, but with unnecessary entries removed. struct BitmapInfoHeader { uint32_t biSize; int32_t biWidth; @@ -133,17 +118,27 @@ namespace WebCore { uint8_t rgbRed; }; - // Determines the size of the BMP info header. Returns true if the size is - // valid. - bool getInfoHeaderSize(SharedBuffer* data); + inline uint16_t readUint16(int offset) const + { + return readUint16(m_data.get(), m_decodedOffset + offset); + } - // Processes the BMP info header. Returns true if the info header could be - // decoded. - bool processInfoHeader(SharedBuffer* data); + 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(); - // Helper function for processInfoHeader() which does the actual reading of - // header values from the byte stream. Returns false on error. - bool readInfoHeader(SharedBuffer* data); + // 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 @@ -155,57 +150,58 @@ namespace WebCore { // 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(SharedBuffer* data); + // 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(SharedBuffer* data); + // 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(SharedBuffer* data); + 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; returns true on // success. // * 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 true if the whole image was decoded. - bool processNonRLEData(SharedBuffer* data, bool inRLE, int numPixels); + // |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 true if the whole image + // was decoded. + bool 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. + // 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) >= size().height()) + ? ((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(SharedBuffer* data, int bytesPerPixel) const + // 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 additionalOffset = m_coord.x() * bytesPerPixel; + const int offset = m_coord.x() * bytesPerPixel; switch (bytesPerPixel) { case 2: - return readUint16(data, additionalOffset); + 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. + // 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, - &data->data()[m_decodedOffset + additionalOffset], 3); + memcpy(&pixel, &m_data->data()[m_decodedOffset + offset], 3); #if PLATFORM(BIG_ENDIAN) pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) | ((pixel & 0xff000000) >> 24); @@ -214,7 +210,7 @@ namespace WebCore { } case 4: - return readUint32(data, additionalOffset); + return readUint32(offset); default: ASSERT_NOT_REACHED(); @@ -222,12 +218,12 @@ namespace WebCore { } } - // Returns the value of the desired component (0, 1, 2, 3 == R, G, B, A) in - // the given pixel data. + // 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]; + return ((pixel & m_bitMasks[component]) >> + m_bitShiftsRight[component]) << m_bitShiftsLeft[component]; } inline unsigned getAlpha(uint32_t pixel) const @@ -240,51 +236,84 @@ namespace WebCore { } // 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. + // 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, + 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) + inline void setRGBA(unsigned red, + unsigned green, + unsigned blue, + unsigned alpha) { - RGBA32Buffer::setRGBA( - m_frameBufferCache.first().bitmap().getAddr32(m_coord.x(), m_coord.y()), - red, green, blue, 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) + // |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|. + // 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(); + // Sets the "decode failure" flag and clears any local storage. For + // caller convenience (since so many callers want to return false after + // calling this), returns false to enable easy tailcalling. + bool setFailed(); + + // 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. + // 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. + // 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 @@ -295,15 +324,17 @@ namespace WebCore { 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. + // 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"...) + // 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]; @@ -320,6 +351,12 @@ namespace WebCore { // 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 diff --git a/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp b/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp new file mode 100644 index 0000000..c53eabd --- /dev/null +++ b/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp @@ -0,0 +1,124 @@ +/* + * 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 { + +RGBA32Buffer::RGBA32Buffer() + : m_hasAlpha(false) + , m_status(FrameEmpty) + , m_duration(0) + , m_disposalMethod(DisposeNotSpecified) +{ +} + +void RGBA32Buffer::clear() +{ + m_bytes.clear(); + 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_bytes.fill(0); + m_hasAlpha = true; +} + +void RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other) +{ + if (this == &other) + return; + + m_bytes = other.m_bytes; + m_size = other.m_size; + setHasAlpha(other.m_hasAlpha); +} + +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_bytes.resize(newWidth * newHeight); + m_size = IntSize(newWidth, newHeight); + + // Zero the image. + zeroFill(); + + return true; +} + +NativeImagePtr RGBA32Buffer::asNewNativeImage() const +{ + return cairo_image_surface_create_for_data( + reinterpret_cast<unsigned char*>(const_cast<PixelData*>( + m_bytes.data())), CAIRO_FORMAT_ARGB32, width(), height(), + width() * sizeof(PixelData)); +} + +bool RGBA32Buffer::hasAlpha() const +{ + return m_hasAlpha; +} + +void RGBA32Buffer::setHasAlpha(bool alpha) +{ + m_hasAlpha = alpha; +} + +void RGBA32Buffer::setStatus(FrameStatus status) +{ + m_status = status; +} + +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; +} + +int RGBA32Buffer::width() const { + return m_size.width(); +} + +int RGBA32Buffer::height() const { + return m_size.height(); +} + +} // namespace WebCore diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index 62d8b5b..87036c9 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -113,24 +113,17 @@ void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) } // Whether or not the size information has been decoded yet. -bool GIFImageDecoder::isSizeAvailable() const +bool GIFImageDecoder::isSizeAvailable() { - // If we have pending data to decode, send it to the GIF reader now. - if (!m_sizeAvailable && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the first - // size is encountered. - decode(GIFSizeQuery, 0); - } + if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) + decode(GIFSizeQuery, 0); - return m_sizeAvailable; + return ImageDecoder::isSizeAvailable(); } // The total number of frames for the image. Will scan the image data for the answer // (without necessarily decoding all of the individual frames). -int GIFImageDecoder::frameCount() +size_t GIFImageDecoder::frameCount() { // If the decoder had an earlier error, we will just return what we had decoded // so far. @@ -177,7 +170,7 @@ int GIFImageDecoder::repetitionCount() const RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index) { - if (index >= static_cast<size_t>(frameCount())) + if (index >= frameCount()) return 0; RGBA32Buffer& frame = m_frameBufferCache[index]; @@ -237,7 +230,7 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) } // Feed data to the GIF reader. -void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const +void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) { if (m_failed) return; @@ -251,10 +244,9 @@ void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const } // Callbacks from the GIF reader. -void GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height) +bool GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height) { - m_size = IntSize(width, height); - m_sizeAvailable = true; + return setSize(width, height); } void GIFImageDecoder::decodingHalted(unsigned bytesLeft) @@ -262,7 +254,7 @@ void GIFImageDecoder::decodingHalted(unsigned bytesLeft) m_reader->setReadOffset(m_data->size() - bytesLeft); } -void GIFImageDecoder::initFrameBuffer(unsigned frameIndex) +bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) { // Initialize the frame rect in our buffer. IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(), @@ -279,7 +271,10 @@ void GIFImageDecoder::initFrameBuffer(unsigned frameIndex) if (frameIndex == 0) { // This is the first frame, so we're not relying on any previous data. - prepEmptyFrameBuffer(buffer); + if (!buffer->setSize(size().width(), size().height())) { + m_failed = true; + return false; + } } else { // The starting state for this frame depends on the previous frame's // disposal method. @@ -302,26 +297,25 @@ void GIFImageDecoder::initFrameBuffer(unsigned frameIndex) if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || (prevMethod == RGBA32Buffer::DisposeKeep)) { // Preserve the last frame as the starting state for this frame. - buffer->bytes() = prevBuffer->bytes(); - buffer->setHasAlpha(prevBuffer->hasAlpha()); + buffer->copyBitmapData(*prevBuffer); } else { // We want to clear the previous frame to transparent, without // affecting pixels in the image outside of the frame. const IntRect& prevRect = prevBuffer->rect(); if ((frameIndex == 0) - || prevRect.contains(IntRect(IntPoint(0, 0), size()))) { + || prevRect.contains(IntRect(IntPoint(), size()))) { // Clearing the first frame, or a frame the size of the whole // image, results in a completely empty image. - prepEmptyFrameBuffer(buffer); + if (!buffer->setSize(size().width(), size().height())) { + m_failed = true; + return false; + } } else { // Copy the whole previous buffer, then clear just its frame. - buffer->bytes() = prevBuffer->bytes(); - buffer->setHasAlpha(prevBuffer->hasAlpha()); + buffer->copyBitmapData(*prevBuffer); for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { - unsigned* const currentRow = - buffer->bytes().data() + (y * m_size.width()); for (int x = prevRect.x(); x < prevRect.right(); ++x) - buffer->setRGBA(*(currentRow + x), 0, 0, 0, 0); + buffer->setRGBA(x, y, 0, 0, 0, 0); } if ((prevRect.width() > 0) && (prevRect.height() > 0)) buffer->setHasAlpha(true); @@ -334,57 +328,47 @@ void GIFImageDecoder::initFrameBuffer(unsigned frameIndex) // Reset the alpha pixel tracker for this frame. m_currentBufferSawAlpha = false; -} - -void GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const -{ - buffer->bytes().resize(size().width() * size().height()); - buffer->bytes().fill(0); - buffer->setHasAlpha(true); + return true; } void GIFImageDecoder::haveDecodedRow(unsigned frameIndex, - unsigned char* rowBuffer, // Pointer to single scanline temporary buffer + unsigned char* rowBuffer, unsigned char* rowEnd, - unsigned rowNumber, // The row index - unsigned repeatCount, // How many times to repeat the row + unsigned rowNumber, + unsigned repeatCount, bool writeTransparentPixels) { - // Initialize the frame if necessary. - RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; - if (buffer.status() == RGBA32Buffer::FrameEmpty) - initFrameBuffer(frameIndex); - - // Do nothing for bogus data. - if (rowBuffer == 0 || static_cast<int>(m_reader->frameYOffset() + rowNumber) >= size().height()) + // The pixel data and coordinates supplied to us are relative to the frame's + // origin within the entire image size, i.e. + // (m_reader->frameXOffset(), m_reader->frameYOffset()). + int x = m_reader->frameXOffset(); + const int y = m_reader->frameYOffset() + rowNumber; + + // Sanity-check the arguments. + if ((rowBuffer == 0) || (y >= size().height())) return; + // Get the colormap. unsigned colorMapSize; unsigned char* colorMap; m_reader->getColorMap(colorMap, colorMapSize); if (!colorMap) return; - // The buffers that we draw are the entire image's width and height, so a final output frame is - // width * height RGBA32 values in size. - // - // A single GIF frame, however, can be smaller than the entire image, i.e., it can represent some sub-rectangle - // within the overall image. The rows we are decoding are within this - // sub-rectangle. This means that if the GIF frame's sub-rectangle is (x,y,w,h) then row 0 is really row - // y, and each row goes from x to x+w. - unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * size().width() + m_reader->frameXOffset(); - unsigned* dst = buffer.bytes().data() + dstPos; - unsigned* dstEnd = dst + size().width() - m_reader->frameXOffset(); - unsigned* currDst = dst; - unsigned char* currentRowByte = rowBuffer; - - while (currentRowByte != rowEnd && currDst < dstEnd) { - if ((!m_reader->isTransparent() || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) { - unsigned colorIndex = *currentRowByte * 3; - unsigned red = colorMap[colorIndex]; - unsigned green = colorMap[colorIndex + 1]; - unsigned blue = colorMap[colorIndex + 2]; - RGBA32Buffer::setRGBA(*currDst, red, green, blue, 255); + // Initialize the frame if necessary. + RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; + if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex)) + return; + + // Write one row's worth of data into the frame. There is no guarantee that + // (rowEnd - rowBuffer) == (size().width() - m_reader->frameXOffset()), so + // we must ensure we don't run off the end of either the source data or the + // row's X-coordinates. + for (unsigned char* sourceAddr = rowBuffer; (sourceAddr != rowEnd) && (x < size().width()); ++sourceAddr, ++x) { + const unsigned char sourceValue = *sourceAddr; + if ((!m_reader->isTransparent() || (sourceValue != m_reader->transparentPixel())) && (sourceValue < colorMapSize)) { + const size_t colorIndex = static_cast<size_t>(sourceValue) * 3; + buffer.setRGBA(x, y, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255); } else { m_currentBufferSawAlpha = true; // We may or may not need to write transparent pixels to the buffer. @@ -395,30 +379,13 @@ void GIFImageDecoder::haveDecodedRow(unsigned frameIndex, // beyond the first, or the initial passes will "show through" the // later ones. if (writeTransparentPixels) - RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0); + buffer.setRGBA(x, y, 0, 0, 0, 0); } - currDst++; - currentRowByte++; } - if (repeatCount > 1) { - // Copy the row |repeatCount|-1 times. - unsigned num = currDst - dst; - unsigned data_size = num * sizeof(unsigned); - unsigned width = size().width(); - unsigned* end = buffer.bytes().data() + width * size().height(); - currDst = dst + width; - for (unsigned i = 1; i < repeatCount; i++) { - if (currDst + num > end) // Protect against a buffer overrun from a bogus repeatCount. - break; - memcpy(currDst, dst, data_size); - currDst += width; - } - } - - // Our partial height is rowNumber + 1, e.g., row 2 is the 3rd row, so that's a height of 3. - // Adding in repeatCount - 1 to rowNumber + 1 works out to just be rowNumber + repeatCount. - buffer.ensureHeight(rowNumber + repeatCount); + // Tell the frame to copy the row data if need be. + if (repeatCount > 1) + buffer.copyRowNTimes(m_reader->frameXOffset(), x, y, std::min(y + static_cast<int>(repeatCount), size().height())); } void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod) @@ -426,10 +393,9 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // Initialize the frame if necessary. Some GIFs insert do-nothing frames, // in which case we never reach haveDecodedRow() before getting here. RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; - if (buffer.status() == RGBA32Buffer::FrameEmpty) - initFrameBuffer(frameIndex); + if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex)) + return; - buffer.ensureHeight(m_size.height()); buffer.setStatus(RGBA32Buffer::FrameComplete); buffer.setDuration(frameDuration); buffer.setDisposalMethod(disposalMethod); @@ -437,7 +403,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, if (!m_currentBufferSawAlpha) { // The whole frame was non-transparent, so it's possible that the entire // resulting buffer was non-transparent, and we can setHasAlpha(false). - if (buffer.rect().contains(IntRect(IntPoint(0, 0), size()))) + if (buffer.rect().contains(IntRect(IntPoint(), size()))) buffer.setHasAlpha(false); else if (frameIndex > 0) { // Tricky case. This frame does not have alpha only if everywhere diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h index abb55a4..5227ea3 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h @@ -44,11 +44,11 @@ namespace WebCore { virtual void setData(SharedBuffer* data, bool allDataReceived); // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; + virtual bool isSizeAvailable(); // The total number of frames for the image. Will scan the image data for the answer // (without necessarily decoding all of the individual frames). - virtual int frameCount(); + virtual size_t frameCount(); // The number of repetitions to perform for an animation loop. virtual int repetitionCount() const; @@ -61,10 +61,10 @@ namespace WebCore { enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery }; - void decode(GIFQuery, unsigned haltAtFrame) const; + void decode(GIFQuery, unsigned haltAtFrame); // Callbacks from the GIF reader. - void sizeNowAvailable(unsigned width, unsigned height); + bool sizeNowAvailable(unsigned width, unsigned height); void decodingHalted(unsigned bytesLeft); void haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels); @@ -73,17 +73,14 @@ namespace WebCore { private: // Called to initialize the frame buffer with the given index, based on the - // previous frame's disposal method. - void initFrameBuffer(unsigned frameIndex); - - // A helper for initFrameBuffer(), this sets the size of the buffer, and - // fills it with transparent pixels. - void prepEmptyFrameBuffer(RGBA32Buffer*) const; + // previous frame's disposal method. Returns true on success. On failure, + // this will mark the image as failed. + bool initFrameBuffer(unsigned frameIndex); bool m_frameCountValid; bool m_currentBufferSawAlpha; mutable int m_repetitionCount; - mutable GIFImageDecoderPrivate* m_reader; + GIFImageDecoderPrivate* m_reader; }; } // namespace WebCore diff --git a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp index 95ab40d..002f67a 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp @@ -311,9 +311,13 @@ int GIFImageReader::do_lzw(const unsigned char *q) while (code >= clear_code) { - if (code == prefix[code]) + if (code >= MAX_BITS || code == prefix[code]) return -1; + // Even though suffix[] only holds characters through suffix[avail - 1], + // allowing code >= avail here lets us be more tolerant of malformed + // data. As long as code < MAX_BITS, the only risk is a garbled image, + // which is no worse than refusing to display it. *stackp++ = suffix[code]; code = prefix[code]; @@ -445,7 +449,10 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, { /* Initialize LZW parser/decoder */ int datasize = *q; - if (datasize > MAX_LZW_BITS) { + // Since we use a codesize of 1 more than the datasize, we need to ensure + // that our datasize is strictly less than the MAX_LZW_BITS value (12). + // This sets the largest possible codemask correctly at 4095. + if (datasize >= MAX_LZW_BITS) { state = gif_error; break; } @@ -468,6 +475,8 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, /* init the tables */ if (!frame_reader->suffix) frame_reader->suffix = new unsigned char[MAX_BITS]; + // Clearing the whole suffix table lets us be more tolerant of bad data. + memset(frame_reader->suffix, 0, MAX_BITS); for (int i = 0; i < frame_reader->clear_code; i++) frame_reader->suffix[i] = i; @@ -508,8 +517,8 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, screen_height = GETINT16(q + 2); // CALLBACK: Inform the decoderplugin of our size. - if (clientptr) - clientptr->sizeNowAvailable(screen_width, screen_height); + if (clientptr && !clientptr->sizeNowAvailable(screen_width, screen_height)) + return false; screen_bgcolor = q[5]; global_colormap_size = 2<<(q[4]&0x07); @@ -734,8 +743,8 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, y_offset = 0; // CALLBACK: Inform the decoderplugin of our size. - if (clientptr) - clientptr->sizeNowAvailable(screen_width, screen_height); + if (clientptr && !clientptr->sizeNowAvailable(screen_width, screen_height)) + return false; } /* Work around more broken GIF files that have zero image diff --git a/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp b/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp new file mode 100644 index 0000000..3542cc2 --- /dev/null +++ b/WebCore/platform/image-decoders/haiku/ImageDecoderHaiku.cpp @@ -0,0 +1,131 @@ +/* + * 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 <Bitmap.h> + + +namespace WebCore { + +RGBA32Buffer::RGBA32Buffer() + : m_hasAlpha(false) + , m_status(FrameEmpty) + , m_duration(0) + , m_disposalMethod(DisposeNotSpecified) +{ +} + +void RGBA32Buffer::clear() +{ + m_bytes.clear(); + 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_bytes.fill(0); + m_hasAlpha = true; +} + +void RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other) +{ + if (this == &other) + return; + + m_bytes = other.m_bytes; + setHasAlpha(other.m_hasAlpha); +} + +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_bytes.resize(newWidth * newHeight); + m_size = IntSize(newWidth, newHeight); + + // Zero the image. + zeroFill(); + + return true; +} + +NativeImagePtr RGBA32Buffer::asNewNativeImage() const +{ + const void* bytes = m_bytes.data(); + + BBitmap* bmp = new BBitmap(BRect(0, 0, width(), height()), B_RGB32); + bmp->SetBits(bytes, m_size.width() * m_size.height(), 0, B_RGB32); + + return bmp; +} + +bool RGBA32Buffer::hasAlpha() const +{ + return m_hasAlpha; +} + +void RGBA32Buffer::setHasAlpha(bool alpha) +{ + m_hasAlpha = alpha; +} + +void RGBA32Buffer::setStatus(FrameStatus status) +{ + m_status = status; +} + +RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) +{ + if (this == &other) + return *this; + + m_bytes = other.m_bytes; + m_size = other.m_size; + setHasAlpha(other.hasAlpha()); + setRect(other.rect()); + setStatus(other.status()); + setDuration(other.duration()); + setDisposalMethod(other.disposalMethod()); + return *this; +} + +int RGBA32Buffer::width() const +{ + return m_size.width(); +} + +int RGBA32Buffer::height() const +{ + return m_size.height(); +} + +} // namespace WebCore + diff --git a/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp b/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp index 5b1a88f..e9296ad 100644 --- a/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp +++ b/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp @@ -1,42 +1,343 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * + * 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: - * 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 + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #include "config.h" #include "ICOImageDecoder.h" -namespace WebCore +#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() + : ImageDecoder() + , m_allDataReceived(false) + , m_decodedOffset(0) { +} -bool ICOImageDecoder::isSizeAvailable() const +ICOImageDecoder::~ICOImageDecoder() { - return false; + deleteAllValues(m_bmpReaders); + deleteAllValues(m_pngDecoders); } - + +void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) +{ + if (failed()) + return; + + ImageDecoder::setData(data, allDataReceived); + m_allDataReceived = 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()) + decodeWithCheckForDataEnded(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) +{ + if (m_frameSize.isEmpty()) + return ImageDecoder::setSize(width, height); + + // The size calculated inside the BMPImageReader had better match the one in + // the icon directory. + if (IntSize(width, height) != m_frameSize) + setFailed(); + return !failed(); +} + +size_t ICOImageDecoder::frameCount() +{ + decodeWithCheckForDataEnded(0, true); + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.resize(m_dirEntries.size()); + // 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) { - return 0; + // Ensure |index| is valid. + if (index >= frameCount()) + return 0; + + // Determine the image type, and if this is a BMP, decode. + decodeWithCheckForDataEnded(index, false); + + // PNGs decode into their own framebuffers, so only use our internal cache + // for non-PNGs (BMP or unknown). + if (!m_pngDecoders[index]) + return &m_frameBufferCache[index]; + + // Fail if the size the PNGImageDecoder calculated does not match the size + // in the directory. + if (m_pngDecoders[index]->isSizeAvailable()) { + const IntSize pngSize(m_pngDecoders[index]->size()); + const IconDirectoryEntry& dirEntry = m_dirEntries[index]; + if (pngSize != dirEntry.m_size) { + setFailed(); + m_pngDecoders[index]->setFailed(); + } + } + + return m_pngDecoders[index]->frameBufferAtIndex(0); +} + +// static +bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a, + const IconDirectoryEntry& b) +{ + // Larger 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(); + if (aEntryArea != bEntryArea) + return (aEntryArea > bEntryArea); + + // Higher bit-depth icons are better. + return (a.m_bitCount > b.m_bitCount); +} + +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(), m_allDataReceived); +} + +void ICOImageDecoder::decodeWithCheckForDataEnded(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))) + && m_allDataReceived) + setFailed(); } -} // namespace WebCore +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]; + if (!m_bmpReaders[index] && !m_pngDecoders[index]) { + // Image type unknown. + const ImageType imageType = imageTypeAtIndex(index); + if (imageType == BMP) { + // 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] = + new BMPImageReader(this, dirEntry.m_imageOffset, 0, true); + m_bmpReaders[index]->setData(m_data.get()); + m_bmpReaders[index]->setBuffer(&m_frameBufferCache[index]); + } else if (imageType == PNG) { + m_pngDecoders[index] = new PNGImageDecoder(); + setDataForPNGDecoderAtIndex(index); + } else { + // Not enough data to determine image type yet. + return false; + } + } + + if (m_bmpReaders[index]) { + m_frameSize = dirEntry.m_size; + bool result = m_bmpReaders[index]->decodeBMP(false); + m_frameSize = IntSize(); + return result; + } + + // For PNGs, we're now done; further decoding will happen when calling + // frameBufferAtIndex() on the PNG decoder. + return true; +} + +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 == 0)) { + setFailed(); + return false; + } + + // Enlarge member vectors to hold all the entries. We must initialize the + // BMP and PNG decoding vectors to 0 so that all entries can be safely + // deleted in our destructor. If we don't do this, they'll contain garbage + // values, and deleting those will corrupt memory. + m_dirEntries.resize(idCount); + m_bmpReaders.fill(0, idCount); + m_pngDecoders.fill(0, 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) { + setFailed(); + return false; + } + } + + // 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(); + setSize(dirEntry.m_size.width(), dirEntry.m_size.height()); + return true; +} + +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 == 0) + width = 256; + int height = static_cast<uint8_t>(m_data->data()[m_decodedOffset + 1]); + if (height == 0) + 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) { + uint8_t colorCount = m_data->data()[m_decodedOffset + 2]; + if (colorCount) { + 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/WebCore/platform/image-decoders/ico/ICOImageDecoder.h b/WebCore/platform/image-decoders/ico/ICOImageDecoder.h index 56a74c3..f8bddf9 100644 --- a/WebCore/platform/image-decoders/ico/ICOImageDecoder.h +++ b/WebCore/platform/image-decoders/ico/ICOImageDecoder.h @@ -1,46 +1,147 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * + * 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: - * 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 + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ICOImageDecoder_h #define ICOImageDecoder_h -#include "ImageDecoder.h" +#include "BMPImageReader.h" namespace WebCore { - class ICOImageReader; + class PNGImageDecoder; // This class decodes the ICO and CUR image formats. class ICOImageDecoder : public ImageDecoder { public: + ICOImageDecoder(); + 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); + + 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. + // + // NOTE: If the desired entry is a PNG, this doesn't actually trigger + // decoding, it merely ensures the decoder is created and ready to + // decode. The caller will then call a function on the PNGImageDecoder + // that actually triggers decoding. + void decodeWithCheckForDataEnded(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); + + // True if we've seen all the data. + bool m_allDataReceived; + + // 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; - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; + // The image decoders for the various frames. + typedef Vector<BMPImageReader*> BMPReaders; + BMPReaders m_bmpReaders; + typedef Vector<PNGImageDecoder*> PNGDecoders; + PNGDecoders m_pngDecoders; - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); + // Valid only while a BMPImageReader is decoding, this holds the size + // for the particular entry being decoded. + IntSize m_frameSize; }; } // namespace WebCore diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp index 38d90bd..ae09586 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp @@ -6,6 +6,8 @@ * 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 @@ -38,17 +40,21 @@ #include "config.h" #include "JPEGImageDecoder.h" #include <assert.h> -#include <stdio.h> +#include <stdio.h> // Needed by jpeglib.h for FILE. + +#if PLATFORM(WINCE) +// 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 COMPILER(MSVC) -// Remove warnings from warning level 4. -#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable -#endif // COMPILER(MSVC) - #include <setjmp.h> namespace WebCore { @@ -188,6 +194,10 @@ public: 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: m_state = JPEG_ERROR; return false; @@ -219,7 +229,10 @@ public: m_state = JPEG_START_DECOMPRESS; // We can fill in the size now that the header is available. - m_decoder->setSize(m_info.image_width, m_info.image_height); + if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) { + m_state = JPEG_ERROR; + return false; + } if (m_decodingSizeOnly) { // We can stop here. @@ -411,19 +424,12 @@ void JPEGImageDecoder::setData(SharedBuffer* data, bool allDataReceived) } // Whether or not the size information has been decoded yet. -bool JPEGImageDecoder::isSizeAvailable() const +bool JPEGImageDecoder::isSizeAvailable() { - // If we have pending data to decode, send it to the JPEG reader now. - if (!m_sizeAvailable && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the - // size is encountered. - decode(true); - } + if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) + decode(true); - return m_sizeAvailable; + return ImageDecoder::isSizeAvailable(); } RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index) @@ -442,7 +448,7 @@ RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index) } // Feed data to the JPEG reader. -void JPEGImageDecoder::decode(bool sizeOnly) const +void JPEGImageDecoder::decode(bool sizeOnly) { if (m_failed) return; @@ -455,45 +461,87 @@ void JPEGImageDecoder::decode(bool sizeOnly) const } } +static void convertCMYKToRGBA(RGBA32Buffer& dest, JSAMPROW src, jpeg_decompress_struct* info) +{ + ASSERT(info->out_color_space == JCS_CMYK); + + for (unsigned x = 0; x < info->output_width; ++x) { + unsigned c = *src++; + unsigned m = *src++; + unsigned y = *src++; + unsigned k = *src++; + + // 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 + // C = C * ( 1 - K ) + K + // M = M * ( 1 - K ) + K + // Y = Y * ( 1 - K ) + K + + // From Inverted CMYK to CMY is thus: + // C = (1-iC) * (1 - (1-iK)) + (1-iK) => 1 - iC*iK + // Same for M and Y + + // Convert from CMY (0..1) to RGB (0..1) + // R = 1 - C => 1 - (1 - iC*iK) => iC*iK + // G = 1 - M => 1 - (1 - iM*iK) => iM*iK + // B = 1 - Y => 1 - (1 - iY*iK) => iY*iK + + // read_scanlines has increased the scanline counter, so we + // actually mean the previous one. + dest.setRGBA(x, info->output_scanline - 1, c * k / 255, m * k / 255, y * k / 255, 0xFF); + } +} + +static void convertRGBToRGBA(RGBA32Buffer& dest, JSAMPROW src, jpeg_decompress_struct* info) +{ + ASSERT(info->out_color_space == JCS_RGB); + + for (unsigned x = 0; x < info->output_width; ++x) { + unsigned r = *src++; + unsigned g = *src++; + unsigned b = *src++; + // read_scanlines has increased the scanline counter, so we + // actually mean the previous one. + dest.setRGBA(x, info->output_scanline - 1, r, g, b, 0xFF); + } +} + bool JPEGImageDecoder::outputScanlines() { if (m_frameBufferCache.isEmpty()) return false; - // Resize to the width and height of the image. + // Initialize the framebuffer if needed. RGBA32Buffer& buffer = m_frameBufferCache[0]; if (buffer.status() == RGBA32Buffer::FrameEmpty) { - // Let's resize our buffer now to the correct width/height. - RGBA32Array& bytes = buffer.bytes(); - bytes.resize(size().width() * size().height()); - - // Update our status to be partially complete. + if (!buffer.setSize(size().width(), size().height())) { + m_failed = true; + return false; + } buffer.setStatus(RGBA32Buffer::FramePartial); + buffer.setHasAlpha(false); // For JPEGs, the frame always fills the entire image. - buffer.setRect(IntRect(0, 0, m_size.width(), m_size.height())); - - // We don't have alpha (this is the default when the buffer is constructed). + buffer.setRect(IntRect(IntPoint(), size())); } jpeg_decompress_struct* info = m_reader->info(); JSAMPARRAY samples = m_reader->samples(); - unsigned* dst = buffer.bytes().data() + info->output_scanline * m_size.width(); - while (info->output_scanline < info->output_height) { /* Request one scanline. Returns 0 or 1 scanlines. */ if (jpeg_read_scanlines(info, samples, 1) != 1) return false; - JSAMPLE *j1 = samples[0]; - for (unsigned x = 0; x < info->output_width; ++x) { - unsigned r = *j1++; - unsigned g = *j1++; - unsigned b = *j1++; - RGBA32Buffer::setRGBA(*dst++, r, g, b, 0xFF); - } - buffer.ensureHeight(info->output_scanline); + if (info->out_color_space == JCS_RGB) + convertRGBToRGBA(buffer, *samples, info); + else if (info->out_color_space == JCS_CMYK) + convertCMYKToRGBA(buffer, *samples, info); + else + return false; } return true; diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h index c01bb5e..56e007d 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h @@ -44,28 +44,21 @@ namespace WebCore { virtual void setData(SharedBuffer* data, bool allDataReceived); // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; + virtual bool isSizeAvailable(); virtual RGBA32Buffer* frameBufferAtIndex(size_t index); virtual bool supportsAlpha() const { return false; } - void decode(bool sizeOnly = false) const; + void decode(bool sizeOnly = false); JPEGImageReader* reader() { return m_reader; } - void setSize(int width, int height) { - if (!m_sizeAvailable) { - m_sizeAvailable = true; - m_size = IntSize(width, height); - } - } - bool outputScanlines(); void jpegComplete(); private: - mutable JPEGImageReader* m_reader; + JPEGImageReader* m_reader; }; } // namespace WebCore diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index dead8ca..d14333f 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -40,11 +40,6 @@ #include "png.h" #include "assert.h" -#if COMPILER(MSVC) -// Remove warnings from warning level 4. -#pragma warning(disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable -#endif // COMPILER(MSVC) - namespace WebCore { // Gamma constants. @@ -143,7 +138,6 @@ private: PNGImageDecoder::PNGImageDecoder() : m_reader(0) { - m_frameBufferCache.resize(1); } PNGImageDecoder::~PNGImageDecoder() @@ -166,19 +160,12 @@ void PNGImageDecoder::setData(SharedBuffer* data, bool allDataReceived) } // Whether or not the size information has been decoded yet. -bool PNGImageDecoder::isSizeAvailable() const +bool PNGImageDecoder::isSizeAvailable() { - // If we have pending data to decode, send it to the PNG reader now. - if (!m_sizeAvailable && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the - // size is encountered. - decode(true); - } + if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) + decode(true); - return m_sizeAvailable; + return ImageDecoder::isSizeAvailable(); } RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index) @@ -186,6 +173,9 @@ RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index) if (index) return 0; + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.resize(1); + RGBA32Buffer& frame = m_frameBufferCache[0]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) // Decode this frame. @@ -194,14 +184,14 @@ RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index) } // Feed data to the PNG reader. -void PNGImageDecoder::decode(bool sizeOnly) const +void PNGImageDecoder::decode(bool sizeOnly) { if (m_failed) return; m_reader->decode(m_data->buffer(), sizeOnly); - if (m_failed || (m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) { + if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) { delete m_reader; m_reader = 0; } @@ -226,7 +216,8 @@ void headerAvailable(png_structp png, png_infop info) static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); } -void PNGImageDecoder::decodingFailed() { +void PNGImageDecoder::decodingFailed() +{ m_failed = true; } @@ -245,9 +236,12 @@ void PNGImageDecoder::headerAvailable() } // We can fill in the size now that the header is available. - if (!m_sizeAvailable) { - m_sizeAvailable = true; - m_size = IntSize(width, height); + if (!ImageDecoder::isSizeAvailable()) { + if (!setSize(width, height)) { + // Size unreasonable, bail out. + longjmp(png->jmpbuf, 1); + return; + } } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; @@ -313,18 +307,22 @@ void rowAvailable(png_structp png, png_bytep rowBuffer, void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) { - // Resize to the width and height of the image. + if (m_frameBufferCache.isEmpty()) + return; + + // Initialize the framebuffer if needed. RGBA32Buffer& buffer = m_frameBufferCache[0]; if (buffer.status() == RGBA32Buffer::FrameEmpty) { - // Let's resize our buffer now to the correct width/height. - RGBA32Array& bytes = buffer.bytes(); - bytes.resize(size().width() * size().height()); - - // Update our status to be partially complete. + if (!buffer.setSize(size().width(), size().height())) { + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(reader()->pngPtr()))->decodingFailed(); + longjmp(reader()->pngPtr()->jmpbuf, 1); + return; + } buffer.setStatus(RGBA32Buffer::FramePartial); + buffer.setHasAlpha(false); // For PNGs, the frame always fills the entire image. - buffer.setRect(IntRect(0, 0, size().width(), size().height())); + buffer.setRect(IntRect(IntPoint(), size())); if (reader()->pngPtr()->interlaced) reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * size().width() * size().height()); @@ -375,21 +373,18 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, // Copy the data into our buffer. int width = size().width(); - unsigned* dst = buffer.bytes().data() + rowIndex * width; bool sawAlpha = false; for (int x = 0; x < width; x++) { unsigned red = *row++; unsigned green = *row++; unsigned blue = *row++; unsigned alpha = (hasAlpha ? *row++ : 255); - RGBA32Buffer::setRGBA(*dst++, red, green, blue, alpha); + buffer.setRGBA(x, rowIndex, red, green, blue, alpha); if (!sawAlpha && alpha < 255) { sawAlpha = true; buffer.setHasAlpha(true); } } - - buffer.ensureHeight(rowIndex + 1); } void pngComplete(png_structp png, png_infop info) @@ -399,6 +394,9 @@ void pngComplete(png_structp png, png_infop info) void PNGImageDecoder::pngComplete() { + if (m_frameBufferCache.isEmpty()) + return; + // Hand back an appropriately sized buffer, even if the image ended up being empty. RGBA32Buffer& buffer = m_frameBufferCache[0]; buffer.setStatus(RGBA32Buffer::FrameComplete); diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.h b/WebCore/platform/image-decoders/png/PNGImageDecoder.h index c5264fd..3c0535b 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.h +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.h @@ -44,11 +44,11 @@ namespace WebCore { virtual void setData(SharedBuffer* data, bool allDataReceived); // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; + virtual bool isSizeAvailable(); virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - void decode(bool sizeOnly = false) const; + void decode(bool sizeOnly = false); PNGImageReader* reader() { return m_reader; } @@ -59,7 +59,7 @@ namespace WebCore { void pngComplete(); private: - mutable PNGImageReader* m_reader; + PNGImageReader* m_reader; }; } // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/BMPImageDecoder.cpp b/WebCore/platform/image-decoders/skia/BMPImageDecoder.cpp deleted file mode 100644 index 08d5df5..0000000 --- a/WebCore/platform/image-decoders/skia/BMPImageDecoder.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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" - -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; - -void BMPImageDecoder::decodeImage(SharedBuffer* data) -{ - // Read and process file header. - if ((m_decodedOffset < sizeOfFileHeader) && !processFileHeader(data)) - return; - - // Decode BMP. - decodeBMP(data); -} - -bool BMPImageDecoder::processFileHeader(SharedBuffer* data) -{ - // Read file header. - ASSERT(!m_decodedOffset); - if (data->size() < sizeOfFileHeader) - return false; - const uint16_t fileType = - (data->data()[0] << 8) | static_cast<uint8_t>(data->data()[1]); - m_imgDataOffset = readUint32(data, 10); - m_decodedOffset = m_headerOffset = sizeOfFileHeader; - - // See if this is a bitmap filetype we understand. - enum { - BMAP = '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 = 'IC', - POINTER = 'PT', - COLORICON = 'CI', - COLORPOINTER = 'CP', - BITMAPARRAY = 'BA', - */ - }; - if (fileType != BMAP) - m_failed = true; - - return !m_failed; -} - -} // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/BMPImageDecoder.h b/WebCore/platform/image-decoders/skia/BMPImageDecoder.h deleted file mode 100644 index 065981f..0000000 --- a/WebCore/platform/image-decoders/skia/BMPImageDecoder.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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" - -namespace WebCore { - - // This class decodes the BMP image format. - class BMPImageDecoder : public BMPImageReader { - public: - virtual String filenameExtension() const { return "bmp"; } - - // BMPImageReader - virtual void decodeImage(SharedBuffer*); - - private: - // Processes the file header at the beginning of the data. Returns true if - // the file header could be decoded. - bool processFileHeader(SharedBuffer*); - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp deleted file mode 100644 index c03452a..0000000 --- a/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/* - * 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 "GIFImageDecoder.h" -#include "GIFImageReader.h" - -namespace WebCore { - -class GIFImageDecoderPrivate { -public: - GIFImageDecoderPrivate(GIFImageDecoder* decoder = 0) - : m_reader(decoder) - , m_readOffset(0) - { - } - - ~GIFImageDecoderPrivate() - { - m_reader.close(); - } - - bool decode(SharedBuffer* data, - GIFImageDecoder::GIFQuery query = GIFImageDecoder::GIFFullQuery, - unsigned int haltFrame = -1) - { - return m_reader.read((const unsigned char*)data->data() + m_readOffset, data->size() - m_readOffset, - query, - haltFrame); - } - - unsigned frameCount() const { return m_reader.images_count; } - int repetitionCount() const { return m_reader.loop_count; } - - void setReadOffset(unsigned o) { m_readOffset = o; } - - bool isTransparent() const { return m_reader.frame_reader->is_transparent; } - - void getColorMap(unsigned char*& map, unsigned& size) const - { - if (m_reader.frame_reader->is_local_colormap_defined) { - map = m_reader.frame_reader->local_colormap; - size = (unsigned)m_reader.frame_reader->local_colormap_size; - } else { - map = m_reader.global_colormap; - size = m_reader.global_colormap_size; - } - } - - unsigned frameXOffset() const { return m_reader.frame_reader->x_offset; } - unsigned frameYOffset() const { return m_reader.frame_reader->y_offset; } - unsigned frameWidth() const { return m_reader.frame_reader->width; } - unsigned frameHeight() const { return m_reader.frame_reader->height; } - - int transparentPixel() const { return m_reader.frame_reader->tpixel; } - - unsigned duration() const { return m_reader.frame_reader->delay_time; } - -private: - GIFImageReader m_reader; - unsigned m_readOffset; -}; - -GIFImageDecoder::GIFImageDecoder() - : m_frameCountValid(true) - , m_repetitionCount(cAnimationLoopOnce) - , m_reader(0) -{ -} - -GIFImageDecoder::~GIFImageDecoder() -{ - delete m_reader; -} - -// Take the data and store it. -void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) -{ - if (m_failed) - return; - - // Cache our new data. - ImageDecoder::setData(data, allDataReceived); - - // Our frame count is now unknown. - m_frameCountValid = false; - - // Create the GIF reader. - if (!m_reader && !m_failed) - m_reader = new GIFImageDecoderPrivate(this); -} - -// Whether or not the size information has been decoded yet. -bool GIFImageDecoder::isSizeAvailable() const -{ - // If we have pending data to decode, send it to the GIF reader now. - if (!ImageDecoder::isSizeAvailable() && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the first - // size is encountered. - decode(GIFSizeQuery, 0); - } - - return ImageDecoder::isSizeAvailable(); -} - -// The total number of frames for the image. Will scan the image data for the answer -// (without necessarily decoding all of the individual frames). -int GIFImageDecoder::frameCount() -{ - // If the decoder had an earlier error, we will just return what we had decoded - // so far. - if (!m_frameCountValid) { - // FIXME: Scanning all the data has O(n^2) behavior if the data were to come in really - // slowly. Might be interesting to try to clone our existing read session to preserve - // state, but for now we just crawl all the data. Note that this is no worse than what - // ImageIO does on Mac right now (it also crawls all the data again). - GIFImageDecoderPrivate reader; - // This function may fail, but we want to keep any partial data it may - // have decoded, so don't mark it is invalid. If there is an overflow - // or some serious error, m_failed will have gotten set for us. - reader.decode(m_data.get(), GIFFrameCountQuery); - m_frameCountValid = true; - m_frameBufferCache.resize(reader.frameCount()); - } - - return m_frameBufferCache.size(); -} - -// The number of repetitions to perform for an animation loop. -int GIFImageDecoder::repetitionCount() const -{ - // This value can arrive at any point in the image data stream. Most GIFs - // in the wild declare it near the beginning of the file, so it usually is - // set by the time we've decoded the size, but (depending on the GIF and the - // packets sent back by the webserver) not always. Our caller is - // responsible for waiting until image decoding has finished to ask this if - // it needs an authoritative answer. In the meantime, we should default to - // "loop once". - if (m_reader) { - // Added wrinkle: ImageSource::clear() may destroy the reader, making - // the result from the reader _less_ authoritative on future calls. To - // detect this, the reader returns cLoopCountNotSeen (-2) instead of - // cAnimationLoopOnce (-1) when its current incarnation hasn't actually - // seen a loop count yet; in this case we return our previously-cached - // value. - const int repetitionCount = m_reader->repetitionCount(); - if (repetitionCount != cLoopCountNotSeen) - m_repetitionCount = repetitionCount; - } - return m_repetitionCount; -} - -RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index) -{ - if (index >= static_cast<size_t>(frameCount())) - return 0; - - RGBA32Buffer& frame = m_frameBufferCache[index]; - if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - decode(GIFFullQuery, index + 1); // Decode this frame. - return &frame; -} - -void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) -{ - // In some cases, like if the decoder was destroyed while animating, we - // can be asked to clear more frames than we currently have. - if (m_frameBufferCache.isEmpty()) - return; // Nothing to do. - - // The "-1" here is tricky. It does not mean that |clearBeforeFrame| is the - // last frame we wish to preserve, but rather that we never want to clear - // the very last frame in the cache: it's empty (so clearing it is - // pointless), it's partial (so we don't want to clear it anyway), or the - // cache could be enlarged with a future setData() call and it could be - // needed to construct the next frame (see comments below). Callers can - // always use ImageSource::clear(true, ...) to completely free the memory in - // this case. - clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1); - const Vector<RGBA32Buffer>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame); - - // 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(); - } -} - -// Feed data to the GIF reader. -void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const -{ - if (m_failed) - return; - - m_failed = !m_reader->decode(m_data.get(), query, haltAtFrame); - - if (m_failed) { - delete m_reader; - m_reader = 0; - } -} - -// Callbacks from the GIF reader. -bool GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height) -{ - return setSize(width, height); -} - -void GIFImageDecoder::decodingHalted(unsigned bytesLeft) -{ - m_reader->setReadOffset(m_data->size() - bytesLeft); -} - -bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) -{ - // Initialize the frame rect in our buffer. - IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(), - m_reader->frameWidth(), m_reader->frameHeight()); - - // Make sure the frameRect doesn't extend past the bottom-right of the buffer. - if (frameRect.right() > size().width()) - frameRect.setWidth(size().width() - m_reader->frameXOffset()); - if (frameRect.bottom() > size().height()) - frameRect.setHeight(size().height() - m_reader->frameYOffset()); - - RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; - buffer->setRect(frameRect); - - if (frameIndex == 0) { - // This is the first frame, so we're not relying on any previous data. - if (!prepEmptyFrameBuffer(buffer)) { - buffer->setStatus(RGBA32Buffer::FrameComplete); - m_failed = true; - return false; - } - } else { - // The starting state for this frame depends on the previous frame's - // disposal method. - // - // Frames that use the DisposeOverwritePrevious method are effectively - // no-ops in terms of changing the starting state of a frame compared to - // the starting state of the previous frame, so skip over them. (If the - // first frame specifies this method, it will get treated like - // DisposeOverwriteBgcolor below and reset to a completely empty image.) - const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - RGBA32Buffer::FrameDisposalMethod prevMethod = - prevBuffer->disposalMethod(); - while ((frameIndex > 0) - && (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) { - prevBuffer = &m_frameBufferCache[--frameIndex]; - prevMethod = prevBuffer->disposalMethod(); - } - ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete); - - if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || - (prevMethod == RGBA32Buffer::DisposeKeep)) { - // Preserve the last frame as the starting state for this frame. - buffer->copyBitmapData(*prevBuffer); - // This next line isn't currently necessary since the alpha state is - // currently carried along in the Skia bitmap data, but it's safe, - // future-proof, and parallel to the Cairo code. - buffer->setHasAlpha(prevBuffer->hasAlpha()); - } else { - // We want to clear the previous frame to transparent, without - // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->rect(); - if ((frameIndex == 0) - || prevRect.contains(IntRect(IntPoint(0, 0), size()))) { - // Clearing the first frame, or a frame the size of the whole - // image, results in a completely empty image. - prepEmptyFrameBuffer(buffer); - } else { - // Copy the whole previous buffer, then clear just its frame. - buffer->copyBitmapData(*prevBuffer); - // Unnecessary (but safe); see comments on the similar call above. - buffer->setHasAlpha(prevBuffer->hasAlpha()); - SkBitmap& bitmap = buffer->bitmap(); -#ifdef ANDROID_ANIMATED_GIF - SkAutoLockPixels alp(bitmap); -#endif - for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { - for (int x = prevRect.x(); x < prevRect.right(); ++x) - buffer->setRGBA(bitmap.getAddr32(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; -} - -bool GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const -{ - if (!buffer->setSize(size().width(), size().height())) - return false; - // This next line isn't currently necessary since Skia's eraseARGB() sets - // this for us, but we do it for similar reasons to the setHasAlpha() calls - // in initFrameBuffer() above. - buffer->setHasAlpha(true); - return true; -} - -void GIFImageDecoder::haveDecodedRow(unsigned frameIndex, - unsigned char* rowBuffer, // Pointer to single scanline temporary buffer - unsigned char* rowEnd, - unsigned rowNumber, // The row index - unsigned repeatCount, // How many times to repeat the row - bool writeTransparentPixels) -{ - // Initialize the frame if necessary. - RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; - if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex)) - return; - - // Do nothing for bogus data. - if (rowBuffer == 0 || static_cast<int>(m_reader->frameYOffset() + rowNumber) >= size().height()) - return; - - unsigned colorMapSize; - unsigned char* colorMap; - m_reader->getColorMap(colorMap, colorMapSize); - if (!colorMap) - return; - -#ifdef ANDROID_ANIMATED_GIF - // Lock the pixels properly. Should be submitted back to webkit. - SkAutoLockPixels alp(buffer.bitmap()); -#endif - // The buffers that we draw are the entire image's width and height, so a final output frame is - // width * height RGBA32 values in size. - // - // A single GIF frame, however, can be smaller than the entire image, i.e., it can represent some sub-rectangle - // within the overall image. The rows we are decoding are within this - // sub-rectangle. This means that if the GIF frame's sub-rectangle is (x,y,w,h) then row 0 is really row - // y, and each row goes from x to x+w. - unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * size().width() + m_reader->frameXOffset(); - unsigned* dst = buffer.bitmap().getAddr32(0, 0) + dstPos; - unsigned* dstEnd = dst + size().width() - m_reader->frameXOffset(); - unsigned* currDst = dst; - unsigned char* currentRowByte = rowBuffer; - - while (currentRowByte != rowEnd && currDst < dstEnd) { - if ((!m_reader->isTransparent() || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) { - unsigned colorIndex = *currentRowByte * 3; - unsigned red = colorMap[colorIndex]; - unsigned green = colorMap[colorIndex + 1]; - unsigned blue = colorMap[colorIndex + 2]; - RGBA32Buffer::setRGBA(currDst, red, green, blue, 255); - } else { - m_currentBufferSawAlpha = true; - // We may or may not need to write transparent pixels to the buffer. - // If we're compositing against a previous image, it's wrong, and if - // we're writing atop a cleared, fully transparent buffer, it's - // unnecessary; but if we're decoding an interlaced gif and - // displaying it "Haeberli"-style, we must write these for passes - // beyond the first, or the initial passes will "show through" the - // later ones. - if (writeTransparentPixels) - RGBA32Buffer::setRGBA(currDst, 0, 0, 0, 0); - } - currDst++; - currentRowByte++; - } - - if (repeatCount > 1) { - // Copy the row |repeatCount|-1 times. - unsigned num = currDst - dst; - unsigned data_size = num * sizeof(unsigned); - unsigned width = size().width(); - unsigned* end = buffer.bitmap().getAddr32(0, 0) + width * size().height(); - currDst = dst + width; - for (unsigned i = 1; i < repeatCount; i++) { - if (currDst + num > end) // Protect against a buffer overrun from a bogus repeatCount. - break; - memcpy(currDst, dst, data_size); - currDst += width; - } - } -} - -void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod) -{ - // Initialize the frame if necessary. Some GIFs insert do-nothing frames, - // in which case we never reach haveDecodedRow() before getting here. - RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; - if ((buffer.status() == RGBA32Buffer::FrameEmpty) && !initFrameBuffer(frameIndex)) - return; - - buffer.setStatus(RGBA32Buffer::FrameComplete); - buffer.setDuration(frameDuration); - buffer.setDisposalMethod(disposalMethod); - - if (!m_currentBufferSawAlpha) { - // The whole frame was non-transparent, so it's possible that the entire - // resulting buffer was non-transparent, and we can setHasAlpha(false). - if (buffer.rect().contains(IntRect(IntPoint(0, 0), size()))) - buffer.setHasAlpha(false); - else if (frameIndex > 0) { - // Tricky case. This frame does not have alpha only if everywhere - // outside its rect doesn't have alpha. To know whether this is - // true, we check the start state of the frame -- if it doesn't have - // alpha, we're safe. - // - // First skip over prior DisposeOverwritePrevious frames (since they - // don't affect the start state of this frame) the same way we do in - // initFrameBuffer(). - const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - while ((frameIndex > 0) - && (prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)) - prevBuffer = &m_frameBufferCache[--frameIndex]; - - // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then - // we can say we have no alpha if that frame had no alpha. But - // since in initFrameBuffer() we already copied that frame's alpha - // state into the current frame's, we need do nothing at all here. - // - // The only remaining case is a DisposeOverwriteBgcolor frame. If - // it had no alpha, and its rect is contained in the current frame's - // rect, we know the current frame has no alpha. - if ((prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwriteBgcolor) - && !prevBuffer->hasAlpha() && buffer.rect().contains(prevBuffer->rect())) - buffer.setHasAlpha(false); - } - } -} - -void GIFImageDecoder::gifComplete() -{ - if (m_reader) - m_repetitionCount = m_reader->repetitionCount(); - delete m_reader; - m_reader = 0; -} - -} // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/GIFImageDecoder.h b/WebCore/platform/image-decoders/skia/GIFImageDecoder.h deleted file mode 100644 index d234a76..0000000 --- a/WebCore/platform/image-decoders/skia/GIFImageDecoder.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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. - */ - -#ifndef GIFImageDecoder_h -#define GIFImageDecoder_h - -#include "ImageDecoder.h" - -namespace WebCore { - - class GIFImageDecoderPrivate; - - // This class decodes the GIF image format. - class GIFImageDecoder : public ImageDecoder { - public: - GIFImageDecoder(); - ~GIFImageDecoder(); - - virtual String filenameExtension() const { return "gif"; } - - // Take the data and store it. - virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; - - // The total number of frames for the image. Will scan the image data for the answer - // (without necessarily decoding all of the individual frames). - virtual int frameCount(); - - // The number of repetitions to perform for an animation loop. - virtual int repetitionCount() const; - - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - - virtual void clearFrameBufferCache(size_t clearBeforeFrame); - - virtual unsigned frameDurationAtIndex(size_t index) { return 0; } - - enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery }; - - void decode(GIFQuery, unsigned haltAtFrame) const; - - // Callbacks from the GIF reader. - bool sizeNowAvailable(unsigned width, unsigned height); - void decodingHalted(unsigned bytesLeft); - void haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, - unsigned repeatCount, bool writeTransparentPixels); - void frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod); - void gifComplete(); - - private: - // Called to initialize the frame buffer with the given index, based on the - // previous frame's disposal method. Returns true on success. On failure, - // this will mark the image as failed. - bool initFrameBuffer(unsigned frameIndex); - - // A helper for initFrameBuffer(), this sets the size of the buffer, and - // fills it with transparent pixels. - bool prepEmptyFrameBuffer(RGBA32Buffer*) const; - - bool m_frameCountValid; - bool m_currentBufferSawAlpha; - mutable int m_repetitionCount; - mutable GIFImageDecoderPrivate* m_reader; - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/skia/GIFImageReader.cpp b/WebCore/platform/image-decoders/skia/GIFImageReader.cpp deleted file mode 100644 index 48df514..0000000 --- a/WebCore/platform/image-decoders/skia/GIFImageReader.cpp +++ /dev/null @@ -1,951 +0,0 @@ -/* -*- 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 - * Google, Inc. - * - * 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" - -using WebCore::GIFImageDecoder; - -// Define the Mozilla macro setup so that we can leave the macros alone. -#define PR_BEGIN_MACRO do { -#define PR_END_MACRO } while (0) - -/* - * GETN(n, s) requests at least 'n' bytes available from 'q', at start of state 's' - * - * Note, the hold will never need to be bigger than 256 bytes to gather up in the hold, - * as each GIF block (except colormaps) can never be bigger than 256 bytes. - * Colormaps are directly copied in the resp. global_colormap or dynamically allocated local_colormap. - * So a fixed buffer in GIFImageReader is good enough. - * This buffer is only needed to copy left-over data from one GifWrite call to the next - */ -#define GETN(n,s) \ - PR_BEGIN_MACRO \ - bytes_to_consume = (n); \ - state = (s); \ - PR_END_MACRO - -/* Get a 16-bit value stored in little-endian format */ -#define GETINT16(p) ((p)[1]<<8|(p)[0]) - -//****************************************************************************** -// Send the data to the display front-end. -void GIFImageReader::output_row() -{ - GIFFrameReader* gs = frame_reader; - - int drow_start, drow_end; - - drow_start = drow_end = gs->irow; - - /* - * Haeberli-inspired hack for interlaced GIFs: Replicate lines while - * displaying to diminish the "venetian-blind" effect as the image is - * loaded. Adjust pixel vertical positions to avoid the appearance of the - * image crawling up the screen as successive passes are drawn. - */ - if (gs->progressive_display && gs->interlaced && gs->ipass < 4) { - unsigned row_dup = 0, row_shift = 0; - - switch (gs->ipass) { - case 1: - row_dup = 7; - row_shift = 3; - break; - case 2: - row_dup = 3; - row_shift = 1; - break; - case 3: - row_dup = 1; - row_shift = 0; - break; - default: - break; - } - - drow_start -= row_shift; - drow_end = drow_start + row_dup; - - /* Extend if bottom edge isn't covered because of the shift upward. */ - if (((gs->height - 1) - drow_end) <= row_shift) - drow_end = gs->height - 1; - - /* Clamp first and last rows to upper and lower edge of image. */ - if (drow_start < 0) - drow_start = 0; - if ((unsigned)drow_end >= gs->height) - drow_end = gs->height - 1; - } - - /* Protect against too much image data */ - if ((unsigned)drow_start >= gs->height) - return; - - // CALLBACK: Let the client know we have decoded a row. - if (clientptr && frame_reader) - clientptr->haveDecodedRow(images_count - 1, frame_reader->rowbuf, frame_reader->rowend, - drow_start, drow_end - drow_start + 1, - gs->progressive_display && gs->interlaced && gs->ipass > 1); - - gs->rowp = gs->rowbuf; - - if (!gs->interlaced) - gs->irow++; - else { - do { - switch (gs->ipass) - { - case 1: - gs->irow += 8; - if (gs->irow >= gs->height) { - gs->ipass++; - gs->irow = 4; - } - break; - - case 2: - gs->irow += 8; - if (gs->irow >= gs->height) { - gs->ipass++; - gs->irow = 2; - } - break; - - case 3: - gs->irow += 4; - if (gs->irow >= gs->height) { - gs->ipass++; - gs->irow = 1; - } - break; - - case 4: - gs->irow += 2; - if (gs->irow >= gs->height){ - gs->ipass++; - gs->irow = 0; - } - break; - - default: - break; - } - } while (gs->irow > (gs->height - 1)); - } -} - -//****************************************************************************** -/* Perform Lempel-Ziv-Welch decoding */ -int GIFImageReader::do_lzw(const unsigned char *q) -{ - GIFFrameReader* gs = frame_reader; - if (!gs) - return 0; - - int code; - int incode; - const unsigned char *ch; - - /* Copy all the decoder state variables into locals so the compiler - * won't worry about them being aliased. The locals will be homed - * back into the GIF decoder structure when we exit. - */ - int avail = gs->avail; - int bits = gs->bits; - int cnt = count; - int codesize = gs->codesize; - int codemask = gs->codemask; - int oldcode = gs->oldcode; - int clear_code = gs->clear_code; - unsigned char firstchar = gs->firstchar; - int datum = gs->datum; - - if (!gs->prefix) { - gs->prefix = new unsigned short[MAX_BITS]; - memset(gs->prefix, 0, MAX_BITS * sizeof(short)); - } - - unsigned short *prefix = gs->prefix; - unsigned char *stackp = gs->stackp; - unsigned char *suffix = gs->suffix; - unsigned char *stack = gs->stack; - unsigned char *rowp = gs->rowp; - unsigned char *rowend = gs->rowend; - unsigned rows_remaining = gs->rows_remaining; - - if (rowp == rowend) - return 0; - -#define OUTPUT_ROW \ - PR_BEGIN_MACRO \ - output_row(); \ - rows_remaining--; \ - rowp = frame_reader->rowp; \ - if (!rows_remaining) \ - goto END; \ - PR_END_MACRO - - for (ch = q; cnt-- > 0; ch++) - { - /* Feed the next byte into the decoder's 32-bit input buffer. */ - datum += ((int) *ch) << bits; - bits += 8; - - /* Check for underflow of decoder's 32-bit input buffer. */ - while (bits >= codesize) - { - /* Get the leading variable-length symbol from the data stream */ - code = datum & codemask; - datum >>= codesize; - bits -= codesize; - - /* Reset the dictionary to its original state, if requested */ - if (code == clear_code) { - codesize = gs->datasize + 1; - codemask = (1 << codesize) - 1; - avail = clear_code + 2; - oldcode = -1; - continue; - } - - /* Check for explicit end-of-stream code */ - if (code == (clear_code + 1)) { - /* end-of-stream should only appear after all image data */ - if (rows_remaining != 0) - return -1; - return 0; - } - - if (oldcode == -1) { - *rowp++ = suffix[code]; - if (rowp == rowend) - OUTPUT_ROW; - - firstchar = oldcode = code; - continue; - } - - incode = code; - if (code >= avail) { - *stackp++ = firstchar; - code = oldcode; - - if (stackp == stack + MAX_BITS) - return -1; - } - - while (code >= clear_code) - { - if (code >= MAX_BITS || code == prefix[code]) - return -1; - - // Even though suffix[] only holds characters through suffix[avail - 1], - // allowing code >= avail here lets us be more tolerant of malformed - // data. As long as code < MAX_BITS, the only risk is a garbled image, - // which is no worse than refusing to display it. - *stackp++ = suffix[code]; - code = prefix[code]; - - if (stackp == stack + MAX_BITS) - return -1; - } - - *stackp++ = firstchar = suffix[code]; - - /* Define a new codeword in the dictionary. */ - if (avail < 4096) { - prefix[avail] = oldcode; - suffix[avail] = firstchar; - avail++; - - /* If we've used up all the codewords of a given length - * increase the length of codewords by one bit, but don't - * exceed the specified maximum codeword size of 12 bits. - */ - if (((avail & codemask) == 0) && (avail < 4096)) { - codesize++; - codemask += avail; - } - } - oldcode = incode; - - /* Copy the decoded data out to the scanline buffer. */ - do { - *rowp++ = *--stackp; - if (rowp == rowend) { - OUTPUT_ROW; - } - } while (stackp > stack); - } - } - - END: - - /* Home the local copies of the GIF decoder state variables */ - gs->avail = avail; - gs->bits = bits; - gs->codesize = codesize; - gs->codemask = codemask; - count = cnt; - gs->oldcode = oldcode; - gs->firstchar = firstchar; - gs->datum = datum; - gs->stackp = stackp; - gs->rowp = rowp; - gs->rows_remaining = rows_remaining; - - return 0; -} - - -/******************************************************************************/ -/* - * process data arriving from the stream for the gif decoder - */ - -bool GIFImageReader::read(const unsigned char *buf, unsigned len, - GIFImageDecoder::GIFQuery query, unsigned haltAtFrame) -{ - if (!len) { - // No new data has come in since the last call, just ignore this call. - return true; - } - - const unsigned char *q = buf; - - // Add what we have so far to the block - // If previous call to me left something in the hold first complete current block - // Or if we are filling the colormaps, first complete the colormap - unsigned char* p = 0; - if (state == gif_global_colormap) - p = global_colormap; - else if (state == gif_image_colormap) - p = frame_reader ? frame_reader->local_colormap : 0; - else if (bytes_in_hold) - p = hold; - else - p = 0; - - if (p || (state == gif_global_colormap) || (state == gif_image_colormap)) { - // Add what we have sofar to the block - unsigned l = len < bytes_to_consume ? len : bytes_to_consume; - if (p) - memcpy(p + bytes_in_hold, buf, l); - - if (l < bytes_to_consume) { - // Not enough in 'buf' to complete current block, get more - bytes_in_hold += l; - bytes_to_consume -= l; - if (clientptr) - clientptr->decodingHalted(0); - return true; - } - // Reset hold buffer count - bytes_in_hold = 0; - // Point 'q' to complete block in hold (or in colormap) - q = p; - } - - // Invariant: - // 'q' is start of current to be processed block (hold, colormap or buf) - // 'bytes_to_consume' is number of bytes to consume from 'buf' - // 'buf' points to the bytes to be consumed from the input buffer - // 'len' is number of bytes left in input buffer from position 'buf'. - // At entrance of the for loop will 'buf' will be moved 'bytes_to_consume' - // to point to next buffer, 'len' is adjusted accordingly. - // So that next round in for loop, q gets pointed to the next buffer. - - for (;len >= bytes_to_consume; q=buf) { - // Eat the current block from the buffer, q keeps pointed at current block - buf += bytes_to_consume; - len -= bytes_to_consume; - - switch (state) - { - case gif_lzw: - if (do_lzw(q) < 0) { - state = gif_error; - break; - } - GETN(1, gif_sub_block); - break; - - case gif_lzw_start: - { - /* Initialize LZW parser/decoder */ - int datasize = *q; - // Since we use a codesize of 1 more than the datasize, we need to ensure - // that our datasize is strictly less than the MAX_LZW_BITS value (12). - // This sets the largest possible codemask correctly at 4095. - if (datasize >= MAX_LZW_BITS) { - state = gif_error; - break; - } - int clear_code = 1 << datasize; - if (clear_code >= MAX_BITS) { - state = gif_error; - break; - } - - if (frame_reader) { - frame_reader->datasize = datasize; - frame_reader->clear_code = clear_code; - frame_reader->avail = frame_reader->clear_code + 2; - frame_reader->oldcode = -1; - frame_reader->codesize = frame_reader->datasize + 1; - frame_reader->codemask = (1 << frame_reader->codesize) - 1; - - frame_reader->datum = frame_reader->bits = 0; - - /* init the tables */ - if (!frame_reader->suffix) - frame_reader->suffix = new unsigned char[MAX_BITS]; - // 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 { - state = gif_error; - break; - } - GETN(7, gif_global_header); - } - break; - - case gif_global_header: - { - /* This is the height and width of the "screen" or - * frame into which images are rendered. The - * individual images can be smaller than the - * screen size and located with an origin anywhere - * within the screen. - */ - - screen_width = GETINT16(q); - screen_height = GETINT16(q + 2); - - // CALLBACK: Inform the decoderplugin of our size. - if (clientptr) { - if (!clientptr->sizeNowAvailable(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 */ - state = gif_done; - break; - } - - if (*q == '!') { /* extension */ - GETN(2, gif_extension); - break; - } - - /* If we get anything other than ',' (image separator), '!' - * (extension), or ';' (trailer), there is extraneous data - * between blocks. The GIF87a spec tells us to keep reading - * until we find an image separator, but GIF89a says such - * a file is corrupt. We follow GIF89a and bail out. */ - if (*q != ',') { - if (images_decoded > 0) { - /* The file is corrupt, but one or more images have - * been decoded correctly. In this case, we proceed - * as if the file were correctly terminated and set - * the state to gif_done, so the GIF will display. - */ - state = gif_done; - } else { - /* No images decoded, there is nothing to display. */ - state = gif_error; - } - break; - } else - GETN(9, gif_image_header); - } - break; - - case gif_extension: - { - int len = count = q[1]; - gstate es = gif_skip_block; - - switch (*q) - { - case 0xf9: - es = gif_control_extension; - break; - - case 0x01: - // ignoring plain text extension - break; - - case 0xff: - es = gif_application_extension; - break; - - case 0xfe: - es = gif_consume_comment; - break; - } - - if (len) - GETN(len, es); - else - GETN(1, gif_image_start); - } - break; - - case gif_consume_block: - if (!*q) - GETN(1, gif_image_start); - else - GETN(*q, gif_skip_block); - break; - - case gif_skip_block: - GETN(1, gif_consume_block); - break; - - case gif_control_extension: - { - if (query != GIFImageDecoder::GIFFrameCountQuery) { - if (!frame_reader) - frame_reader = new GIFFrameReader(); - } - - if (frame_reader) { - if (*q & 0x1) { - frame_reader->tpixel = q[3]; - frame_reader->is_transparent = true; - } else { - frame_reader->is_transparent = false; - // ignoring gfx control extension - } - // NOTE: This relies on the values in the FrameDisposalMethod enum - // matching those in the GIF spec! - frame_reader->disposal_method = (WebCore::RGBA32Buffer::FrameDisposalMethod)(((*q) >> 2) & 0x7); - // Some specs say 3rd bit (value 4), other specs say value 3 - // Let's choose 3 (the more popular) - if (frame_reader->disposal_method == 4) - frame_reader->disposal_method = WebCore::RGBA32Buffer::DisposeOverwritePrevious; - frame_reader->delay_time = GETINT16(q + 1) * 10; - } - GETN(1, gif_consume_block); - } - break; - - case gif_comment_extension: - { - if (*q) - GETN(*q, gif_consume_comment); - else - GETN(1, gif_image_start); - } - break; - - case gif_consume_comment: - GETN(1, gif_comment_extension); - break; - - case gif_application_extension: - /* Check for netscape application extension */ - if (!strncmp((char*)q, "NETSCAPE2.0", 11) || - !strncmp((char*)q, "ANIMEXTS1.0", 11)) - GETN(1, gif_netscape_extension_block); - else - GETN(1, gif_consume_block); - break; - - /* Netscape-specific GIF extension: animation looping */ - case gif_netscape_extension_block: - if (*q) - GETN(*q, gif_consume_netscape_extension); - else - GETN(1, gif_image_start); - break; - - /* Parse netscape-specific application extensions */ - case gif_consume_netscape_extension: - { - int netscape_extension = q[0] & 7; - - /* Loop entire animation specified # of times. Only read the - loop count during the first iteration. */ - if (netscape_extension == 1) { - loop_count = GETINT16(q + 1); - - GETN(1, gif_netscape_extension_block); - } - /* Wait for specified # of bytes to enter buffer */ - else if (netscape_extension == 2) { - // Don't do this, this extension doesn't exist (isn't used at all) - // and doesn't do anything, as our streaming/buffering takes care of it all... - // See: http://semmix.pl/color/exgraf/eeg24.htm - GETN(1, gif_netscape_extension_block); - } else - state = gif_error; // 0,3-7 are yet to be defined netscape - // extension codes - - break; - } - - case gif_image_header: - { - unsigned height, width, x_offset, y_offset; - - /* Get image offsets, with respect to the screen origin */ - x_offset = GETINT16(q); - y_offset = GETINT16(q + 2); - - /* Get image width and height. */ - width = GETINT16(q + 4); - height = GETINT16(q + 6); - - /* Work around broken GIF files where the logical screen - * size has weird width or height. We assume that GIF87a - * files don't contain animations. - */ - if ((images_decoded == 0) && - ((screen_height < height) || (screen_width < width) || - (version == 87))) - { - screen_height = height; - screen_width = width; - x_offset = 0; - y_offset = 0; - - // CALLBACK: Inform the decoderplugin of our size. - if (clientptr) - clientptr->sizeNowAvailable(screen_width, screen_height); - } - - /* Work around more broken GIF files that have zero image - width or height */ - if (!height || !width) { - height = screen_height; - width = screen_width; - if (!height || !width) { - state = gif_error; - break; - } - } - - if (query == GIFImageDecoder::GIFSizeQuery || haltAtFrame == images_decoded) { - // The decoder needs to stop. Hand back the number of bytes we consumed from - // buffer minus 9 (the amount we consumed to read the header). - if (clientptr) - clientptr->decodingHalted(len + 9); - GETN(9, gif_image_header); - return true; - } - - images_count = images_decoded + 1; - - if (query == GIFImageDecoder::GIFFullQuery && !frame_reader) - frame_reader = new GIFFrameReader(); - - if (frame_reader) { - frame_reader->x_offset = x_offset; - frame_reader->y_offset = y_offset; - frame_reader->height = height; - frame_reader->width = width; - - /* This case will never be taken if this is the first image */ - /* being decoded. If any of the later images are larger */ - /* than the screen size, we need to reallocate buffers. */ - if (screen_width < width) { - /* XXX Deviant! */ - - delete []frame_reader->rowbuf; - screen_width = width; - frame_reader->rowbuf = new unsigned char[screen_width]; - } else if (!frame_reader->rowbuf) { - frame_reader->rowbuf = new unsigned char[screen_width]; - } - - if (!frame_reader->rowbuf) { - state = gif_oom; - break; - } - if (screen_height < height) - screen_height = height; - - if (q[8] & 0x40) { - frame_reader->interlaced = true; - frame_reader->ipass = 1; - } else { - frame_reader->interlaced = false; - frame_reader->ipass = 0; - } - - if (images_decoded == 0) { - frame_reader->progressive_display = true; - } else { - /* Overlaying interlaced, transparent GIFs over - existing image data using the Haeberli display hack - requires saving the underlying image in order to - avoid jaggies at the transparency edges. We are - unprepared to deal with that, so don't display such - images progressively */ - frame_reader->progressive_display = false; - } - - /* Clear state from last image */ - frame_reader->irow = 0; - frame_reader->rows_remaining = frame_reader->height; - frame_reader->rowend = frame_reader->rowbuf + frame_reader->width; - frame_reader->rowp = frame_reader->rowbuf; - - /* bits per pixel is q[8]&0x07 */ - } - - if (q[8] & 0x80) /* has a local colormap? */ - { - int num_colors = 2 << (q[8] & 0x7); - const unsigned size = 3*num_colors; - unsigned char *map = frame_reader ? frame_reader->local_colormap : 0; - if (frame_reader && (!map || (num_colors > frame_reader->local_colormap_size))) { - delete []map; - map = new unsigned char[size]; - if (!map) { - state = gif_oom; - break; - } - } - - /* Switch to the new local palette after it loads */ - if (frame_reader) { - frame_reader->local_colormap = map; - frame_reader->local_colormap_size = num_colors; - frame_reader->is_local_colormap_defined = true; - } - - if (len < size) { - // Use 'hold' pattern to get the image colormap - GETN(size, gif_image_colormap); - break; - } - // Copy everything and directly go to gif_lzw_start - if (frame_reader) - memcpy(frame_reader->local_colormap, buf, size); - buf += size; - len -= size; - } else if (frame_reader) { - /* Switch back to the global palette */ - frame_reader->is_local_colormap_defined = false; - } - GETN(1, gif_lzw_start); - } - break; - - case gif_image_colormap: - // Everything is already copied into local_colormap - GETN(1, gif_lzw_start); - break; - - case gif_sub_block: - { - if ((count = *q) != 0) - /* Still working on the same image: Process next LZW data block */ - { - /* Make sure there are still rows left. If the GIF data */ - /* is corrupt, we may not get an explicit terminator. */ - if (frame_reader && frame_reader->rows_remaining == 0) { - /* This is an illegal GIF, but we remain tolerant. */ - GETN(1, gif_sub_block); - } - GETN(count, gif_lzw); - } - else - /* See if there are any more images in this sequence. */ - { - images_decoded++; - - // CALLBACK: The frame is now complete. - if (clientptr && frame_reader) - clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, - frame_reader->disposal_method); - - /* Clear state from this image */ - if (frame_reader) { - frame_reader->is_local_colormap_defined = false; - frame_reader->is_transparent = false; - } - - GETN(1, gif_image_start); - } - } - break; - - case gif_done: - // When the GIF is done, we can stop. - if (clientptr) - clientptr->gifComplete(); - return true; - - // Handle out of memory errors - case gif_oom: - return false; - - // Handle general errors - case gif_error: - // nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count); - return false; - - // We shouldn't ever get here. - default: - break; - } - } - - // Copy the leftover into gs->hold - bytes_in_hold = len; - if (len) { - // Add what we have sofar to the block - unsigned char* p; - if (state == gif_global_colormap) - p = global_colormap; - else if (state == gif_image_colormap) - p = frame_reader ? frame_reader->local_colormap : 0; - else - p = hold; - if (p) - memcpy(p, buf, len); - bytes_to_consume -= len; - } - - if (clientptr) - clientptr->decodingHalted(0); - return true; -} diff --git a/WebCore/platform/image-decoders/skia/GIFImageReader.h b/WebCore/platform/image-decoders/skia/GIFImageReader.h deleted file mode 100644 index f0d127f..0000000 --- a/WebCore/platform/image-decoders/skia/GIFImageReader.h +++ /dev/null @@ -1,215 +0,0 @@ -/* -*- 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_oom, - gif_error, - gif_comment_extension, - gif_application_extension, - gif_netscape_extension_block, - gif_consume_netscape_extension, - gif_consume_comment -} gstate; - -struct GIFFrameReader { - /* LZW decoder state machine */ - unsigned char *stackp; /* Current stack pointer */ - int datasize; - int codesize; - int codemask; - int clear_code; /* Codeword used to trigger dictionary reset */ - int avail; /* Index of next available slot in dictionary */ - int oldcode; - unsigned char firstchar; - int bits; /* Number of unread bits in "datum" */ - int datum; /* 32-bit input buffer */ - - /* Output state machine */ - int ipass; /* Interlace pass; Ranges 1-4 if interlaced. */ - unsigned int rows_remaining; /* Rows remaining to be output */ - unsigned int irow; /* Current output row, starting at zero */ - unsigned char *rowbuf; /* Single scanline temporary buffer */ - unsigned char *rowend; /* Pointer to end of rowbuf */ - unsigned char *rowp; /* Current output pointer */ - - /* Parameters for image frame currently being decoded */ - unsigned int x_offset, y_offset; /* With respect to "screen" origin */ - unsigned int height, width; - int tpixel; /* Index of transparent pixel */ - WebCore::RGBA32Buffer::FrameDisposalMethod disposal_method; /* Restore to background, leave in place, etc.*/ - unsigned char *local_colormap; /* Per-image colormap */ - int local_colormap_size; /* Size of local colormap array. */ - - bool is_local_colormap_defined : 1; - bool progressive_display : 1; /* If TRUE, do Haeberli interlace hack */ - bool interlaced : 1; /* TRUE, if scanlines arrive interlaced order */ - bool is_transparent : 1; /* TRUE, if tpixel is valid */ - - unsigned delay_time; /* Display time, in milliseconds, - for this image in a multi-image GIF */ - - - unsigned short* prefix; /* LZW decoding tables */ - unsigned char* suffix; /* LZW decoding tables */ - unsigned char* stack; /* Base of LZW decoder stack */ - - - GIFFrameReader() { - stackp = 0; - datasize = codesize = codemask = clear_code = avail = oldcode = 0; - firstchar = 0; - bits = datum = 0; - ipass = 0; - rows_remaining = irow = 0; - rowbuf = rowend = rowp = 0; - - x_offset = y_offset = width = height = 0; - tpixel = 0; - disposal_method = WebCore::RGBA32Buffer::DisposeNotSpecified; - - local_colormap = 0; - local_colormap_size = 0; - is_local_colormap_defined = progressive_display = is_transparent = interlaced = false; - - delay_time = 0; - - prefix = 0; - suffix = stack = 0; - } - - ~GIFFrameReader() { - delete []rowbuf; - delete []local_colormap; - delete []prefix; - delete []suffix; - delete []stack; - } -}; - -struct GIFImageReader { - WebCore::GIFImageDecoder* clientptr; - /* Parsing state machine */ - gstate state; /* Current decoder master state */ - unsigned bytes_to_consume; /* Number of bytes to accumulate */ - unsigned bytes_in_hold; /* bytes accumulated so far*/ - unsigned char hold[MAX_HOLD_SIZE]; /* Accumulation buffer */ - unsigned char* global_colormap; /* (3* MAX_COLORS in size) Default colormap if local not supplied, 3 bytes for each color */ - - /* Global (multi-image) state */ - int screen_bgcolor; /* Logical screen background color */ - int version; /* Either 89 for GIF89 or 87 for GIF87 */ - unsigned screen_width; /* Logical screen width & height */ - unsigned screen_height; - int global_colormap_size; /* Size of global colormap array. */ - 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() { - close(); - } - - void close() { - delete []global_colormap; - global_colormap = 0; - delete frame_reader; - frame_reader = 0; - } - - bool read(const unsigned char * buf, unsigned int numbytes, - WebCore::GIFImageDecoder::GIFQuery query = WebCore::GIFImageDecoder::GIFFullQuery, unsigned haltAtFrame = -1); - -private: - void output_row(); - int do_lzw(const unsigned char *q); -}; - -#endif diff --git a/WebCore/platform/image-decoders/skia/ICOImageDecoder.cpp b/WebCore/platform/image-decoders/skia/ICOImageDecoder.cpp deleted file mode 100644 index c340896..0000000 --- a/WebCore/platform/image-decoders/skia/ICOImageDecoder.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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" - -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; - -void ICOImageDecoder::decodeImage(SharedBuffer* data) -{ - // Read and process directory. - if ((m_decodedOffset < sizeOfDirectory) && !processDirectory(data)) - return; - - // Read and process directory entries. - if ((m_decodedOffset < (sizeOfDirectory + (m_directory.idCount * sizeOfDirEntry))) - && !processDirectoryEntries(data)) - return; - - // Check if this entry is a PNG; we need 4 bytes to check the magic number. - if (m_imageType == Unknown) { - if (data->size() < (m_dirEntry.dwImageOffset + 4)) - return; - m_imageType = - strncmp(&data->data()[m_dirEntry.dwImageOffset], "\x89PNG", 4) ? - BMP : PNG; - } - - // Decode selected entry. - if (m_imageType == PNG) - decodePNG(data); - else { - // Note that we don't try to limit the bytes we give to the decoder to - // just the size specified in the icon directory. If the size given in - // the directory is insufficient to decode the whole image, the image is - // corrupt anyway, so whatever we do may be wrong. The easiest choice - // (which we do here) is to simply aggressively consume bytes until we - // run out of bytes, finish decoding, or hit a sequence that makes the - // decoder fail. - decodeBMP(data); - } -} - -RGBA32Buffer* ICOImageDecoder::frameBufferAtIndex(size_t index) -{ - return (m_imageType == PNG) ? m_pngDecoder.frameBufferAtIndex(0) : - BMPImageReader::frameBufferAtIndex(0); -} - -bool ICOImageDecoder::isSizeAvailable() const -{ - return (m_imageType == PNG) ? m_pngDecoder.isSizeAvailable() : - BMPImageReader::isSizeAvailable(); -} - -IntSize ICOImageDecoder::size() const -{ - return (m_imageType == PNG) ? m_pngDecoder.size() : BMPImageReader::size(); -} - -bool ICOImageDecoder::processDirectory(SharedBuffer* data) -{ - // Read directory. - ASSERT(!m_decodedOffset); - if (data->size() < sizeOfDirectory) - return false; - const uint16_t fileType = readUint16(data, 2); - m_directory.idCount = readUint16(data, 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)) || - (m_directory.idCount == 0)) - m_failed = true; - - return !m_failed; -} - -bool ICOImageDecoder::processDirectoryEntries(SharedBuffer* data) -{ - // Read directory entries. - ASSERT(m_decodedOffset == sizeOfDirectory); - if ((m_decodedOffset > data->size()) || (data->size() - m_decodedOffset) < - (m_directory.idCount * sizeOfDirEntry)) - return false; - for (int i = 0; i < m_directory.idCount; ++i) { - const IconDirectoryEntry dirEntry = readDirectoryEntry(data); - if ((i == 0) || isBetterEntry(dirEntry)) - m_dirEntry = dirEntry; - } - - // Make sure the specified image offset is past the end of the directory - // entries, and that the offset isn't so large that it overflows when we add - // 4 bytes to it (which we do in decodeImage() while ensuring it's safe to - // examine the first 4 bytes of the image data). - if ((m_dirEntry.dwImageOffset < m_decodedOffset) || - ((m_dirEntry.dwImageOffset + 4) < m_dirEntry.dwImageOffset)) { - m_failed = true; - return false; - } - - // Ready to decode the image at the specified offset. - m_decodedOffset = m_headerOffset = m_dirEntry.dwImageOffset; - return true; -} - -ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry( - SharedBuffer* data) -{ - // Read icon data. - IconDirectoryEntry entry; - entry.bWidth = static_cast<uint8_t>(data->data()[m_decodedOffset]); - if (entry.bWidth == 0) - entry.bWidth = 256; - entry.bHeight = static_cast<uint8_t>(data->data()[m_decodedOffset + 1]); - if (entry.bHeight == 0) - entry.bHeight = 256; - entry.wBitCount = readUint16(data, 6); - entry.dwImageOffset = readUint32(data, 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.wBitCount) { - uint8_t colorCount = data->data()[m_decodedOffset + 2]; - if (colorCount) { - for (--colorCount; colorCount; colorCount >>= 1) - ++entry.wBitCount; - } - } - - m_decodedOffset += sizeOfDirEntry; - return entry; -} - -bool ICOImageDecoder::isBetterEntry(const IconDirectoryEntry& entry) const -{ - const IntSize entrySize(entry.bWidth, entry.bHeight); - const IntSize dirEntrySize(m_dirEntry.bWidth, m_dirEntry.bHeight); - const int entryArea = entry.bWidth * entry.bHeight; - const int dirEntryArea = m_dirEntry.bWidth * m_dirEntry.bHeight; - - if ((entrySize != dirEntrySize) && !m_preferredIconSize.isEmpty()) { - // An icon of exactly the preferred size is best. - if (entrySize == m_preferredIconSize) - return true; - if (dirEntrySize == m_preferredIconSize) - return false; - - // The icon closest to the preferred area without being smaller is - // better. - if (entryArea != dirEntryArea) { - return (entryArea < dirEntryArea) - && (entryArea >= (m_preferredIconSize.width() * m_preferredIconSize.height())); - } - } - - // Larger icons are better. - if (entryArea != dirEntryArea) - return (entryArea > dirEntryArea); - - // Higher bit-depth icons are better. - return (entry.wBitCount > m_dirEntry.wBitCount); -} - -void ICOImageDecoder::decodePNG(SharedBuffer* data) -{ - // Copy out PNG data to a separate vector and instantiate PNG decoder. - // It would be nice to save this copy, if I could figure out how to just - // offset the perceived start of |data| by |m_dirEntry.dwImageOffset| when - // passing it to setData()... - RefPtr<SharedBuffer> pngData( - SharedBuffer::create(&data->data()[m_dirEntry.dwImageOffset], - data->size() - m_dirEntry.dwImageOffset)); - m_pngDecoder.setData(pngData.get(), true); - - // Decode PNG as a side effect of asking for the frame. Strangely, it's - // seemingly unsafe to call decode() or isSizeAvailable() before calling - // this, as this is the only function that enlarges the framebuffer to - // nonzero size, and before this happens any decoded image data is silently - // thrown away and never decoded again (!). - m_pngDecoder.frameBufferAtIndex(0); - m_failed = m_pngDecoder.failed(); - - // Sanity-check that the size is what we expected. - if (isSizeAvailable() && ((size().width() != m_dirEntry.bWidth) || - (size().height() != m_dirEntry.bHeight))) - m_failed = true; -} - -} diff --git a/WebCore/platform/image-decoders/skia/ICOImageDecoder.h b/WebCore/platform/image-decoders/skia/ICOImageDecoder.h deleted file mode 100644 index f0e1acc..0000000 --- a/WebCore/platform/image-decoders/skia/ICOImageDecoder.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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" -#include "PNGImageDecoder.h" - -namespace WebCore { - - // This class decodes the ICO and CUR image formats. - class ICOImageDecoder : public BMPImageReader { - public: - // See comments on |m_preferredIconSize| below. - ICOImageDecoder(const IntSize& preferredIconSize) - : m_preferredIconSize(preferredIconSize) - , m_imageType(Unknown) - { - m_andMaskState = NotYetDecoded; - } - - virtual String filenameExtension() const { return "ico"; } - - // BMPImageReader - virtual void decodeImage(SharedBuffer* data); - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - - // ImageDecoder - virtual bool isSizeAvailable() const; - virtual IntSize size() const; - - private: - enum ImageType { - Unknown, - BMP, - PNG, - }; - - // These are based on the Windows ICONDIR and ICONDIRENTRY structs, but - // with unnecessary entries removed. - struct IconDirectory { - uint16_t idCount; - }; - struct IconDirectoryEntry { - uint16_t bWidth; // 16 bits so we can represent 256 as 256, not 0 - uint16_t bHeight; // " - uint16_t wBitCount; - uint32_t dwImageOffset; - }; - - // Processes the ICONDIR at the beginning of the data. Returns true if the - // directory could be decoded. - bool processDirectory(SharedBuffer*); - - // 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(SharedBuffer*); - - // Reads and returns a directory entry from the current offset into |data|. - IconDirectoryEntry readDirectoryEntry(SharedBuffer*); - - // Returns true if |entry| is a preferable icon entry to m_dirEntry. - // Larger sizes, or greater bitdepths at the same size, are preferable. - bool isBetterEntry(const IconDirectoryEntry&) const; - - // Called when the image to be decoded is a PNG rather than a BMP. - // Instantiates a PNGImageDecoder, decodes the image, and copies the - // results locally. - void decodePNG(SharedBuffer*); - - // The entry size we should prefer. If this is empty, we choose the - // largest available size. If no entries of the desired size are - // available, we pick the next larger size. - IntSize m_preferredIconSize; - - // The headers for the ICO. - IconDirectory m_directory; - IconDirectoryEntry m_dirEntry; - - // The PNG decoder, if we need to use one. - PNGImageDecoder m_pngDecoder; - - // What kind of image data is stored at the entry we're decoding. - ImageType m_imageType; - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/skia/ImageDecoder.h b/WebCore/platform/image-decoders/skia/ImageDecoder.h deleted file mode 100644 index c198420..0000000 --- a/WebCore/platform/image-decoders/skia/ImageDecoder.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * 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. - */ - -#ifndef ImageDecoder_h -#define ImageDecoder_h - -#include "IntRect.h" -#include "ImageSource.h" -#include "NativeImageSkia.h" -#include "PlatformString.h" -#include "SharedBuffer.h" -#include <wtf/Assertions.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> - -#include "SkBitmap.h" -#ifdef ANDROID_ANIMATED_GIF -#include "SkColor.h" -#endif - -namespace WebCore { - - // 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 - }; - - RGBA32Buffer() - : m_status(FrameEmpty) - , m_duration(0) - , m_disposalMethod(DisposeNotSpecified) - { - } - - // This constructor doesn't create a new copy of the image data, it only - // increases the ref count of the existing bitmap. - RGBA32Buffer(const RGBA32Buffer& other) - { - operator=(other); - } - - ~RGBA32Buffer() - { - } - - // Initialize with another buffer. This function doesn't create a new copy - // of the image data, it only increases the refcount of the existing bitmap. - // - // Normal callers should not generally be using this function. If you want - // to create a copy on which you can modify the image data independently, - // use copyBitmapData() instead. - 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()); - return *this; - } - - void 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. - } - - // This function creates a new copy of the image data in |other|, so the - // two images can be modified independently. - void copyBitmapData(const RGBA32Buffer& other) - { - if (this == &other) - return; - - m_bitmap.reset(); - const NativeImageSkia& otherBitmap = other.bitmap(); - otherBitmap.copyTo(&m_bitmap, otherBitmap.config()); - } - - NativeImageSkia& bitmap() { return m_bitmap; } - const NativeImageSkia& bitmap() const { return m_bitmap; } - - // Must be called before any pixels are written. Will return true on - // success, false if the memory allocation fails. - bool setSize(int width, int height) - { - // This function should only be called once, it will leak memory - // otherwise. - ASSERT(m_bitmap.width() == 0 && m_bitmap.height() == 0); - m_bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); - if (!m_bitmap.allocPixels()) - return false; // Allocation failure, maybe the bitmap was too big. - - // Clear the image. - m_bitmap.eraseARGB(0, 0, 0, 0); - - return true; - } - - int width() const { return m_bitmap.width(); } - int height() const { return m_bitmap.height(); } - - 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 hasAlpha() const { return !m_bitmap.isOpaque(); } - - void setRect(const IntRect& r) { m_rect = r; } - void setStatus(FrameStatus s) - { - if (s == FrameComplete) - m_bitmap.setDataComplete(); // Tell the bitmap it's done. - m_status = s; - } - void setDuration(unsigned duration) { m_duration = duration; } - void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; } - void setHasAlpha(bool alpha) { m_bitmap.setIsOpaque(!alpha); } - - static void setRGBA(uint32_t* dest, uint8_t r, uint8_t g, uint8_t b, uint8_t a) - { - // We store this data pre-multiplied. -#ifdef ANDROID_ANIMATED_GIF - // Chrome should take this change as well. - *dest = SkPreMultiplyARGB(a, r, g, b); -#else - if (a == 0) - *dest = 0; - else { - if (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); - } - *dest = (a << 24 | r << 16 | g << 8 | b); - } -#endif - } - - void setRGBA(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) - { - setRGBA(m_bitmap.getAddr32(x, y), r, g, b, a); - } - - private: - NativeImageSkia m_bitmap; - 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. - }; - - // 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. - class ImageDecoder { - public: - ImageDecoder() - : m_failed(false) - , m_sizeAvailable(false) - { - } - - virtual ~ImageDecoder() {} - - // The the filename extension usually associated with an undecoded image of this type. - virtual String filenameExtension() const = 0; - - // All specific decoder plugins must do something with the data they are given. - virtual void setData(SharedBuffer* data, bool allDataReceived) { m_data = data; } - - // 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() const - { - return !m_failed && m_sizeAvailable; - } - - // Requests the size. - virtual IntSize size() const - { - // Requesting the size of an invalid bitmap is meaningless. - ASSERT(!m_failed); - return m_size; - } - - // 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 int 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 index) = 0; - - // Whether or not the underlying image format even supports alpha transparency. - virtual bool supportsAlpha() const { return true; } - - bool failed() const { return m_failed; } - void setFailed() { m_failed = true; } - - // 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) { } - - protected: - // Called by the image decoders to set their decoded size, this also check - // 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. - bool setSize(unsigned width, unsigned height) - { - if (isOverSize(width, height)) { - m_failed = true; - return false; - } - m_size = IntSize(width, height); - m_sizeAvailable = true; - return true; - } - - RefPtr<SharedBuffer> m_data; // The encoded data. - Vector<RGBA32Buffer> m_frameBufferCache; - mutable bool m_failed; - - private: - // This function allows us to make sure the image is not too large. Very - // large images, even if the allocation succeeds, can take a very long time - // to process, giving the appearance of a DoS. - // - // WebKit also seems to like to ask for the size before data is available - // and in some cases when the failed flag is set. Some of these code paths - // such as BitmapImage::resetAnimation then compute the size of the image - // based on the width and height. Because of this, our total computed image - // byte size must never overflow an int. - static bool isOverSize(unsigned width, unsigned height) - { - unsigned long long total_size = static_cast<unsigned long long>(width) - * static_cast<unsigned long long>(height); - if (total_size > 32 * 1024 * 1024) // 32M = 128MB memory total (32 bpp). - return true; - return false; - } - - IntSize m_size; - bool m_sizeAvailable; - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp b/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp new file mode 100644 index 0000000..ea7f227 --- /dev/null +++ b/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp @@ -0,0 +1,130 @@ +/* + * 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" + +namespace WebCore { + +RGBA32Buffer::RGBA32Buffer() + : m_status(FrameEmpty) + , m_duration(0) + , m_disposalMethod(DisposeNotSpecified) +{ +} + +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); +} + +void RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other) +{ + if (this == &other) + return; + + m_bitmap.reset(); + const NativeImageSkia& otherBitmap = other.m_bitmap; + 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()) { + // Allocation failure, maybe the bitmap was too big. + setStatus(FrameComplete); + return false; + } + + // Zero the image. + zeroFill(); + + return true; +} + +NativeImagePtr RGBA32Buffer::asNewNativeImage() const +{ + return new NativeImageSkia(m_bitmap); +} + +bool RGBA32Buffer::hasAlpha() const +{ + return !m_bitmap.isOpaque(); +} + +void RGBA32Buffer::setHasAlpha(bool alpha) +{ + m_bitmap.setIsOpaque(!alpha); +} + +void RGBA32Buffer::setStatus(FrameStatus status) +{ + m_status = status; + if (m_status == FrameComplete) + m_bitmap.setDataComplete(); // Tell the bitmap it's done. +} + +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()); + return *this; +} + +int RGBA32Buffer::width() const +{ + return m_bitmap.width(); +} + +int RGBA32Buffer::height() const +{ + return m_bitmap.height(); +} + +} // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/JPEGImageDecoder.cpp b/WebCore/platform/image-decoders/skia/JPEGImageDecoder.cpp deleted file mode 100644 index ab74012..0000000 --- a/WebCore/platform/image-decoders/skia/JPEGImageDecoder.cpp +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2008, 2009 Google, Inc. - * - * Portions are Copyright (C) 2001-6 mozilla.org - * - * Other contributors: - * Stuart Parmenter <stuart@mozilla.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Alternatively, the contents of this file may be used under the terms - * of either the Mozilla Public License Version 1.1, found at - * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public - * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html - * (the "GPL"), in which case the provisions of the MPL or the GPL are - * applicable instead of those above. If you wish to allow use of your - * version of this file only under the terms of one of those two - * licenses (the MPL or the GPL) and not to allow others to use your - * version of this file under the LGPL, indicate your decision by - * deletingthe provisions above and replace them with the notice and - * other provisions required by the MPL or the GPL, as the case may be. - * If you do not delete the provisions above, a recipient may use your - * version of this file under any of the LGPL, the MPL or the GPL. - */ - -#include "config.h" -#include "JPEGImageDecoder.h" -#include <assert.h> - -extern "C" { -#include "jpeglib.h" -} - -#include <setjmp.h> - -namespace WebCore { - -struct decoder_error_mgr { - struct jpeg_error_mgr pub; /* "public" fields for IJG library*/ - jmp_buf setjmp_buffer; /* For handling catastropic errors */ -}; - -enum jstate { - JPEG_HEADER, /* Reading JFIF headers */ - JPEG_START_DECOMPRESS, - JPEG_DECOMPRESS_PROGRESSIVE, /* Output progressive pixels */ - JPEG_DECOMPRESS_SEQUENTIAL, /* Output sequential pixels */ - JPEG_DONE, - JPEG_SINK_NON_JPEG_TRAILER, /* Some image files have a */ - /* non-JPEG trailer */ - JPEG_ERROR -}; - -void init_source(j_decompress_ptr jd); -boolean fill_input_buffer(j_decompress_ptr jd); -void skip_input_data(j_decompress_ptr jd, long num_bytes); -void term_source(j_decompress_ptr jd); -void error_exit(j_common_ptr cinfo); - -/* - * Implementation of a JPEG src object that understands our state machine - */ -struct decoder_source_mgr { - /* public fields; must be first in this struct! */ - struct jpeg_source_mgr pub; - - JPEGImageReader *decoder; -}; - -class JPEGImageReader -{ -public: - JPEGImageReader(JPEGImageDecoder* decoder) - : m_decoder(decoder) - , m_bufferLength(0) - , m_bytesToSkip(0) - , m_state(JPEG_HEADER) - , m_samples(0) - { - memset(&m_info, 0, sizeof(jpeg_decompress_struct)); - - /* We set up the normal JPEG error routines, then override error_exit. */ - m_info.err = jpeg_std_error(&m_err.pub); - m_err.pub.error_exit = error_exit; - - /* Allocate and initialize JPEG decompression object */ - jpeg_create_decompress(&m_info); - - decoder_source_mgr* src = 0; - if (!m_info.src) { - src = (decoder_source_mgr*)fastCalloc(sizeof(decoder_source_mgr), 1); - if (!src) { - m_state = JPEG_ERROR; - return; - } - } - - m_info.src = (jpeg_source_mgr*)src; - - /* Set up callback functions. */ - src->pub.init_source = init_source; - src->pub.fill_input_buffer = fill_input_buffer; - src->pub.skip_input_data = skip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; - src->pub.term_source = term_source; - src->decoder = this; - } - - ~JPEGImageReader() - { - close(); - } - - void close() { - decoder_source_mgr* src = (decoder_source_mgr*)m_info.src; - if (src) - fastFree(src); - m_info.src = 0; - - jpeg_destroy_decompress(&m_info); - } - - void skipBytes(long num_bytes) { - decoder_source_mgr* src = (decoder_source_mgr*)m_info.src; - long bytesToSkip = std::min(num_bytes, (long)src->pub.bytes_in_buffer); - src->pub.bytes_in_buffer -= (size_t)bytesToSkip; - src->pub.next_input_byte += bytesToSkip; - - if (num_bytes > bytesToSkip) - m_bytesToSkip = (size_t)(num_bytes - bytesToSkip); - else - m_bytesToSkip = 0; - } - - bool decode(const Vector<char>& data, bool sizeOnly) { - m_decodingSizeOnly = sizeOnly; - - unsigned newByteCount = data.size() - m_bufferLength; - unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer; - - m_info.src->bytes_in_buffer += newByteCount; - m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset; - - // If we still have bytes to skip, try to skip those now. - if (m_bytesToSkip) - skipBytes(m_bytesToSkip); - - m_bufferLength = data.size(); - - // We need to do the setjmp here. Otherwise bad things will happen - if (setjmp(m_err.setjmp_buffer)) { - m_state = JPEG_SINK_NON_JPEG_TRAILER; - close(); - return false; - } - - switch (m_state) { - case JPEG_HEADER: - { - /* Read file parameters with jpeg_read_header() */ - if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) - return true; /* I/O suspension */ - - /* let libjpeg take care of gray->RGB and YCbCr->RGB conversions */ - switch (m_info.jpeg_color_space) { - case JCS_GRAYSCALE: - case JCS_RGB: - case JCS_YCbCr: - m_info.out_color_space = JCS_RGB; - break; - case JCS_CMYK: - case JCS_YCCK: - default: - m_state = JPEG_ERROR; - return false; - } - - /* - * Don't allocate a giant and superfluous memory buffer - * when the image is a sequential JPEG. - */ - m_info.buffered_image = jpeg_has_multiple_scans(&m_info); - - /* Used to set up image size so arrays can be allocated */ - jpeg_calc_output_dimensions(&m_info); - - /* - * Make a one-row-high sample array that will go away - * when done with image. Always make it big enough to - * hold an RGB row. Since this uses the IJG memory - * manager, it must be allocated before the call to - * jpeg_start_compress(). - */ - int row_stride = m_info.output_width * 4; // RGBA buffer - - - m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, - JPOOL_IMAGE, - row_stride, 1); - - m_state = JPEG_START_DECOMPRESS; - - // We can fill in the size now that the header is available. - if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) { - m_state = JPEG_ERROR; - return false; - } - - if (m_decodingSizeOnly) { - // We can stop here. - // Reduce our buffer length and available data. - m_bufferLength -= m_info.src->bytes_in_buffer; - m_info.src->bytes_in_buffer = 0; - return true; - } - } - - case JPEG_START_DECOMPRESS: - { - /* Set parameters for decompression */ - /* FIXME -- Should reset dct_method and dither mode - * for final pass of progressive JPEG - */ - m_info.dct_method = JDCT_ISLOW; - m_info.dither_mode = JDITHER_FS; - m_info.do_fancy_upsampling = true; - m_info.enable_2pass_quant = false; - m_info.do_block_smoothing = true; - - /* Start decompressor */ - if (!jpeg_start_decompress(&m_info)) - return true; /* I/O suspension */ - - /* If this is a progressive JPEG ... */ - m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; - } - - case JPEG_DECOMPRESS_SEQUENTIAL: - { - if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { - - if (!m_decoder->outputScanlines()) - return true; /* I/O suspension */ - - /* If we've completed image output ... */ - assert(m_info.output_scanline == m_info.output_height); - m_state = JPEG_DONE; - } - } - - case JPEG_DECOMPRESS_PROGRESSIVE: - { - if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { - int status; - do { - status = jpeg_consume_input(&m_info); - } while ((status != JPEG_SUSPENDED) && - (status != JPEG_REACHED_EOI)); - - for (;;) { - if (m_info.output_scanline == 0) { - int scan = m_info.input_scan_number; - - /* if we haven't displayed anything yet (output_scan_number==0) - and we have enough data for a complete scan, force output - of the last full scan */ - if ((m_info.output_scan_number == 0) && - (scan > 1) && - (status != JPEG_REACHED_EOI)) - scan--; - - if (!jpeg_start_output(&m_info, scan)) - return true; /* I/O suspension */ - } - - if (m_info.output_scanline == 0xffffff) - m_info.output_scanline = 0; - - if (!m_decoder->outputScanlines()) { - if (m_info.output_scanline == 0) - /* didn't manage to read any lines - flag so we don't call - jpeg_start_output() multiple times for the same scan */ - m_info.output_scanline = 0xffffff; - return true; /* I/O suspension */ - } - - if (m_info.output_scanline == m_info.output_height) { - if (!jpeg_finish_output(&m_info)) - return true; /* I/O suspension */ - - if (jpeg_input_complete(&m_info) && - (m_info.input_scan_number == m_info.output_scan_number)) - break; - - m_info.output_scanline = 0; - } - } - - m_state = JPEG_DONE; - } - } - - case JPEG_DONE: - { - /* Finish decompression */ - if (!jpeg_finish_decompress(&m_info)) - return true; /* I/O suspension */ - - m_state = JPEG_SINK_NON_JPEG_TRAILER; - - /* we're done */ - break; - } - - case JPEG_SINK_NON_JPEG_TRAILER: - break; - - case JPEG_ERROR: - break; - } - - return true; - } - - jpeg_decompress_struct* info() { return &m_info; } - JSAMPARRAY samples() const { return m_samples; } - JPEGImageDecoder* decoder() { return m_decoder; } - -private: - JPEGImageDecoder* m_decoder; - unsigned m_bufferLength; - int m_bytesToSkip; - bool m_decodingSizeOnly; - bool m_initialized; - - jpeg_decompress_struct m_info; - decoder_error_mgr m_err; - jstate m_state; - - JSAMPARRAY m_samples; -}; - -/* Override the standard error method in the IJG JPEG decoder code. */ -void error_exit(j_common_ptr cinfo) -{ - /* Return control to the setjmp point. */ - decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err; - longjmp(err->setjmp_buffer, -1); -} - -void init_source(j_decompress_ptr jd) -{ -} - -void skip_input_data(j_decompress_ptr jd, long num_bytes) -{ - decoder_source_mgr *src = (decoder_source_mgr *)jd->src; - src->decoder->skipBytes(num_bytes); -} - -boolean fill_input_buffer(j_decompress_ptr jd) -{ - // Our decode step always sets things up properly, so if this method is ever - // called, then we have hit the end of the buffer. A return value of FALSE indicates - // that we have no data to supply yet. - return false; -} - -void term_source (j_decompress_ptr jd) -{ - decoder_source_mgr *src = (decoder_source_mgr *)jd->src; - src->decoder->decoder()->jpegComplete(); -} - -JPEGImageDecoder::JPEGImageDecoder() -: m_reader(0) -{} - -JPEGImageDecoder::~JPEGImageDecoder() -{ - delete m_reader; -} - -// Take the data and store it. -void JPEGImageDecoder::setData(SharedBuffer* data, bool allDataReceived) -{ - if (m_failed) - return; - - // Cache our new data. - ImageDecoder::setData(data, allDataReceived); - - // Create the JPEG reader. - if (!m_reader && !m_failed) - m_reader = new JPEGImageReader(this); -} - -// Whether or not the size information has been decoded yet. -bool JPEGImageDecoder::isSizeAvailable() const -{ - // If we have pending data to decode, send it to the JPEG reader now. - if (!ImageDecoder::isSizeAvailable() && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the - // size is encountered. - decode(true); - } - - return ImageDecoder::isSizeAvailable(); -} - -RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index) -{ - if (index) - return 0; - - if (m_frameBufferCache.isEmpty()) - m_frameBufferCache.resize(1); - - RGBA32Buffer& frame = m_frameBufferCache[0]; - if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - // Decode this frame. - decode(); - return &frame; -} - -// Feed data to the JPEG reader. -void JPEGImageDecoder::decode(bool sizeOnly) const -{ - if (m_failed) - return; - - m_failed = !m_reader->decode(m_data->buffer(), sizeOnly); - - if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) { - delete m_reader; - m_reader = 0; - } -} - -bool JPEGImageDecoder::outputScanlines() -{ - if (m_frameBufferCache.isEmpty()) - return false; - - // Resize to the width and height of the image. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - if (buffer.status() == RGBA32Buffer::FrameEmpty) { - // Let's resize our buffer now to the correct width/height. This will - // also initialize it to transparent. - if (!buffer.setSize(size().width(), size().height())) { - m_failed = true; - buffer.setStatus(RGBA32Buffer::FrameComplete); - return false; - } - - // Update our status to be partially complete. - buffer.setStatus(RGBA32Buffer::FramePartial); - - // For JPEGs, the frame always fills the entire image. - buffer.setRect(IntRect(0, 0, size().width(), size().height())); - - // We don't have alpha (this is the default when the buffer is constructed). - } - - jpeg_decompress_struct* info = m_reader->info(); - JSAMPARRAY samples = m_reader->samples(); - - while (info->output_scanline < info->output_height) { - /* Request one scanline. Returns 0 or 1 scanlines. */ - if (jpeg_read_scanlines(info, samples, 1) != 1) - return false; - JSAMPLE *j1 = samples[0]; - for (unsigned x = 0; x < info->output_width; ++x) { - unsigned r = *j1++; - unsigned g = *j1++; - unsigned b = *j1++; - // read_scanlines has increased the scanline counter, so we - // actually mean the previous one. - buffer.setRGBA(x, info->output_scanline - 1, r, g, b, 0xFF); - } - } - - return true; -} - -void JPEGImageDecoder::jpegComplete() -{ - if (m_frameBufferCache.isEmpty()) - return; - - // Hand back an appropriately sized buffer, even if the image ended up being empty. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - buffer.setStatus(RGBA32Buffer::FrameComplete); -} - -} diff --git a/WebCore/platform/image-decoders/skia/JPEGImageDecoder.h b/WebCore/platform/image-decoders/skia/JPEGImageDecoder.h deleted file mode 100644 index 51dd050..0000000 --- a/WebCore/platform/image-decoders/skia/JPEGImageDecoder.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -#ifndef JPEGImageDecoder_h -#define JPEGImageDecoder_h - -#include "ImageDecoder.h" - -namespace WebCore { - - class JPEGImageReader; - - // This class decodes the JPEG image format. - class JPEGImageDecoder : public ImageDecoder { - public: - JPEGImageDecoder(); - ~JPEGImageDecoder(); - - virtual String filenameExtension() const { return "jpg"; } - - // Take the data and store it. - virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; - - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - - virtual bool supportsAlpha() const { return false; } - - void decode(bool sizeOnly = false) const; - - JPEGImageReader* reader() { return m_reader; } - - bool outputScanlines(); - void jpegComplete(); - - private: - friend class JPEGImageReader; - mutable JPEGImageReader* m_reader; - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/skia/PNGImageDecoder.cpp b/WebCore/platform/image-decoders/skia/PNGImageDecoder.cpp deleted file mode 100644 index ad12b86..0000000 --- a/WebCore/platform/image-decoders/skia/PNGImageDecoder.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. - * Copyright (C) 2008, 2009 Google, Inc. - * - * Portions are Copyright (C) 2001 mozilla.org - * - * Other contributors: - * Stuart Parmenter <stuart@mozilla.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Alternatively, the contents of this file may be used under the terms - * of either the Mozilla Public License Version 1.1, found at - * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public - * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html - * (the "GPL"), in which case the provisions of the MPL or the GPL are - * applicable instead of those above. If you wish to allow use of your - * version of this file only under the terms of one of those two - * licenses (the MPL or the GPL) and not to allow others to use your - * version of this file under the LGPL, indicate your decision by - * deletingthe provisions above and replace them with the notice and - * other provisions required by the MPL or the GPL, as the case may be. - * If you do not delete the provisions above, a recipient may use your - * version of this file under any of the LGPL, the MPL or the GPL. - */ - -#include "config.h" -#include "PNGImageDecoder.h" -#include "png.h" -#include "assert.h" - -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_ptr, png_const_charp error_msg); - -// Callbacks given to the read struct. The first is for warnings (we want to treat a particular warning -// as an error, which is why we have to register this callback. -static void PNGAPI decodingWarning(png_structp png_ptr, png_const_charp warning_msg); - -// Called when we have obtained the header information (including the size). -static void PNGAPI headerAvailable(png_structp png_ptr, png_infop info_ptr); - -// Called when a row is ready. -static void PNGAPI rowAvailable(png_structp png_ptr, png_bytep new_row, - png_uint_32 row_num, int pass); - -// Called when we have completely finished decoding the image. -static void PNGAPI pngComplete(png_structp png_ptr, png_infop info_ptr); - -class PNGImageReader -{ -public: - PNGImageReader(PNGImageDecoder* decoder) - : m_readOffset(0) - , m_decodingSizeOnly(false) - , m_interlaceBuffer(0) - , m_hasAlpha(0) - { - m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 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) - png_destroy_read_struct(&m_png, &m_info, 0); // Will zero the pointers. - delete []m_interlaceBuffer; - m_interlaceBuffer = 0; - m_readOffset = 0; - } - - void decode(const Vector<char>& data, bool sizeOnly) - { - m_decodingSizeOnly = sizeOnly; - - // We need to do the setjmp here. Otherwise bad things will happen - if (setjmp(m_png->jmpbuf)) { - close(); - return; - } - - // Go ahead and assume we consumed all the data. If we consume less, the - // callback will adjust our read offset accordingly. Do not attempt to adjust the - // offset after png_process_data returns. - unsigned offset = m_readOffset; - unsigned remaining = data.size() - m_readOffset; - m_readOffset = data.size(); - png_process_data(m_png, m_info, (png_bytep)(data.data()) + offset, remaining); - } - - bool decodingSizeOnly() const { return m_decodingSizeOnly; } - png_structp pngPtr() const { return m_png; } - png_infop infoPtr() const { return m_info; } - png_bytep interlaceBuffer() const { return m_interlaceBuffer; } - bool hasAlpha() const { return m_hasAlpha; } - - void setReadOffset(unsigned offset) { m_readOffset = offset; } - void setHasAlpha(bool b) { m_hasAlpha = b; } - - void createInterlaceBuffer(int size) { - m_interlaceBuffer = new png_byte[size]; - } - -private: - unsigned m_readOffset; - bool m_decodingSizeOnly; - png_structp m_png; - png_infop m_info; - png_bytep m_interlaceBuffer; - bool m_hasAlpha; -}; - -PNGImageDecoder::PNGImageDecoder() - : m_reader(0) -{ -} - -PNGImageDecoder::~PNGImageDecoder() -{ - delete m_reader; -} - -// Take the data and store it. -void PNGImageDecoder::setData(SharedBuffer* data, bool allDataReceived) -{ - if (m_failed) - return; - - // Cache our new data. - ImageDecoder::setData(data, allDataReceived); - - // Create the PNG reader. - if (!m_reader && !m_failed) - m_reader = new PNGImageReader(this); -} - -// Whether or not the size information has been decoded yet. -bool PNGImageDecoder::isSizeAvailable() const -{ - // If we have pending data to decode, send it to the PNG reader now. - if (!ImageDecoder::isSizeAvailable() && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the - // size is encountered. - decode(true); - } - - return ImageDecoder::isSizeAvailable(); -} - -RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index) -{ - if (index) - return 0; - - if (m_frameBufferCache.isEmpty()) - m_frameBufferCache.resize(1); - - RGBA32Buffer& frame = m_frameBufferCache[0]; - if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - // Decode this frame. - decode(); - return &frame; -} - -// Feed data to the PNG reader. -void PNGImageDecoder::decode(bool sizeOnly) const -{ - if (m_failed) - return; - - m_reader->decode(m_data->buffer(), sizeOnly); - - if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) { - delete m_reader; - m_reader = 0; - } -} - -void decodingFailed(png_structp png, png_const_charp errorMsg) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed(); - longjmp(png->jmpbuf, 1); -} - -void decodingWarning(png_structp png, png_const_charp warningMsg) -{ - // Mozilla did this, so we will too. - // Convert a tRNS warning to be an error (documented in bugzilla.mozilla.org bug #251381) - if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24)) - png_error(png, warningMsg); -} - -void headerAvailable(png_structp png, png_infop info) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); -} - -void PNGImageDecoder::decodingFailed() { - m_failed = true; -} - -void PNGImageDecoder::headerAvailable() -{ - png_structp png = reader()->pngPtr(); - png_infop info = reader()->infoPtr(); - png_uint_32 width = png->width; - png_uint_32 height = png->height; - - // Protect against large images. - if (png->width > cMaxPNGSize || png->height > cMaxPNGSize) { - m_failed = true; - longjmp(png->jmpbuf, 1); - return; - } - - // We can fill in the size now that the header is available. - if (!ImageDecoder::isSizeAvailable()) { - if (!setSize(width, height)) { - // Size unreasonable, bail out. - longjmp(png->jmpbuf, 1); - return; - } - } - - int bitDepth, colorType, interlaceType, compressionType, filterType, channels; - png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, - &interlaceType, &compressionType, &filterType); - - // The options we set here match what Mozilla does. - - // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. - if (colorType == PNG_COLOR_TYPE_PALETTE || - (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) - png_set_expand(png); - - png_bytep trns = 0; - int trnsCount = 0; - if (png_get_valid(png, info, PNG_INFO_tRNS)) { - png_get_tRNS(png, info, &trns, &trnsCount, 0); - png_set_expand(png); - } - - if (bitDepth == 16) - png_set_strip_16(png); - - if (colorType == PNG_COLOR_TYPE_GRAY || - colorType == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_gray_to_rgb(png); - - // Deal with gamma and keep it under our control. - double gamma; - if (png_get_gAMA(png, info, &gamma)) { - if ((gamma <= 0.0) || (gamma > cMaxGamma)) { - gamma = cInverseGamma; - png_set_gAMA(png, info, gamma); - } - png_set_gamma(png, cDefaultGamma, gamma); - } - else - png_set_gamma(png, cDefaultGamma, cInverseGamma); - - // Tell libpng to send us rows for interlaced pngs. - if (interlaceType == PNG_INTERLACE_ADAM7) - png_set_interlace_handling(png); - - // Update our info now - png_read_update_info(png, info); - channels = png_get_channels(png, info); - assert(channels == 3 || channels == 4); - - reader()->setHasAlpha(channels == 4); - - if (reader()->decodingSizeOnly()) { - // If we only needed the size, halt the reader. - reader()->setReadOffset(m_data->size() - png->buffer_size); - png->buffer_size = 0; - } -} - -void rowAvailable(png_structp png, png_bytep rowBuffer, - png_uint_32 rowIndex, int interlacePass) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); -} - -void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) -{ - if (m_frameBufferCache.isEmpty()) - return; - - // Resize to the width and height of the image. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - if (buffer.status() == RGBA32Buffer::FrameEmpty) { - // Let's resize our buffer now to the correct width/height. - if (!buffer.setSize(size().width(), size().height())) { - // Error allocating the bitmap. We should not continue. - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(reader()->pngPtr()))->decodingFailed(); - longjmp(reader()->pngPtr()->jmpbuf, 1); - return; - } - - // Update our status to be partially complete. - buffer.setStatus(RGBA32Buffer::FramePartial); - - // For PNGs, the frame always fills the entire image. - buffer.setRect(IntRect(0, 0, size().width(), size().height())); - - if (reader()->pngPtr()->interlaced) - reader()->createInterlaceBuffer((reader()->hasAlpha() ? 4 : 3) * size().width() * size().height()); - } - - if (rowBuffer == 0) - return; - - /* libpng comments (pasted in here to explain what follows) - * - * this function is called for every row in the image. If the - * image is interlacing, and you turned on the interlace handler, - * this function will be called for every row in every pass. - * Some of these rows will not be changed from the previous pass. - * When the row is not changed, the new_row variable will be NULL. - * The rows and passes are called in order, so you don't really - * need the row_num and pass, but I'm supplying them because it - * may make your life easier. - * - * For the non-NULL rows of interlaced images, you must call - * png_progressive_combine_row() passing in the row and the - * old row. You can call this function for NULL rows (it will - * just return) and for non-interlaced images (it just does the - * memcpy for you) if it will make the code easier. Thus, you - * can just do this for all cases: - * - * png_progressive_combine_row(png_ptr, old_row, new_row); - * - * where old_row is what was displayed for previous rows. Note - * that the first pass (pass == 0 really) will completely cover - * the old row, so the rows do not have to be initialized. After - * the first pass (and only for interlaced images), you will have - * to pass the current row, and the function will combine the - * old row and the new row. - */ - - png_structp png = reader()->pngPtr(); - bool hasAlpha = reader()->hasAlpha(); - unsigned colorChannels = hasAlpha ? 4 : 3; - png_bytep row; - png_bytep interlaceBuffer = reader()->interlaceBuffer(); - if (interlaceBuffer) { - row = interlaceBuffer + (rowIndex * colorChannels * size().width()); - png_progressive_combine_row(png, row, rowBuffer); - } - else - row = rowBuffer; - - // Copy the data into our buffer. - int width = size().width(); - bool sawAlpha = false; - for (int x = 0; x < width; x++) { - unsigned red = *row++; - unsigned green = *row++; - unsigned blue = *row++; - unsigned alpha = (hasAlpha ? *row++ : 255); - buffer.setRGBA(x, rowIndex, red, green, blue, alpha); - if (!sawAlpha && alpha < 255) { - sawAlpha = true; - buffer.setHasAlpha(true); - } - } -} - -void pngComplete(png_structp png, png_infop info) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); -} - -void PNGImageDecoder::pngComplete() -{ - if (m_frameBufferCache.isEmpty()) - return; - - // Hand back an appropriately sized buffer, even if the image ended up being empty. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - buffer.setStatus(RGBA32Buffer::FrameComplete); -} - -} // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/PNGImageDecoder.h b/WebCore/platform/image-decoders/skia/PNGImageDecoder.h deleted file mode 100644 index 83cf343..0000000 --- a/WebCore/platform/image-decoders/skia/PNGImageDecoder.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -#ifndef PNGImageDecoder_h -#define PNGImageDecoder_h - -#include "ImageDecoder.h" - -namespace WebCore { - - class PNGImageReader; - - // This class decodes the PNG image format. - class PNGImageDecoder : public ImageDecoder { - public: - PNGImageDecoder(); - ~PNGImageDecoder(); - - virtual String filenameExtension() const { return "png"; } - - // Take the data and store it. - virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; - - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - - void decode(bool sizeOnly = false) const; - - PNGImageReader* reader() { return m_reader; } - - // Callbacks from libpng - void decodingFailed(); - void headerAvailable(); - void rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass); - void pngComplete(); - - private: - mutable PNGImageReader* m_reader; - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/skia/XBMImageDecoder.cpp b/WebCore/platform/image-decoders/skia/XBMImageDecoder.cpp deleted file mode 100644 index 8908504..0000000 --- a/WebCore/platform/image-decoders/skia/XBMImageDecoder.cpp +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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 "XBMImageDecoder.h" - -#include <algorithm> - -namespace WebCore { - -XBMImageDecoder::XBMImageDecoder() - : m_decodeOffset(0) - , m_allDataReceived(false) - , m_decodedHeader(false) - , m_dataType(Unknown) - , m_bitsDecoded(0) -{ -} - -void XBMImageDecoder::setData(SharedBuffer* data, bool allDataReceived) -{ - ImageDecoder::setData(data, allDataReceived); - - const Vector<char>& buf = m_data->buffer(); - if (buf.size() > m_xbmString.size()) - m_xbmString.append(&buf[m_xbmString.size()], buf.size() - m_xbmString.size()); - - m_allDataReceived = allDataReceived; -} - -bool XBMImageDecoder::isSizeAvailable() const -{ - // This method should either (a) not be const, or (b) not be expected to - // do anything that changes m_sizeAvailable. The png and jpeg decoders - // get around this with callbacks from external libraries. - // - // FIXME: Find out if we can patch webkit to take care of this. - if (!ImageDecoder::isSizeAvailable() && !m_failed) - const_cast<XBMImageDecoder*>(this)->decodeXBM(true); - - return !m_failed && ImageDecoder::isSizeAvailable(); -} - -RGBA32Buffer* XBMImageDecoder::frameBufferAtIndex(size_t index) -{ - // Allocate a framebuffer if necessary. New framebuffers have their status - // initialized to RGBA32Buffer::FrameEmpty. - if (m_frameBufferCache.isEmpty()) - m_frameBufferCache.resize(1); - - RGBA32Buffer& frame = m_frameBufferCache[0]; - - // Attempt to get the size if we don't have it yet. - if (!ImageDecoder::isSizeAvailable()) - decodeXBM(true); - - // Size the framebuffer once we know the right size. - if (ImageDecoder::isSizeAvailable() && - frame.status() == RGBA32Buffer::FrameEmpty) { - if (!frame.setSize(size().width(), size().height())) { - m_failed = true; - frame.setStatus(RGBA32Buffer::FrameComplete); - return 0; - } - frame.setStatus(RGBA32Buffer::FramePartial); - } - - // Keep trying to decode until we've got the entire image. - if (frame.status() != RGBA32Buffer::FrameComplete) - decodeXBM(false); - - return &frame; -} - -bool XBMImageDecoder::decodeHeader() -{ - ASSERT(m_decodeOffset <= m_xbmString.size()); - ASSERT(!m_decodedHeader); - - const char* input = m_xbmString.c_str(); - - // At least 2 "#define <string> <unsigned>" sequences are required. These - // specify the width and height of the image. - int width, height; - if (!ImageDecoder::isSizeAvailable()) { - int count; - if (sscanf(&input[m_decodeOffset], "#define %*s %i #define %*s %i%n", - &width, &height, &count) != 2) - return false; - - // The width and height need to follow some rules. - if (width < 0 || width > maxDimension || height < 0 || height > maxDimension) { - // If this happens, decoding should not continue. - setFailed(); - return false; - } - - if (!setSize(width, height)) { - setFailed(); - return false; - } - m_decodeOffset += count; - ASSERT(m_decodeOffset <= m_xbmString.size()); - } - - ASSERT(ImageDecoder::isSizeAvailable()); - - // Now we're looking for something that tells us that we've seen all of the - // "#define <string> <unsigned>" sequences that we're going to. Mozilla - // just looks for " char " or " short ". We'll do the same. - if (m_dataType == Unknown) { - const char* x11hint = " char "; - const char* x11HintLocation = strstr(&input[m_decodeOffset], x11hint); - if (x11HintLocation) { - m_dataType = X11; - m_decodeOffset += ((x11HintLocation - &input[m_decodeOffset]) + strlen(x11hint)); - } else { - const char* x10hint = " short "; - const char* x10HintLocation = strstr(&input[m_decodeOffset], x10hint); - if (x10HintLocation) { - m_dataType = X10; - m_decodeOffset += ((x10HintLocation - &input[m_decodeOffset]) + strlen(x10hint)); - } else - return false; - } - ASSERT(m_decodeOffset <= m_xbmString.size()); - } - - // Find the start of the data. Again, we do what mozilla does and just - // look for a '{' in the input. - const char* found = strchr(&input[m_decodeOffset], '{'); - if (!found) - return false; - - // Advance to character after the '{' - m_decodeOffset += ((found - &input[m_decodeOffset]) + 1); - ASSERT(m_decodeOffset <= m_xbmString.size()); - m_decodedHeader = true; - - return true; -} - -// The data in an XBM file is provided as an array of either "char" or "short" -// values. These values are decoded one at a time using strtoul() and the bits -// are used to set the alpha value for the image. -// -// The value for the color is always set to RGB(0,0,0), the alpha value takes -// care of whether or not the pixel shows up. -// -// Since the data may arrive in chunks, and many prefixes of valid numbers are -// themselves valid numbers, this code needs to check to make sure that the -// value is not truncated. This is done by consuming space after the value -// read until a ',' or a '}' occurs. In a valid XBM, one of these characters -// will occur after each value. -// -// The checks after strtoul are based on Mozilla's nsXBMDecoder.cpp. -bool XBMImageDecoder::decodeDatum(uint16_t* result) -{ - const char* input = m_xbmString.c_str(); - char* endPtr; - const uint16_t value = strtoul(&input[m_decodeOffset], &endPtr, 0); - - // End of input or couldn't decode anything, can't go any further. - if (endPtr == &input[m_decodeOffset] || !*endPtr) - return false; - - // Possibly a hex value truncated at "0x". Need more data. - if (value == 0 && (*endPtr == 'x' || *endPtr == 'X')) - return false; - - // Skip whitespace - while (*endPtr && isspace(*endPtr)) - ++endPtr; - - // Out of input, don't know what comes next. - if (!*endPtr) - return false; - - // If the next non-whitespace character is not one of these, it's an error. - // Every valid entry in the data array needs to be followed by ',' or '}'. - if (*endPtr != ',' && *endPtr != '}') { - setFailed(); - return false; - } - - // At this point we have a value. - *result = value; - - // Skip over the decoded value plus the delimiter (',' or '}'). - m_decodeOffset += ((endPtr - &input[m_decodeOffset]) + 1); - ASSERT(m_decodeOffset <= m_xbmString.size()); - - return true; -} - -bool XBMImageDecoder::decodeData() -{ - ASSERT(m_decodeOffset <= m_xbmString.size()); - ASSERT(m_decodedHeader && !m_frameBufferCache.isEmpty()); - - RGBA32Buffer& frame = m_frameBufferCache[0]; - - ASSERT(frame.status() == RGBA32Buffer::FramePartial); - - const int bitsPerRow = size().width(); - - ASSERT(m_dataType != Unknown); - - while (m_bitsDecoded < (size().width() * size().height())) { - uint16_t value; - if (!decodeDatum(&value)) - return false; - - int x = m_bitsDecoded % bitsPerRow; - const int y = m_bitsDecoded / bitsPerRow; - - // How many bits will be written? - const int bits = std::min(bitsPerRow - x, (m_dataType == X11) ? 8 : 16); - - // Only the alpha channel matters here, so the color values are always - // set to 0. - for (int i = 0; i < bits; ++i) - frame.setRGBA(x++, y, 0, 0, 0, value & (1 << i) ? 255 : 0); - - m_bitsDecoded += bits; - } - - frame.setStatus(RGBA32Buffer::FrameComplete); - - return true; -} - -// Decode as much as we can of the XBM file. -void XBMImageDecoder::decodeXBM(bool sizeOnly) -{ - if (failed()) - return; - - bool decodeResult = false; - - if (!m_decodedHeader) - decodeResult = decodeHeader(); - - if (m_decodedHeader && !sizeOnly) - decodeResult = decodeData(); - - // The header or the data could not be decoded, but there is no more - // data: decoding has failed. - if (!decodeResult && m_allDataReceived) - setFailed(); -} - -} // namespace WebCore diff --git a/WebCore/platform/image-decoders/skia/XBMImageDecoder.h b/WebCore/platform/image-decoders/skia/XBMImageDecoder.h deleted file mode 100644 index cdcf8e6..0000000 --- a/WebCore/platform/image-decoders/skia/XBMImageDecoder.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 XBMImageDecoder_h -#define XBMImageDecoder_h - -#include <string> -#include "ImageDecoder.h" - -namespace WebCore { - - // This class decodes the XBM image format. - class XBMImageDecoder : public ImageDecoder { - public: - XBMImageDecoder(); - virtual ~XBMImageDecoder() {} - - virtual String filenameExtension() const { return "xbm"; } - - virtual void setData(SharedBuffer* data, bool allDataReceived); - // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - - private: - // Restricts image size to something "reasonable". - // This protects agains ridiculously large XBMs and prevents bad things - // like overflow of m_bitsDecoded. - static const int maxDimension = 65535; - - // In X10, an array of type "short" is used to declare the image bits, - // but in X11, the type is "char". - enum DataType { - Unknown, - X10, - X11, - }; - - bool decodeHeader(); - bool decodeDatum(uint16_t* result); - bool decodeData(); - void decodeXBM(bool sizeOnly); - - std::string m_xbmString; // Null-terminated copy of the XBM data. - size_t m_decodeOffset; // The current offset in m_xbmString for decoding. - bool m_allDataReceived; - bool m_decodedHeader; - enum DataType m_dataType; - int m_bitsDecoded; - }; - -} // namespace WebCore - -#endif diff --git a/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp b/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp new file mode 100644 index 0000000..8e8809e --- /dev/null +++ b/WebCore/platform/image-decoders/wx/ImageDecoderWx.cpp @@ -0,0 +1,172 @@ +/* + * 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 { + +RGBA32Buffer::RGBA32Buffer() + : m_hasAlpha(false) + , m_status(FrameEmpty) + , m_duration(0) + , m_disposalMethod(DisposeNotSpecified) +{ +} + +void RGBA32Buffer::clear() +{ + m_bytes.clear(); + 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_bytes.fill(0); + m_hasAlpha = true; +} + +void RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other) +{ + if (this == &other) + return; + + m_bytes = other.m_bytes; + m_size = other.m_size; + setHasAlpha(other.m_hasAlpha); +} + +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_bytes.resize(newWidth * newHeight); + m_size = IntSize(newWidth, newHeight); + + // Zero the image. + zeroFill(); + + return true; +} + +NativeImagePtr RGBA32Buffer::asNewNativeImage() const +{ + const unsigned char* bytes = (const unsigned char*)m_bytes.data(); + + typedef wxPixelData<wxBitmap, wxAlphaPixelFormat> WxPixelData; + + wxBitmap* bmp = new wxBitmap(width(), height(), 32); + WxPixelData data(*bmp); + + int rowCounter = 0; + long pixelCounter = 0; + + WxPixelData::Iterator p(data); + + WxPixelData::Iterator rowStart = p; + + // 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. + for (long i = 0; i < m_bytes.size() * 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 + ASSERT(bmp->IsOk()); + +#if USE(WXGC) + wxGraphicsBitmap* bitmap = new wxGraphicsBitmap(wxGraphicsRenderer::GetDefaultRenderer()->CreateBitmap(*bmp)); + delete bmp; + return bitmap; +#else + return bmp; +#endif +} + +bool RGBA32Buffer::hasAlpha() const +{ + return m_hasAlpha; +} + +void RGBA32Buffer::setHasAlpha(bool alpha) +{ + m_hasAlpha = alpha; +} + +void RGBA32Buffer::setStatus(FrameStatus status) +{ + m_status = status; +} + +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; +} + +int RGBA32Buffer::width() const { + return m_size.width(); +} + +int RGBA32Buffer::height() const { + return m_size.height(); +} + +} // namespace WebCore diff --git a/WebCore/platform/image-decoders/xbm/XBMImageDecoder.cpp b/WebCore/platform/image-decoders/xbm/XBMImageDecoder.cpp index 3e8ae57..332bc72 100644 --- a/WebCore/platform/image-decoders/xbm/XBMImageDecoder.cpp +++ b/WebCore/platform/image-decoders/xbm/XBMImageDecoder.cpp @@ -1,42 +1,278 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * + * 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: - * 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 + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #include "config.h" #include "XBMImageDecoder.h" - -namespace WebCore + +#include "ASCIICType.h" + +#include <algorithm> +#include <cstdio> + +namespace WebCore { + +XBMImageDecoder::XBMImageDecoder() + : m_decodeOffset(0) + , m_allDataReceived(false) + , m_decodedHeader(false) + , m_dataType(Unknown) + , m_bitsDecoded(0) { +} -bool XBMImageDecoder::isSizeAvailable() const +void XBMImageDecoder::setData(SharedBuffer* data, bool allDataReceived) { - return false; + ImageDecoder::setData(data, allDataReceived); + m_xbmString = data->buffer(); + m_xbmString.append('\0'); + + m_allDataReceived = allDataReceived; } - + +bool XBMImageDecoder::isSizeAvailable() +{ + if (!ImageDecoder::isSizeAvailable() && !m_failed) + decode(true); + + return ImageDecoder::isSizeAvailable(); +} + RGBA32Buffer* XBMImageDecoder::frameBufferAtIndex(size_t index) { - return 0; + if (index) + return 0; + + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.resize(1); + + // Attempt to get the size if we don't have it yet. + if (!ImageDecoder::isSizeAvailable()) + decode(true); + + // Initialize the framebuffer if needed. + RGBA32Buffer& buffer = m_frameBufferCache[0]; + if (!failed() && ImageDecoder::isSizeAvailable() + && (buffer.status() == RGBA32Buffer::FrameEmpty)) { + if (!buffer.setSize(size().width(), size().height())) { + m_failed = true; + return 0; + } + buffer.setStatus(RGBA32Buffer::FramePartial); + + // For XBMs, the frame always fills the entire image. + buffer.setRect(IntRect(IntPoint(), size())); + } + + // Keep trying to decode until we've got the entire image. + if (buffer.status() == RGBA32Buffer::FramePartial) + decode(false); + + return &buffer; +} + +bool XBMImageDecoder::decodeHeader() +{ + ASSERT(m_decodeOffset <= m_xbmString.size()); + ASSERT(!m_decodedHeader); + + const char* input = m_xbmString.data(); + + // At least 2 "#define <string> <unsigned>" sequences are required. These + // specify the width and height of the image. + int width, height; + if (!ImageDecoder::isSizeAvailable()) { + int count; + if (sscanf(&input[m_decodeOffset], "#define %*s %i #define %*s %i%n", + &width, &height, &count) != 2) + return false; + + // The width and height need to follow some rules. + if (width < 0 || width > maxDimension || height < 0 || height > maxDimension) { + // If this happens, decoding should not continue. + setFailed(); + return false; + } + + if (!setSize(width, height)) { + setFailed(); + return false; + } + m_decodeOffset += count; + ASSERT(m_decodeOffset <= m_xbmString.size()); + } + + ASSERT(ImageDecoder::isSizeAvailable()); + + // Now we're looking for something that tells us that we've seen all of the + // "#define <string> <unsigned>" sequences that we're going to. Mozilla + // just looks for " char " or " short ". We'll do the same. + if (m_dataType == Unknown) { + const char* x11hint = " char "; + const char* x11HintLocation = strstr(&input[m_decodeOffset], x11hint); + if (x11HintLocation) { + m_dataType = X11; + m_decodeOffset += ((x11HintLocation - &input[m_decodeOffset]) + strlen(x11hint)); + } else { + const char* x10hint = " short "; + const char* x10HintLocation = strstr(&input[m_decodeOffset], x10hint); + if (x10HintLocation) { + m_dataType = X10; + m_decodeOffset += ((x10HintLocation - &input[m_decodeOffset]) + strlen(x10hint)); + } else + return false; + } + ASSERT(m_decodeOffset <= m_xbmString.size()); + } + + // Find the start of the data. Again, we do what mozilla does and just + // look for a '{' in the input. + const char* found = strchr(&input[m_decodeOffset], '{'); + if (!found) + return false; + + // Advance to character after the '{' + m_decodeOffset += ((found - &input[m_decodeOffset]) + 1); + ASSERT(m_decodeOffset <= m_xbmString.size()); + m_decodedHeader = true; + + return true; +} + +// The data in an XBM file is provided as an array of either "char" or "short" +// values. These values are decoded one at a time using strtoul() and the bits +// are used to set the alpha value for the image. +// +// The value for the color is always set to RGB(0,0,0), the alpha value takes +// care of whether or not the pixel shows up. +// +// Since the data may arrive in chunks, and many prefixes of valid numbers are +// themselves valid numbers, this code needs to check to make sure that the +// value is not truncated. This is done by consuming space after the value +// read until a ',' or a '}' occurs. In a valid XBM, one of these characters +// will occur after each value. +// +// The checks after strtoul are based on Mozilla's nsXBMDecoder.cpp. +bool XBMImageDecoder::decodeDatum(uint16_t* result) +{ + const char* input = m_xbmString.data(); + char* endPtr; + const uint16_t value = strtoul(&input[m_decodeOffset], &endPtr, 0); + + // End of input or couldn't decode anything, can't go any further. + if (endPtr == &input[m_decodeOffset] || !*endPtr) + return false; + + // Possibly a hex value truncated at "0x". Need more data. + if (value == 0 && (*endPtr == 'x' || *endPtr == 'X')) + return false; + + // Skip whitespace + while (*endPtr && isASCIISpace(*endPtr)) + ++endPtr; + + // Out of input, don't know what comes next. + if (!*endPtr) + return false; + + // If the next non-whitespace character is not one of these, it's an error. + // Every valid entry in the data array needs to be followed by ',' or '}'. + if (*endPtr != ',' && *endPtr != '}') { + setFailed(); + return false; + } + + // At this point we have a value. + *result = value; + + // Skip over the decoded value plus the delimiter (',' or '}'). + m_decodeOffset += ((endPtr - &input[m_decodeOffset]) + 1); + ASSERT(m_decodeOffset <= m_xbmString.size()); + + return true; +} + +bool XBMImageDecoder::decodeData() +{ + ASSERT(m_decodeOffset <= m_xbmString.size()); + ASSERT(m_decodedHeader && !m_frameBufferCache.isEmpty()); + + RGBA32Buffer& frame = m_frameBufferCache[0]; + + ASSERT(frame.status() == RGBA32Buffer::FramePartial); + + const int bitsPerRow = size().width(); + + ASSERT(m_dataType != Unknown); + + while (m_bitsDecoded < (size().width() * size().height())) { + uint16_t value; + if (!decodeDatum(&value)) + return false; + + int x = m_bitsDecoded % bitsPerRow; + const int y = m_bitsDecoded / bitsPerRow; + + // How many bits will be written? + const int bits = std::min(bitsPerRow - x, (m_dataType == X11) ? 8 : 16); + + // Only the alpha channel matters here, so the color values are always + // set to 0. + for (int i = 0; i < bits; ++i) + frame.setRGBA(x++, y, 0, 0, 0, value & (1 << i) ? 255 : 0); + + m_bitsDecoded += bits; + } + + frame.setStatus(RGBA32Buffer::FrameComplete); + + return true; +} + +// Decode as much as we can of the XBM file. +void XBMImageDecoder::decode(bool sizeOnly) +{ + if (failed()) + return; + + bool decodeResult = false; + + if (!m_decodedHeader) + decodeResult = decodeHeader(); + + if (m_decodedHeader && !sizeOnly) + decodeResult = decodeData(); + + // The header or the data could not be decoded, but there is no more + // data: decoding has failed. + if (!decodeResult && m_allDataReceived) + setFailed(); } } // namespace WebCore diff --git a/WebCore/platform/image-decoders/xbm/XBMImageDecoder.h b/WebCore/platform/image-decoders/xbm/XBMImageDecoder.h index 80cfb3f..00f62ce 100644 --- a/WebCore/platform/image-decoders/xbm/XBMImageDecoder.h +++ b/WebCore/platform/image-decoders/xbm/XBMImageDecoder.h @@ -1,26 +1,31 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * + * 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: - * 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 + * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef XBMImageDecoder_h @@ -30,17 +35,47 @@ namespace WebCore { - class XBMImageReader; - // This class decodes the XBM image format. class XBMImageDecoder : public ImageDecoder { public: + XBMImageDecoder(); + virtual ~XBMImageDecoder() {} + virtual String filenameExtension() const { return "xbm"; } + virtual void setData(SharedBuffer* data, bool allDataReceived); // Whether or not the size information has been decoded yet. - virtual bool isSizeAvailable() const; - + virtual bool isSizeAvailable(); virtual RGBA32Buffer* frameBufferAtIndex(size_t index); + + private: + // Restricts image size to something "reasonable". + // This protects agains ridiculously large XBMs and prevents bad things + // like overflow of m_bitsDecoded. + static const int maxDimension = 65535; + + // In X10, an array of type "short" is used to declare the image bits, + // but in X11, the type is "char". + enum DataType { + Unknown, + X10, + X11, + }; + + bool decodeHeader(); + bool decodeDatum(uint16_t* result); + bool decodeData(); + void decode(bool sizeOnly); + + // FIXME: Copying all the XBM data just so we can NULL-terminate, just + // so we can use sscanf() and friends, is lame. The decoder should be + // rewritten to operate on m_data directly. + Vector<char> m_xbmString; // Null-terminated copy of the XBM data. + size_t m_decodeOffset; // The current offset in m_xbmString for decoding. + bool m_allDataReceived; + bool m_decodedHeader; + enum DataType m_dataType; + int m_bitsDecoded; }; } // namespace WebCore |