diff options
Diffstat (limited to 'Source/WebCore/platform/image-decoders')
19 files changed, 1011 insertions, 1104 deletions
diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.cpp b/Source/WebCore/platform/image-decoders/ImageDecoder.cpp index b9656552f..ad6e40298 100644 --- a/Source/WebCore/platform/image-decoders/ImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/ImageDecoder.cpp @@ -1,4 +1,5 @@ /* + * Copyright (C) 2016 Apple Inc. All rights reserved. * Copyright (C) 2008-2009 Torch Mobile, Inc. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * @@ -95,138 +96,35 @@ bool matchesCURSignature(char* contents) } -ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +RefPtr<ImageDecoder> ImageDecoder::create(const SharedBuffer& data, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) { static const unsigned lengthOfLongestSignature = 14; // To wit: "RIFF????WEBPVP" char contents[lengthOfLongestSignature]; unsigned length = copyFromSharedBuffer(contents, lengthOfLongestSignature, data, 0); if (length < lengthOfLongestSignature) - return 0; + return nullptr; if (matchesGIFSignature(contents)) - return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption); + return adoptRef(*new GIFImageDecoder(alphaOption, gammaAndColorProfileOption)); if (matchesPNGSignature(contents)) - return new PNGImageDecoder(alphaOption, gammaAndColorProfileOption); + return adoptRef(*new PNGImageDecoder(alphaOption, gammaAndColorProfileOption)); if (matchesICOSignature(contents) || matchesCURSignature(contents)) - return new ICOImageDecoder(alphaOption, gammaAndColorProfileOption); + return adoptRef(*new ICOImageDecoder(alphaOption, gammaAndColorProfileOption)); if (matchesJPEGSignature(contents)) - return new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption); + return adoptRef(*new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption)); #if USE(WEBP) if (matchesWebPSignature(contents)) - return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption); + return adoptRef(*new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption)); #endif if (matchesBMPSignature(contents)) - return new BMPImageDecoder(alphaOption, gammaAndColorProfileOption); - - return 0; -} - -ImageFrame::ImageFrame() - : m_hasAlpha(false) - , m_status(FrameEmpty) - , m_duration(0) - , m_disposalMethod(DisposeNotSpecified) - , m_premultiplyAlpha(true) -{ -} - -ImageFrame& ImageFrame::operator=(const ImageFrame& other) -{ - if (this == &other) - return *this; - - copyBitmapData(other); - setOriginalFrameRect(other.originalFrameRect()); - setStatus(other.status()); - setDuration(other.duration()); - setDisposalMethod(other.disposalMethod()); - setPremultiplyAlpha(other.premultiplyAlpha()); - return *this; -} - -void ImageFrame::clearPixelData() -{ - m_backingStore.clear(); - m_bytes = 0; - m_status = FrameEmpty; - // NOTE: Do not reset other members here; clearFrameBufferCache() calls this - // to free the bitmap data, but other functions like initFrameBuffer() and - // frameComplete() may still need to read other metadata out of this frame - // later. -} - -void ImageFrame::zeroFillPixelData() -{ - memset(m_bytes, 0, m_size.width() * m_size.height() * sizeof(PixelData)); - m_hasAlpha = true; -} - -void ImageFrame::zeroFillFrameRect(const IntRect& rect) -{ - ASSERT(IntRect(IntPoint(), m_size).contains(rect)); - - if (rect.isEmpty()) - return; - - size_t rectWidthInBytes = rect.width() * sizeof(PixelData); - PixelData* start = m_bytes + (rect.y() * width()) + rect.x(); - for (int i = 0; i < rect.height(); ++i) { - memset(start, 0, rectWidthInBytes); - start += width(); - } - - setHasAlpha(true); -} - -bool ImageFrame::copyBitmapData(const ImageFrame& other) -{ - if (this == &other) - return true; - - m_backingStore = other.m_backingStore; - m_bytes = m_backingStore.data(); - m_size = other.m_size; - setHasAlpha(other.m_hasAlpha); - return true; -} - -bool ImageFrame::setSize(int newWidth, int newHeight) -{ - ASSERT(!width() && !height()); - size_t backingStoreSize = newWidth * newHeight; - if (!m_backingStore.tryReserveCapacity(backingStoreSize)) - return false; - m_backingStore.resize(backingStoreSize); - m_bytes = m_backingStore.data(); - m_size = IntSize(newWidth, newHeight); - - zeroFillPixelData(); - return true; -} - -bool ImageFrame::hasAlpha() const -{ - return m_hasAlpha; -} - -void ImageFrame::setHasAlpha(bool alpha) -{ - m_hasAlpha = alpha; -} - -void ImageFrame::setColorProfile(const ColorProfile& colorProfile) -{ - m_colorProfile = colorProfile; -} + return adoptRef(*new BMPImageDecoder(alphaOption, gammaAndColorProfileOption)); -void ImageFrame::setStatus(FrameStatus status) -{ - m_status = status; + return nullptr; } namespace { @@ -270,11 +168,17 @@ template <MatchType type> int getScaledValue(const Vector<int>& scaledValues, in } +bool ImageDecoder::frameIsCompleteAtIndex(size_t index) +{ + ImageFrame* buffer = frameBufferAtIndex(index); + return buffer && buffer->isComplete(); +} + bool ImageDecoder::frameHasAlphaAtIndex(size_t index) const { if (m_frameBufferCache.size() <= index) return true; - if (m_frameBufferCache[index].status() == ImageFrame::FrameComplete) + if (m_frameBufferCache[index].isComplete()) return m_frameBufferCache[index].hasAlpha(); return true; } @@ -284,7 +188,38 @@ unsigned ImageDecoder::frameBytesAtIndex(size_t index) const if (m_frameBufferCache.size() <= index) return 0; // FIXME: Use the dimension of the requested frame. - return m_size.area() * sizeof(ImageFrame::PixelData); + return (m_size.area() * sizeof(RGBA32)).unsafeGet(); +} + +float ImageDecoder::frameDurationAtIndex(size_t index) +{ + ImageFrame* buffer = frameBufferAtIndex(index); + if (!buffer || buffer->isEmpty()) + return 0; + + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify + // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> + // for more information. + const float duration = buffer->duration() / 1000.0f; + if (duration < 0.011f) + return 0.100f; + return duration; +} + +NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, const std::optional<IntSize>&) +{ + // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail. + if (size().isEmpty()) + return nullptr; + + ImageFrame* buffer = frameBufferAtIndex(index); + if (!buffer || buffer->isEmpty() || !buffer->hasBackingStore()) + return nullptr; + + // Return the buffer contents as a native image. For some ports, the data + // is already in a native container, and this just increments its refcount. + return buffer->backingStore()->image(); } void ImageDecoder::prepareScaleDataIfNecessary() diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.h b/Source/WebCore/platform/image-decoders/ImageDecoder.h index 89657d000..758987cb9 100644 --- a/Source/WebCore/platform/image-decoders/ImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/ImageDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2016 Apple Inc. All rights reserved. * Copyright (C) 2008-2009 Torch Mobile, Inc. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) @@ -13,10 +13,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,206 +26,55 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ImageDecoder_h -#define ImageDecoder_h +#pragma once +#include "ImageFrame.h" #include "IntRect.h" -#include "ImageSource.h" +#include "IntSize.h" #include "PlatformScreen.h" #include "SharedBuffer.h" #include <wtf/Assertions.h> +#include <wtf/Optional.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> #include <wtf/text/WTFString.h> -#if USE(QCMSLIB) -#include "qcms.h" -#if OS(DARWIN) -#include "GraphicsContextCG.h" -#include <ApplicationServices/ApplicationServices.h> -#include <wtf/RetainPtr.h> -#endif -#endif - namespace WebCore { - // ImageFrame represents the decoded image data. This buffer is what all - // decoders write a single frame into. - class ImageFrame { - 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 - }; - typedef unsigned PixelData; - - ImageFrame(); - - ImageFrame(const ImageFrame& 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. - ImageFrame& operator=(const ImageFrame& other); - - // These do not touch other metadata, only the raw pixel data. - void clearPixelData(); - void zeroFillPixelData(); - void zeroFillFrameRect(const IntRect&); - - // Makes this frame have an independent copy of the provided image's - // pixel data, so that modifications in one frame are not reflected in - // the other. Returns whether the copy succeeded. - bool copyBitmapData(const ImageFrame&); - - // Copies the pixel data at [(startX, startY), (endX, startY)) to the - // same X-coordinates on each subsequent row up to but not including - // endY. - void copyRowNTimes(int startX, int endX, int startY, int endY) - { - ASSERT(startX < width()); - ASSERT(endX <= width()); - ASSERT(startY < height()); - ASSERT(endY <= height()); - const int rowBytes = (endX - startX) * sizeof(PixelData); - const PixelData* const startAddr = getAddr(startX, startY); - for (int destY = startY + 1; destY < endY; ++destY) - memcpy(getAddr(startX, destY), startAddr, rowBytes); - } - - // Allocates space for the pixel data. Must be called before any pixels - // are written. Must only be called once. Returns whether allocation - // succeeded. - bool setSize(int newWidth, int newHeight); - - // Returns a caller-owned pointer to the underlying native image data. - // (Actual use: This pointer will be owned by BitmapImage and freed in - // FrameData::clear()). - PassNativeImagePtr asNewNativeImage() const; - - bool hasAlpha() const; - const IntRect& originalFrameRect() const { return m_originalFrameRect; } - FrameStatus status() const { return m_status; } - unsigned duration() const { return m_duration; } - FrameDisposalMethod disposalMethod() const { return m_disposalMethod; } - bool premultiplyAlpha() const { return m_premultiplyAlpha; } - - void setHasAlpha(bool alpha); - void setColorProfile(const ColorProfile&); - void setOriginalFrameRect(const IntRect& r) { m_originalFrameRect = r; } - void setStatus(FrameStatus status); - void setDuration(unsigned duration) { m_duration = duration; } - void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; } - void setPremultiplyAlpha(bool premultiplyAlpha) { m_premultiplyAlpha = premultiplyAlpha; } - - inline void setRGBA(int x, int y, unsigned r, unsigned g, unsigned b, unsigned a) - { - setRGBA(getAddr(x, y), r, g, b, a); - } - - inline PixelData* getAddr(int x, int y) - { - return m_bytes + (y * width()) + x; - } - - inline bool hasPixelData() const - { - return m_bytes; - } - - // Use fix point multiplier instead of integer division or floating point math. - // This multipler produces exactly the same result for all values in range 0 - 255. - static const unsigned fixPointShift = 24; - static const unsigned fixPointMult = static_cast<unsigned>(1.0 / 255.0 * (1 << fixPointShift)) + 1; - // Multiplies unsigned value by fixpoint value and converts back to unsigned. - static unsigned fixPointUnsignedMultiply(unsigned fixed, unsigned v) - { - return (fixed * v) >> fixPointShift; - } - - inline void setRGBA(PixelData* dest, unsigned r, unsigned g, unsigned b, unsigned a) - { - if (m_premultiplyAlpha && a < 255) { - if (!a) { - *dest = 0; - return; - } - - unsigned alphaMult = a * fixPointMult; - r = fixPointUnsignedMultiply(r, alphaMult); - g = fixPointUnsignedMultiply(g, alphaMult); - b = fixPointUnsignedMultiply(b, alphaMult); - } - *dest = (a << 24 | r << 16 | g << 8 | b); - } - - private: - int width() const - { - return m_size.width(); - } - - int height() const - { - return m_size.height(); - } - - Vector<PixelData> m_backingStore; - PixelData* m_bytes; // The memory is backed by m_backingStore. - IntSize m_size; - // FIXME: Do we need m_colorProfile anymore? - ColorProfile m_colorProfile; - bool m_hasAlpha; - IntRect m_originalFrameRect; // 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; - unsigned m_duration; - FrameDisposalMethod m_disposalMethod; - bool m_premultiplyAlpha; - }; - // ImageDecoder is a base for all format-specific decoders // (e.g. JPEGImageDecoder). This base manages the ImageFrame cache. // // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to downsample // at decode time. Image decoders will downsample any images larger than // |m_maxNumPixels|. FIXME: Not yet supported by all decoders. - class ImageDecoder { + class ImageDecoder : public RefCounted<ImageDecoder> { WTF_MAKE_NONCOPYABLE(ImageDecoder); WTF_MAKE_FAST_ALLOCATED; public: - ImageDecoder(ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) - : m_scaled(false) - , m_premultiplyAlpha(alphaOption == ImageSource::AlphaPremultiplied) - , m_ignoreGammaAndColorProfile(gammaAndColorProfileOption == ImageSource::GammaAndColorProfileIgnored) - , m_sizeAvailable(false) - , m_maxNumPixels(-1) - , m_isAllDataReceived(false) - , m_failed(false) { } - - virtual ~ImageDecoder() { } - - // Returns a caller-owned decoder of the appropriate type. Returns 0 if - // we can't sniff a supported type from the provided data (possibly + ImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) + : m_premultiplyAlpha(alphaOption == AlphaOption::Premultiplied) + , m_ignoreGammaAndColorProfile(gammaAndColorProfileOption == GammaAndColorProfileOption::Ignored) + { + } + + virtual ~ImageDecoder() + { + } + + // Returns nullptr if we can't sniff a supported type from the provided data (possibly // because there isn't enough data yet). - static ImageDecoder* create(const SharedBuffer& data, ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + static RefPtr<ImageDecoder> create(const SharedBuffer& data, AlphaOption, GammaAndColorProfileOption); virtual String filenameExtension() const = 0; + + bool premultiplyAlpha() const { return m_premultiplyAlpha; } bool isAllDataReceived() const { return m_isAllDataReceived; } - virtual void setData(SharedBuffer* data, bool allDataReceived) + virtual void setData(SharedBuffer& data, bool allDataReceived) { if (m_failed) return; - m_data = data; + m_data = &data; m_isAllDataReceived = allDataReceived; } @@ -237,9 +86,9 @@ namespace WebCore { return !m_failed && m_sizeAvailable; } - virtual IntSize size() const { return m_size; } + virtual IntSize size() { return isSizeAvailable() ? m_size : IntSize(); } - IntSize scaledSize() const + IntSize scaledSize() { return m_scaled ? IntSize(m_scaledColumns.size(), m_scaledRows.size()) : size(); } @@ -249,18 +98,18 @@ namespace WebCore { // sizes. This does NOT differ from size() for GIF, since decoding GIFs // composites any smaller frames against previous frames to create full- // size frames. - virtual IntSize frameSizeAtIndex(size_t) const + virtual IntSize frameSizeAtIndex(size_t, SubsamplingLevel) { return size(); } // Returns whether the size is legal (i.e. not going to result in // overflow elsewhere). If not, marks decoding as failed. - virtual bool setSize(unsigned width, unsigned height) + virtual bool setSize(const IntSize& size) { - if (isOverSize(width, height)) + if (ImageBackingStore::isOverSize(size)) return setFailed(); - m_size = IntSize(width, height); + m_size = size; m_sizeAvailable = true; return true; } @@ -269,24 +118,32 @@ namespace WebCore { // possible), without decoding the individual frames. // FIXME: Right now that has to be done by each subclass; factor the // decode call out and use it here. - virtual size_t frameCount() { return 1; } + virtual size_t frameCount() const { return 1; } - virtual int repetitionCount() const { return cAnimationNone; } + virtual RepetitionCount repetitionCount() const { return RepetitionCountNone; } // Decodes as much of the requested frame as possible, and returns an // ImageDecoder-owned pointer. virtual ImageFrame* frameBufferAtIndex(size_t) = 0; + bool frameIsCompleteAtIndex(size_t); + // Make the best effort guess to check if the requested frame has alpha channel. - virtual bool frameHasAlphaAtIndex(size_t) const; + bool frameHasAlphaAtIndex(size_t) const; // Number of bytes in the decoded frame requested. Return 0 if not yet decoded. - virtual unsigned frameBytesAtIndex(size_t) const; + unsigned frameBytesAtIndex(size_t) const; + + float frameDurationAtIndex(size_t); + + NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDraw = { }); void setIgnoreGammaAndColorProfile(bool flag) { m_ignoreGammaAndColorProfile = flag; } bool ignoresGammaAndColorProfile() const { return m_ignoreGammaAndColorProfile; } - ImageOrientation orientation() const { return m_orientation; } + ImageOrientation frameOrientationAtIndex(size_t) const { return m_orientation; } + + bool frameAllowSubsamplingAtIndex(size_t) const { return false; } enum { iccColorProfileHeaderLength = 128 }; @@ -297,6 +154,10 @@ namespace WebCore { return !memcmp(&profileData[16], "RGB ", 4); } + static size_t bytesDecodedToDetermineProperties() { return 0; } + + static SubsamplingLevel subsamplingLevelForScale(float, SubsamplingLevel) { return SubsamplingLevel::Default; } + static bool inputDeviceColorProfile(const char* profileData, unsigned profileLength) { ASSERT_UNUSED(profileLength, profileLength >= iccColorProfileHeaderLength); @@ -304,43 +165,6 @@ namespace WebCore { return !memcmp(&profileData[12], "mntr", 4) || !memcmp(&profileData[12], "scnr", 4); } -#if USE(QCMSLIB) - static qcms_profile* qcmsOutputDeviceProfile() - { - static qcms_profile* outputDeviceProfile = 0; - - static bool qcmsInitialized = false; - if (!qcmsInitialized) { - qcmsInitialized = true; - // FIXME: Add optional ICCv4 support. -#if OS(DARWIN) - RetainPtr<CGColorSpaceRef> monitorColorSpace = adoptCF(CGDisplayCopyColorSpace(CGMainDisplayID())); - CFDataRef iccProfile(CGColorSpaceCopyICCProfile(monitorColorSpace.get())); - if (iccProfile) { - size_t length = CFDataGetLength(iccProfile); - const unsigned char* systemProfile = CFDataGetBytePtr(iccProfile); - outputDeviceProfile = qcms_profile_from_memory(systemProfile, length); - } -#else - // FIXME: add support for multiple monitors. - ColorProfile profile; - screenColorProfile(profile); - if (!profile.isEmpty()) - outputDeviceProfile = qcms_profile_from_memory(profile.data(), profile.size()); -#endif - if (outputDeviceProfile && qcms_profile_is_bogus(outputDeviceProfile)) { - qcms_profile_release(outputDeviceProfile); - outputDeviceProfile = 0; - } - if (!outputDeviceProfile) - outputDeviceProfile = qcms_profile_sRGB(); - if (outputDeviceProfile) - qcms_profile_precache_output_transform(outputDeviceProfile); - } - return outputDeviceProfile; - } -#endif - // Sets the "decode failure" flag. For caller convenience (since so // many callers want to return false after calling this), returns false // to enable easy tailcalling. Subclasses may override this to also @@ -358,13 +182,9 @@ namespace WebCore { // compositing). virtual void clearFrameBufferCache(size_t) { } -#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) - void setMaxNumPixels(int m) { m_maxNumPixels = m; } -#endif - // If the image has a cursor hot-spot, stores it in the argument // and returns true. Otherwise returns false. - virtual bool hotSpot(IntPoint&) const { return false; } + virtual std::optional<IntPoint> hotSpot() const { return std::nullopt; } protected: void prepareScaleDataIfNecessary(); @@ -376,9 +196,7 @@ namespace WebCore { RefPtr<SharedBuffer> m_data; // The encoded data. Vector<ImageFrame, 1> m_frameBufferCache; - // FIXME: Do we need m_colorProfile any more, for any port? - ColorProfile m_colorProfile; - bool m_scaled; + bool m_scaled { false }; Vector<int> m_scaledColumns; Vector<int> m_scaledRows; bool m_premultiplyAlpha; @@ -386,22 +204,15 @@ namespace WebCore { ImageOrientation m_orientation; private: - // Some code paths compute the size of the image as "width * height * 4" - // and return it as a (signed) int. Avoid overflow. - static bool isOverSize(unsigned width, unsigned height) - { - unsigned long long total_size = static_cast<unsigned long long>(width) - * static_cast<unsigned long long>(height); - return total_size > ((1 << 29) - 1); - } - IntSize m_size; - bool m_sizeAvailable; - int m_maxNumPixels; - bool m_isAllDataReceived; - bool m_failed; + bool m_sizeAvailable { false }; +#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) + static const int m_maxNumPixels { 1024 * 1024 }; +#else + static const int m_maxNumPixels { -1 }; +#endif + bool m_isAllDataReceived { false }; + bool m_failed { false }; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp index 6011b65bc..368bccad2 100644 --- a/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp @@ -32,7 +32,6 @@ #include "BMPImageDecoder.h" #include "BMPImageReader.h" -#include <wtf/PassOwnPtr.h> namespace WebCore { @@ -41,21 +40,20 @@ namespace WebCore { // don't pack). static const size_t sizeOfFileHeader = 14; -BMPImageDecoder::BMPImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +BMPImageDecoder::BMPImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) , m_decodedOffset(0) { } -void BMPImageDecoder::setData(SharedBuffer* data, bool allDataReceived) +void BMPImageDecoder::setData(SharedBuffer& data, bool allDataReceived) { if (failed()) return; ImageDecoder::setData(data, allDataReceived); if (m_reader) - m_reader->setData(data); + m_reader->setData(&data); } bool BMPImageDecoder::isSizeAvailable() @@ -71,20 +69,18 @@ ImageFrame* BMPImageDecoder::frameBufferAtIndex(size_t index) if (index) return 0; - if (m_frameBufferCache.isEmpty()) { + if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(1); - m_frameBufferCache.first().setPremultiplyAlpha(m_premultiplyAlpha); - } ImageFrame* buffer = &m_frameBufferCache.first(); - if (buffer->status() != ImageFrame::FrameComplete) + if (!buffer->isComplete()) decode(false); return buffer; } bool BMPImageDecoder::setFailed() { - m_reader.clear(); + m_reader = nullptr; return ImageDecoder::setFailed(); } @@ -99,8 +95,8 @@ void BMPImageDecoder::decode(bool onlySize) setFailed(); // If we're done decoding the image, we don't need the BMPImageReader // anymore. (If we failed, |m_reader| has already been cleared.) - else if (!m_frameBufferCache.isEmpty() && (m_frameBufferCache.first().status() == ImageFrame::FrameComplete)) - m_reader.clear(); + else if (!m_frameBufferCache.isEmpty() && m_frameBufferCache.first().isComplete()) + m_reader = nullptr; } bool BMPImageDecoder::decodeHelper(bool onlySize) @@ -110,7 +106,7 @@ bool BMPImageDecoder::decodeHelper(bool onlySize) return false; if (!m_reader) { - m_reader = adoptPtr(new BMPImageReader(this, m_decodedOffset, imgDataOffset, false)); + m_reader = std::make_unique<BMPImageReader>(this, m_decodedOffset, imgDataOffset, false); m_reader->setData(m_data.get()); } diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h index f5b9d5e27..6b6270ce1 100644 --- a/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h @@ -28,28 +28,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BMPImageDecoder_h -#define BMPImageDecoder_h +#pragma once #include "BMPImageReader.h" -#include <wtf/OwnPtr.h> namespace WebCore { // This class decodes the BMP image format. - class BMPImageDecoder : public ImageDecoder { + class BMPImageDecoder final : public ImageDecoder { public: - BMPImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + BMPImageDecoder(AlphaOption, GammaAndColorProfileOption); // ImageDecoder - virtual String filenameExtension() const { return "bmp"; } - virtual void setData(SharedBuffer*, bool allDataReceived); - virtual bool isSizeAvailable(); - virtual ImageFrame* frameBufferAtIndex(size_t index); + String filenameExtension() const override { return "bmp"; } + void setData(SharedBuffer&, bool allDataReceived) override; + bool isSizeAvailable() override; + ImageFrame* frameBufferAtIndex(size_t index) override; // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid // accessing deleted memory, especially when calling this from inside // BMPImageReader! - virtual bool setFailed(); + bool setFailed() override; private: inline uint32_t readUint32(int offset) const @@ -77,9 +75,7 @@ namespace WebCore { size_t m_decodedOffset; // The reader used to do most of the BMP decoding. - OwnPtr<BMPImageReader> m_reader; + std::unique_ptr<BMPImageReader> m_reader; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp index 5c005534e..2212f14ff 100644 --- a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp +++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp @@ -77,17 +77,12 @@ 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() == ImageFrame::FrameEmpty) { - if (!m_buffer->setSize(m_parent->size().width(), m_parent->size().height())) + if (m_buffer->isEmpty()) { + if (!m_buffer->initialize(m_parent->size(), m_parent->premultiplyAlpha())) return m_parent->setFailed(); // Unable to allocate. - m_buffer->setStatus(ImageFrame::FramePartial); - // setSize() calls eraseARGB(), which resets the alpha flag, so we force - // it back to false here. We'll set it true below in all cases where - // these 0s could actually show through. - m_buffer->setHasAlpha(false); - // For BMPs, the frame always fills the entire image. - m_buffer->setOriginalFrameRect(IntRect(IntPoint(), m_parent->size())); + m_buffer->setDecoding(ImageFrame::Decoding::Partial); + m_buffer->setHasAlpha(false); if (!m_isTopDown) m_coord.setY(m_parent->size().height() - 1); @@ -122,7 +117,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) } // Done! - m_buffer->setStatus(ImageFrame::FrameComplete); + m_buffer->setDecoding(ImageFrame::Decoding::Complete); return true; } @@ -171,7 +166,7 @@ bool BMPImageReader::processInfoHeader() return m_parent->setFailed(); // Set our size. - if (!m_parent->setSize(m_infoHeader.biWidth, m_infoHeader.biHeight)) + if (!m_parent->setSize(IntSize(m_infoHeader.biWidth, m_infoHeader.biHeight))) return false; // For paletted images, bitmaps can set biClrUsed to 0 to mean "all @@ -674,7 +669,7 @@ BMPImageReader::ProcessingResult BMPImageReader::processNonRLEData(bool inRLE, i // transparent, on the assumption that most ICOs on the // web will not be doing a lot of inverting. if (colorIndex) { - setRGBA(0, 0, 0, 0); + setPixel(0, 0, 0, 0); m_buffer->setHasAlpha(true); } else m_coord.move(1, 0); @@ -707,13 +702,14 @@ BMPImageReader::ProcessingResult BMPImageReader::processNonRLEData(bool inRLE, i } else { m_seenNonZeroAlphaPixel = true; if (m_seenZeroAlphaPixel) { - m_buffer->zeroFillPixelData(); + m_buffer->backingStore()->clear(); + m_buffer->setHasAlpha(true); m_seenZeroAlphaPixel = false; } else if (alpha != 255) m_buffer->setHasAlpha(true); } - setRGBA(getComponent(pixel, 0), getComponent(pixel, 1), + setPixel(getComponent(pixel, 0), getComponent(pixel, 1), getComponent(pixel, 2), alpha); } } diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h index b5cb96e3f..03bcac60a 100644 --- a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h +++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h @@ -239,16 +239,16 @@ 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); + setPixel(m_colorTable[colorIndex].rgbRed, m_colorTable[colorIndex].rgbGreen, m_colorTable[colorIndex].rgbBlue, 0xff); } // Like setI(), but with the individual component values specified. - inline void setRGBA(unsigned red, + inline void setPixel(unsigned red, unsigned green, unsigned blue, unsigned alpha) { - m_buffer->setRGBA(m_coord.x(), m_coord.y(), red, green, blue, alpha); + m_buffer->backingStore()->setPixel(m_coord.x(), m_coord.y(), red, green, blue, alpha); m_coord.move(1, 0); } @@ -262,8 +262,10 @@ namespace WebCore { unsigned blue, unsigned alpha) { - while (m_coord.x() < endCoord) - setRGBA(red, green, blue, alpha); + if (endCoord <= m_coord.x()) + return; + m_buffer->backingStore()->fillRect(IntRect(m_coord.x(), m_coord.y(), endCoord - m_coord.x(), 1), red, green, blue, alpha); + m_coord.setX(endCoord); } // Resets the relevant local variables to start drawing at the left edge diff --git a/Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp b/Source/WebCore/platform/image-decoders/cairo/ImageBackingStoreCairo.cpp index 1d99a56fa..78032c4e2 100644 --- a/Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp +++ b/Source/WebCore/platform/image-decoders/cairo/ImageBackingStoreCairo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,17 +24,22 @@ */ #include "config.h" -#include "ImageDecoder.h" +#include "ImageBackingStore.h" #include <cairo.h> namespace WebCore { -PassNativeImagePtr ImageFrame::asNewNativeImage() const +NativeImagePtr ImageBackingStore::image() const { - return adoptRef(cairo_image_surface_create_for_data( - reinterpret_cast<unsigned char*>(const_cast<PixelData*>(m_bytes)), - CAIRO_FORMAT_ARGB32, width(), height(), width() * sizeof(PixelData))); + m_pixels->ref(); + RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create_for_data( + reinterpret_cast<unsigned char*>(const_cast<RGBA32*>(m_pixelsPtr)), + CAIRO_FORMAT_ARGB32, size().width(), size().height(), size().width() * sizeof(RGBA32))); + static cairo_user_data_key_t s_surfaceDataKey; + cairo_surface_set_user_data(surface.get(), &s_surfaceDataKey, m_pixels.get(), [](void* data) { static_cast<SharedBuffer*>(data)->deref(); }); + + return surface; } } // namespace WebCore diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index 07304769c..f8c9893ce 100644 --- a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,14 +28,11 @@ #include "GIFImageReader.h" #include <limits> -#include <wtf/PassOwnPtr.h> namespace WebCore { -GIFImageDecoder::GIFImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) - , m_repetitionCount(cAnimationLoopOnce) { } @@ -43,14 +40,14 @@ GIFImageDecoder::~GIFImageDecoder() { } -void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) +void GIFImageDecoder::setData(SharedBuffer& data, bool allDataReceived) { if (failed()) return; ImageDecoder::setData(data, allDataReceived); if (m_reader) - m_reader->setData(data); + m_reader->setData(&data); } bool GIFImageDecoder::isSizeAvailable() @@ -61,25 +58,25 @@ bool GIFImageDecoder::isSizeAvailable() return ImageDecoder::isSizeAvailable(); } -bool GIFImageDecoder::setSize(unsigned width, unsigned height) +bool GIFImageDecoder::setSize(const IntSize& size) { - if (ImageDecoder::isSizeAvailable() && size() == IntSize(width, height)) + if (ImageDecoder::isSizeAvailable() && this->size() == size) return true; - if (!ImageDecoder::setSize(width, height)) + if (!ImageDecoder::setSize(size)) return false; prepareScaleDataIfNecessary(); return true; } -size_t GIFImageDecoder::frameCount() +size_t GIFImageDecoder::frameCount() const { - decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery); + const_cast<GIFImageDecoder*>(this)->decode(std::numeric_limits<unsigned>::max(), GIFFrameCountQuery); return m_frameBufferCache.size(); } -int GIFImageDecoder::repetitionCount() const +RepetitionCount GIFImageDecoder::repetitionCount() const { // This value can arrive at any point in the image data stream. Most GIFs // in the wild declare it near the beginning of the file, so it usually is @@ -106,7 +103,7 @@ int GIFImageDecoder::repetitionCount() const // later in the stream. It is also possible that no frames are in the // stream. In these cases we should just loop once. if (failed() || (m_reader && (!m_reader->imagesCount()))) - m_repetitionCount = cAnimationLoopOnce; + m_repetitionCount = RepetitionCountOnce; else if (m_reader && m_reader->loopCount() != cLoopCountNotSeen) m_repetitionCount = m_reader->loopCount(); return m_repetitionCount; @@ -118,14 +115,14 @@ ImageFrame* GIFImageDecoder::frameBufferAtIndex(size_t index) return 0; ImageFrame& frame = m_frameBufferCache[index]; - if (frame.status() != ImageFrame::FrameComplete) + if (!frame.isComplete()) decode(index + 1, GIFFullQuery); return &frame; } bool GIFImageDecoder::setFailed() { - m_reader.clear(); + m_reader = nullptr; return ImageDecoder::setFailed(); } @@ -136,6 +133,10 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) if (m_frameBufferCache.isEmpty()) return; // Nothing to do. + // Lock the decodelock here, as we are going to destroy the GIFImageReader and doing so while + // there's an ongoing decode will cause a crash. + LockHolder locker(m_decodeLock); + // The "-1" here is tricky. It does not mean that |clearBeforeFrame| is the // last frame we wish to preserve, but rather that we never want to clear // the very last frame in the cache: it's empty (so clearing it is @@ -157,26 +158,30 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) // not to have non-empty frames after the frame we're currently decoding. // So, scan backwards from |end| as follows: // * If the frame is empty, we're still past any frames we care about. - // * If the frame is complete, but is DisposeOverwritePrevious, we'll + // * If the frame is complete, but is DisposalMethod::RestoreToPrevious, we'll // skip over it in future initFrameBuffer() calls. We can clear it // unless it's |end|, and keep scanning. For any other disposal method, // stop scanning, as we've found the frame initFrameBuffer() will need // next. // * If the frame is partial, we're decoding it, so don't clear it; if it - // has a disposal method other than DisposeOverwritePrevious, stop + // has a disposal method other than DisposalMethod::RestoreToPrevious, stop // scanning, as we'll only need this frame when decoding the next one. Vector<ImageFrame>::iterator i(end); - for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::FrameEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) { - if ((i->status() == ImageFrame::FrameComplete) && (i != end)) - i->clearPixelData(); + for (; (i != m_frameBufferCache.begin()) && (i->isEmpty() || (i->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)); --i) { + if (i->isComplete() && (i != end)) + i->clear(); } // Now |i| holds the last frame we need to preserve; clear prior frames. for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { - ASSERT(j->status() != ImageFrame::FramePartial); - if (j->status() != ImageFrame::FrameEmpty) - j->clearPixelData(); + ASSERT(!j->isPartial()); + if (j->isEmpty()) + j->clear(); } + + // When some frames are cleared, the reader is out of sync, since the caller might ask for any frame not + // necessarily in the order expected by the reader. See https://bugs.webkit.org/show_bug.cgi?id=159089. + m_reader = nullptr; } bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels) @@ -210,16 +215,16 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned // Initialize the frame if necessary. ImageFrame& buffer = m_frameBufferCache[frameIndex]; - if (((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex)) || !buffer.hasPixelData()) + if ((buffer.isEmpty() && !initFrameBuffer(frameIndex)) || !buffer.hasBackingStore()) return false; - ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); + RGBA32* currentAddress = buffer.backingStore()->pixelAt(xBegin, yBegin); // Write one row's worth of data into the frame. for (int x = xBegin; x < xEnd; ++x) { const unsigned char sourceValue = rowBuffer[(m_scaled ? m_scaledColumns[x] : x) - frameContext->xOffset]; if ((!frameContext->isTransparent || (sourceValue != frameContext->tpixel)) && (sourceValue < colorMapSize)) { const size_t colorIndex = static_cast<size_t>(sourceValue) * 3; - buffer.setRGBA(currentAddress, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255); + buffer.backingStore()->setPixel(currentAddress, colorMap[colorIndex], colorMap[colorIndex + 1], colorMap[colorIndex + 2], 255); } else { m_currentBufferSawAlpha = true; // We may or may not need to write transparent pixels to the buffer. @@ -230,34 +235,36 @@ bool GIFImageDecoder::haveDecodedRow(unsigned frameIndex, const Vector<unsigned // beyond the first, or the initial passes will "show through" the // later ones. if (writeTransparentPixels) - buffer.setRGBA(currentAddress, 0, 0, 0, 0); + buffer.backingStore()->setPixel(currentAddress, 0, 0, 0, 0); } ++currentAddress; } // Tell the frame to copy the row data if need be. if (repeatCount > 1) - buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); + buffer.backingStore()->repeatFirstRow(IntRect(xBegin, yBegin, xEnd - xBegin , yEnd - yBegin)); return true; } -bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod) +bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::DisposalMethod disposalMethod) { // Initialize the frame if necessary. Some GIFs insert do-nothing frames, // in which case we never reach haveDecodedRow() before getting here. ImageFrame& buffer = m_frameBufferCache[frameIndex]; - if ((buffer.status() == ImageFrame::FrameEmpty) && !initFrameBuffer(frameIndex)) + if (buffer.isEmpty() && !initFrameBuffer(frameIndex)) return false; // initFrameBuffer() has already called setFailed(). - buffer.setStatus(ImageFrame::FrameComplete); + buffer.setDecoding(ImageFrame::Decoding::Complete); buffer.setDuration(frameDuration); buffer.setDisposalMethod(disposalMethod); if (!m_currentBufferSawAlpha) { + IntRect rect = buffer.backingStore()->frameRect(); + // The whole frame was non-transparent, so it's possible that the entire // resulting buffer was non-transparent, and we can setHasAlpha(false). - if (buffer.originalFrameRect().contains(IntRect(IntPoint(), scaledSize()))) + if (rect.contains(IntRect(IntPoint(), scaledSize()))) buffer.setHasAlpha(false); else if (frameIndex) { // Tricky case. This frame does not have alpha only if everywhere @@ -265,22 +272,23 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // true, we check the start state of the frame -- if it doesn't have // alpha, we're safe. // - // First skip over prior DisposeOverwritePrevious frames (since they + // First skip over prior DisposalMethod::RestoreToPrevious frames (since they // don't affect the start state of this frame) the same way we do in // initFrameBuffer(). const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; - while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposeOverwritePrevious)) + while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)) prevBuffer = &m_frameBufferCache[--frameIndex]; - // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then + // Now, if we're at a DisposalMethod::Unspecified or DisposalMethod::DoNotDispose frame, then // we can say we have no alpha if that frame had no alpha. But // since in initFrameBuffer() we already copied that frame's alpha // state into the current frame's, we need do nothing at all here. // - // The only remaining case is a DisposeOverwriteBgcolor frame. If + // The only remaining case is a DisposalMethod::RestoreToBackground 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() == ImageFrame::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect())) + IntRect prevRect = prevBuffer->backingStore()->frameRect(); + if ((prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToBackground) && !prevBuffer->hasAlpha() && rect.contains(prevRect)) buffer.setHasAlpha(false); } } @@ -294,7 +302,7 @@ void GIFImageDecoder::gifComplete() // going to be. repetitionCount(); - m_reader.clear(); + m_reader = nullptr; } void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) @@ -302,9 +310,10 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) if (failed()) return; + LockHolder locker(m_decodeLock); if (!m_reader) { - m_reader = adoptPtr(new GIFImageReader(this)); - m_reader->setData(m_data); + m_reader = std::make_unique<GIFImageReader>(this); + m_reader->setData(m_data.get()); } if (query == GIFSizeQuery) { @@ -318,10 +327,7 @@ void GIFImageDecoder::decode(unsigned haltAtFrame, GIFQuery query) return; } - const size_t oldSize = m_frameBufferCache.size(); m_frameBufferCache.resize(m_reader->imagesCount()); - for (size_t i = oldSize; i < m_reader->imagesCount(); ++i) - m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha); if (query == GIFFrameCountQuery) return; @@ -342,66 +348,68 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) // Initialize the frame rect in our buffer. const GIFFrameContext* frameContext = m_reader->frameContext(); IntRect frameRect(frameContext->xOffset, frameContext->yOffset, frameContext->width, frameContext->height); - - // Make sure the frameRect doesn't extend outside the buffer. - if (frameRect.maxX() > size().width()) - frameRect.setWidth(size().width() - frameContext->xOffset); - if (frameRect.maxY() > size().height()) - frameRect.setHeight(size().height() - frameContext->yOffset); - ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; - int left = upperBoundScaledX(frameRect.x()); - int right = lowerBoundScaledX(frameRect.maxX(), left); - int top = upperBoundScaledY(frameRect.y()); - int bottom = lowerBoundScaledY(frameRect.maxY(), top); - buffer->setOriginalFrameRect(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())) + if (!buffer->initialize(scaledSize(), m_premultiplyAlpha)) return setFailed(); } else { // The starting state for this frame depends on the previous frame's // disposal method. // - // Frames that use the DisposeOverwritePrevious method are effectively + // Frames that use the DisposalMethod::RestoreToPrevious 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.) + // DisposalMethod::RestoreToBackground below and reset to a completely empty image.) const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; - ImageFrame::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod(); - while (frameIndex && (prevMethod == ImageFrame::DisposeOverwritePrevious)) { + ImageFrame::DisposalMethod prevMethod = prevBuffer->disposalMethod(); + while (frameIndex && (prevMethod == ImageFrame::DisposalMethod::RestoreToPrevious)) { prevBuffer = &m_frameBufferCache[--frameIndex]; prevMethod = prevBuffer->disposalMethod(); } - ASSERT(prevBuffer->status() == ImageFrame::FrameComplete); - if ((prevMethod == ImageFrame::DisposeNotSpecified) || (prevMethod == ImageFrame::DisposeKeep)) { + ASSERT(prevBuffer->isComplete()); + + if ((prevMethod == ImageFrame::DisposalMethod::Unspecified) || (prevMethod == ImageFrame::DisposalMethod::DoNotDispose)) { // Preserve the last frame as the starting state for this frame. - if (!buffer->copyBitmapData(*prevBuffer)) + if (!prevBuffer->backingStore() || !buffer->initialize(*prevBuffer->backingStore())) return setFailed(); } else { // We want to clear the previous frame to transparent, without // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->originalFrameRect(); + IntRect prevRect = prevBuffer->backingStore()->frameRect(); 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())) + if (!buffer->initialize(bufferSize, m_premultiplyAlpha)) return setFailed(); } else { // Copy the whole previous buffer, then clear just its frame. - if (!buffer->copyBitmapData(*prevBuffer)) + if (!prevBuffer->backingStore() || !buffer->initialize(*prevBuffer->backingStore())) return setFailed(); - buffer->zeroFillFrameRect(prevRect); + buffer->backingStore()->clearRect(prevRect); + buffer->setHasAlpha(true); } } } + // Make sure the frameRect doesn't extend outside the buffer. + if (frameRect.maxX() > size().width()) + frameRect.setWidth(size().width() - frameContext->xOffset); + if (frameRect.maxY() > size().height()) + frameRect.setHeight(size().height() - frameContext->yOffset); + + int left = upperBoundScaledX(frameRect.x()); + int right = lowerBoundScaledX(frameRect.maxX(), left); + int top = upperBoundScaledY(frameRect.y()); + int bottom = lowerBoundScaledY(frameRect.maxY(), top); + buffer->backingStore()->setFrameRect(IntRect(left, top, right - left, bottom - top)); + // Update our status to be partially complete. - buffer->setStatus(ImageFrame::FramePartial); + buffer->setDecoding(ImageFrame::Decoding::Partial); // Reset the alpha pixel tracker for this frame. m_currentBufferSawAlpha = false; diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h index f9e1b84d3..56a4010cc 100644 --- a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,41 +23,40 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GIFImageDecoder_h -#define GIFImageDecoder_h +#pragma once #include "ImageDecoder.h" -#include <wtf/OwnPtr.h> +#include <wtf/Lock.h> class GIFImageReader; namespace WebCore { // This class decodes the GIF image format. - class GIFImageDecoder : public ImageDecoder { + class GIFImageDecoder final : public ImageDecoder { public: - GIFImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + GIFImageDecoder(AlphaOption, GammaAndColorProfileOption); virtual ~GIFImageDecoder(); enum GIFQuery { GIFFullQuery, GIFSizeQuery, GIFFrameCountQuery }; // ImageDecoder - virtual String filenameExtension() const { return "gif"; } - virtual void setData(SharedBuffer* data, bool allDataReceived); - virtual bool isSizeAvailable(); - virtual bool setSize(unsigned width, unsigned height); - virtual size_t frameCount(); - virtual int repetitionCount() const; - virtual ImageFrame* frameBufferAtIndex(size_t index); + String filenameExtension() const override { return "gif"; } + void setData(SharedBuffer& data, bool allDataReceived) override; + bool isSizeAvailable() override; + bool setSize(const IntSize&) override; + size_t frameCount() const override; + RepetitionCount repetitionCount() const override; + ImageFrame* frameBufferAtIndex(size_t index) override; // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid // accessing deleted memory, especially when calling this from inside // GIFImageReader! - virtual bool setFailed(); - virtual void clearFrameBufferCache(size_t clearBeforeFrame); + bool setFailed() override; + void clearFrameBufferCache(size_t clearBeforeFrame) override; // Callbacks from the GIF reader. bool haveDecodedRow(unsigned frameIndex, const Vector<unsigned char>& rowBuffer, size_t width, size_t rowNumber, unsigned repeatCount, bool writeTransparentPixels); - bool frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::FrameDisposalMethod disposalMethod); + bool frameComplete(unsigned frameIndex, unsigned frameDuration, ImageFrame::DisposalMethod); void gifComplete(); private: @@ -73,10 +72,9 @@ namespace WebCore { bool initFrameBuffer(unsigned frameIndex); bool m_currentBufferSawAlpha; - mutable int m_repetitionCount; - OwnPtr<GIFImageReader> m_reader; + mutable RepetitionCount m_repetitionCount { RepetitionCountOnce }; + std::unique_ptr<GIFImageReader> m_reader; + Lock m_decodeLock; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp index b21bbf65c..0965859fe 100644 --- a/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp +++ b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp @@ -21,7 +21,7 @@ * * Contributor(s): * Chris Saari <saari@netscape.com> - * Apple Computer + * Apple Inc. * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -77,7 +77,6 @@ mailing address. #include <string.h> #include "GIFImageDecoder.h" -#include "ImageSource.h" using WebCore::GIFImageDecoder; @@ -322,9 +321,9 @@ bool GIFFrameContext::decode(const unsigned char* data, size_t length, WebCore:: if (!isDataSizeDefined() || !isHeaderDefined()) return true; - m_lzwContext = adoptPtr(new GIFLZWContext(client, this)); + m_lzwContext = std::make_unique<GIFLZWContext>(client, this); if (!m_lzwContext->prepareToDecode()) { - m_lzwContext.clear(); + m_lzwContext = nullptr; return false; } @@ -346,7 +345,7 @@ bool GIFFrameContext::decode(const unsigned char* data, size_t length, WebCore:: // There will be no more decoding for this frame so it's time to cleanup. if (isComplete()) { *frameDecoded = true; - m_lzwContext.clear(); + m_lzwContext = nullptr; } return true; } @@ -447,7 +446,7 @@ bool GIFImageReader::parse(size_t dataPosition, size_t len, bool parseSizeOnly) // CALLBACK: Inform the decoderplugin of our size. // Note: A subsequent frame might have dimensions larger than the "screen" dimensions. - if (m_client && !m_client->setSize(m_screenWidth, m_screenHeight)) + if (m_client && !m_client->setSize(WebCore::IntSize(m_screenWidth, m_screenHeight))) return false; m_screenBgcolor = currentComponent[5]; @@ -568,14 +567,14 @@ bool GIFImageReader::parse(size_t dataPosition, size_t len, bool parseSizeOnly) // We ignore the "user input" bit. - // NOTE: This relies on the values in the FrameDisposalMethod enum + // NOTE: This relies on the values in the DisposalMethod enum // matching those in the GIF spec! int disposalMethod = ((*currentComponent) >> 2) & 0x7; - currentFrame->disposalMethod = static_cast<WebCore::ImageFrame::FrameDisposalMethod>(disposalMethod); + currentFrame->disposalMethod = static_cast<WebCore::ImageFrame::DisposalMethod>(disposalMethod); // Some specs say that disposal method 3 is "overwrite previous", others that setting // the third bit of the field (i.e. method 4) is. We map both to the same value. if (disposalMethod == 4) - currentFrame->disposalMethod = WebCore::ImageFrame::DisposeOverwritePrevious; + currentFrame->disposalMethod = WebCore::ImageFrame::DisposalMethod::RestoreToPrevious; currentFrame->delayTime = GETINT16(currentComponent + 1) * 10; GETN(1, GIFConsumeBlock); break; @@ -624,7 +623,7 @@ bool GIFImageReader::parse(size_t dataPosition, size_t len, bool parseSizeOnly) // Zero loop count is infinite animation loop request. if (!m_loopCount) - m_loopCount = WebCore::cAnimationLoopInfinite; + m_loopCount = WebCore::RepetitionCountInfinite; GETN(1, GIFNetscapeExtensionBlock); } else if (netscapeExtension == 2) { @@ -664,7 +663,7 @@ bool GIFImageReader::parse(size_t dataPosition, size_t len, bool parseSizeOnly) yOffset = 0; // CALLBACK: Inform the decoderplugin of our size. - if (m_client && !m_client->setSize(m_screenWidth, m_screenHeight)) + if (m_client && !m_client->setSize(WebCore::IntSize(m_screenWidth, m_screenHeight))) return false; } @@ -781,7 +780,7 @@ void GIFImageReader::setRemainingBytes(size_t remainingBytes) void GIFImageReader::addFrameIfNecessary() { if (m_frames.isEmpty() || m_frames.last()->isComplete()) - m_frames.append(adoptPtr(new GIFFrameContext(m_frames.size()))); + m_frames.append(std::make_unique<GIFFrameContext>(m_frames.size())); } // FIXME: Move this method to close to doLZW(). diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h index bda8997d9..40b1d69f1 100644 --- a/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h +++ b/Source/WebCore/platform/image-decoders/gif/GIFImageReader.h @@ -35,15 +35,12 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef GIFImageReader_h -#define GIFImageReader_h +#pragma once // Define ourselves as the clientPtr. Mozilla just hacked their C++ callback class into this old C decoder, // so we will too. #include "GIFImageDecoder.h" #include "SharedBuffer.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> #define MAX_LZW_BITS 12 @@ -157,7 +154,7 @@ public: unsigned width; unsigned height; int tpixel; // Index of transparent pixel. - WebCore::ImageFrame::FrameDisposalMethod disposalMethod; // Restore to background, leave in place, etc. + WebCore::ImageFrame::DisposalMethod disposalMethod; // Restore to background, leave in place, etc. size_t localColormapPosition; // Per-image colormap. int localColormapSize; // Size of local colormap array. int datasize; @@ -176,7 +173,7 @@ public: , width(0) , height(0) , tpixel(0) - , disposalMethod(WebCore::ImageFrame::DisposeNotSpecified) + , disposalMethod(WebCore::ImageFrame::DisposalMethod::Unspecified) , localColormapPosition(0) , localColormapSize(0) , datasize(0) @@ -215,7 +212,7 @@ public: } private: - OwnPtr<GIFLZWContext> m_lzwContext; + std::unique_ptr<GIFLZWContext> m_lzwContext; Vector<GIFLZWBlock> m_lzwBlocks; // LZW blocks for this frame. size_t m_currentLzwBlock; bool m_isComplete; @@ -248,7 +245,7 @@ public: { } - void setData(PassRefPtr<WebCore::SharedBuffer> data) { m_data = data; } + void setData(WebCore::SharedBuffer* data) { m_data = data; } // FIXME: haltAtFrame should be size_t. bool decode(WebCore::GIFImageDecoder::GIFQuery, unsigned haltAtFrame); @@ -319,11 +316,9 @@ private: int m_globalColormapSize; // Size of global colormap array. int m_loopCount; // Netscape specific extension block to control the number of animation loops a GIF renders. - Vector<OwnPtr<GIFFrameContext> > m_frames; + Vector<std::unique_ptr<GIFFrameContext> > m_frames; size_t m_currentDecodingFrame; RefPtr<WebCore::SharedBuffer> m_data; bool m_parseCompleted; }; - -#endif diff --git a/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp index 33869f8c8..64c2c358a 100644 --- a/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp @@ -35,7 +35,6 @@ #include "BMPImageReader.h" #include "PNGImageDecoder.h" -#include <wtf/PassOwnPtr.h> namespace WebCore { @@ -45,8 +44,7 @@ namespace WebCore { static const size_t sizeOfDirectory = 6; static const size_t sizeOfDirEntry = 16; -ICOImageDecoder::ICOImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +ICOImageDecoder::ICOImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) , m_decodedOffset(0) { @@ -56,7 +54,7 @@ ICOImageDecoder::~ICOImageDecoder() { } -void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) +void ICOImageDecoder::setData(SharedBuffer& data, bool allDataReceived) { if (failed()) return; @@ -65,7 +63,7 @@ void ICOImageDecoder::setData(SharedBuffer* data, bool allDataReceived) for (BMPReaders::iterator i(m_bmpReaders.begin()); i != m_bmpReaders.end(); ++i) { if (*i) - (*i)->setData(data); + (*i)->setData(&data); } for (size_t i = 0; i < m_pngDecoders.size(); ++i) setDataForPNGDecoderAtIndex(i); @@ -79,34 +77,26 @@ bool ICOImageDecoder::isSizeAvailable() return ImageDecoder::isSizeAvailable(); } -IntSize ICOImageDecoder::size() const +IntSize ICOImageDecoder::size() { return m_frameSize.isEmpty() ? ImageDecoder::size() : m_frameSize; } -IntSize ICOImageDecoder::frameSizeAtIndex(size_t index) const +IntSize ICOImageDecoder::frameSizeAtIndex(size_t index, SubsamplingLevel) { return (index && (index < m_dirEntries.size())) ? m_dirEntries[index].m_size : size(); } -bool ICOImageDecoder::setSize(unsigned width, unsigned height) +bool ICOImageDecoder::setSize(const IntSize& size) { // The size calculated inside the BMPImageReader had better match the one in // the icon directory. - return m_frameSize.isEmpty() ? ImageDecoder::setSize(width, height) : ((IntSize(width, height) == m_frameSize) || setFailed()); + return m_frameSize.isEmpty() ? ImageDecoder::setSize(size) : ((size == m_frameSize) || setFailed()); } -size_t ICOImageDecoder::frameCount() +size_t ICOImageDecoder::frameCount() const { - decode(0, true); - if (m_frameBufferCache.isEmpty()) { - m_frameBufferCache.resize(m_dirEntries.size()); - for (size_t i = 0; i < m_dirEntries.size(); ++i) - m_frameBufferCache[i].setPremultiplyAlpha(m_premultiplyAlpha); - } - // CAUTION: We must not resize m_frameBufferCache again after this, as - // decodeAtIndex() may give a BMPImageReader a pointer to one of the - // entries. + const_cast<ICOImageDecoder*>(this)->decode(0, true); return m_frameBufferCache.size(); } @@ -117,7 +107,7 @@ ImageFrame* ICOImageDecoder::frameBufferAtIndex(size_t index) return 0; ImageFrame* buffer = &m_frameBufferCache[index]; - if (buffer->status() != ImageFrame::FrameComplete) + if (!buffer->isComplete()) decode(index, false); return buffer; } @@ -129,21 +119,20 @@ bool ICOImageDecoder::setFailed() return ImageDecoder::setFailed(); } -bool ICOImageDecoder::hotSpot(IntPoint& hotSpot) const +std::optional<IntPoint> ICOImageDecoder::hotSpot() const { // When unspecified, the default frame is always frame 0. This is consistent with // BitmapImage where currentFrame() starts at 0 and only increases when animation is // requested. - return hotSpotAtIndex(0, hotSpot); + return hotSpotAtIndex(0); } -bool ICOImageDecoder::hotSpotAtIndex(size_t index, IntPoint& hotSpot) const +std::optional<IntPoint> ICOImageDecoder::hotSpotAtIndex(size_t index) const { if (index >= m_dirEntries.size() || m_fileType != CURSOR) - return false; + return std::nullopt; - hotSpot = m_dirEntries[index].m_hotSpot; - return true; + return m_dirEntries[index].m_hotSpot; } @@ -166,7 +155,7 @@ void ICOImageDecoder::setDataForPNGDecoderAtIndex(size_t index) // FIXME: Save this copy by making the PNG decoder able to take an // optional offset. RefPtr<SharedBuffer> pngData(SharedBuffer::create(&m_data->data()[dirEntry.m_imageOffset], m_data->size() - dirEntry.m_imageOffset)); - m_pngDecoders[index]->setData(pngData.get(), isAllDataReceived()); + m_pngDecoders[index]->setData(*pngData, isAllDataReceived()); } void ICOImageDecoder::decode(size_t index, bool onlySize) @@ -181,10 +170,16 @@ void ICOImageDecoder::decode(size_t index, bool onlySize) // If we're done decoding this frame, we don't need the BMPImageReader or // PNGImageDecoder anymore. (If we failed, these have already been // cleared.) - else if ((m_frameBufferCache.size() > index) && (m_frameBufferCache[index].status() == ImageFrame::FrameComplete)) { - m_bmpReaders[index].clear(); - m_pngDecoders[index].clear(); + else if ((m_frameBufferCache.size() > index) && m_frameBufferCache[index].isComplete()) { + m_bmpReaders[index] = nullptr; + m_pngDecoders[index] = nullptr; } + + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.resize(m_dirEntries.size()); + // CAUTION: We must not resize m_frameBufferCache again after this, as + // decodeAtIndex() may give a BMPImageReader a pointer to one of the + // entries. } bool ICOImageDecoder::decodeDirectory() @@ -210,7 +205,7 @@ bool ICOImageDecoder::decodeAtIndex(size_t 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] = adoptPtr(new BMPImageReader(this, dirEntry.m_imageOffset, 0, true)); + m_bmpReaders[index] = std::make_unique<BMPImageReader>(this, dirEntry.m_imageOffset, 0, true); m_bmpReaders[index]->setData(m_data.get()); m_bmpReaders[index]->setBuffer(&m_frameBufferCache[index]); } @@ -221,9 +216,9 @@ bool ICOImageDecoder::decodeAtIndex(size_t index) } if (!m_pngDecoders[index]) { - m_pngDecoders[index] = adoptPtr( - new PNGImageDecoder(m_premultiplyAlpha ? ImageSource::AlphaPremultiplied : ImageSource::AlphaNotPremultiplied, - m_ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied)); + m_pngDecoders[index] = std::make_unique< + PNGImageDecoder>(m_premultiplyAlpha ? AlphaOption::Premultiplied : AlphaOption::NotPremultiplied, + m_ignoreGammaAndColorProfile ? GammaAndColorProfileOption::Ignored : GammaAndColorProfileOption::Applied); setDataForPNGDecoderAtIndex(index); } // Fail if the size the PNGImageDecoder calculated does not match the size @@ -281,7 +276,7 @@ bool ICOImageDecoder::processDirectoryEntries() const IconDirectoryEntry& dirEntry = m_dirEntries.first(); // Technically, this next call shouldn't be able to fail, since the width // and height here are each <= 256, and |m_frameSize| is empty. - return setSize(dirEntry.m_size.width(), dirEntry.m_size.height()); + return setSize(dirEntry.m_size); } ICOImageDecoder::IconDirectoryEntry ICOImageDecoder::readDirectoryEntry() diff --git a/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h index a909deb80..b1bc5ade6 100644 --- a/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ICOImageDecoder_h -#define ICOImageDecoder_h +#pragma once #include "BMPImageReader.h" @@ -38,25 +37,25 @@ namespace WebCore { class PNGImageDecoder; // This class decodes the ICO and CUR image formats. - class ICOImageDecoder : public ImageDecoder { + class ICOImageDecoder final : public ImageDecoder { public: - ICOImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + ICOImageDecoder(AlphaOption, GammaAndColorProfileOption); virtual ~ICOImageDecoder(); // ImageDecoder - virtual String filenameExtension() const { return "ico"; } - virtual void setData(SharedBuffer*, bool allDataReceived); - virtual bool isSizeAvailable(); - virtual IntSize size() const; - virtual IntSize frameSizeAtIndex(size_t) const; - virtual bool setSize(unsigned width, unsigned height); - virtual size_t frameCount(); - virtual ImageFrame* frameBufferAtIndex(size_t); + String filenameExtension() const override { return "ico"; } + void setData(SharedBuffer&, bool allDataReceived) override; + bool isSizeAvailable() override; + IntSize size() override; + IntSize frameSizeAtIndex(size_t, SubsamplingLevel) override; + bool setSize(const IntSize&) override; + size_t frameCount() const override; + ImageFrame* frameBufferAtIndex(size_t) override; // CAUTION: setFailed() deletes all readers and decoders. Be careful to // avoid accessing deleted memory, especially when calling this from // inside BMPImageReader! - virtual bool setFailed(); - virtual bool hotSpot(IntPoint&) const; + bool setFailed() override; + std::optional<IntPoint> hotSpot() const override; private: enum ImageType { @@ -116,9 +115,8 @@ namespace WebCore { // could be decoded. bool processDirectoryEntries(); - // Stores the hot-spot for |index| in |hotSpot| and returns true, - // or returns false if there is none. - bool hotSpotAtIndex(size_t index, IntPoint& hotSpot) const; + // Returns the hot-spot for |index|, returns std::nullopt if there is none. + std::optional<IntPoint> hotSpotAtIndex(size_t) const; // Reads and returns a directory entry from the current offset into // |data|. @@ -141,9 +139,9 @@ namespace WebCore { IconDirectoryEntries m_dirEntries; // The image decoders for the various frames. - typedef Vector<OwnPtr<BMPImageReader> > BMPReaders; + typedef Vector<std::unique_ptr<BMPImageReader>> BMPReaders; BMPReaders m_bmpReaders; - typedef Vector<OwnPtr<PNGImageDecoder> > PNGDecoders; + typedef Vector<std::unique_ptr<PNGImageDecoder>> PNGDecoders; PNGDecoders m_pngDecoders; // Valid only while a BMPImageReader is decoding, this holds the size @@ -152,5 +150,3 @@ namespace WebCore { }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp index 38e18d3ee..40bd88bbc 100644 --- a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * * Portions are Copyright (C) 2001-6 mozilla.org * @@ -39,14 +39,10 @@ #include "config.h" #include "JPEGImageDecoder.h" -#include <wtf/PassOwnPtr.h> extern "C" { #if USE(ICCJPEG) -#include "iccjpeg.h" -#endif -#if USE(QCMSLIB) -#include "qcms.h" +#include <iccjpeg.h> #endif #include <setjmp.h> } @@ -200,36 +196,6 @@ static ImageOrientation readImageOrientation(jpeg_decompress_struct* info) return ImageOrientation(); } -static ColorProfile readColorProfile(jpeg_decompress_struct* info) -{ -#if USE(ICCJPEG) - JOCTET* profile; - unsigned int profileLength; - - if (!read_icc_profile(info, &profile, &profileLength)) - return ColorProfile(); - - // Only accept RGB color profiles from input class devices. - bool ignoreProfile = false; - char* profileData = reinterpret_cast<char*>(profile); - if (profileLength < ImageDecoder::iccColorProfileHeaderLength) - ignoreProfile = true; - else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) - ignoreProfile = true; - else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) - ignoreProfile = true; - - ColorProfile colorProfile; - if (!ignoreProfile) - colorProfile.append(profileData, profileLength); - free(profile); - return colorProfile; -#else - UNUSED_PARAM(info); - return ColorProfile(); -#endif -} - class JPEGImageReader { WTF_MAKE_FAST_ALLOCATED; public: @@ -239,9 +205,6 @@ public: , m_bytesToSkip(0) , m_state(JPEG_HEADER) , m_samples(0) -#if USE(QCMSLIB) - , m_transform(0) -#endif { memset(&m_info, 0, sizeof(jpeg_decompress_struct)); @@ -292,11 +255,6 @@ public: fastFree(src); m_info.src = 0; -#if USE(QCMSLIB) - if (m_transform) - qcms_transform_release(m_transform); - m_transform = 0; -#endif jpeg_destroy_decompress(&m_info); } @@ -333,7 +291,7 @@ public: switch (m_state) { case JPEG_HEADER: // Read file parameters with jpeg_read_header(). - if (jpeg_read_header(&m_info, true) == JPEG_SUSPENDED) + if (jpeg_read_header(&m_info, TRUE) == JPEG_SUSPENDED) return false; // I/O suspension. switch (m_info.jpeg_color_space) { @@ -364,7 +322,7 @@ public: m_state = JPEG_START_DECOMPRESS; // We can fill in the size now that the header is available. - if (!m_decoder->setSize(m_info.image_width, m_info.image_height)) + if (!m_decoder->setSize(IntSize(m_info.image_width, m_info.image_height))) return false; m_decoder->setOrientation(readImageOrientation(info())); @@ -375,21 +333,6 @@ public: if (m_decoder->willDownSample() && turboSwizzled(m_info.out_color_space)) m_info.out_color_space = JCS_RGB; #endif - // Allow color management of the decoded RGBA pixels if possible. - if (!m_decoder->ignoresGammaAndColorProfile()) { - ColorProfile rgbInputDeviceColorProfile = readColorProfile(info()); - if (!rgbInputDeviceColorProfile.isEmpty()) - m_decoder->setColorProfile(rgbInputDeviceColorProfile); -#if USE(QCMSLIB) - createColorTransform(rgbInputDeviceColorProfile, colorSpaceHasAlpha(m_info.out_color_space)); -#if defined(TURBO_JPEG_RGB_SWIZZLE) - // Input RGBA data to qcms. Note: restored to BGRA on output. - if (m_transform && m_info.out_color_space == JCS_EXT_BGRA) - m_info.out_color_space = JCS_EXT_RGBA; -#endif -#endif - } - // 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); @@ -419,9 +362,9 @@ public: // of progressive JPEG. m_info.dct_method = dctMethod(); m_info.dither_mode = ditherMode(); - m_info.do_fancy_upsampling = doFancyUpsampling(); - m_info.enable_2pass_quant = false; - m_info.do_block_smoothing = true; + m_info.do_fancy_upsampling = doFancyUpsampling() ? TRUE : FALSE; + m_info.enable_2pass_quant = FALSE; + m_info.do_block_smoothing = TRUE; // Start decompressor. if (!jpeg_start_decompress(&m_info)) @@ -467,7 +410,13 @@ public: if (m_info.output_scanline == 0xffffff) m_info.output_scanline = 0; - if (!m_decoder->outputScanlines()) { + // If outputScanlines() fails, it deletes |this|. Therefore, + // copy the decoder pointer and use it to check for failure + // to avoid member access in the failure case. + JPEGImageDecoder* decoder = m_decoder; + if (!decoder->outputScanlines()) { + if (decoder->failed()) // Careful; |this| is deleted. + return false; if (!m_info.output_scanline) // Didn't manage to read any lines - flag so we // don't call jpeg_start_output() multiple times for @@ -506,31 +455,6 @@ public: jpeg_decompress_struct* info() { return &m_info; } JSAMPARRAY samples() const { return m_samples; } JPEGImageDecoder* decoder() { return m_decoder; } -#if USE(QCMSLIB) - qcms_transform* colorTransform() const { return m_transform; } - - void createColorTransform(const ColorProfile& colorProfile, bool hasAlpha) - { - if (m_transform) - qcms_transform_release(m_transform); - m_transform = 0; - - if (colorProfile.isEmpty()) - return; - qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); - if (!deviceProfile) - return; - qcms_profile* inputProfile = qcms_profile_from_memory(colorProfile.data(), colorProfile.size()); - if (!inputProfile) - return; - // We currently only support color profiles for RGB profiled images. - ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); - qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_8; - // FIXME: Don't force perceptual intent if the image profile contains an intent. - m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProfile, dataFormat, QCMS_INTENT_PERCEPTUAL); - qcms_profile_release(inputProfile); - } -#endif private: JPEGImageDecoder* m_decoder; @@ -543,10 +467,6 @@ private: jstate m_state; JSAMPARRAY m_samples; - -#if USE(QCMSLIB) - qcms_transform* m_transform; -#endif }; // Override the standard error method in the IJG JPEG decoder code. @@ -572,7 +492,7 @@ boolean fill_input_buffer(j_decompress_ptr) // Our decode step always sets things up properly, so if this method is ever // called, then we have hit the end of the buffer. A return value of false // indicates that we have no data to supply yet. - return false; + return FALSE; } void term_source(j_decompress_ptr jd) @@ -581,8 +501,7 @@ void term_source(j_decompress_ptr jd) src->decoder->decoder()->jpegComplete(); } -JPEGImageDecoder::JPEGImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +JPEGImageDecoder::JPEGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) { } @@ -599,9 +518,9 @@ bool JPEGImageDecoder::isSizeAvailable() return ImageDecoder::isSizeAvailable(); } -bool JPEGImageDecoder::setSize(unsigned width, unsigned height) +bool JPEGImageDecoder::setSize(const IntSize& size) { - if (!ImageDecoder::setSize(width, height)) + if (!ImageDecoder::setSize(size)) return false; prepareScaleDataIfNecessary(); @@ -613,31 +532,29 @@ ImageFrame* JPEGImageDecoder::frameBufferAtIndex(size_t index) if (index) return 0; - if (m_frameBufferCache.isEmpty()) { + if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(1); - m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); - } ImageFrame& frame = m_frameBufferCache[0]; - if (frame.status() != ImageFrame::FrameComplete) + if (!frame.isComplete()) decode(false); return &frame; } bool JPEGImageDecoder::setFailed() { - m_reader.clear(); + m_reader = nullptr; return ImageDecoder::setFailed(); } template <J_COLOR_SPACE colorSpace> -void setPixel(ImageFrame& buffer, ImageFrame::PixelData* currentAddress, JSAMPARRAY samples, int column) +void setPixel(ImageFrame& buffer, RGBA32* currentAddress, JSAMPARRAY samples, int column) { JSAMPLE* jsample = *samples + column * (colorSpace == JCS_RGB ? 3 : 4); switch (colorSpace) { case JCS_RGB: - buffer.setRGBA(currentAddress, jsample[0], jsample[1], jsample[2], 0xFF); + buffer.backingStore()->setPixel(currentAddress, jsample[0], jsample[1], jsample[2], 0xFF); break; case JCS_CMYK: // Source is 'Inverted CMYK', output is RGB. @@ -650,7 +567,7 @@ void setPixel(ImageFrame& buffer, ImageFrame::PixelData* currentAddress, JSAMPAR // From CMY (0..1) to RGB (0..1): // R = 1 - C => 1 - (1 - iC*iK) => iC*iK [G and B similar] unsigned k = jsample[3]; - buffer.setRGBA(currentAddress, jsample[0] * k / 255, jsample[1] * k / 255, jsample[2] * k / 255, 0xFF); + buffer.backingStore()->setPixel(currentAddress, jsample[0] * k / 255, jsample[1] * k / 255, jsample[2] * k / 255, 0xFF); break; } } @@ -674,12 +591,7 @@ bool JPEGImageDecoder::outputScanlines(ImageFrame& buffer) if (destY < 0) continue; -#if USE(QCMSLIB) - if (m_reader->colorTransform() && colorSpace == JCS_RGB) - qcms_transform_data(m_reader->colorTransform(), *samples, *samples, info->output_width); -#endif - - ImageFrame::PixelData* currentAddress = buffer.getAddr(0, destY); + RGBA32* currentAddress = buffer.backingStore()->pixelAt(0, destY); for (int x = 0; x < width; ++x) { setPixel<colorSpace>(buffer, currentAddress, samples, isScaled ? m_scaledColumns[x] : x); ++currentAddress; @@ -701,17 +613,13 @@ bool JPEGImageDecoder::outputScanlines() // Initialize the framebuffer if needed. ImageFrame& buffer = m_frameBufferCache[0]; - if (buffer.status() == ImageFrame::FrameEmpty) { - if (!buffer.setSize(scaledSize().width(), scaledSize().height())) + if (buffer.isEmpty()) { + if (!buffer.initialize(scaledSize(), m_premultiplyAlpha)) return setFailed(); - buffer.setStatus(ImageFrame::FramePartial); + buffer.setDecoding(ImageFrame::Decoding::Partial); // The buffer is transparent outside the decoded area while the image is // loading. The completed image will be marked fully opaque in jpegComplete(). buffer.setHasAlpha(true); - buffer.setColorProfile(m_colorProfile); - - // For JPEGs, the frame always fills the entire image. - buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } jpeg_decompress_struct* info = m_reader->info(); @@ -719,13 +627,9 @@ bool JPEGImageDecoder::outputScanlines() #if defined(TURBO_JPEG_RGB_SWIZZLE) if (!m_scaled && turboSwizzled(info->out_color_space)) { while (info->output_scanline < info->output_height) { - unsigned char* row = reinterpret_cast<unsigned char*>(buffer.getAddr(0, info->output_scanline)); + unsigned char* row = reinterpret_cast<unsigned char*>(buffer.backingStore()->pixelAt(0, info->output_scanline)); if (jpeg_read_scanlines(info, &row, 1) != 1) return false; -#if USE(QCMSLIB) - if (qcms_transform* transform = m_reader->colorTransform()) - qcms_transform_data_type(transform, row, row, info->output_width, rgbOutputColorSpace() == JCS_EXT_BGRA ? QCMS_OUTPUT_BGRX : QCMS_OUTPUT_RGBX); -#endif } return true; } @@ -756,7 +660,7 @@ void JPEGImageDecoder::jpegComplete() // empty. ImageFrame& buffer = m_frameBufferCache[0]; buffer.setHasAlpha(false); - buffer.setStatus(ImageFrame::FrameComplete); + buffer.setDecoding(ImageFrame::Decoding::Complete); } void JPEGImageDecoder::decode(bool onlySize) @@ -765,7 +669,7 @@ void JPEGImageDecoder::decode(bool onlySize) return; if (!m_reader) - m_reader = adoptPtr(new JPEGImageReader(this)); + m_reader = std::make_unique<JPEGImageReader>(this); // If we couldn't decode the image but we've received all the data, decoding // has failed. @@ -773,8 +677,8 @@ void JPEGImageDecoder::decode(bool onlySize) setFailed(); // If we're done decoding the image, we don't need the JPEGImageReader // anymore. (If we failed, |m_reader| has already been cleared.) - else if (!m_frameBufferCache.isEmpty() && (m_frameBufferCache[0].status() == ImageFrame::FrameComplete)) - m_reader.clear(); + else if (!m_frameBufferCache.isEmpty() && (m_frameBufferCache[0].isComplete())) + m_reader = nullptr; } } diff --git a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h index b1f61a14d..2d79532af 100644 --- a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * Copyright (C) 2008-2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,22 +24,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JPEGImageDecoder_h -#define JPEGImageDecoder_h +#pragma once #include "ImageDecoder.h" #include <stdio.h> // Needed by jpeglib.h for FILE. -#include <wtf/OwnPtr.h> - -#if OS(WINCE) -// Remove warning: 'FAR' macro redefinition -#undef FAR - -// jmorecfg.h in libjpeg checks for XMD_H with the comment: "X11/xmd.h correctly defines INT32" -// fix INT32 redefinition error by pretending we are X11/xmd.h -#define XMD_H -#endif +// ICU defines TRUE and FALSE macros, breaking libjpeg v9 headers +#undef TRUE +#undef FALSE extern "C" { #include "jpeglib.h" } @@ -49,20 +41,20 @@ namespace WebCore { class JPEGImageReader; // This class decodes the JPEG image format. - class JPEGImageDecoder : public ImageDecoder { + class JPEGImageDecoder final : public ImageDecoder { public: - JPEGImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + JPEGImageDecoder(AlphaOption, GammaAndColorProfileOption); virtual ~JPEGImageDecoder(); // ImageDecoder - virtual String filenameExtension() const { return "jpg"; } - virtual bool isSizeAvailable(); - virtual bool setSize(unsigned width, unsigned height); - virtual ImageFrame* frameBufferAtIndex(size_t index); + String filenameExtension() const override { return "jpg"; } + bool isSizeAvailable() override; + bool setSize(const IntSize&) override; + ImageFrame* frameBufferAtIndex(size_t index) override; // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid // accessing deleted memory, especially when calling this from inside // JPEGImageReader! - virtual bool setFailed(); + bool setFailed() override; bool willDownSample() { @@ -73,7 +65,6 @@ namespace WebCore { bool outputScanlines(); void jpegComplete(); - void setColorProfile(const ColorProfile& colorProfile) { m_colorProfile = colorProfile; } void setOrientation(ImageOrientation orientation) { m_orientation = orientation; } private: @@ -88,9 +79,7 @@ namespace WebCore { template <J_COLOR_SPACE colorSpace, bool isScaled> bool outputScanlines(ImageFrame& buffer); - OwnPtr<JPEGImageReader> m_reader; + std::unique_ptr<JPEGImageReader> m_reader; }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index 0c2b2d5d2..0abcaf2bd 100644 --- a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006 Apple Inc. * Copyright (C) 2007-2009 Torch Mobile, Inc. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * @@ -7,6 +7,7 @@ * * Other contributors: * Stuart Parmenter <stuart@mozilla.com> + * Max Stepin <maxstepin@gmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -41,14 +42,9 @@ #include "PNGImageDecoder.h" #include "Color.h" -#include "png.h" -#include <wtf/PassOwnPtr.h> +#include <png.h> #include <wtf/StdLibExtras.h> -#if USE(QCMSLIB) -#include "qcms.h" -#endif - #if defined(PNG_LIBPNG_VER_MAJOR) && defined(PNG_LIBPNG_VER_MINOR) && (PNG_LIBPNG_VER_MAJOR > 1 || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4)) #define JMPBUF(png_ptr) png_jmpbuf(png_ptr) #else @@ -101,6 +97,21 @@ static void PNGAPI pngComplete(png_structp png, png_infop) static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->pngComplete(); } +#if ENABLE(APNG) +// Called when we have the frame header. +static void PNGAPI frameHeader(png_structp png, png_infop) +{ + static_cast<PNGImageDecoder*>(png_get_progressive_ptr(png))->frameHeader(); +} + +// Called when we found user chunks. +static int PNGAPI readChunks(png_structp png, png_unknown_chunkp chunk) +{ + static_cast<PNGImageDecoder*>(png_get_user_chunk_ptr(png))->readChunks(chunk); + return 1; +} +#endif + class PNGImageReader { WTF_MAKE_FAST_ALLOCATED; public: @@ -110,14 +121,16 @@ public: , m_decodingSizeOnly(false) , m_hasAlpha(false) , m_interlaceBuffer(0) -#if USE(QCMSLIB) - , m_transform(0) - , m_rowBuffer() -#endif { m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, decodingWarning); m_info = png_create_info_struct(m_png); png_set_progressive_read_fn(m_png, decoder, headerAvailable, rowAvailable, pngComplete); +#if ENABLE(APNG) + png_byte apngChunks[]= {"acTL\0fcTL\0fdAT\0"}; + png_set_keep_unknown_chunks(m_png, 1, apngChunks, 3); + png_set_read_user_chunk_fn(m_png, static_cast<png_voidp>(decoder), readChunks); + decoder->init(); +#endif } ~PNGImageReader() @@ -130,17 +143,12 @@ public: if (m_png && m_info) // This will zero the pointers. png_destroy_read_struct(&m_png, &m_info, 0); -#if USE(QCMSLIB) - if (m_transform) - qcms_transform_release(m_transform); - m_transform = 0; -#endif delete[] m_interlaceBuffer; m_interlaceBuffer = 0; m_readOffset = 0; } - bool decode(const SharedBuffer& data, bool sizeOnly) + bool decode(const SharedBuffer& data, bool sizeOnly, unsigned haltAtFrame) { m_decodingSizeOnly = sizeOnly; PNGImageDecoder* decoder = static_cast<PNGImageDecoder*>(png_get_progressive_ptr(m_png)); @@ -157,7 +165,7 @@ public: // We explicitly specify the superclass isSizeAvailable() because we // merely want to check if we've managed to set the size, not // (recursively) trigger additional decoding if we haven't. - if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isComplete()) + if (sizeOnly ? decoder->ImageDecoder::isSizeAvailable() : decoder->isCompleteAtIndex(haltAtFrame)) return true; } return false; @@ -174,33 +182,6 @@ public: png_bytep interlaceBuffer() const { return m_interlaceBuffer; } void createInterlaceBuffer(int size) { m_interlaceBuffer = new png_byte[size]; } -#if USE(QCMSLIB) - png_bytep rowBuffer() const { return m_rowBuffer.get(); } - void createRowBuffer(int size) { m_rowBuffer = std::make_unique<png_byte[]>(size); } - qcms_transform* colorTransform() const { return m_transform; } - - void createColorTransform(const ColorProfile& colorProfile, bool hasAlpha) - { - if (m_transform) - qcms_transform_release(m_transform); - m_transform = 0; - - if (colorProfile.isEmpty()) - return; - qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); - if (!deviceProfile) - return; - qcms_profile* inputProfile = qcms_profile_from_memory(colorProfile.data(), colorProfile.size()); - if (!inputProfile) - return; - // We currently only support color profiles for RGB and RGBA images. - ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); - qcms_data_type dataFormat = hasAlpha ? QCMS_DATA_RGBA_8 : QCMS_DATA_RGB_8; - // FIXME: Don't force perceptual intent if the image profile contains an intent. - m_transform = qcms_transform_create(inputProfile, dataFormat, deviceProfile, dataFormat, QCMS_INTENT_PERCEPTUAL); - qcms_profile_release(inputProfile); - } -#endif private: png_structp m_png; @@ -210,16 +191,35 @@ private: bool m_decodingSizeOnly; bool m_hasAlpha; png_bytep m_interlaceBuffer; -#if USE(QCMSLIB) - qcms_transform* m_transform; - std::unique_ptr<png_byte[]> m_rowBuffer; -#endif }; -PNGImageDecoder::PNGImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +PNGImageDecoder::PNGImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) , m_doNothingOnFailure(false) + , m_currentFrame(0) +#if ENABLE(APNG) + , m_png(nullptr) + , m_info(nullptr) + , m_isAnimated(false) + , m_frameInfo(false) + , m_frameIsHidden(false) + , m_hasInfo(false) + , m_gamma(45455) + , m_frameCount(1) + , m_playCount(0) + , m_totalFrames(0) + , m_sizePLTE(0) + , m_sizetRNS(0) + , m_sequenceNumber(0) + , m_width(0) + , m_height(0) + , m_xOffset(0) + , m_yOffset(0) + , m_delayNumerator(1) + , m_delayDenominator(1) + , m_dispose(0) + , m_blend(0) +#endif { } @@ -227,17 +227,29 @@ PNGImageDecoder::~PNGImageDecoder() { } +#if ENABLE(APNG) +RepetitionCount PNGImageDecoder::repetitionCount() const +{ + // APNG format uses 0 to indicate that an animation must play indefinitely. But + // the RepetitionCount enumeration uses RepetitionCountInfinite, so we need to adapt this. + if (!m_playCount) + return RepetitionCountInfinite; + + return m_playCount; +} +#endif + bool PNGImageDecoder::isSizeAvailable() { if (!ImageDecoder::isSizeAvailable()) - decode(true); + decode(true, 0); return ImageDecoder::isSizeAvailable(); } -bool PNGImageDecoder::setSize(unsigned width, unsigned height) +bool PNGImageDecoder::setSize(const IntSize& size) { - if (!ImageDecoder::setSize(width, height)) + if (!ImageDecoder::setSize(size)) return false; prepareScaleDataIfNecessary(); @@ -246,17 +258,23 @@ bool PNGImageDecoder::setSize(unsigned width, unsigned height) ImageFrame* PNGImageDecoder::frameBufferAtIndex(size_t index) { +#if ENABLE(APNG) + if (!isSizeAvailable()) + return nullptr; + + if (index >= frameCount()) + index = frameCount() - 1; +#else if (index) - return 0; + return nullptr; +#endif - if (m_frameBufferCache.isEmpty()) { + if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(1); - m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); - } - ImageFrame& frame = m_frameBufferCache[0]; - if (frame.status() != ImageFrame::FrameComplete) - decode(false); + ImageFrame& frame = m_frameBufferCache[index]; + if (!frame.isComplete()) + decode(false, index); return &frame; } @@ -264,41 +282,10 @@ bool PNGImageDecoder::setFailed() { if (m_doNothingOnFailure) return false; - m_reader.clear(); + m_reader = nullptr; return ImageDecoder::setFailed(); } -static void readColorProfile(png_structp png, png_infop info, ColorProfile& colorProfile) -{ - ASSERT(colorProfile.isEmpty()); - -#ifdef PNG_iCCP_SUPPORTED - char* profileName; - int compressionType; -#if (PNG_LIBPNG_VER < 10500) - png_charp profile; -#else - png_bytep profile; -#endif - png_uint_32 profileLength; - if (!png_get_iCCP(png, info, &profileName, &compressionType, &profile, &profileLength)) - return; - - // Only accept RGB color profiles from input class devices. - bool ignoreProfile = false; - char* profileData = reinterpret_cast<char*>(profile); - if (profileLength < ImageDecoder::iccColorProfileHeaderLength) - ignoreProfile = true; - else if (!ImageDecoder::rgbColorProfile(profileData, profileLength)) - ignoreProfile = true; - else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileLength)) - ignoreProfile = true; - - if (!ignoreProfile) - colorProfile.append(profileData, profileLength); -#endif -} - void PNGImageDecoder::headerAvailable() { png_structp png = m_reader->pngPtr(); @@ -318,7 +305,7 @@ void PNGImageDecoder::headerAvailable() // will cease to exist. Note that we'll still properly set the failure flag // in this case as soon as we longjmp(). m_doNothingOnFailure = true; - bool result = setSize(width, height); + bool result = setSize(IntSize(width, height)); m_doNothingOnFailure = false; if (!result) { longjmp(JMPBUF(png), 1); @@ -330,14 +317,64 @@ void PNGImageDecoder::headerAvailable() // The options we set here match what Mozilla does. +#if ENABLE(APNG) + m_hasInfo = true; + if (m_isAnimated) { + png_save_uint_32(m_dataIHDR, 13); + memcpy(m_dataIHDR + 4, "IHDR", 4); + png_save_uint_32(m_dataIHDR + 8, width); + png_save_uint_32(m_dataIHDR + 12, height); + m_dataIHDR[16] = bitDepth; + m_dataIHDR[17] = colorType; + m_dataIHDR[18] = compressionType; + m_dataIHDR[19] = filterType; + m_dataIHDR[20] = interlaceType; + } +#endif + // 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) { +#if ENABLE(APNG) + if (m_isAnimated) { + png_colorp palette; + int paletteSize = 0; + png_get_PLTE(png, info, &palette, &paletteSize); + paletteSize *= 3; + png_save_uint_32(m_dataPLTE, paletteSize); + memcpy(m_dataPLTE + 4, "PLTE", 4); + memcpy(m_dataPLTE + 8, palette, paletteSize); + m_sizePLTE = paletteSize + 12; + } +#endif + png_set_expand(png); + } + + if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) png_set_expand(png); png_bytep trns = 0; int trnsCount = 0; + png_color_16p transValues; if (png_get_valid(png, info, PNG_INFO_tRNS)) { - png_get_tRNS(png, info, &trns, &trnsCount, 0); + png_get_tRNS(png, info, &trns, &trnsCount, &transValues); +#if ENABLE(APNG) + if (m_isAnimated) { + if (colorType == PNG_COLOR_TYPE_RGB) { + png_save_uint_16(m_datatRNS + 8, transValues->red); + png_save_uint_16(m_datatRNS + 10, transValues->green); + png_save_uint_16(m_datatRNS + 12, transValues->blue); + trnsCount = 6; + } else if (colorType == PNG_COLOR_TYPE_GRAY) { + png_save_uint_16(m_datatRNS + 8, transValues->gray); + trnsCount = 2; + } else if (colorType == PNG_COLOR_TYPE_PALETTE) + memcpy(m_datatRNS + 8, trns, trnsCount); + + png_save_uint_32(m_datatRNS, trnsCount); + memcpy(m_datatRNS + 4, "tRNS", 4); + m_sizetRNS = trnsCount + 12; + } +#endif png_set_expand(png); } @@ -347,21 +384,6 @@ void PNGImageDecoder::headerAvailable() if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png); - if ((colorType & PNG_COLOR_MASK_COLOR) && !m_ignoreGammaAndColorProfile) { - // We only support color profiles for color PALETTE and RGB[A] PNG. Supporting - // color profiles for gray-scale images is slightly tricky, at least using the - // CoreGraphics ICC library, because we expand gray-scale images to RGB but we - // do not similarly transform the color profile. We'd either need to transform - // the color profile or we'd need to decode into a gray-scale image buffer and - // hand that to CoreGraphics. - readColorProfile(png, info, m_colorProfile); -#if USE(QCMSLIB) - bool decodedImageHasAlpha = (colorType & PNG_COLOR_MASK_ALPHA) || trnsCount; - m_reader->createColorTransform(m_colorProfile, decodedImageHasAlpha); - m_colorProfile.clear(); -#endif - } - // Deal with gamma and keep it under our control. double gamma; if (!m_ignoreGammaAndColorProfile && png_get_gAMA(png, info, &gamma)) { @@ -370,6 +392,9 @@ void PNGImageDecoder::headerAvailable() png_set_gAMA(png, info, gamma); } png_set_gamma(png, cDefaultGamma, gamma); +#if ENABLE(APNG) + m_gamma = static_cast<int>(gamma * 100000); +#endif } else png_set_gamma(png, cDefaultGamma, cInverseGamma); @@ -396,67 +421,42 @@ void PNGImageDecoder::headerAvailable() } } -static inline void setPixelRGB(ImageFrame::PixelData* dest, png_bytep pixel) -{ - *dest = 0xFF000000U | pixel[0] << 16 | pixel[1] << 8 | pixel[2]; -} - -static inline void setPixelRGBA(ImageFrame::PixelData* dest, png_bytep pixel, unsigned char& nonTrivialAlphaMask) -{ - unsigned char a = pixel[3]; - *dest = a << 24 | pixel[0] << 16 | pixel[1] << 8 | pixel[2]; - nonTrivialAlphaMask |= (255 - a); -} - -static inline void setPixelPremultipliedRGBA(ImageFrame::PixelData* dest, png_bytep pixel, unsigned char& nonTrivialAlphaMask) -{ - unsigned char a = pixel[3]; - unsigned char r = fastDivideBy255(pixel[0] * a); - unsigned char g = fastDivideBy255(pixel[1] * a); - unsigned char b = fastDivideBy255(pixel[2] * a); - - *dest = a << 24 | r << 16 | g << 8 | b; - nonTrivialAlphaMask |= (255 - a); -} - void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) { if (m_frameBufferCache.isEmpty()) return; // Initialize the framebuffer if needed. - ImageFrame& buffer = m_frameBufferCache[0]; - if (buffer.status() == ImageFrame::FrameEmpty) { +#if ENABLE(APNG) + if (m_currentFrame >= frameCount()) + return; +#endif + ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; + if (buffer.isEmpty()) { png_structp png = m_reader->pngPtr(); - if (!buffer.setSize(scaledSize().width(), scaledSize().height())) { + if (!buffer.initialize(scaledSize(), m_premultiplyAlpha)) { longjmp(JMPBUF(png), 1); return; } unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; - if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { - m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); + if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr()) + || m_currentFrame) { + if (!m_reader->interlaceBuffer()) + m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); if (!m_reader->interlaceBuffer()) { longjmp(JMPBUF(png), 1); return; } } -#if USE(QCMSLIB) - if (m_reader->colorTransform()) { - m_reader->createRowBuffer(colorChannels * size().width()); - if (!m_reader->rowBuffer()) { - longjmp(JMPBUF(png), 1); - return; - } - } -#endif - buffer.setStatus(ImageFrame::FramePartial); + buffer.setDecoding(ImageFrame::Decoding::Partial); buffer.setHasAlpha(false); - buffer.setColorProfile(m_colorProfile); - // For PNGs, the frame always fills the entire image. - buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); +#if ENABLE(APNG) + if (m_currentFrame) + initFrameBuffer(m_currentFrame); +#endif } /* libpng comments (here to explain what follows). @@ -505,27 +505,26 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { row = interlaceBuffer + (rowIndex * colorChannels * size().width()); +#if ENABLE(APNG) + if (m_currentFrame) { + png_progressive_combine_row(m_png, row, rowBuffer); + return; // Only do incremental image display for the first frame. + } +#endif png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); } -#if USE(QCMSLIB) - if (qcms_transform* transform = m_reader->colorTransform()) { - qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); - row = m_reader->rowBuffer(); - } -#endif - // Write the decoded row pixels to the frame buffer. - ImageFrame::PixelData* address = buffer.getAddr(0, y); + RGBA32* address = buffer.backingStore()->pixelAt(0, y); int width = scaledSize().width(); unsigned char nonTrivialAlphaMask = 0; #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) if (m_scaled) { - for (int x = 0; x < width; ++x) { + for (int x = 0; x < width; ++x, ++address) { png_bytep pixel = row + m_scaledColumns[x] * colorChannels; unsigned alpha = hasAlpha ? pixel[3] : 255; - buffer.setRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); + buffer.backingStore()->setPixel(address, pixel[0], pixel[1], pixel[2], alpha); nonTrivialAlphaMask |= (255 - alpha); } } else @@ -533,46 +532,422 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, { png_bytep pixel = row; if (hasAlpha) { - if (buffer.premultiplyAlpha()) { - for (int x = 0; x < width; ++x, pixel += 4) - setPixelPremultipliedRGBA(address++, pixel, nonTrivialAlphaMask); - } else { - for (int x = 0; x < width; ++x, pixel += 4) - setPixelRGBA(address++, pixel, nonTrivialAlphaMask); + for (int x = 0; x < width; ++x, pixel += 4, ++address) { + unsigned alpha = pixel[3]; + buffer.backingStore()->setPixel(address, pixel[0], pixel[1], pixel[2], alpha); + nonTrivialAlphaMask |= (255 - alpha); } } else { - for (int x = 0; x < width; ++x, pixel += 3) - setPixelRGB(address++, pixel); + for (int x = 0; x < width; ++x, pixel += 3, ++address) + *address = makeRGB(pixel[0], pixel[1], pixel[2]); } } - if (nonTrivialAlphaMask && !buffer.hasAlpha()) buffer.setHasAlpha(true); } void PNGImageDecoder::pngComplete() { +#if ENABLE(APNG) + if (m_isAnimated) { + if (!processingFinish() && m_frameCount == m_currentFrame) + return; + + fallbackNotAnimated(); + } +#endif if (!m_frameBufferCache.isEmpty()) - m_frameBufferCache.first().setStatus(ImageFrame::FrameComplete); + m_frameBufferCache.first().setDecoding(ImageFrame::Decoding::Complete); } -void PNGImageDecoder::decode(bool onlySize) +void PNGImageDecoder::decode(bool onlySize, unsigned haltAtFrame) { if (failed()) return; if (!m_reader) - m_reader = adoptPtr(new PNGImageReader(this)); + m_reader = std::make_unique<PNGImageReader>(this); // If we couldn't decode the image but we've received all the data, decoding // has failed. - if (!m_reader->decode(*m_data, onlySize) && isAllDataReceived()) + if (!m_reader->decode(*m_data, onlySize, haltAtFrame) && isAllDataReceived()) setFailed(); // If we're done decoding the image, we don't need the PNGImageReader // anymore. (If we failed, |m_reader| has already been cleared.) else if (isComplete()) - m_reader.clear(); + m_reader = nullptr; } +#if ENABLE(APNG) +void PNGImageDecoder::readChunks(png_unknown_chunkp chunk) +{ + if (!memcmp(chunk->name, "acTL", 4) && chunk->size == 8) { + if (m_hasInfo || m_isAnimated) + return; + + m_frameCount = png_get_uint_32(chunk->data); + m_playCount = png_get_uint_32(chunk->data + 4); + + if (!m_frameCount || m_frameCount > PNG_UINT_31_MAX || m_playCount > PNG_UINT_31_MAX) { + fallbackNotAnimated(); + return; + } + + m_isAnimated = true; + if (!m_frameInfo) + m_frameIsHidden = true; + + if (m_frameBufferCache.size() == m_frameCount) + return; + + m_frameBufferCache.resize(m_frameCount); + } else if (!memcmp(chunk->name, "fcTL", 4) && chunk->size == 26) { + if (m_hasInfo && !m_isAnimated) + return; + + m_frameInfo = false; + + if (processingFinish()) { + fallbackNotAnimated(); + return; + } + + // At this point the old frame is done. Let's start a new one. + unsigned sequenceNumber = png_get_uint_32(chunk->data); + if (sequenceNumber != m_sequenceNumber++) { + fallbackNotAnimated(); + return; + } + + m_width = png_get_uint_32(chunk->data + 4); + m_height = png_get_uint_32(chunk->data + 8); + m_xOffset = png_get_uint_32(chunk->data + 12); + m_yOffset = png_get_uint_32(chunk->data + 16); + m_delayNumerator = png_get_uint_16(chunk->data + 20); + m_delayDenominator = png_get_uint_16(chunk->data + 22); + m_dispose = chunk->data[24]; + m_blend = chunk->data[25]; + + png_structp png = m_reader->pngPtr(); + png_infop info = m_reader->infoPtr(); + png_uint_32 width = png_get_image_width(png, info); + png_uint_32 height = png_get_image_height(png, info); + + if (m_width > cMaxPNGSize || m_height > cMaxPNGSize + || m_xOffset > cMaxPNGSize || m_yOffset > cMaxPNGSize + || m_xOffset + m_width > width + || m_yOffset + m_height > height + || m_dispose > 2 || m_blend > 1) { + fallbackNotAnimated(); + return; + } + + if (m_frameBufferCache.isEmpty()) + m_frameBufferCache.resize(1); + + if (m_currentFrame < m_frameBufferCache.size()) { + ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; + + if (!m_delayDenominator) + buffer.setDuration(m_delayNumerator * 10); + else + buffer.setDuration(m_delayNumerator * 1000 / m_delayDenominator); + + if (m_dispose == 2) + buffer.setDisposalMethod(ImageFrame::DisposalMethod::RestoreToPrevious); + else if (m_dispose == 1) + buffer.setDisposalMethod(ImageFrame::DisposalMethod::RestoreToBackground); + else + buffer.setDisposalMethod(ImageFrame::DisposalMethod::DoNotDispose); + } + + m_frameInfo = true; + m_frameIsHidden = false; + + if (processingStart(chunk)) { + fallbackNotAnimated(); + return; + } + } else if (!memcmp(chunk->name, "fdAT", 4) && chunk->size >= 4) { + if (!m_frameInfo || !m_isAnimated) + return; + + unsigned sequenceNumber = png_get_uint_32(chunk->data); + if (sequenceNumber != m_sequenceNumber++) { + fallbackNotAnimated(); + return; + } + + if (setjmp(JMPBUF(m_png))) { + fallbackNotAnimated(); + return; + } + + png_save_uint_32(chunk->data, chunk->size - 4); + png_process_data(m_png, m_info, chunk->data, 4); + memcpy(chunk->data, "IDAT", 4); + png_process_data(m_png, m_info, chunk->data, chunk->size); + png_process_data(m_png, m_info, chunk->data, 4); + } +} + +void PNGImageDecoder::frameHeader() +{ + int colorType = png_get_color_type(m_png, m_info); + + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand(m_png); + + int bitDepth = png_get_bit_depth(m_png, m_info); + if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) + png_set_expand(m_png); + + if (png_get_valid(m_png, m_info, PNG_INFO_tRNS)) + png_set_expand(m_png); + + if (bitDepth == 16) + png_set_strip_16(m_png); + + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(m_png); + + double gamma; + if (png_get_gAMA(m_png, m_info, &gamma)) + png_set_gamma(m_png, cDefaultGamma, gamma); + + png_set_interlace_handling(m_png); + + png_read_update_info(m_png, m_info); +} + +void PNGImageDecoder::init() +{ + m_isAnimated = false; + m_frameInfo = false; + m_frameIsHidden = false; + m_hasInfo = false; + m_currentFrame = 0; + m_totalFrames = 0; + m_sequenceNumber = 0; +} + +void PNGImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) +{ + if (m_frameBufferCache.isEmpty()) + return; + + // See GIFImageDecoder for full explanation. + clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1); + const Vector<ImageFrame>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame); + + Vector<ImageFrame>::iterator i(end); + for (; (i != m_frameBufferCache.begin()) && (i->isEmpty() || (i->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)); --i) { + if (i->isComplete() && (i != end)) + i->clear(); + } + + // Now |i| holds the last frame we need to preserve; clear prior frames. + for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { + ASSERT(!j->isPartial()); + if (j->isEmpty()) + j->clear(); + } +} + +void PNGImageDecoder::initFrameBuffer(size_t frameIndex) +{ + if (frameIndex >= frameCount()) + return; + + ImageFrame& buffer = m_frameBufferCache[frameIndex]; + + // The starting state for this frame depends on the previous frame's + // disposal method. + // + // Frames that use the DisposalMethod::RestoreToPrevious 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 ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; + ImageFrame::DisposalMethod prevMethod = prevBuffer->disposalMethod(); + while (frameIndex && (prevMethod == ImageFrame::DisposalMethod::RestoreToPrevious)) { + prevBuffer = &m_frameBufferCache[--frameIndex]; + prevMethod = prevBuffer->disposalMethod(); + } + + png_structp png = m_reader->pngPtr(); + ASSERT(prevBuffer->isComplete()); + + if (prevMethod == ImageFrame::DisposalMethod::DoNotDispose) { + // Preserve the last frame as the starting state for this frame. + if (!prevBuffer->backingStore() || !buffer.initialize(*prevBuffer->backingStore())) + longjmp(JMPBUF(png), 1); + } else { + // We want to clear the previous frame to transparent, without + // affecting pixels in the image outside of the frame. + IntRect prevRect = prevBuffer->backingStore()->frameRect(); + 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. + buffer.backingStore()->clear(); + buffer.setHasAlpha(true); + } else { + // Copy the whole previous buffer, then clear just its frame. + if (!prevBuffer->backingStore() || !buffer.initialize(*prevBuffer->backingStore())) { + longjmp(JMPBUF(png), 1); + return; + } + buffer.backingStore()->clearRect(prevRect); + buffer.setHasAlpha(true); + } + } + + IntRect frameRect(m_xOffset, m_yOffset, m_width, m_height); + + // Make sure the frameRect doesn't extend outside the buffer. + if (frameRect.maxX() > size().width()) + frameRect.setWidth(size().width() - m_xOffset); + if (frameRect.maxY() > size().height()) + frameRect.setHeight(size().height() - m_yOffset); + + int left = upperBoundScaledX(frameRect.x()); + int right = lowerBoundScaledX(frameRect.maxX(), left); + int top = upperBoundScaledY(frameRect.y()); + int bottom = lowerBoundScaledY(frameRect.maxY(), top); + buffer.backingStore()->setFrameRect(IntRect(left, top, right - left, bottom - top)); +} + +void PNGImageDecoder::frameComplete() +{ + if (m_frameIsHidden || m_currentFrame >= frameCount()) + return; + + ImageFrame& buffer = m_frameBufferCache[m_currentFrame]; + buffer.setDecoding(ImageFrame::Decoding::Complete); + + png_bytep interlaceBuffer = m_reader->interlaceBuffer(); + + if (m_currentFrame && interlaceBuffer) { + IntRect rect = buffer.backingStore()->frameRect(); + bool hasAlpha = m_reader->hasAlpha(); + unsigned colorChannels = hasAlpha ? 4 : 3; + bool nonTrivialAlpha = false; + if (m_blend && !hasAlpha) + m_blend = 0; + +#if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) + for (int y = 0; y < rect.maxY() - rect.y(); ++y) { + png_bytep row = interlaceBuffer + (m_scaled ? m_scaledRows[y] : y) * colorChannels * size().width(); + RGBA32* address = buffer.backingStore()->pixelAt(rect.x(), y + rect.y()); + for (int x = 0; x < rect.maxX() - rect.x(); ++x) { + png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels; + unsigned alpha = hasAlpha ? pixel[3] : 255; + nonTrivialAlpha |= alpha < 255; + if (!m_blend) + buffer.backingStore()->setPixel(address++, pixel[0], pixel[1], pixel[2], alpha); + else + buffer.backingStore()->blendPixel(address++, pixel[0], pixel[1], pixel[2], alpha); + } + } +#else + ASSERT(!m_scaled); + png_bytep row = interlaceBuffer; + for (int y = rect.y(); y < rect.maxY(); ++y, row += colorChannels * size().width()) { + png_bytep pixel = row; + RGBA32* address = buffer.backingStore()->pixelAt(rect.x(), y); + for (int x = rect.x(); x < rect.maxX(); ++x, pixel += colorChannels) { + unsigned alpha = hasAlpha ? pixel[3] : 255; + nonTrivialAlpha |= alpha < 255; + if (!m_blend) + buffer.backingStore()->setPixel(address++, pixel[0], pixel[1], pixel[2], alpha); + else + buffer.backingStore()->blendPixel(address++, pixel[0], pixel[1], pixel[2], alpha); + } + } +#endif + + if (!nonTrivialAlpha) { + IntRect rect = buffer.backingStore()->frameRect(); + if (rect.contains(IntRect(IntPoint(), scaledSize()))) + buffer.setHasAlpha(false); + else { + size_t frameIndex = m_currentFrame; + const ImageFrame* prevBuffer = &m_frameBufferCache[--frameIndex]; + while (frameIndex && (prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToPrevious)) + prevBuffer = &m_frameBufferCache[--frameIndex]; + + IntRect prevRect = prevBuffer->backingStore()->frameRect(); + if ((prevBuffer->disposalMethod() == ImageFrame::DisposalMethod::RestoreToBackground) && !prevBuffer->hasAlpha() && rect.contains(prevRect)) + buffer.setHasAlpha(false); + } + } else if (!m_blend && !buffer.hasAlpha()) + buffer.setHasAlpha(nonTrivialAlpha); + } + m_currentFrame++; +} + +int PNGImageDecoder::processingStart(png_unknown_chunkp chunk) +{ + static png_byte dataPNG[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + static png_byte datagAMA[16] = {0, 0, 0, 4, 103, 65, 77, 65}; + + if (!m_hasInfo) + return 0; + + m_totalFrames++; + + m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, decodingFailed, 0); + m_info = png_create_info_struct(m_png); + if (setjmp(JMPBUF(m_png))) + return 1; + + png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); + png_set_progressive_read_fn(m_png, static_cast<png_voidp>(this), + WebCore::frameHeader, WebCore::rowAvailable, 0); + + memcpy(m_dataIHDR + 8, chunk->data + 4, 8); + png_save_uint_32(datagAMA + 8, m_gamma); + + png_process_data(m_png, m_info, dataPNG, 8); + png_process_data(m_png, m_info, m_dataIHDR, 25); + png_process_data(m_png, m_info, datagAMA, 16); + if (m_sizePLTE > 0) + png_process_data(m_png, m_info, m_dataPLTE, m_sizePLTE); + if (m_sizetRNS > 0) + png_process_data(m_png, m_info, m_datatRNS, m_sizetRNS); + + return 0; +} + +int PNGImageDecoder::processingFinish() +{ + static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130}; + + if (!m_hasInfo) + return 0; + + if (m_totalFrames) { + if (setjmp(JMPBUF(m_png))) + return 1; + + png_process_data(m_png, m_info, dataIEND, 12); + png_destroy_read_struct(&m_png, &m_info, 0); + } + + frameComplete(); + return 0; +} + +void PNGImageDecoder::fallbackNotAnimated() +{ + m_isAnimated = false; + m_frameCount = 1; + m_playCount = 0; + m_currentFrame = 0; + m_frameBufferCache.resize(1); +} +#endif + } // namespace WebCore diff --git a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h index ec2e85740..fee08f5b3 100644 --- a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,52 +23,109 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PNGImageDecoder_h -#define PNGImageDecoder_h +#pragma once #include "ImageDecoder.h" -#include <wtf/OwnPtr.h> +#if ENABLE(APNG) +#include <png.h> +#endif namespace WebCore { class PNGImageReader; // This class decodes the PNG image format. - class PNGImageDecoder : public ImageDecoder { + class PNGImageDecoder final : public ImageDecoder { public: - PNGImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + PNGImageDecoder(AlphaOption, GammaAndColorProfileOption); virtual ~PNGImageDecoder(); // ImageDecoder - virtual String filenameExtension() const { return "png"; } - virtual bool isSizeAvailable(); - virtual bool setSize(unsigned width, unsigned height); - virtual ImageFrame* frameBufferAtIndex(size_t index); + String filenameExtension() const override { return "png"; } +#if ENABLE(APNG) + size_t frameCount() const override { return m_frameCount; } + RepetitionCount repetitionCount() const override; +#endif + bool isSizeAvailable() override; + bool setSize(const IntSize&) override; + ImageFrame* frameBufferAtIndex(size_t index) override; // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid // accessing deleted memory, especially when calling this from inside // PNGImageReader! - virtual bool setFailed(); + bool setFailed() override; // Callbacks from libpng void headerAvailable(); void rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass); void pngComplete(); +#if ENABLE(APNG) + void readChunks(png_unknown_chunkp); + void frameHeader(); + + void init(); + void clearFrameBufferCache(size_t clearBeforeFrame) override; +#endif bool isComplete() const { - return !m_frameBufferCache.isEmpty() && (m_frameBufferCache.first().status() == ImageFrame::FrameComplete); + if (m_frameBufferCache.isEmpty()) + return false; + + for (auto& imageFrame : m_frameBufferCache) { + if (!imageFrame.isComplete()) + return false; + } + + return true; + } + + bool isCompleteAtIndex(size_t index) + { + return (index < m_frameBufferCache.size() && m_frameBufferCache[index].isComplete()); } private: // Decodes the image. If |onlySize| is true, stops decoding after // calculating the image size. If decoding fails but there is no more // data coming, sets the "decode failure" flag. - void decode(bool onlySize); + void decode(bool onlySize, unsigned haltAtFrame); +#if ENABLE(APNG) + void initFrameBuffer(size_t frameIndex); + void frameComplete(); + int processingStart(png_unknown_chunkp); + int processingFinish(); + void fallbackNotAnimated(); +#endif - OwnPtr<PNGImageReader> m_reader; + std::unique_ptr<PNGImageReader> m_reader; bool m_doNothingOnFailure; + unsigned m_currentFrame; +#if ENABLE(APNG) + png_structp m_png; + png_infop m_info; + bool m_isAnimated; + bool m_frameInfo; + bool m_frameIsHidden; + bool m_hasInfo; + int m_gamma; + size_t m_frameCount; + unsigned m_playCount; + unsigned m_totalFrames; + unsigned m_sizePLTE; + unsigned m_sizetRNS; + unsigned m_sequenceNumber; + unsigned m_width; + unsigned m_height; + unsigned m_xOffset; + unsigned m_yOffset; + unsigned m_delayNumerator; + unsigned m_delayDenominator; + unsigned m_dispose; + unsigned m_blend; + png_byte m_dataIHDR[12 + 13]; + png_byte m_dataPLTE[12 + 256 * 3]; + png_byte m_datatRNS[12 + 256]; +#endif }; } // namespace WebCore - -#endif diff --git a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp index f7f8ca550..4a38f580b 100644 --- a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,14 +31,6 @@ #if USE(WEBP) -#ifdef QCMS_WEBP_COLOR_CORRECTION -#include "qcms.h" -#include "webp/demux.h" -#else -#undef ICCP_FLAG -#define ICCP_FLAG 0 -#endif - // Backward emulation for earlier versions than 0.1.99. #if (WEBP_DECODER_ABI_VERSION < 0x0163) #define MODE_rgbA MODE_RGBA @@ -53,17 +45,10 @@ inline WEBP_CSP_MODE outputMode(bool hasAlpha) { return hasAlpha ? MODE_bgrA : M namespace WebCore { -WEBPImageDecoder::WEBPImageDecoder(ImageSource::AlphaOption alphaOption, - ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) +WEBPImageDecoder::WEBPImageDecoder(AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption) : ImageDecoder(alphaOption, gammaAndColorProfileOption) , m_decoder(0) , m_hasAlpha(false) - , m_formatFlags(0) -#ifdef QCMS_WEBP_COLOR_CORRECTION - , m_haveReadProfile(false) - , m_transform(0) - , m_decodedHeight(0) -#endif { } @@ -74,11 +59,6 @@ WEBPImageDecoder::~WEBPImageDecoder() void WEBPImageDecoder::clear() { -#ifdef QCMS_WEBP_COLOR_CORRECTION - if (m_transform) - qcms_transform_release(m_transform); - m_transform = 0; -#endif if (m_decoder) WebPIDelete(m_decoder); m_decoder = 0; @@ -97,105 +77,15 @@ ImageFrame* WEBPImageDecoder::frameBufferAtIndex(size_t index) if (index) return 0; - if (m_frameBufferCache.isEmpty()) { + if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(1); - m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); - } ImageFrame& frame = m_frameBufferCache[0]; - if (frame.status() != ImageFrame::FrameComplete) + if (!frame.isComplete()) decode(false); return &frame; } -#ifdef QCMS_WEBP_COLOR_CORRECTION - -void WEBPImageDecoder::createColorTransform(const char* data, size_t size) -{ - if (m_transform) - qcms_transform_release(m_transform); - m_transform = 0; - - qcms_profile* deviceProfile = ImageDecoder::qcmsOutputDeviceProfile(); - if (!deviceProfile) - return; - qcms_profile* inputProfile = qcms_profile_from_memory(data, size); - if (!inputProfile) - return; - - // We currently only support color profiles for RGB profiled images. - ASSERT(icSigRgbData == qcms_profile_get_color_space(inputProfile)); - // The input image pixels are RGBA format. - qcms_data_type format = QCMS_DATA_RGBA_8; - // FIXME: Don't force perceptual intent if the image profile contains an intent. - m_transform = qcms_transform_create(inputProfile, format, deviceProfile, QCMS_DATA_RGBA_8, QCMS_INTENT_PERCEPTUAL); - - qcms_profile_release(inputProfile); -} - -void WEBPImageDecoder::readColorProfile(const uint8_t* data, size_t size) -{ - WebPChunkIterator chunkIterator; - WebPData inputData = { data, size }; - WebPDemuxState state; - - WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state); - if (!WebPDemuxGetChunk(demuxer, "ICCP", 1, &chunkIterator)) { - WebPDemuxReleaseChunkIterator(&chunkIterator); - WebPDemuxDelete(demuxer); - return; - } - - const char* profileData = reinterpret_cast<const char*>(chunkIterator.chunk.bytes); - size_t profileSize = chunkIterator.chunk.size; - - // Only accept RGB color profiles from input class devices. - bool ignoreProfile = false; - if (profileSize < ImageDecoder::iccColorProfileHeaderLength) - ignoreProfile = true; - else if (!ImageDecoder::rgbColorProfile(profileData, profileSize)) - ignoreProfile = true; - else if (!ImageDecoder::inputDeviceColorProfile(profileData, profileSize)) - ignoreProfile = true; - - if (!ignoreProfile) - createColorTransform(profileData, profileSize); - - WebPDemuxReleaseChunkIterator(&chunkIterator); - WebPDemuxDelete(demuxer); -} - -void WEBPImageDecoder::applyColorProfile(const uint8_t* data, size_t size, ImageFrame& buffer) -{ - int width; - int decodedHeight; - if (!WebPIDecGetRGB(m_decoder, &decodedHeight, &width, 0, 0)) - return; // See also https://bugs.webkit.org/show_bug.cgi?id=74062 - if (decodedHeight <= 0) - return; - - if (!m_haveReadProfile) { - readColorProfile(data, size); - m_haveReadProfile = true; - } - - ASSERT(width == scaledSize().width()); - ASSERT(decodedHeight <= scaledSize().height()); - - for (int y = m_decodedHeight; y < decodedHeight; ++y) { - uint8_t* row = reinterpret_cast<uint8_t*>(buffer.getAddr(0, y)); - if (qcms_transform* transform = colorTransform()) - qcms_transform_data_type(transform, row, row, width, QCMS_OUTPUT_RGBX); - uint8_t* pixel = row; - for (int x = 0; x < width; ++x, pixel += 4) - buffer.setRGBA(x, y, pixel[0], pixel[1], pixel[2], pixel[3]); - } - - m_decodedHeight = decodedHeight; -} - -#endif // QCMS_WEBP_COLOR_CORRECTION - bool WEBPImageDecoder::decode(bool onlySize) { if (failed()) @@ -209,22 +99,7 @@ bool WEBPImageDecoder::decode(bool onlySize) if (dataSize < imageHeaderSize) return false; int width, height; -#ifdef QCMS_WEBP_COLOR_CORRECTION - WebPData inputData = { dataBytes, dataSize }; - WebPDemuxState state; - WebPDemuxer* demuxer = WebPDemuxPartial(&inputData, &state); - if (!demuxer) - return setFailed(); - - width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); - height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); - m_formatFlags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); - m_hasAlpha = !!(m_formatFlags & ALPHA_FLAG); - - WebPDemuxDelete(demuxer); - if (state <= WEBP_DEMUX_PARSING_HEADER) - return false; -#elif (WEBP_DECODER_ABI_VERSION >= 0x0163) +#if (WEBP_DECODER_ABI_VERSION >= 0x0163) WebPBitstreamFeatures features; if (WebPGetFeatures(dataBytes, dataSize, &features) != VP8_STATUS_OK) return setFailed(); @@ -237,7 +112,7 @@ bool WEBPImageDecoder::decode(bool onlySize) return setFailed(); m_hasAlpha = false; #endif - if (!setSize(width, height)) + if (!setSize(IntSize(width, height))) return setFailed(); } @@ -247,24 +122,21 @@ bool WEBPImageDecoder::decode(bool onlySize) ASSERT(!m_frameBufferCache.isEmpty()); ImageFrame& buffer = m_frameBufferCache[0]; - ASSERT(buffer.status() != ImageFrame::FrameComplete); + ASSERT(!buffer.isComplete()); - if (buffer.status() == ImageFrame::FrameEmpty) { - if (!buffer.setSize(size().width(), size().height())) + if (buffer.isEmpty()) { + if (!buffer.initialize(size(), m_premultiplyAlpha)) return setFailed(); - buffer.setStatus(ImageFrame::FramePartial); + buffer.setDecoding(ImageFrame::Decoding::Partial); buffer.setHasAlpha(m_hasAlpha); - buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } if (!m_decoder) { WEBP_CSP_MODE mode = outputMode(m_hasAlpha); if (!m_premultiplyAlpha) mode = outputMode(false); - if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) - mode = MODE_RGBA; // Decode to RGBA for input to libqcms. - int rowStride = size().width() * sizeof(ImageFrame::PixelData); - uint8_t* output = reinterpret_cast<uint8_t*>(buffer.getAddr(0, 0)); + int rowStride = size().width() * sizeof(RGBA32); + uint8_t* output = reinterpret_cast<uint8_t*>(buffer.backingStore()->pixelAt(0, 0)); int outputSize = size().height() * rowStride; m_decoder = WebPINewRGB(mode, output, outputSize, rowStride); if (!m_decoder) @@ -273,14 +145,10 @@ bool WEBPImageDecoder::decode(bool onlySize) switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) { case VP8_STATUS_OK: - if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) - applyColorProfile(dataBytes, dataSize, buffer); - buffer.setStatus(ImageFrame::FrameComplete); + buffer.setDecoding(ImageFrame::Decoding::Complete); clear(); return true; case VP8_STATUS_SUSPENDED: - if ((m_formatFlags & ICCP_FLAG) && !ignoresGammaAndColorProfile()) - applyColorProfile(dataBytes, dataSize, buffer); return false; default: clear(); diff --git a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h index 38b8d1d7f..e282b2276 100644 --- a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h @@ -10,7 +10,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,53 +26,35 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WEBPImageDecoder_h -#define WEBPImageDecoder_h +#pragma once #include "ImageDecoder.h" #if USE(WEBP) #include "webp/decode.h" -#if USE(QCMSLIB) && (WEBP_DECODER_ABI_VERSION > 0x200) -#define QCMS_WEBP_COLOR_CORRECTION -#endif namespace WebCore { -class WEBPImageDecoder : public ImageDecoder { +class WEBPImageDecoder final : public ImageDecoder { public: - WEBPImageDecoder(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); + WEBPImageDecoder(AlphaOption, GammaAndColorProfileOption); virtual ~WEBPImageDecoder(); - virtual String filenameExtension() const { return "webp"; } - virtual bool isSizeAvailable(); - virtual ImageFrame* frameBufferAtIndex(size_t index); + String filenameExtension() const override { return "webp"; } + bool isSizeAvailable() override; + ImageFrame* frameBufferAtIndex(size_t index) override; private: bool decode(bool onlySize); WebPIDecoder* m_decoder; bool m_hasAlpha; - int m_formatFlags; - -#ifdef QCMS_WEBP_COLOR_CORRECTION - qcms_transform* colorTransform() const { return m_transform; } - void createColorTransform(const char* data, size_t); - void readColorProfile(const uint8_t* data, size_t); - void applyColorProfile(const uint8_t* data, size_t, ImageFrame&); - bool m_haveReadProfile; - qcms_transform* m_transform; - int m_decodedHeight; -#else void applyColorProfile(const uint8_t*, size_t, ImageFrame&) { }; -#endif void clear(); }; } // namespace WebCore #endif - -#endif |