REGRESSION(r198782, r201043): [image-decoders] Flickering with some animated gif
[WebKit-https.git] / Source / WebCore / platform / image-decoders / ImageDecoder.cpp
index 58a7981..6fc1a22 100644 (file)
@@ -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.
  *
  */
 
 #include "config.h"
-
 #include "ImageDecoder.h"
 
-#include <algorithm>
-#include <cmath>
-
 #include "BMPImageDecoder.h"
 #include "GIFImageDecoder.h"
 #include "ICOImageDecoder.h"
 #include "JPEGImageDecoder.h"
 #include "PNGImageDecoder.h"
-#include "WEBPImageDecoder.h"
 #include "SharedBuffer.h"
+#if USE(WEBP)
+#include "WEBPImageDecoder.h"
+#endif
+
+#include <algorithm>
+#include <cmath>
 
 using namespace std;
 
 namespace WebCore {
 
-static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset)
+namespace {
+
+unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset)
 {
     unsigned bytesExtracted = 0;
     const char* moreData;
@@ -53,57 +57,77 @@ static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const
     return bytesExtracted;
 }
 
-ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+bool matchesGIFSignature(char* contents)
 {
-    // We need at least 4 bytes to figure out what kind of image we're dealing
-    // with.
-    static const unsigned maxMarkerLength = 4;
-    char contents[maxMarkerLength];
-    unsigned length = copyFromSharedBuffer(contents, maxMarkerLength, data, 0);
-    if (length < maxMarkerLength)
-        return 0;
-
-    // GIFs begin with GIF8(7 or 9).
-    if (strncmp(contents, "GIF8", 4) == 0)
-        return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption);
+    return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
+}
 
-    // Test for PNG.
-    if (!memcmp(contents, "\x89\x50\x4E\x47", 4))
-        return new PNGImageDecoder(alphaOption, gammaAndColorProfileOption);
+bool matchesPNGSignature(char* contents)
+{
+    return !memcmp(contents, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8);
+}
 
-    // JPEG
-    if (!memcmp(contents, "\xFF\xD8\xFF", 3))
-        return new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption);
+bool matchesJPEGSignature(char* contents)
+{
+    return !memcmp(contents, "\xFF\xD8\xFF", 3);
+}
 
 #if USE(WEBP)
-    if (!memcmp(contents, "RIFF", 4)) {
-        static const unsigned webpExtraMarker = 6;
-        static const unsigned webpExtraMarkeroffset = 8;
-        char header[webpExtraMarker];
-        unsigned length = copyFromSharedBuffer(header, webpExtraMarker, data, webpExtraMarkeroffset);
-        if (length >= webpExtraMarker) {
-            if (!memcmp(header, "WEBPVP", webpExtraMarker))
-                return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption);
-        }
-    }
+bool matchesWebPSignature(char* contents)
+{
+    return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6);
+}
 #endif
 
-    // BMP
-    if (strncmp(contents, "BM", 2) == 0)
-        return new BMPImageDecoder(alphaOption, gammaAndColorProfileOption);
+bool matchesBMPSignature(char* contents)
+{
+    return !memcmp(contents, "BM", 2);
+}
+
+bool matchesICOSignature(char* contents)
+{
+    return !memcmp(contents, "\x00\x00\x01\x00", 4);
+}
 
-    // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
-    // CURs begin with 2-byte 0 followed by 2-byte 2.
-    if (!memcmp(contents, "\x00\x00\x01\x00", 4) || !memcmp(contents, "\x00\x00\x02\x00", 4))
-        return new ICOImageDecoder(alphaOption, gammaAndColorProfileOption);
+bool matchesCURSignature(char* contents)
+{
+    return !memcmp(contents, "\x00\x00\x02\x00", 4);
+}
 
-    // Give up. We don't know what the heck this is.
-    return 0;
 }
 
-#if !PLATFORM(SKIA)
+std::unique_ptr<ImageDecoder> ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::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 nullptr;
+
+    if (matchesGIFSignature(contents))
+        return std::unique_ptr<ImageDecoder> { std::make_unique<GIFImageDecoder>(alphaOption, gammaAndColorProfileOption) };
+
+    if (matchesPNGSignature(contents))
+        return std::unique_ptr<ImageDecoder> { std::make_unique<PNGImageDecoder>(alphaOption, gammaAndColorProfileOption) };
+
+    if (matchesICOSignature(contents) || matchesCURSignature(contents))
+        return std::unique_ptr<ImageDecoder> { std::make_unique<ICOImageDecoder>(alphaOption, gammaAndColorProfileOption) };
+
+    if (matchesJPEGSignature(contents))
+        return std::unique_ptr<ImageDecoder> { std::make_unique<JPEGImageDecoder>(alphaOption, gammaAndColorProfileOption) };
 
-RGBA32Buffer::RGBA32Buffer()
+#if USE(WEBP)
+    if (matchesWebPSignature(contents))
+        return std::unique_ptr<ImageDecoder> { std::make_unique<WEBPImageDecoder>(alphaOption, gammaAndColorProfileOption) };
+#endif
+
+    if (matchesBMPSignature(contents))
+        return std::unique_ptr<ImageDecoder> { std::make_unique<BMPImageDecoder>(alphaOption, gammaAndColorProfileOption) };
+
+    return nullptr;
+}
+
+ImageFrame::ImageFrame()
     : m_hasAlpha(false)
     , m_status(FrameEmpty)
     , m_duration(0)
@@ -112,13 +136,13 @@ RGBA32Buffer::RGBA32Buffer()
 {
 } 
 
-RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other)
+ImageFrame& ImageFrame::operator=(const ImageFrame& other)
 {
     if (this == &other)
         return *this;
 
-    copyReferenceToBitmapData(other);
-    setRect(other.rect());
+    copyBitmapData(other);
+    setOriginalFrameRect(other.originalFrameRect());
     setStatus(other.status());
     setDuration(other.duration());
     setDisposalMethod(other.disposalMethod());
@@ -126,7 +150,7 @@ RGBA32Buffer& RGBA32Buffer::operator=(const RGBA32Buffer& other)
     return *this;
 }
 
-void RGBA32Buffer::clear()
+void ImageFrame::clearPixelData()
 {
     m_backingStore.clear();
     m_bytes = 0;
@@ -137,21 +161,30 @@ void RGBA32Buffer::clear()
     // later.
 }
 
-void RGBA32Buffer::zeroFill()
+void ImageFrame::zeroFillPixelData()
 {
     memset(m_bytes, 0, m_size.width() * m_size.height() * sizeof(PixelData));
     m_hasAlpha = true;
 }
 
-#if !PLATFORM(CG)
-
-void RGBA32Buffer::copyReferenceToBitmapData(const RGBA32Buffer& other)
+void ImageFrame::zeroFillFrameRect(const IntRect& rect)
 {
-    ASSERT(this != &other);
-    copyBitmapData(other);
+    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 RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other)
+bool ImageFrame::copyBitmapData(const ImageFrame& other)
 {
     if (this == &other)
         return true;
@@ -163,54 +196,40 @@ bool RGBA32Buffer::copyBitmapData(const RGBA32Buffer& other)
     return true;
 }
 
-bool RGBA32Buffer::setSize(int newWidth, int newHeight)
+bool ImageFrame::setSize(int newWidth, int newHeight)
 {
-    // NOTE: This has no way to check for allocation failure if the requested
-    // size was too big...
-    m_backingStore.resize(newWidth * newHeight);
+    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);
 
-    // Zero the image.
-    zeroFill();
-
+    zeroFillPixelData();
     return true;
 }
 
-#endif
-
-bool RGBA32Buffer::hasAlpha() const
+bool ImageFrame::hasAlpha() const
 {
     return m_hasAlpha;
 }
 
-void RGBA32Buffer::setHasAlpha(bool alpha)
+void ImageFrame::setHasAlpha(bool alpha)
 {
     m_hasAlpha = alpha;
 }
 
-void RGBA32Buffer::setColorProfile(const ColorProfile& colorProfile)
+void ImageFrame::setColorProfile(const ColorProfile& colorProfile)
 {
     m_colorProfile = colorProfile;
 }
 
-void RGBA32Buffer::setStatus(FrameStatus status)
+void ImageFrame::setStatus(FrameStatus status)
 {
     m_status = status;
 }
 
-int RGBA32Buffer::width() const
-{
-    return m_size.width();
-}
-
-int RGBA32Buffer::height() const
-{
-    return m_size.height();
-}
-
-#endif
-
 namespace {
 
 enum MatchType {
@@ -252,6 +271,59 @@ template <MatchType type> int getScaledValue(const Vector<int>& scaledValues, in
 
 }
 
+bool ImageDecoder::frameIsCompleteAtIndex(size_t index)
+{
+    ImageFrame* buffer = frameBufferAtIndex(index);
+    return buffer && buffer->status() == ImageFrame::FrameComplete;
+}
+
+bool ImageDecoder::frameHasAlphaAtIndex(size_t index) const
+{
+    if (m_frameBufferCache.size() <= index)
+        return true;
+    if (m_frameBufferCache[index].status() == ImageFrame::FrameComplete)
+        return m_frameBufferCache[index].hasAlpha();
+    return true;
+}
+
+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);
+}
+
+float ImageDecoder::frameDurationAtIndex(size_t index)
+{
+    ImageFrame* buffer = frameBufferAtIndex(index);
+    if (!buffer || buffer->status() == ImageFrame::FrameEmpty)
+        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)
+{
+    ImageFrame* buffer = frameBufferAtIndex(index);
+    // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail.
+    // It's important to check the size after calling frameBufferAtIndex() to ensure the decoder has updated the size.
+    // See https://bugs.webkit.org/show_bug.cgi?id=159089.
+    if (!buffer || buffer->status() == ImageFrame::FrameEmpty || size().isEmpty())
+        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->asNewNativeImage();
+}
+
 void ImageDecoder::prepareScaleDataIfNecessary()
 {
     m_scaled = false;