summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/image-decoders
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/image-decoders')
-rw-r--r--Source/WebCore/platform/image-decoders/ImageDecoder.cpp163
-rw-r--r--Source/WebCore/platform/image-decoders/ImageDecoder.h303
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.cpp22
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageDecoder.h22
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp24
-rw-r--r--Source/WebCore/platform/image-decoders/bmp/BMPImageReader.h12
-rw-r--r--Source/WebCore/platform/image-decoders/cairo/ImageBackingStoreCairo.cpp (renamed from Source/WebCore/platform/image-decoders/cairo/ImageDecoderCairo.cpp)21
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp150
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.h42
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageReader.cpp23
-rw-r--r--Source/WebCore/platform/image-decoders/gif/GIFImageReader.h17
-rw-r--r--Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.cpp65
-rw-r--r--Source/WebCore/platform/image-decoders/ico/ICOImageDecoder.h38
-rw-r--r--Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp162
-rw-r--r--Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h41
-rw-r--r--Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp727
-rw-r--r--Source/WebCore/platform/image-decoders/png/PNGImageDecoder.h93
-rw-r--r--Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp158
-rw-r--r--Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h32
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