REGRESSION(r198782, r201043): [image-decoders] Flickering with some animated gif
[WebKit-https.git] / Source / WebCore / platform / image-decoders / ImageDecoder.cpp
index aa9cc3e..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;
 
@@ -57,12 +59,12 @@ unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedB
 
 bool matchesGIFSignature(char* contents)
 {
-    return !memcmp(contents, "GIF8", 4);
+    return !memcmp(contents, "GIF87a", 6) || !memcmp(contents, "GIF89a", 6);
 }
 
 bool matchesPNGSignature(char* contents)
 {
-    return !memcmp(contents, "\x89\x50\x4E\x47", 4);
+    return !memcmp(contents, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8);
 }
 
 bool matchesJPEGSignature(char* contents)
@@ -94,39 +96,37 @@ bool matchesCURSignature(char* contents)
 
 }
 
-ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption)
+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 0;
+        return nullptr;
 
     if (matchesGIFSignature(contents))
-        return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption);
+        return std::unique_ptr<ImageDecoder> { std::make_unique<GIFImageDecoder>(alphaOption, gammaAndColorProfileOption) };
 
     if (matchesPNGSignature(contents))
-        return new PNGImageDecoder(alphaOption, gammaAndColorProfileOption);
+        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 new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption);
+        return std::unique_ptr<ImageDecoder> { std::make_unique<JPEGImageDecoder>(alphaOption, gammaAndColorProfileOption) };
 
 #if USE(WEBP)
     if (matchesWebPSignature(contents))
-        return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption);
+        return std::unique_ptr<ImageDecoder> { std::make_unique<WEBPImageDecoder>(alphaOption, gammaAndColorProfileOption) };
 #endif
 
     if (matchesBMPSignature(contents))
-        return new BMPImageDecoder(alphaOption, gammaAndColorProfileOption);
+        return std::unique_ptr<ImageDecoder> { std::make_unique<BMPImageDecoder>(alphaOption, gammaAndColorProfileOption) };
 
-    if (matchesICOSignature(contents) || matchesCURSignature(contents))
-        return new ICOImageDecoder(alphaOption, gammaAndColorProfileOption);
-
-    return 0;
+    return nullptr;
 }
 
-#if !PLATFORM(SKIA)
-
 ImageFrame::ImageFrame()
     : m_hasAlpha(false)
     , m_status(FrameEmpty)
@@ -141,7 +141,7 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other)
     if (this == &other)
         return *this;
 
-    copyReferenceToBitmapData(other);
+    copyBitmapData(other);
     setOriginalFrameRect(other.originalFrameRect());
     setStatus(other.status());
     setDuration(other.duration());
@@ -167,12 +167,21 @@ void ImageFrame::zeroFillPixelData()
     m_hasAlpha = true;
 }
 
-#if !PLATFORM(CG)
-
-void ImageFrame::copyReferenceToBitmapData(const ImageFrame& 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 ImageFrame::copyBitmapData(const ImageFrame& other)
@@ -189,19 +198,18 @@ bool ImageFrame::copyBitmapData(const ImageFrame& other)
 
 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);
 
     zeroFillPixelData();
-
     return true;
 }
 
-#endif
-
 bool ImageFrame::hasAlpha() const
 {
     return m_hasAlpha;
@@ -222,18 +230,6 @@ void ImageFrame::setStatus(FrameStatus status)
     m_status = status;
 }
 
-int ImageFrame::width() const
-{
-    return m_size.width();
-}
-
-int ImageFrame::height() const
-{
-    return m_size.height();
-}
-
-#endif
-
 namespace {
 
 enum MatchType {
@@ -275,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;