diff options
Diffstat (limited to 'WebCore/platform/image-decoders')
18 files changed, 623 insertions, 847 deletions
diff --git a/WebCore/platform/image-decoders/ImageDecoder.cpp b/WebCore/platform/image-decoders/ImageDecoder.cpp index ed13048..86bcb45 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.cpp +++ b/WebCore/platform/image-decoders/ImageDecoder.cpp @@ -58,30 +58,24 @@ static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const // TODO: Find a better fix. ImageDecoder* ImageDecoder::create(const SharedBuffer& data) { - // We need at least 4 bytes to figure out what kind of image we're dealing with. + // We need at least 4 bytes to figure out what kind of image we're dealing + // with. static const unsigned maxMarkerLength = 4; char contents[maxMarkerLength]; unsigned length = copyFromSharedBuffer(contents, maxMarkerLength, data, 0); if (length < maxMarkerLength) return 0; - const unsigned char* uContents = reinterpret_cast<const unsigned char*>(contents); - // GIFs begin with GIF8(7 or 9). if (strncmp(contents, "GIF8", 4) == 0) return new GIFImageDecoder(); // Test for PNG. - if (uContents[0]==0x89 && - uContents[1]==0x50 && - uContents[2]==0x4E && - uContents[3]==0x47) + if (!memcmp(contents, "\x89\x50\x4E\x47", 4)) return new PNGImageDecoder(); // JPEG - if (uContents[0]==0xFF && - uContents[1]==0xD8 && - uContents[2]==0xFF) + if (!memcmp(contents, "\xFF\xD8\xFF", 3)) return new JPEGImageDecoder(); // BMP @@ -90,8 +84,7 @@ ImageDecoder* ImageDecoder::create(const SharedBuffer& data) // ICOs always begin with a 2-byte 0 followed by a 2-byte 1. // CURs begin with 2-byte 0 followed by 2-byte 2. - if (!memcmp(contents, "\000\000\001\000", 4) || - !memcmp(contents, "\000\000\002\000", 4)) + if (!memcmp(contents, "\x00\x00\x01\x00", 4) || !memcmp(contents, "\x00\x00\x02\x00", 4)) return new ICOImageDecoder(); // Give up. We don't know what the heck this is. @@ -109,14 +102,27 @@ RGBA32Buffer::RGBA32Buffer() { } +RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) +{ + if (this == &other) + return *this; + + copyBitmapData(other); + setRect(other.rect()); + setStatus(other.status()); + setDuration(other.duration()); + setDisposalMethod(other.disposalMethod()); + return *this; +} + void RGBA32Buffer::clear() { m_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. + // 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() @@ -137,8 +143,8 @@ void RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other) bool RGBA32Buffer::setSize(int newWidth, int newHeight) { - // NOTE: This has no way to check for allocation failure if the - // requested size was too big... + // 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); @@ -163,19 +169,6 @@ 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(); @@ -200,13 +193,11 @@ inline void fillScaledValues(Vector<int>& scaledValues, double scaleRate, int le { double inflateRate = 1. / scaleRate; scaledValues.reserveCapacity(static_cast<int>(length * scaleRate + 0.5)); - for (int scaledIndex = 0;;) { + for (int scaledIndex = 0; ; ++scaledIndex) { int index = static_cast<int>(scaledIndex * inflateRate + 0.5); - if (index < length) { - scaledValues.append(index); - ++scaledIndex; - } else + if (index >= length) break; + scaledValues.append(index); } } diff --git a/WebCore/platform/image-decoders/ImageDecoder.h b/WebCore/platform/image-decoders/ImageDecoder.h index 8d27072..002395b 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.h +++ b/WebCore/platform/image-decoders/ImageDecoder.h @@ -46,18 +46,21 @@ 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. + // The RGBA32Buffer object represents the decoded image data in RGBA32 + // format. This buffer is what all decoders write a single frame into. + // Frames are then instantiated for drawing by being handed this buffer. class RGBA32Buffer { public: enum FrameStatus { FrameEmpty, FramePartial, FrameComplete }; enum FrameDisposalMethod { - // If you change the numeric values of these, make sure you audit all - // users, as some users may cast raw values to/from these constants. - DisposeNotSpecified, // Leave frame in framebuffer - DisposeKeep, // Leave frame in framebuffer - DisposeOverwriteBgcolor, // Clear frame to transparent - DisposeOverwritePrevious, // Clear frame to previous framebuffer contents + // If you change the numeric values of these, make sure you audit + // all users, as some users may cast raw values to/from these + // constants. + DisposeNotSpecified, // Leave frame in framebuffer + DisposeKeep, // Leave frame in framebuffer + DisposeOverwriteBgcolor, // Clear frame to transparent + DisposeOverwritePrevious, // Clear frame to previous framebuffer + // contents }; #if PLATFORM(SKIA) || PLATFORM(QT) typedef uint32_t PixelData; @@ -67,15 +70,11 @@ namespace WebCore { 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); - } + RGBA32Buffer(const RGBA32Buffer& other) { operator=(other); } + + // For backends which refcount their data, this operator doesn't need to + // create a new copy of the image data, only increase the ref count. + RGBA32Buffer& operator=(const RGBA32Buffer& other); // Deletes the pixel data entirely; used by ImageDecoder to save memory // when we no longer need to display a frame and only need its metadata. @@ -142,8 +141,6 @@ namespace WebCore { #endif private: - RGBA32Buffer& operator=(const RGBA32Buffer& other); - int width() const; int height() const; @@ -188,26 +185,33 @@ namespace WebCore { 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. + bool m_hasAlpha; // Whether or not any of the pixels in the buffer + // have transparency. #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. + 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. + // 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. + // The ImageDecoder class represents a base class for specific image format + // decoders (e.g., GIF, JPG, PNG, ICO) to derive from. All decoders decode + // into RGBA32 format and the base class manages the RGBA32 frame cache. + // + // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write + // directly to scaled output buffers by down sampling. Call + // setMaxNumPixels() to specify the biggest size that decoded images can + // have. Image decoders will deflate those images that are bigger than + // m_maxNumPixels. (Not supported by all image decoders yet) class ImageDecoder : public Noncopyable { public: - // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write directly to - // scaled output buffers by down sampling. Call setMaxNumPixels() to specify the - // biggest size that decoded images can have. Image decoders will deflate those - // images that are bigger than m_maxNumPixels. (Not supported by all image decoders yet) ImageDecoder() : m_scaled(false) , m_failed(false) @@ -224,21 +228,22 @@ namespace WebCore { // needing to write a dedicated setData() implementation. static ImageDecoder* create(const SharedBuffer& data); - // The the filename extension usually associated with an undecoded image of this type. + // 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. bool isAllDataReceived() const { return m_isAllDataReceived; } + virtual void setData(SharedBuffer* data, bool allDataReceived) { m_data = data; m_isAllDataReceived = allDataReceived; } - // Whether or not the size information has been decoded yet. This default - // implementation just returns true if the size has been set and we have not - // seen a failure. Decoders may want to override this to lazily decode - // enough of the image to get the size. + // 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; @@ -266,10 +271,10 @@ namespace WebCore { return 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. + // Called by the image decoders to set their decoded size, this also + // checks the size for validity. It will return true if the size was + // set, or false if there is an error. On error, the m_failed flag will + // be set and the caller should immediately stop decoding. virtual bool setSize(unsigned width, unsigned height) { if (isOverSize(width, height)) { @@ -281,29 +286,30 @@ namespace WebCore { 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). + // The total number of frames for the image. Classes that support + // multiple frames will scan the image data for the answer if they need + // to (without necessarily decoding all of the individual frames). virtual size_t frameCount() { return 1; } // The number of repetitions to perform for an animation loop. virtual int repetitionCount() const { return cAnimationNone; } - // Called to obtain the RGBA32Buffer full of decoded data for rendering. The - // decoder plugin will decode as much of the frame as it can before handing - // back the buffer. + // Called to obtain the RGBA32Buffer full of decoded data for rendering. + // The decoder plugin will decode as much of the frame as it can before + // handing back the buffer. virtual RGBA32Buffer* frameBufferAtIndex(size_t) = 0; - // Whether or not the underlying image format even supports alpha transparency. + // 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. + // 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) { } diff --git a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp index de0690f..fb9f9f2 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp +++ b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp @@ -41,9 +41,7 @@ namespace WebCore { static const size_t sizeOfFileHeader = 14; BMPImageDecoder::BMPImageDecoder() - : ImageDecoder() - , m_allDataReceived(false) - , m_decodedOffset(0) + : m_decodedOffset(0) { } @@ -53,7 +51,6 @@ void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) return; ImageDecoder::setData(data, allDataReceived); - m_allDataReceived = allDataReceived; if (m_reader) m_reader->setData(data); } @@ -61,7 +58,7 @@ void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) bool BMPImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed()) - decodeWithCheckForDataEnded(true); + decode(true); return ImageDecoder::isSizeAvailable(); } @@ -76,31 +73,29 @@ RGBA32Buffer* BMPImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer* buffer = &m_frameBufferCache.first(); if (buffer->status() != RGBA32Buffer::FrameComplete && !failed()) - decodeWithCheckForDataEnded(false); + decode(false); return buffer; } -void BMPImageDecoder::decodeWithCheckForDataEnded(bool onlySize) +void BMPImageDecoder::decode(bool onlySize) { if (failed()) return; // If we couldn't decode the image but we've received all the data, decoding // has failed. - if (!decode(onlySize) && m_allDataReceived) + if (!decodeHelper(onlySize) && isAllDataReceived()) setFailed(); } -bool BMPImageDecoder::decode(bool onlySize) +bool BMPImageDecoder::decodeHelper(bool onlySize) { size_t imgDataOffset = 0; - if ((m_decodedOffset < sizeOfFileHeader) - && !processFileHeader(&imgDataOffset)) + if ((m_decodedOffset < sizeOfFileHeader) && !processFileHeader(&imgDataOffset)) return false; if (!m_reader) { - m_reader.set(new BMPImageReader(this, m_decodedOffset, imgDataOffset, - false)); + m_reader.set(new BMPImageReader(this, m_decodedOffset, imgDataOffset, false)); m_reader->setData(m_data.get()); } @@ -118,8 +113,7 @@ bool BMPImageDecoder::processFileHeader(size_t* imgDataOffset) 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]); + const uint16_t fileType = (m_data->data()[0] << 8) | static_cast<uint8_t>(m_data->data()[1]); *imgDataOffset = readUint32(10); m_decodedOffset = sizeOfFileHeader; diff --git a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h index c793585..15be0a2 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h +++ b/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h @@ -50,29 +50,23 @@ namespace WebCore { private: inline uint32_t readUint32(int offset) const { - return BMPImageReader::readUint32(m_data.get(), - m_decodedOffset + offset); + return BMPImageReader::readUint32(m_data.get(), m_decodedOffset + offset); } // Decodes the image. If |onlySize| is true, stops decoding after // calculating the image size. If decoding fails but there is no more // data coming, sets the "decode failure" flag. - void decodeWithCheckForDataEnded(bool onlySize); + void decode(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); + bool decodeHelper(bool onlySize); // Processes the file header at the beginning of the data. Sets // |*imgDataOffset| based on the header contents. Returns true if the // file header could be decoded. bool processFileHeader(size_t* imgDataOffset); - // 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. diff --git a/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp b/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp index 1936d81..2f3bffa 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp +++ b/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp @@ -33,10 +33,7 @@ namespace WebCore { -BMPImageReader::BMPImageReader(ImageDecoder* parent, - size_t decodedAndHeaderOffset, - size_t imgDataOffset, - bool usesAndMask) +BMPImageReader::BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask) : m_parent(parent) , m_buffer(0) , m_decodedOffset(decodedAndHeaderOffset) @@ -63,8 +60,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) return false; // Read and process info header. - if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) - && !processInfoHeader()) + if ((m_decodedOffset < (m_headerOffset + m_infoHeader.biSize)) && !processInfoHeader()) return false; // processInfoHeader() set the size, so if that's all we needed, we're done. @@ -82,8 +78,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) // 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())) + 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 @@ -100,9 +95,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) // Decode the data. if ((m_andMaskState != Decoding) && !pastEndOfImage(0)) { - if ((m_infoHeader.biCompression == RLE4) - || (m_infoHeader.biCompression == RLE8) - || (m_infoHeader.biCompression == RLE24)) { + if ((m_infoHeader.biCompression == RLE4) || (m_infoHeader.biCompression == RLE8) || (m_infoHeader.biCompression == RLE24)) { if (!processRLEData()) return false; } else if (!processNonRLEData(false, 0)) @@ -133,8 +126,7 @@ bool BMPImageReader::readInfoHeaderSize() { // Get size of info header. ASSERT(m_decodedOffset == m_headerOffset); - if ((m_decodedOffset > m_data->size()) - || ((m_data->size() - m_decodedOffset) < 4)) + if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < 4)) return false; m_infoHeader.biSize = readUint32(0); // Don't increment m_decodedOffset here, it just makes the code in @@ -143,9 +135,7 @@ bool BMPImageReader::readInfoHeaderSize() // Don't allow the header to overflow (which would be harmless here, but // problematic or at least confusing in other places), or to overrun the // image data. - if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) - || (m_imgDataOffset - && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize)))) + if (((m_headerOffset + m_infoHeader.biSize) < m_headerOffset) || (m_imgDataOffset && (m_imgDataOffset < (m_headerOffset + m_infoHeader.biSize)))) return setFailed(); // See if this is a header size we understand: @@ -156,9 +146,7 @@ bool BMPImageReader::readInfoHeaderSize() else if ((m_infoHeader.biSize == 40) || isWindowsV4Plus()) ; // OS/2 2.x: any multiple of 4 between 16 and 64, inclusive, or 42 or 46 - else if ((m_infoHeader.biSize >= 16) && (m_infoHeader.biSize <= 64) - && (((m_infoHeader.biSize & 3) == 0) || (m_infoHeader.biSize == 42) - || (m_infoHeader.biSize == 46))) + else if ((m_infoHeader.biSize >= 16) && (m_infoHeader.biSize <= 64) && (!(m_infoHeader.biSize & 3) || (m_infoHeader.biSize == 42) || (m_infoHeader.biSize == 46))) m_isOS22x = true; else return setFailed(); @@ -170,9 +158,7 @@ bool BMPImageReader::processInfoHeader() { // Read info header. ASSERT(m_decodedOffset == m_headerOffset); - if ((m_decodedOffset > m_data->size()) - || ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) - || !readInfoHeader()) + if ((m_decodedOffset > m_data->size()) || ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) || !readInfoHeader()) return false; m_decodedOffset += m_infoHeader.biSize; @@ -188,11 +174,9 @@ bool BMPImageReader::processInfoHeader() // colors", so set it to the maximum number of colors for this bit depth. // Also do this for bitmaps that put too large a value here. if (m_infoHeader.biBitCount < 16) { - const uint32_t maxColors = - static_cast<uint32_t>(1) << m_infoHeader.biBitCount; - if ((m_infoHeader.biClrUsed == 0) - || (m_infoHeader.biClrUsed > maxColors)) - m_infoHeader.biClrUsed = maxColors; + const uint32_t maxColors = static_cast<uint32_t>(1) << m_infoHeader.biBitCount; + if (!m_infoHeader.biClrUsed || (m_infoHeader.biClrUsed > maxColors)) + m_infoHeader.biClrUsed = maxColors; } // For any bitmaps that set their BitCount to the wrong value, reset the @@ -206,7 +190,7 @@ bool BMPImageReader::processInfoHeader() // Tell caller what still needs to be processed. if (m_infoHeader.biBitCount >= 16) m_needToProcessBitmasks = true; - else if (m_infoHeader.biBitCount > 0) + else if (m_infoHeader.biBitCount) m_needToProcessColorTable = true; return true; @@ -246,8 +230,7 @@ bool BMPImageReader::readInfoHeader() } else if (biCompression > 5) return setFailed(); // Some type we don't understand. else - m_infoHeader.biCompression = - static_cast<CompressionType>(biCompression); + m_infoHeader.biCompression = static_cast<CompressionType>(biCompression); } // Read colors used, if present. @@ -288,7 +271,7 @@ bool BMPImageReader::isInfoHeaderValid() const { // Non-positive widths/heights are invalid. (We've already flipped the // sign of the height for top-down bitmaps.) - if ((m_infoHeader.biWidth <= 0) || (m_infoHeader.biHeight == 0)) + if ((m_infoHeader.biWidth <= 0) || !m_infoHeader.biHeight) return false; // Only Windows V3+ has top-down bitmaps. @@ -296,16 +279,10 @@ bool BMPImageReader::isInfoHeaderValid() const return false; // Only bit depths of 1, 4, 8, or 24 are universally supported. - if ((m_infoHeader.biBitCount != 1) && (m_infoHeader.biBitCount != 4) - && (m_infoHeader.biBitCount != 8) - && (m_infoHeader.biBitCount != 24)) { + if ((m_infoHeader.biBitCount != 1) && (m_infoHeader.biBitCount != 4) && (m_infoHeader.biBitCount != 8) && (m_infoHeader.biBitCount != 24)) { // Windows V3+ additionally supports bit depths of 0 (for embedded // JPEG/PNG images), 16, and 32. - if (m_isOS21x || m_isOS22x) - return false; - if ((m_infoHeader.biBitCount != 0) - && (m_infoHeader.biBitCount != 16) - && (m_infoHeader.biBitCount != 32)) + if (m_isOS21x || m_isOS22x || (m_infoHeader.biBitCount && (m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32))) return false; } @@ -314,7 +291,7 @@ bool BMPImageReader::isInfoHeaderValid() const // some compression types. switch (m_infoHeader.biCompression) { case RGB: - if (m_infoHeader.biBitCount == 0) + if (!m_infoHeader.biBitCount) return false; break; @@ -323,46 +300,38 @@ bool BMPImageReader::isInfoHeaderValid() const // Compression = RLE4" (which means "4 bit, but with a 2-color table"), // so also allow the paletted RLE compression types to have too low a // bit count; we'll correct this later. - if (m_infoHeader.biBitCount == 0 || m_infoHeader.biBitCount > 8) + if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 8)) return false; break; case RLE4: // See comments in RLE8. - if (m_infoHeader.biBitCount == 0 || m_infoHeader.biBitCount > 4) + if (!m_infoHeader.biBitCount || (m_infoHeader.biBitCount > 4)) return false; break; case BITFIELDS: // Only valid for Windows V3+. - if (m_isOS21x || m_isOS22x) - return false; - if ((m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32)) + if (m_isOS21x || m_isOS22x || ((m_infoHeader.biBitCount != 16) && (m_infoHeader.biBitCount != 32))) return false; break; case JPEG: case PNG: // Only valid for Windows V3+. - if (m_isOS21x || m_isOS22x) - return false; - if (m_infoHeader.biBitCount != 0) + if (m_isOS21x || m_isOS22x || m_infoHeader.biBitCount) return false; break; case HUFFMAN1D: // Only valid for OS/2 2.x. - if (!m_isOS22x) - return false; - if (m_infoHeader.biBitCount != 1) + if (!m_isOS22x || (m_infoHeader.biBitCount != 1)) return false; break; case RLE24: // Only valid for OS/2 2.x. - if (!m_isOS22x) - return false; - if (m_infoHeader.biBitCount != 24) + if (!m_isOS22x || (m_infoHeader.biBitCount != 24)) return false; break; @@ -374,8 +343,7 @@ bool BMPImageReader::isInfoHeaderValid() const } // Top-down bitmaps cannot be compressed; they must be RGB or BITFIELDS. - if (m_isTopDown && (m_infoHeader.biCompression != RGB) - && (m_infoHeader.biCompression != BITFIELDS)) + if (m_isTopDown && (m_infoHeader.biCompression != RGB) && (m_infoHeader.biCompression != BITFIELDS)) return false; // Reject the following valid bitmap types that we don't currently bother @@ -385,13 +353,11 @@ bool BMPImageReader::isInfoHeaderValid() const // * Bitmaps larger than 2^16 pixels in either dimension (Windows // probably doesn't draw these well anyway, and the decoded data would // take a lot of memory). - if ((m_infoHeader.biWidth >= (1 << 16)) - || (m_infoHeader.biHeight >= (1 << 16))) + if ((m_infoHeader.biWidth >= (1 << 16)) || (m_infoHeader.biHeight >= (1 << 16))) return false; // * Windows V3+ JPEG-in-BMP and PNG-in-BMP bitmaps (supposedly not found // in the wild, only used to send data to printers?). - if ((m_infoHeader.biCompression == JPEG) - || (m_infoHeader.biCompression == PNG)) + if ((m_infoHeader.biCompression == JPEG) || (m_infoHeader.biCompression == PNG)) return false; // * OS/2 2.x Huffman-encoded monochrome bitmaps (see // http://www.fileformat.info/mirror/egff/ch09_05.htm , re: "G31D" @@ -413,11 +379,8 @@ bool BMPImageReader::processBitmasks() // 16 bits: MSB <- xRRRRRGG GGGBBBBB -> LSB // 24/32 bits: MSB <- [AAAAAAAA] RRRRRRRR GGGGGGGG BBBBBBBB -> LSB const int numBits = (m_infoHeader.biBitCount == 16) ? 5 : 8; - for (int i = 0; i <= 2; ++i) { - m_bitMasks[i] = - ((static_cast<uint32_t>(1) << (numBits * (3 - i))) - 1) ^ - ((static_cast<uint32_t>(1) << (numBits * (2 - i))) - 1); - } + for (int i = 0; i <= 2; ++i) + m_bitMasks[i] = ((static_cast<uint32_t>(1) << (numBits * (3 - i))) - 1) ^ ((static_cast<uint32_t>(1) << (numBits * (2 - i))) - 1); // For Windows V4+ 32-bit RGB, don't overwrite the alpha mask from the // header (see note in readInfoHeader()). @@ -431,10 +394,7 @@ bool BMPImageReader::processBitmasks() // Fail if we don't have enough file space for the bitmasks. static const size_t SIZEOF_BITMASKS = 12; - if (((m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS) < - (m_headerOffset + m_infoHeader.biSize)) - || (m_imgDataOffset && (m_imgDataOffset < - (m_headerOffset + m_infoHeader.biSize + SIZEOF_BITMASKS)))) + 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. @@ -461,8 +421,7 @@ bool BMPImageReader::processBitmasks() // 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 @@ -507,15 +466,11 @@ 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)))) + 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 > m_data->size()) - || ((m_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) { @@ -573,7 +528,7 @@ bool BMPImageReader::processRLEData() // the image. 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)) + if ((count || (code != 1)) && pastEndOfImage(0)) return setFailed(); // Decode. @@ -590,10 +545,7 @@ bool BMPImageReader::processRLEData() case 1: // Magic token: EOF // Skip any remaining pixels in the image. - if ((m_coord.x() < m_parent->size().width()) - || (m_isTopDown - ? (m_coord.y() < (m_parent->size().height() - 1)) - : (m_coord.y() > 0))) + 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; @@ -607,10 +559,9 @@ bool BMPImageReader::processRLEData() // past the end of the image. const uint8_t dx = m_data->data()[m_decodedOffset + 2]; const uint8_t dy = m_data->data()[m_decodedOffset + 3]; - if ((dx != 0) || (dy != 0)) + if (dx || dy) m_buffer->setHasAlpha(true); - if (((m_coord.x() + dx) > m_parent->size().width()) || - pastEndOfImage(dy)) + if (((m_coord.x() + dx) > m_parent->size().width()) || pastEndOfImage(dy)) return setFailed(); // Skip intervening pixels. @@ -637,8 +588,7 @@ bool BMPImageReader::processRLEData() // The following color data is repeated for |count| total pixels. // Strangely, some BMPs seem to specify excessively large counts // here; ignore pixels past the end of the row. - const int endX = - std::min(m_coord.x() + count, m_parent->size().width()); + 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. @@ -646,8 +596,7 @@ bool BMPImageReader::processRLEData() return false; // One BGR triple that we copy |count| times. - fillRGBA(endX, m_data->data()[m_decodedOffset + 3], - m_data->data()[m_decodedOffset + 2], code, 0xff); + 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 @@ -658,8 +607,7 @@ bool BMPImageReader::processRLEData() colorIndexes[0] = (colorIndexes[0] >> 4) & 0xf; colorIndexes[1] &= 0xf; } - if ((colorIndexes[0] >= m_infoHeader.biClrUsed) - || (colorIndexes[1] >= m_infoHeader.biClrUsed)) + if ((colorIndexes[0] >= m_infoHeader.biClrUsed) || (colorIndexes[1] >= m_infoHeader.biClrUsed)) return setFailed(); for (int which = 0; m_coord.x() < endX; ) { setI(colorIndexes[which]); @@ -689,9 +637,7 @@ bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) // requires. const size_t pixelsPerByte = 8 / m_infoHeader.biBitCount; const size_t bytesPerPixel = m_infoHeader.biBitCount / 8; - const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) - ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) - : (numPixels * bytesPerPixel); + const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) : (numPixels * bytesPerPixel); // RLE runs are zero-padded at the end to a multiple of 16 bits. Non-RLE // data is in rows and is zero-padded to a multiple of 32 bits. const size_t alignBits = inRLE ? 1 : 3; @@ -711,10 +657,8 @@ bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) const uint8_t mask = (1 << m_infoHeader.biBitCount) - 1; for (size_t byte = 0; byte < unpaddedNumBytes; ++byte) { uint8_t pixelData = m_data->data()[m_decodedOffset + byte]; - for (size_t pixel = 0; - (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) { - const size_t colorIndex = - (pixelData >> (8 - m_infoHeader.biBitCount)) & mask; + for (size_t pixel = 0; (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) { + const size_t colorIndex = (pixelData >> (8 - m_infoHeader.biBitCount)) & mask; if (m_andMaskState == Decoding) { // There's no way to accurately represent an AND + XOR // operation as an RGBA image, so where the AND values @@ -749,7 +693,7 @@ bool BMPImageReader::processNonRLEData(bool inRLE, int numPixels) // images where all alpha values are 255; opaque images are // faster to draw. int alpha = getAlpha(pixel); - if (!m_seenNonZeroAlphaPixel && (alpha == 0)) { + if (!m_seenNonZeroAlphaPixel && !alpha) { m_seenZeroAlphaPixel = true; alpha = 255; } else { diff --git a/WebCore/platform/image-decoders/bmp/BMPImageReader.h b/WebCore/platform/image-decoders/bmp/BMPImageReader.h index 3536e3b..a30a721 100644 --- a/WebCore/platform/image-decoders/bmp/BMPImageReader.h +++ b/WebCore/platform/image-decoders/bmp/BMPImageReader.h @@ -57,8 +57,7 @@ namespace WebCore { uint32_t result; memcpy(&result, &data->data()[offset], 4); #if CPU(BIG_ENDIAN) - result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) | - ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24); + result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) | ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24); #endif return result; } @@ -67,10 +66,7 @@ namespace WebCore { // |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); + BMPImageReader(ImageDecoder* parent, size_t decodedAndHeaderOffset, size_t imgDataOffset, bool usesAndMask); void setBuffer(RGBA32Buffer* buffer) { m_buffer = buffer; } void setData(SharedBuffer* data) { m_data = data; } @@ -179,9 +175,7 @@ namespace WebCore { // image", so downwards for m_isTopDown images and upwards otherwise. inline bool pastEndOfImage(int numRows) { - return m_isTopDown - ? ((m_coord.y() + numRows) >= m_parent->size().height()) - : ((m_coord.y() - numRows) < 0); + return m_isTopDown ? ((m_coord.y() + numRows) >= m_parent->size().height()) : ((m_coord.y() - numRows) < 0); } // Returns the pixel data for the current X coordinate in a uint32_t. @@ -203,8 +197,7 @@ namespace WebCore { uint32_t pixel; memcpy(&pixel, &m_data->data()[m_decodedOffset + offset], 3); #if CPU(BIG_ENDIAN) - pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) | - ((pixel & 0xff000000) >> 24); + pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) | ((pixel & 0xff000000) >> 24); #endif return pixel; } @@ -222,17 +215,13 @@ namespace WebCore { // 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 { // For images without alpha, return alpha of 0xff. - if (m_bitMasks[3] == 0) - return 0xff; - - return getComponent(pixel, 3); + return m_bitMasks[3] ? getComponent(pixel, 3) : 0xff; } // Sets the current pixel to the color given by |colorIndex|. This also @@ -240,9 +229,7 @@ namespace WebCore { // right by one. inline void setI(size_t colorIndex) { - setRGBA(m_colorTable[colorIndex].rgbRed, - m_colorTable[colorIndex].rgbGreen, - m_colorTable[colorIndex].rgbBlue, 0xff); + setRGBA(m_colorTable[colorIndex].rgbRed, m_colorTable[colorIndex].rgbGreen, m_colorTable[colorIndex].rgbBlue, 0xff); } // Like setI(), but with the individual component values specified. @@ -251,8 +238,7 @@ namespace WebCore { unsigned blue, unsigned alpha) { - m_buffer->setRGBA(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); } diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index 1124bd2..807d57c 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -30,7 +30,7 @@ namespace WebCore { GIFImageDecoder::GIFImageDecoder() - : m_frameCountValid(true) + : m_alreadyScannedThisDataForFrameCount(true) , m_repetitionCount(cAnimationLoopOnce) , m_readOffset(0) { @@ -40,56 +40,45 @@ GIFImageDecoder::~GIFImageDecoder() { } -// 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; + // We need to rescan the frame count, as the new data may have changed it. + m_alreadyScannedThisDataForFrameCount = false; - // Create the GIF reader. if (!m_reader && !m_failed) m_reader.set(new GIFImageReader(this)); } -// Whether or not the size information has been decoded yet. bool GIFImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) - decode(GIFSizeQuery, 0); + decode(0, GIFSizeQuery); 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). size_t 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). + if (!m_alreadyScannedThisDataForFrameCount) { + // FIXME: Scanning all the data has O(n^2) behavior if the data were to + // come in really slowly. Might be interesting to try to clone our + // existing read session to preserve state, but for now we just crawl + // all the data. Note that this is no worse than what ImageIO does on + // Mac right now (it also crawls all the data again). GIFImageReader reader(0); - // 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.read((const unsigned char*)m_data->data(), m_data->size(), GIFFrameCountQuery, static_cast<unsigned>(-1)); - m_frameCountValid = true; + m_alreadyScannedThisDataForFrameCount = true; m_frameBufferCache.resize(reader.images_count); } 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 @@ -120,7 +109,7 @@ RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer& frame = m_frameBufferCache[index]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - decode(GIFFullQuery, index + 1); // Decode this frame. + decode(index + 1, GIFFullQuery); // Decode this frame. return &frame; } @@ -174,23 +163,11 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) } } -// Feed data to the GIF reader. -void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) -{ - if (m_failed) - return; - - m_failed = !m_reader->read((const unsigned char*)m_data->data() + m_readOffset, m_data->size() - m_readOffset, query, haltAtFrame); - - if (m_failed) - m_reader.clear(); -} - -// Callbacks from the GIF reader. bool GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height) { if (!setSize(width, height)) return false; + prepareScaleDataIfNecessary(); return true; } @@ -200,94 +177,7 @@ void GIFImageDecoder::decodingHalted(unsigned bytesLeft) m_readOffset = m_data->size() - bytesLeft; } -bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) -{ - // Initialize the frame rect in our buffer. - const GIFFrameReader* frameReader = m_reader->frame_reader; - IntRect frameRect(frameReader->x_offset, frameReader->y_offset, frameReader->width, frameReader->height); - - // Make sure the frameRect doesn't extend past the bottom-right of the buffer. - if (frameRect.right() > size().width()) - frameRect.setWidth(size().width() - frameReader->x_offset); - if (frameRect.bottom() > size().height()) - frameRect.setHeight(size().height() - frameReader->y_offset); - - RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; - int left = upperBoundScaledX(frameRect.x()); - int right = lowerBoundScaledX(frameRect.right(), left); - int top = upperBoundScaledY(frameRect.y()); - int bottom = lowerBoundScaledY(frameRect.bottom(), top); - buffer->setRect(IntRect(left, top, right - left, bottom - top)); - - if (frameIndex == 0) { - // This is the first frame, so we're not relying on any previous data. - if (!buffer->setSize(scaledSize().width(), scaledSize().height())) { - 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); - } else { - // We want to clear the previous frame to transparent, without - // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->rect(); - const IntSize& bufferSize = scaledSize(); - if ((frameIndex == 0) - || prevRect.contains(IntRect(IntPoint(), bufferSize))) { - // Clearing the first frame, or a frame the size of the whole - // image, results in a completely empty image. - if (!buffer->setSize(bufferSize.width(), bufferSize.height())) { - m_failed = true; - return false; - } - } else { - // Copy the whole previous buffer, then clear just its frame. - buffer->copyBitmapData(*prevBuffer); - for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { - for (int x = prevRect.x(); x < prevRect.right(); ++x) - buffer->setRGBA(x, y, 0, 0, 0, 0); - } - if ((prevRect.width() > 0) && (prevRect.height() > 0)) - buffer->setHasAlpha(true); - } - } - } - - // Update our status to be partially complete. - buffer->setStatus(RGBA32Buffer::FramePartial); - - // Reset the alpha pixel tracker for this frame. - m_currentBufferSawAlpha = false; - return true; -} - -bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, - unsigned char* rowBuffer, - unsigned char* rowEnd, - unsigned rowNumber, - unsigned repeatCount, - bool writeTransparentPixels) +bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, unsigned repeatCount, bool writeTransparentPixels) { const GIFFrameReader* frameReader = m_reader->frame_reader; // The pixel data and coordinates supplied to us are relative to the frame's @@ -365,7 +255,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // resulting buffer was non-transparent, and we can setHasAlpha(false). if (buffer.rect().contains(IntRect(IntPoint(), scaledSize()))) buffer.setHasAlpha(false); - else if (frameIndex > 0) { + else if (frameIndex) { // Tricky case. This frame does not have alpha only if everywhere // outside its rect doesn't have alpha. To know whether this is // true, we check the start state of the frame -- if it doesn't have @@ -375,8 +265,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // 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)) + while (frameIndex && (prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)) prevBuffer = &m_frameBufferCache[--frameIndex]; // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then @@ -387,8 +276,7 @@ void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // 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())) + if ((prevBuffer->disposalMethod() == RGBA32Buffer::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.rect().contains(prevBuffer->rect())) buffer.setHasAlpha(false); } } @@ -401,4 +289,93 @@ void GIFImageDecoder::gifComplete() m_reader.clear(); } +void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) +{ + if (m_failed) + return; + + m_failed = !m_reader->read((const unsigned char*)m_data->data() + m_readOffset, m_data->size() - m_readOffset, query, haltAtFrame); + + if (m_failed) + m_reader.clear(); +} + +bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) +{ + // Initialize the frame rect in our buffer. + const GIFFrameReader* frameReader = m_reader->frame_reader; + IntRect frameRect(frameReader->x_offset, frameReader->y_offset, frameReader->width, frameReader->height); + + // Make sure the frameRect doesn't extend outside the buffer. + if (frameRect.right() > size().width()) + frameRect.setWidth(size().width() - frameReader->x_offset); + if (frameRect.bottom() > size().height()) + frameRect.setHeight(size().height() - frameReader->y_offset); + + RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; + int left = upperBoundScaledX(frameRect.x()); + int right = lowerBoundScaledX(frameRect.right(), left); + int top = upperBoundScaledY(frameRect.y()); + int bottom = lowerBoundScaledY(frameRect.bottom(), top); + buffer->setRect(IntRect(left, top, right - left, bottom - top)); + + if (!frameIndex) { + // This is the first frame, so we're not relying on any previous data. + if (!buffer->setSize(scaledSize().width(), scaledSize().height())) { + 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 && (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); + } else { + // We want to clear the previous frame to transparent, without + // affecting pixels in the image outside of the frame. + const IntRect& prevRect = prevBuffer->rect(); + const IntSize& bufferSize = scaledSize(); + if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) { + // Clearing the first frame, or a frame the size of the whole + // image, results in a completely empty image. + if (!buffer->setSize(bufferSize.width(), bufferSize.height())) { + m_failed = true; + return false; + } + } else { + // Copy the whole previous buffer, then clear just its frame. + buffer->copyBitmapData(*prevBuffer); + for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { + for (int x = prevRect.x(); x < prevRect.right(); ++x) + buffer->setRGBA(x, y, 0, 0, 0, 0); + } + if ((prevRect.width() > 0) && (prevRect.height() > 0)) + buffer->setHasAlpha(true); + } + } + } + + // Update our status to be partially complete. + buffer->setStatus(RGBA32Buffer::FramePartial); + + // Reset the alpha pixel tracker for this frame. + m_currentBufferSawAlpha = false; + return true; +} + } // namespace WebCore diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h index 011ca96..0aa5387 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.h +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.h @@ -37,48 +37,38 @@ namespace WebCore { class GIFImageDecoder : public ImageDecoder { public: GIFImageDecoder(); - ~GIFImageDecoder(); + virtual ~GIFImageDecoder(); - virtual String filenameExtension() const { return "gif"; } + enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery }; - // Take the data and store it. + // ImageDecoder + virtual String filenameExtension() const { return "gif"; } virtual void setData(SharedBuffer* data, bool allDataReceived); - - // Whether or not the size information has been decoded yet. 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 size_t 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); - // Callbacks from the GIF reader. bool sizeNowAvailable(unsigned width, unsigned height); void decodingHalted(unsigned bytesLeft); - bool haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, - unsigned repeatCount, bool writeTransparentPixels); + bool 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. + // If the query is GIFFullQuery, decodes the image up to (but not + // including) |haltAtFrame|. Otherwise, decodes as much as is needed to + // answer the query, ignoring bitmap data. + void decode(unsigned haltAtFrame, GIFQuery); + + // Called to initialize the frame buffer with the given index, based on + // the previous frame's disposal method. Returns true on success. On + // failure, this will mark the image as failed. bool initFrameBuffer(unsigned frameIndex); - bool m_frameCountValid; + bool m_alreadyScannedThisDataForFrameCount; bool m_currentBufferSawAlpha; mutable int m_repetitionCount; OwnPtr<GIFImageReader> m_reader; diff --git a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp index ffb1310..755a48d 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageReader.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageReader.cpp @@ -290,7 +290,7 @@ bool GIFImageReader::do_lzw(const unsigned char *q) /* Check for explicit end-of-stream code */ if (code == (clear_code + 1)) { /* end-of-stream should only appear after all image data */ - return rows_remaining == 0; + return !rows_remaining; } if (oldcode == -1) { @@ -494,11 +494,11 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, /* All GIF files begin with "GIF87a" or "GIF89a" */ case gif_type: { - if (!strncmp((char*)q, "GIF89a", 6)) { + if (!strncmp((char*)q, "GIF89a", 6)) version = 89; - } else if (!strncmp((char*)q, "GIF87a", 6)) { + else if (!strncmp((char*)q, "GIF87a", 6)) version = 87; - } else { + else { state = gif_error; break; } @@ -712,9 +712,10 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, // 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 + } else { + // 0,3-7 are yet to be defined netscape extension codes + state = gif_error; + } break; } @@ -893,8 +894,7 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, // CALLBACK: The frame is now complete. if (clientptr && frame_reader) - clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, - frame_reader->disposal_method); + clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, frame_reader->disposal_method); /* Clear state from this image */ if (frame_reader) { @@ -919,7 +919,6 @@ bool GIFImageReader::read(const unsigned char *buf, unsigned len, // Handle general errors case gif_error: - // nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count); return false; // We shouldn't ever get here. diff --git a/WebCore/platform/image-decoders/gif/GIFImageReader.h b/WebCore/platform/image-decoders/gif/GIFImageReader.h index 14c2fb4..5982827 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageReader.h +++ b/WebCore/platform/image-decoders/gif/GIFImageReader.h @@ -194,10 +194,6 @@ struct GIFImageReader { } ~GIFImageReader() { - close(); - } - - void close() { delete []global_colormap; global_colormap = 0; delete frame_reader; diff --git a/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp b/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp index a0ec4f7..1a202bc 100644 --- a/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp +++ b/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp @@ -45,9 +45,11 @@ static const size_t sizeOfDirectory = 6; static const size_t sizeOfDirEntry = 16; ICOImageDecoder::ICOImageDecoder() - : ImageDecoder() - , m_allDataReceived(false) - , m_decodedOffset(0) + : m_decodedOffset(0) +{ +} + +ICOImageDecoder::~ICOImageDecoder() { } @@ -57,10 +59,8 @@ void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) return; ImageDecoder::setData(data, allDataReceived); - m_allDataReceived = allDataReceived; - for (BMPReaders::iterator i(m_bmpReaders.begin()); - i != m_bmpReaders.end(); ++i) { + for (BMPReaders::iterator i(m_bmpReaders.begin()); i != m_bmpReaders.end(); ++i) { if (*i) (*i)->setData(data); } @@ -71,7 +71,7 @@ void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) bool ICOImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable()) - decodeWithCheckForDataEnded(0, true); + decode(0, true); return ImageDecoder::isSizeAvailable(); } @@ -83,8 +83,7 @@ IntSize ICOImageDecoder::size() const IntSize ICOImageDecoder::frameSizeAtIndex(size_t index) const { - return (index && (index < m_dirEntries.size())) ? - m_dirEntries[index].m_size : size(); + return (index && (index < m_dirEntries.size())) ? m_dirEntries[index].m_size : size(); } bool ICOImageDecoder::setSize(unsigned width, unsigned height) @@ -101,7 +100,7 @@ bool ICOImageDecoder::setSize(unsigned width, unsigned height) size_t ICOImageDecoder::frameCount() { - decodeWithCheckForDataEnded(0, true); + decode(0, true); if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(m_dirEntries.size()); // CAUTION: We must not resize m_frameBufferCache again after this, as @@ -116,40 +115,19 @@ RGBA32Buffer* ICOImageDecoder::frameBufferAtIndex(size_t index) 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); + RGBA32Buffer* buffer = &m_frameBufferCache[index]; + if (buffer->status() != RGBA32Buffer::FrameComplete) + decode(index, false); + return buffer; } // static -bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a, - const IconDirectoryEntry& b) +bool ICOImageDecoder::compareEntries(const IconDirectoryEntry& a, const IconDirectoryEntry& b) { - // Larger icons are better. + // Larger icons are better. After that, higher bit-depth icons are better. const int aEntryArea = a.m_size.width() * a.m_size.height(); const int bEntryArea = b.m_size.width() * b.m_size.height(); - if (aEntryArea != bEntryArea) - return (aEntryArea > bEntryArea); - - // Higher bit-depth icons are better. - return (a.m_bitCount > b.m_bitCount); + return (aEntryArea == bEntryArea) ? (a.m_bitCount > b.m_bitCount) : (aEntryArea > bEntryArea); } void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t index) @@ -161,21 +139,18 @@ void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t 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); + RefPtr<SharedBuffer> pngData(SharedBuffer::create(&m_data->data()[dirEntry.m_imageOffset], m_data->size() - dirEntry.m_imageOffset)); + m_pngDecoders[index]->setData(pngData.get(), isAllDataReceived()); } -void ICOImageDecoder::decodeWithCheckForDataEnded(size_t index, bool onlySize) +void ICOImageDecoder::decode(size_t index, bool onlySize) { if (failed()) return; // If we couldn't decode the image but we've received all the data, decoding // has failed. - if ((!decodeDirectory() || (!onlySize && !decodeAtIndex(index))) - && m_allDataReceived) + if ((!decodeDirectory() || (!onlySize && !decodeAtIndex(index))) && isAllDataReceived()) setFailed(); } @@ -186,45 +161,47 @@ bool ICOImageDecoder::decodeDirectory() return false; // Read and process directory entries. - return (m_decodedOffset >= - (sizeOfDirectory + (m_dirEntries.size() * sizeOfDirEntry))) - || processDirectoryEntries(); + 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) { + const ImageType imageType = imageTypeAtIndex(index); + if (imageType == Unknown) + return false; // Not enough data to determine image type yet. + + if (imageType == BMP) { + if (!m_bmpReaders[index]) { // We need to have already sized m_frameBufferCache before this, and // we must not resize it again later (see caution in frameCount()). ASSERT(m_frameBufferCache.size() == m_dirEntries.size()); - m_bmpReaders[index].set( - new BMPImageReader(this, dirEntry.m_imageOffset, 0, true)); + m_bmpReaders[index].set(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].set(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; + if (!m_pngDecoders[index]) { + m_pngDecoders[index].set(new PNGImageDecoder()); + setDataForPNGDecoderAtIndex(index); + } + // Fail if the size the PNGImageDecoder calculated does not match the size + // in the directory. + if (m_pngDecoders[index]->isSizeAvailable() && (m_pngDecoders[index]->size() != dirEntry.m_size)) { + setFailed(); + return false; + } + m_frameBufferCache[index] = *m_pngDecoders[index]->frameBufferAtIndex(0); + if (!m_pngDecoders[index]->failed()) + return true; + setFailed(); + return false; } bool ICOImageDecoder::processDirectory() @@ -259,18 +236,14 @@ 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))) + 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) + 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) { + for (IconDirectoryEntries::iterator i(m_dirEntries.begin()); i != m_dirEntries.end(); ++i) { if (i->m_imageOffset < m_decodedOffset) { setFailed(); return false; @@ -309,8 +282,7 @@ ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry() // this isn't quite what the bitmap info header says later, as we only use // this value to determine which icon entry is best. if (!entry.m_bitCount) { - int colorCount = - static_cast<uint8_t>(m_data->data()[m_decodedOffset + 2]); + int colorCount = static_cast<uint8_t>(m_data->data()[m_decodedOffset + 2]); if (!colorCount) colorCount = 256; // Vague in the spec, needed by real-world icons. for (--colorCount; colorCount; colorCount >>= 1) diff --git a/WebCore/platform/image-decoders/ico/ICOImageDecoder.h b/WebCore/platform/image-decoders/ico/ICOImageDecoder.h index 6117e06..c1f29c9 100644 --- a/WebCore/platform/image-decoders/ico/ICOImageDecoder.h +++ b/WebCore/platform/image-decoders/ico/ICOImageDecoder.h @@ -41,6 +41,7 @@ namespace WebCore { class ICOImageDecoder : public ImageDecoder { public: ICOImageDecoder(); + virtual ~ICOImageDecoder(); // ImageDecoder virtual String filenameExtension() const { return "ico"; } @@ -67,19 +68,16 @@ namespace WebCore { // 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); + 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); + 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); + return BMPImageReader::readUint32(m_data.get(), m_decodedOffset + offset); } // If the desired PNGImageDecoder exists, gives it the appropriate data. @@ -88,12 +86,7 @@ namespace WebCore { // 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); + void decode(size_t index, bool onlySize); // Decodes the directory and directory entries at the beginning of the // data, and initializes members. Returns true if all decoding @@ -120,9 +113,6 @@ namespace WebCore { // 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. diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp index aaa9047..9ed20b6 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp @@ -59,18 +59,17 @@ extern "C" { namespace WebCore { struct decoder_error_mgr { - struct jpeg_error_mgr pub; /* "public" fields for IJG library*/ - jmp_buf setjmp_buffer; /* For handling catastropic errors */ + 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_HEADER, // Reading JFIF headers JPEG_START_DECOMPRESS, - JPEG_DECOMPRESS_PROGRESSIVE, /* Output progressive pixels */ - JPEG_DECOMPRESS_SEQUENTIAL, /* Output sequential pixels */ + 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_SINK_NON_JPEG_TRAILER, // Some image files have a non-JPEG trailer JPEG_ERROR }; @@ -80,14 +79,12 @@ 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 - */ +// 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; + // public fields; must be first in this struct! + struct jpeg_source_mgr pub; - JPEGImageReader *decoder; + JPEGImageReader* decoder; }; class JPEGImageReader @@ -102,11 +99,11 @@ public: { memset(&m_info, 0, sizeof(jpeg_decompress_struct)); - /* We set up the normal JPEG error routines, then override error_exit. */ + // 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 */ + // Allocate and initialize JPEG decompression object. jpeg_create_decompress(&m_info); decoder_source_mgr* src = 0; @@ -120,7 +117,7 @@ public: m_info.src = (jpeg_source_mgr*)src; - /* Set up callback functions. */ + // 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; @@ -148,22 +145,20 @@ public: 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; + + m_bytesToSkip = std::max(num_bytes - bytesToSkip, static_cast<long>(0)); } - bool decode(const Vector<char>& data, bool sizeOnly) { - m_decodingSizeOnly = sizeOnly; - + bool decode(const Vector<char>& data, bool onlySize) + { + m_decodingSizeOnly = onlySize; + unsigned newByteCount = data.size() - m_bufferLength; unsigned readOffset = m_bufferLength - m_info.src->bytes_in_buffer; m_info.src->bytes_in_buffer += newByteCount; m_info.src->next_input_byte = (JOCTET*)(data.data()) + readOffset; - + // If we still have bytes to skip, try to skip those now. if (m_bytesToSkip) skipBytes(m_bytesToSkip); @@ -178,172 +173,150 @@ public: } 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: - // 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; - } - - /* - * 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 + 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: + // 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; + } + // 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); - m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, - JPOOL_IMAGE, - row_stride, 1); + // Used to set up image size so arrays can be allocated. + jpeg_calc_output_dimensions(&m_info); - m_state = JPEG_START_DECOMPRESS; + // Make a one-row-high sample array that will go away when done with + // image. Always make it big enough to hold an RGB row. Since this + // uses the IJG memory manager, it must be allocated before the call + // to jpeg_start_compress(). + m_samples = (*m_info.mem->alloc_sarray)((j_common_ptr) &m_info, JPOOL_IMAGE, m_info.output_width * 4, 1); - // 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; - } + m_state = JPEG_START_DECOMPRESS; - 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; - } + // 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; } - 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; + 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_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; - } + // FALL THROUGH + + case JPEG_START_DECOMPRESS: + // Set parameters for decompression. + // FIXME -- Should reset dct_method and dither mode for final pass + // of progressive JPEG. + m_info.dct_method = JDCT_ISLOW; + m_info.dither_mode = JDITHER_FS; + m_info.do_fancy_upsampling = true; + m_info.enable_2pass_quant = false; + m_info.do_block_smoothing = true; + + // Start decompressor. + if (!jpeg_start_decompress(&m_info)) + return true; // I/O suspension. + + // If this is a progressive JPEG ... + m_state = (m_info.buffered_image) ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL; + // FALL THROUGH + + case JPEG_DECOMPRESS_SEQUENTIAL: + if (m_state == JPEG_DECOMPRESS_SEQUENTIAL) { + + if (!m_decoder->outputScanlines()) + return true; // I/O suspension. + + // If we've completed image output... + ASSERT(m_info.output_scanline == m_info.output_height); + m_state = JPEG_DONE; } + // FALL THROUGH + + case JPEG_DECOMPRESS_PROGRESSIVE: + if (m_state == JPEG_DECOMPRESS_PROGRESSIVE) { + int status; + do { + status = jpeg_consume_input(&m_info); + } while ((status != JPEG_SUSPENDED) && (status != JPEG_REACHED_EOI)); + + for (;;) { + if (!m_info.output_scanline) { + int scan = m_info.input_scan_number; + + // If we haven't displayed anything yet + // (output_scan_number == 0) and we have enough data for + // a complete scan, force output of the last full scan. + if (!m_info.output_scan_number && (scan > 1) && (status != JPEG_REACHED_EOI)) + --scan; + + if (!jpeg_start_output(&m_info, scan)) + return true; // I/O suspension. + } + + if (m_info.output_scanline == 0xffffff) + m_info.output_scanline = 0; - 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; - } + if (!m_decoder->outputScanlines()) { + if (!m_info.output_scanline) + // Didn't manage to read any lines - flag so we + // don't call jpeg_start_output() multiple times for + // the same scan. + m_info.output_scanline = 0xffffff; + return true; // I/O suspension. } - m_state = JPEG_DONE; - } - } + if (m_info.output_scanline == m_info.output_height) { + if (!jpeg_finish_output(&m_info)) + return true; // I/O suspension. - case JPEG_DONE: - { - /* Finish decompression */ - if (!jpeg_finish_decompress(&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_state = JPEG_SINK_NON_JPEG_TRAILER; + m_info.output_scanline = 0; + } + } - /* we're done */ - break; + m_state = JPEG_DONE; } - - case JPEG_SINK_NON_JPEG_TRAILER: - break; + // FALL THROUGH - case JPEG_ERROR: - break; + case JPEG_DONE: + // Finish decompression. + if (!jpeg_finish_decompress(&m_info)) + return true; // I/O suspension. + + m_state = JPEG_SINK_NON_JPEG_TRAILER; + break; + + case JPEG_SINK_NON_JPEG_TRAILER: + break; + + case JPEG_ERROR: + break; } return true; @@ -367,10 +340,10 @@ private: JSAMPARRAY m_samples; }; -/* Override the standard error method in the IJG JPEG decoder code. */ +// Override the standard error method in the IJG JPEG decoder code. void error_exit(j_common_ptr cinfo) { - /* Return control to the setjmp point. */ + // Return control to the setjmp point. decoder_error_mgr *err = (decoder_error_mgr *) cinfo->err; longjmp(err->setjmp_buffer, -1); } @@ -388,12 +361,12 @@ void skip_input_data(j_decompress_ptr jd, long 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. + // 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) +void term_source(j_decompress_ptr jd) { decoder_source_mgr *src = (decoder_source_mgr *)jd->src; src->decoder->decoder()->jpegComplete(); @@ -407,21 +380,17 @@ JPEGImageDecoder::~JPEGImageDecoder() { } -// 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.set(new JPEGImageReader(this)); } -// Whether or not the size information has been decoded yet. bool JPEGImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) @@ -434,6 +403,7 @@ bool JPEGImageDecoder::setSize(unsigned width, unsigned height) { if (!ImageDecoder::setSize(width, height)) return false; + prepareScaleDataIfNecessary(); return true; } @@ -448,23 +418,10 @@ RGBA32Buffer* JPEGImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer& frame = m_frameBufferCache[0]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - // Decode this frame. - decode(); + decode(false); return &frame; } -// Feed data to the JPEG reader. -void JPEGImageDecoder::decode(bool sizeOnly) -{ - 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)) - m_reader.clear(); -} - bool JPEGImageDecoder::outputScanlines() { if (m_frameBufferCache.isEmpty()) @@ -531,9 +488,20 @@ 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); + // Hand back an appropriately sized buffer, even if the image ended up being + // empty. + m_frameBufferCache[0].setStatus(RGBA32Buffer::FrameComplete); +} + +void JPEGImageDecoder::decode(bool onlySize) +{ + if (m_failed) + return; + + m_failed = !m_reader->decode(m_data->buffer(), onlySize); + + if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) + m_reader.clear(); } } diff --git a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h index d8bfd70..2a95dbe 100644 --- a/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h +++ b/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h @@ -38,28 +38,24 @@ namespace WebCore { class JPEGImageDecoder : public ImageDecoder { public: JPEGImageDecoder(); - ~JPEGImageDecoder(); + virtual ~JPEGImageDecoder(); + // ImageDecoder 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(); - virtual bool setSize(unsigned width, unsigned height); - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - virtual bool supportsAlpha() const { return false; } - void decode(bool sizeOnly = false); - bool outputScanlines(); void jpegComplete(); private: + // Decodes the image. If |onlySize| is true, stops decoding after + // calculating the image size. + void decode(bool onlySize); + OwnPtr<JPEGImageReader> m_reader; }; diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index 35c8af0..36f818f 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -52,21 +52,41 @@ const double cInverseGamma = 0.45455; 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); +static void PNGAPI decodingFailed(png_structp png, png_const_charp) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->decodingFailed(); + longjmp(png->jmpbuf, 1); +} -// Callbacks given to the read struct. The first is for warnings (we want to treat a particular warning -// as an error, which is why we have to register this callback. -static void PNGAPI decodingWarning(png_structp png_ptr, png_const_charp warning_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, png_const_charp warningMsg) +{ + // Mozilla did this, so we will too. + // Convert a tRNS warning to be an error (see + // http://bugzilla.mozilla.org/show_bug.cgi?id=251381 ) + if (!strncmp(warningMsg, "Missing PLTE before tRNS", 24)) + png_error(png, warningMsg); +} // Called when we have obtained the header information (including the size). -static void PNGAPI headerAvailable(png_structp png_ptr, png_infop info_ptr); +static void PNGAPI headerAvailable(png_structp png, png_infop) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->headerAvailable(); +} // Called when a row is ready. -static void PNGAPI rowAvailable(png_structp png_ptr, png_bytep new_row, - png_uint_32 row_num, int pass); +static void PNGAPI rowAvailable(png_structp png, png_bytep rowBuffer, png_uint_32 rowIndex, int interlacePass) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->rowAvailable(rowBuffer, rowIndex, interlacePass); +} // Called when we have completely finished decoding the image. -static void PNGAPI pngComplete(png_structp png_ptr, png_infop info_ptr); +static void PNGAPI pngComplete(png_structp png, png_infop) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); +} class PNGImageReader { @@ -75,7 +95,7 @@ public: : m_readOffset(0) , m_decodingSizeOnly(false) , m_interlaceBuffer(0) - , m_hasAlpha(0) + , m_hasAlpha(false) , m_hasFinishedDecoding(false) , m_currentBufferSize(0) { @@ -89,10 +109,12 @@ public: close(); } - void close() { + void close() + { if (m_png && m_info) - png_destroy_read_struct(&m_png, &m_info, 0); // Will zero the pointers. - delete []m_interlaceBuffer; + // This will zero the pointers. + png_destroy_read_struct(&m_png, &m_info, 0); + delete[] m_interlaceBuffer; m_interlaceBuffer = 0; m_readOffset = 0; m_hasFinishedDecoding = false; @@ -106,7 +128,7 @@ public: { m_decodingSizeOnly = sizeOnly; - // We need to do the setjmp here. Otherwise bad things will happen + // We need to do the setjmp here. Otherwise bad things will happen. if (setjmp(m_png->jmpbuf)) { close(); return; @@ -134,9 +156,7 @@ public: void setReadOffset(unsigned offset) { m_readOffset = offset; } void setHasAlpha(bool b) { m_hasAlpha = b; } - void createInterlaceBuffer(int size) { - m_interlaceBuffer = new png_byte[size]; - } + void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; } private: unsigned m_readOffset; @@ -157,21 +177,16 @@ PNGImageDecoder::~PNGImageDecoder() { } -// 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.set(new PNGImageReader(this)); } - -// Whether or not the size information has been decoded yet. bool PNGImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable() && !failed() && m_reader) @@ -191,41 +206,10 @@ RGBA32Buffer* PNGImageDecoder::frameBufferAtIndex(size_t index) RGBA32Buffer& frame = m_frameBufferCache[0]; if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) // Decode this frame. - decode(); + decode(false); return &frame; } -// Feed data to the PNG reader. -void PNGImageDecoder::decode(bool sizeOnly) -{ - if (m_failed) - return; - - m_reader->decode(*m_data, sizeOnly); - - if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) - m_reader.clear(); -} - -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; @@ -256,14 +240,12 @@ void PNGImageDecoder::headerAvailable() } int bitDepth, colorType, interlaceType, compressionType, filterType, channels; - png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, - &interlaceType, &compressionType, &filterType); + 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)) + if (colorType == PNG_COLOR_TYPE_PALETTE || (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)) png_set_expand(png); png_bytep trns = 0; @@ -276,8 +258,7 @@ void PNGImageDecoder::headerAvailable() if (bitDepth == 16) png_set_strip_16(png); - if (colorType == PNG_COLOR_TYPE_GRAY || - colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + 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. @@ -288,15 +269,14 @@ void PNGImageDecoder::headerAvailable() png_set_gAMA(png, info, gamma); } png_set_gamma(png, cDefaultGamma, gamma); - } - else + } 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 + // Update our info now. png_read_update_info(png, info); channels = png_get_channels(png, info); ASSERT(channels == 3 || channels == 4); @@ -310,12 +290,6 @@ void PNGImageDecoder::headerAvailable() } } -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()) @@ -339,36 +313,36 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height()); } - if (rowBuffer == 0) + if (!rowBuffer) return; - /* libpng comments (pasted in here to explain what follows) - * - * this function is called for every row in the image. If the - * image is interlacing, and you turned on the interlace handler, - * this function will be called for every row in every pass. - * Some of these rows will not be changed from the previous pass. - * When the row is not changed, the new_row variable will be NULL. - * The rows and passes are called in order, so you don't really - * need the row_num and pass, but I'm supplying them because it - * may make your life easier. - * - * For the non-NULL rows of interlaced images, you must call - * png_progressive_combine_row() passing in the row and the - * old row. You can call this function for NULL rows (it will - * just return) and for non-interlaced images (it just does the - * memcpy for you) if it will make the code easier. Thus, you - * can just do this for all cases: - * - * png_progressive_combine_row(png_ptr, old_row, new_row); - * - * where old_row is what was displayed for previous rows. Note - * that the first pass (pass == 0 really) will completely cover - * the old row, so the rows do not have to be initialized. After - * the first pass (and only for interlaced images), you will have - * to pass the current row, and the function will combine the - * old row and the new row. - */ + // libpng comments (pasted in here to explain what follows) + /* + * this function is called for every row in the image. If the + * image is interlacing, and you turned on the interlace handler, + * this function will be called for every row in every pass. + * Some of these rows will not be changed from the previous pass. + * When the row is not changed, the new_row variable will be NULL. + * The rows and passes are called in order, so you don't really + * need the row_num and pass, but I'm supplying them because it + * may make your life easier. + * + * For the non-NULL rows of interlaced images, you must call + * png_progressive_combine_row() passing in the row and the + * old row. You can call this function for NULL rows (it will + * just return) and for non-interlaced images (it just does the + * memcpy for you) if it will make the code easier. Thus, you + * can just do this for all cases: + * + * png_progressive_combine_row(png_ptr, old_row, new_row); + * + * where old_row is what was displayed for previous rows. Note + * that the first pass (pass == 0 really) will completely cover + * the old row, so the rows do not have to be initialized. After + * the first pass (and only for interlaced images), you will have + * to pass the current row, and the function will combine the + * old row and the new row. + */ png_structp png = m_reader->pngPtr(); bool hasAlpha = m_reader->hasAlpha(); @@ -378,8 +352,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, if (interlaceBuffer) { row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(png, row, rowBuffer); - } - else + } else row = rowBuffer; // Copy the data into our buffer. @@ -388,7 +361,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, if (destY < 0) return; bool sawAlpha = buffer.hasAlpha(); - for (int x = 0; x < width; x++) { + for (int x = 0; x < width; ++x) { png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels; unsigned alpha = hasAlpha ? pixel[3] : 255; buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha); @@ -399,21 +372,23 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, } } -void pngComplete(png_structp png, png_infop info) -{ - static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); -} - void PNGImageDecoder::pngComplete() { m_reader->setComplete(); - if (m_frameBufferCache.isEmpty()) + if (!m_frameBufferCache.isEmpty()) + m_frameBufferCache.first().setStatus(RGBA32Buffer::FrameComplete); +} + +void PNGImageDecoder::decode(bool onlySize) +{ + if (m_failed) return; - // Hand back an appropriately sized buffer, even if the image ended up being empty. - RGBA32Buffer& buffer = m_frameBufferCache[0]; - buffer.setStatus(RGBA32Buffer::FrameComplete); + m_reader->decode(*m_data, onlySize); + + if (m_failed || (!m_frameBufferCache.isEmpty() && m_frameBufferCache[0].status() == RGBA32Buffer::FrameComplete)) + m_reader.clear(); } } // namespace WebCore diff --git a/WebCore/platform/image-decoders/png/PNGImageDecoder.h b/WebCore/platform/image-decoders/png/PNGImageDecoder.h index 07a0b3a..ba0e19a 100644 --- a/WebCore/platform/image-decoders/png/PNGImageDecoder.h +++ b/WebCore/platform/image-decoders/png/PNGImageDecoder.h @@ -37,20 +37,14 @@ namespace WebCore { class PNGImageDecoder : public ImageDecoder { public: PNGImageDecoder(); - ~PNGImageDecoder(); + virtual ~PNGImageDecoder(); + // ImageDecoder 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(); - virtual RGBA32Buffer* frameBufferAtIndex(size_t index); - void decode(bool sizeOnly = false); - // Callbacks from libpng void decodingFailed(); void headerAvailable(); @@ -58,6 +52,10 @@ namespace WebCore { void pngComplete(); private: + // Decodes the image. If |onlySize| is true, stops decoding after + // calculating the image size. + void decode(bool onlySize); + OwnPtr<PNGImageReader> m_reader; }; diff --git a/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp b/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp index d3218cd..b2e5e17 100644 --- a/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp +++ b/WebCore/platform/image-decoders/qt/RGBA32BufferQt.cpp @@ -42,12 +42,17 @@ RGBA32Buffer::RGBA32Buffer() { } -// The image must not have format 8888 pre multiplied... -void RGBA32Buffer::setDecodedImage(const QImage& image) +RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) { - m_image = image; - m_size = image.size(); - m_hasAlpha = image.hasAlphaChannel(); + if (this == &other) + return *this; + + copyBitmapData(other); + setRect(other.rect()); + setStatus(other.status()); + setDuration(other.duration()); + setDisposalMethod(other.disposalMethod()); + return *this; } void RGBA32Buffer::clear() @@ -115,17 +120,12 @@ void RGBA32Buffer::setStatus(FrameStatus status) m_status = status; } -RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other) +// The image must not have format 8888 pre multiplied... +void RGBA32Buffer::setDecodedImage(const QImage& image) { - if (this == &other) - return *this; - - copyBitmapData(other); - setRect(other.rect()); - setStatus(other.status()); - setDuration(other.duration()); - setDisposalMethod(other.disposalMethod()); - return *this; + m_image = image; + m_size = image.size(); + m_hasAlpha = image.hasAlphaChannel(); } int RGBA32Buffer::width() const diff --git a/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp b/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp index 543eca8..928524a 100644 --- a/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp +++ b/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp @@ -39,6 +39,22 @@ RGBA32Buffer::RGBA32Buffer() { } +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; +} + void RGBA32Buffer::clear() { m_bitmap.reset(); @@ -105,22 +121,6 @@ void RGBA32Buffer::setStatus(FrameStatus status) 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(); |