Title: [208365] trunk/Source/WebCore
Revision
208365
Author
[email protected]
Date
2016-11-03 19:57:49 -0700 (Thu, 03 Nov 2016)

Log Message

Add the asynchronous image decoding mode
https://bugs.webkit.org/show_bug.cgi?id=155546

Patch by Said Abou-Hallawa <[email protected]> on 2016-11-03
Reviewed by Simon Fraser.

The asynchronous image decoding feature targets enhancing the rendering
in two scenarios: the animated images and scrolling a page which large
images. Enabling this feature for these two scenarios will be landed
separately.

The goal of the asynchronous image decoding is to have the decoded image
frame ready before it has to be drawn. Drawing an image does not have to
wait the image frame to be decoded.

* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::frameImageAtIndex): Use the negation of frameHasValidNativeImageAtIndex().
* platform/graphics/BitmapImage.h:
(WebCore::BitmapImage::frameIsBeingDecodedAtIndex): Answers whether a frame is being decoded.
(WebCore::BitmapImage::frameHasValidNativeImageAtIndex): Checks the validity of a frame.
(WebCore::BitmapImage::frameHasInvalidNativeImageAtIndex): Deleted.
* platform/graphics/Image.h:
(WebCore::Image::newFrameNativeImageAvailableAtIndex): Notifies the image with the availability of a frame NativeImage.
* platform/graphics/ImageFrame.h:
(WebCore::ImageFrame::isBeingDecoded): Answers whether the frame is being decoded.
(WebCore::ImageFrame::hasValidNativeImage): Checks the validity of the frame.
(WebCore::ImageFrame::hasInvalidNativeImage): Deleted.
* platform/graphics/ImageFrameCache.cpp:
(WebCore::ImageFrameCache::~ImageFrameCache): Asserts the decoding loop was ended before deleting the ImageFrameCache.
(WebCore::ImageFrameCache::setFrameNativeImageAtIndex): Rename this function to matches the other which take the frame index.
(WebCore::ImageFrameCache::setFrameMetadataAtIndex): Ditto.
(WebCore::ImageFrameCache::replaceFrameNativeImageAtIndex): It setts the ImageFrame's members and updates the decoded size.
(WebCore::ImageFrameCache::cacheFrameNativeImageAtIndex): Replaces the frame NativeImage and notifies the Image with the new frame.
(WebCore::ImageFrameCache::decodingQueue): Ensures the decoding WorkQueue is created and returns it.
(WebCore::ImageFrameCache::startAsyncDecodingQueue): Starts a decoding WorkQueue which loops until m_frameRequestQueue is closed.
(WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Allows ImageSource to send a request to start asynchronous frame image decoding.
(WebCore::ImageFrameCache::stopAsyncDecodingQueue): Stops the decoding WorkQueue by closing m_frameRequestQueue.
(WebCore::ImageFrameCache::frameAtIndex): Call replaceFrameNativeImageAtIndex().
(WebCore::ImageFrameCache::frameIsBeingDecodedAtIndex): Returns true if a request for the image frame is issued but not finished yet.
(WebCore::ImageFrameCache::frameHasValidNativeImageAtIndex): Checks the validity of a frame.
(WebCore::ImageFrameCache::setFrameNativeImage): Deleted. Was renamed to be setFrameNativeImageAtIndex.
(WebCore::ImageFrameCache::setFrameMetadata): Deleted. Was renamed to be setFrameMetadataAtIndex
(WebCore::ImageFrameCache::frameHasInvalidNativeImageAtIndex): Deleted. Was renamed to be frameHasValidNativeImageAtIndex.
* platform/graphics/ImageFrameCache.h:
(WebCore::ImageFrameCache::create): The decoding queue needs to hold a reference to this class so it can stop decoding safely without blocking.
(WebCore::ImageFrameCache::hasDecodingQueue): Returns true if a decoding queue has started.
* platform/graphics/ImageSource.cpp:
(WebCore::ImageSource::ImageSource): Call ImageFrameCache::create().
(WebCore::ImageSource::clear): Deleting the decoder is unnecessary for asynchronous decoding because ImageFrameCache manages all the memory.

(WebCore::ImageSource::destroyDecodedData):
(WebCore::ImageSource::destroyDecodedDataIfNecessary):
(WebCore::ImageSource::ensureDecoderAvailable):
(WebCore::ImageSource::dataChanged):
(WebCore::ImageSource::isAllDataReceived):
(WebCore::ImageSource::isAsyncDecodingRequired): Answers the question whether the async image decoding is required for this ImageSource.
(WebCore::ImageSource::frameImageAtIndex):
* platform/graphics/ImageSource.h:
(WebCore::ImageSource::decodedSize):
(WebCore::ImageSource::requestFrameAsyncDecodingAtIndex):
(WebCore::ImageSource::stopAsyncDecodingQueue):
(WebCore::ImageSource::isSizeAvailable):
(WebCore::ImageSource::frameCount):
(WebCore::ImageSource::repetitionCount):
(WebCore::ImageSource::filenameExtension):
(WebCore::ImageSource::hotSpot):
(WebCore::ImageSource::size):
(WebCore::ImageSource::sizeRespectingOrientation):
(WebCore::ImageSource::singlePixelSolidColor):
(WebCore::ImageSource::frameIsBeingDecodedAtIndex):
(WebCore::ImageSource::frameIsCompleteAtIndex):
(WebCore::ImageSource::frameHasAlphaAtIndex):
(WebCore::ImageSource::frameHasImageAtIndex):
(WebCore::ImageSource::frameSubsamplingLevelAtIndex):
(WebCore::ImageSource::frameSizeAtIndex):
(WebCore::ImageSource::frameBytesAtIndex):
(WebCore::ImageSource::frameDurationAtIndex):
(WebCore::ImageSource::frameOrientationAtIndex):
 Make m_frameCache a type Ref<ImageFrameCache>. Use '->' instead of '.' when accessing its members.

(WebCore::ImageSource::frameHasValidNativeImageAtIndex): Checks the validity of a frame.
(WebCore::ImageSource::frameHasInvalidNativeImageAtIndex): Deleted. Was renamed to be frameHasValidNativeImageAtIndex.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (208364 => 208365)


--- trunk/Source/WebCore/ChangeLog	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/ChangeLog	2016-11-04 02:57:49 UTC (rev 208365)
@@ -1,3 +1,87 @@
+2016-11-03  Said Abou-Hallawa  <[email protected]>
+
+        Add the asynchronous image decoding mode
+        https://bugs.webkit.org/show_bug.cgi?id=155546
+
+        Reviewed by Simon Fraser.
+
+        The asynchronous image decoding feature targets enhancing the rendering
+        in two scenarios: the animated images and scrolling a page which large
+        images. Enabling this feature for these two scenarios will be landed
+        separately. 
+
+        The goal of the asynchronous image decoding is to have the decoded image
+        frame ready before it has to be drawn. Drawing an image does not have to
+        wait the image frame to be decoded.
+
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::frameImageAtIndex): Use the negation of frameHasValidNativeImageAtIndex().
+        * platform/graphics/BitmapImage.h:
+        (WebCore::BitmapImage::frameIsBeingDecodedAtIndex): Answers whether a frame is being decoded.
+        (WebCore::BitmapImage::frameHasValidNativeImageAtIndex): Checks the validity of a frame.
+        (WebCore::BitmapImage::frameHasInvalidNativeImageAtIndex): Deleted.
+        * platform/graphics/Image.h:
+        (WebCore::Image::newFrameNativeImageAvailableAtIndex): Notifies the image with the availability of a frame NativeImage.
+        * platform/graphics/ImageFrame.h:
+        (WebCore::ImageFrame::isBeingDecoded): Answers whether the frame is being decoded.
+        (WebCore::ImageFrame::hasValidNativeImage): Checks the validity of the frame.
+        (WebCore::ImageFrame::hasInvalidNativeImage): Deleted.
+        * platform/graphics/ImageFrameCache.cpp:
+        (WebCore::ImageFrameCache::~ImageFrameCache): Asserts the decoding loop was ended before deleting the ImageFrameCache.
+        (WebCore::ImageFrameCache::setFrameNativeImageAtIndex): Rename this function to matches the other which take the frame index.
+        (WebCore::ImageFrameCache::setFrameMetadataAtIndex): Ditto.
+        (WebCore::ImageFrameCache::replaceFrameNativeImageAtIndex): It setts the ImageFrame's members and updates the decoded size.
+        (WebCore::ImageFrameCache::cacheFrameNativeImageAtIndex): Replaces the frame NativeImage and notifies the Image with the new frame.
+        (WebCore::ImageFrameCache::decodingQueue): Ensures the decoding WorkQueue is created and returns it.
+        (WebCore::ImageFrameCache::startAsyncDecodingQueue): Starts a decoding WorkQueue which loops until m_frameRequestQueue is closed.
+        (WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Allows ImageSource to send a request to start asynchronous frame image decoding.
+        (WebCore::ImageFrameCache::stopAsyncDecodingQueue): Stops the decoding WorkQueue by closing m_frameRequestQueue.
+        (WebCore::ImageFrameCache::frameAtIndex): Call replaceFrameNativeImageAtIndex().
+        (WebCore::ImageFrameCache::frameIsBeingDecodedAtIndex): Returns true if a request for the image frame is issued but not finished yet.
+        (WebCore::ImageFrameCache::frameHasValidNativeImageAtIndex): Checks the validity of a frame.
+        (WebCore::ImageFrameCache::setFrameNativeImage): Deleted. Was renamed to be setFrameNativeImageAtIndex.
+        (WebCore::ImageFrameCache::setFrameMetadata): Deleted. Was renamed to be setFrameMetadataAtIndex
+        (WebCore::ImageFrameCache::frameHasInvalidNativeImageAtIndex): Deleted. Was renamed to be frameHasValidNativeImageAtIndex.
+        * platform/graphics/ImageFrameCache.h:
+        (WebCore::ImageFrameCache::create): The decoding queue needs to hold a reference to this class so it can stop decoding safely without blocking.
+        (WebCore::ImageFrameCache::hasDecodingQueue): Returns true if a decoding queue has started.
+        * platform/graphics/ImageSource.cpp:
+        (WebCore::ImageSource::ImageSource): Call ImageFrameCache::create().
+        (WebCore::ImageSource::clear): Deleting the decoder is unnecessary for asynchronous decoding because ImageFrameCache manages all the memory.
+        
+        (WebCore::ImageSource::destroyDecodedData):
+        (WebCore::ImageSource::destroyDecodedDataIfNecessary):
+        (WebCore::ImageSource::ensureDecoderAvailable):
+        (WebCore::ImageSource::dataChanged):
+        (WebCore::ImageSource::isAllDataReceived):
+        (WebCore::ImageSource::isAsyncDecodingRequired): Answers the question whether the async image decoding is required for this ImageSource.
+        (WebCore::ImageSource::frameImageAtIndex):
+        * platform/graphics/ImageSource.h:
+        (WebCore::ImageSource::decodedSize):
+        (WebCore::ImageSource::requestFrameAsyncDecodingAtIndex):
+        (WebCore::ImageSource::stopAsyncDecodingQueue):
+        (WebCore::ImageSource::isSizeAvailable):
+        (WebCore::ImageSource::frameCount):
+        (WebCore::ImageSource::repetitionCount):
+        (WebCore::ImageSource::filenameExtension):
+        (WebCore::ImageSource::hotSpot):
+        (WebCore::ImageSource::size):
+        (WebCore::ImageSource::sizeRespectingOrientation):
+        (WebCore::ImageSource::singlePixelSolidColor):
+        (WebCore::ImageSource::frameIsBeingDecodedAtIndex):
+        (WebCore::ImageSource::frameIsCompleteAtIndex):
+        (WebCore::ImageSource::frameHasAlphaAtIndex):
+        (WebCore::ImageSource::frameHasImageAtIndex):
+        (WebCore::ImageSource::frameSubsamplingLevelAtIndex):
+        (WebCore::ImageSource::frameSizeAtIndex):
+        (WebCore::ImageSource::frameBytesAtIndex):
+        (WebCore::ImageSource::frameDurationAtIndex):
+        (WebCore::ImageSource::frameOrientationAtIndex):
+         Make m_frameCache a type Ref<ImageFrameCache>. Use '->' instead of '.' when accessing its members.
+
+        (WebCore::ImageSource::frameHasValidNativeImageAtIndex): Checks the validity of a frame.
+        (WebCore::ImageSource::frameHasInvalidNativeImageAtIndex): Deleted. Was renamed to be frameHasValidNativeImageAtIndex.
+
 2016-11-03  Myles C. Maxfield  <[email protected]>
 
         [WebGL2] Implement getBufferSubData()

Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp	2016-11-04 02:57:49 UTC (rev 208365)
@@ -82,7 +82,7 @@
 
 NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
 {
-    if (frameHasInvalidNativeImageAtIndex(index, subsamplingLevel)) {
+    if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel)) {
         LOG(Images, "BitmapImage %p frameImageAtIndex - subsamplingLevel was %d, resampling", this, static_cast<int>(frameSubsamplingLevelAtIndex(index)));
         invalidatePlatformData();
     }

Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/BitmapImage.h	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h	2016-11-04 02:57:49 UTC (rev 208365)
@@ -82,9 +82,10 @@
 
     void setAllowSubsampling(bool allowSubsampling) { m_source.setAllowSubsampling(allowSubsampling); }
 
+    bool frameIsBeingDecodedAtIndex(size_t index) const { return m_source.frameIsBeingDecodedAtIndex(index); }
     bool frameIsCompleteAtIndex(size_t index) const { return m_source.frameIsCompleteAtIndex(index); }
     bool frameHasAlphaAtIndex(size_t index) const { return m_source.frameHasAlphaAtIndex(index); }
-    bool frameHasInvalidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const { return m_source.frameHasInvalidNativeImageAtIndex(index, subsamplingLevel); }
+    bool frameHasValidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const { return m_source.frameHasValidNativeImageAtIndex(index, subsamplingLevel); }
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) const { return m_source.frameSubsamplingLevelAtIndex(index); }
 
     float frameDurationAtIndex(size_t index) const { return m_source.frameDurationAtIndex(index); }

Modified: trunk/Source/WebCore/platform/graphics/Image.h (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/Image.h	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/Image.h	2016-11-04 02:57:49 UTC (rev 208365)
@@ -129,6 +129,7 @@
     virtual void startAnimation() { }
     virtual void stopAnimation() {}
     virtual void resetAnimation() {}
+    virtual void newFrameNativeImageAvailableAtIndex(size_t) { }
     
     // Typically the CachedImage that owns us.
     ImageObserver* imageObserver() const { return m_imageObserver; }

Modified: trunk/Source/WebCore/platform/graphics/ImageFrame.h (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/ImageFrame.h	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/ImageFrame.h	2016-11-04 02:57:49 UTC (rev 208365)
@@ -82,7 +82,7 @@
     friend class ImageFrameCache;
 public:
     enum class Caching { Empty, Metadata, MetadataAndImage };
-    enum class Decoding { Empty, Partial, Complete };
+    enum class Decoding { Empty, BeingDecoded, Partial, Complete };
 
     ImageFrame();
     ImageFrame(const ImageFrame& other) { operator=(other); }
@@ -104,6 +104,7 @@
     void setDecoding(Decoding decoding) { m_decoding = decoding; }
     Decoding decoding() const { return m_decoding; }
     bool isEmpty() const { return m_decoding == Decoding::Empty; }
+    bool isBeingDecoded() const { return m_decoding == Decoding::BeingDecoded; }
     bool isPartial() const { return m_decoding == Decoding::Partial; }
     bool isComplete() const { return m_decoding == Decoding::Complete; }
 
@@ -130,7 +131,7 @@
     bool hasAlpha() const { return !hasMetadata() || m_hasAlpha; }
 
     bool hasNativeImage() const { return m_nativeImage; }
-    bool hasInvalidNativeImage(SubsamplingLevel subsamplingLevel) const { return hasNativeImage() && subsamplingLevel < m_subsamplingLevel; }
+    bool hasValidNativeImage(SubsamplingLevel subsamplingLevel) const { return hasNativeImage() && subsamplingLevel >= m_subsamplingLevel; }
     bool hasMetadata() const { return !size().isEmpty(); }
 
 #if !USE(CG)

Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp	2016-11-04 02:57:49 UTC (rev 208365)
@@ -39,8 +39,9 @@
 #endif
 
 #include <wtf/CheckedArithmetic.h>
+#include <wtf/MainThread.h>
+#include <wtf/RunLoop.h>
 
-
 namespace WebCore {
 
 ImageFrameCache::ImageFrameCache(Image* image)
@@ -64,6 +65,11 @@
     m_sizeRespectingOrientation = m_size;
 }
 
+ImageFrameCache::~ImageFrameCache()
+{
+    ASSERT(!hasDecodingQueue());
+}
+
 void ImageFrameCache::destroyDecodedData(bool destroyAll, size_t count)
 {
     if (destroyAll)
@@ -178,7 +184,7 @@
     frame.m_hasAlpha = nativeImageHasAlpha(frame.m_nativeImage);
 }
 
-void ImageFrameCache::setFrameNativeImage(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
+void ImageFrameCache::setFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
@@ -186,10 +192,10 @@
     ASSERT(isDecoderAvailable());
 
     frame.m_nativeImage = WTFMove(nativeImage);
-    setFrameMetadata(index, subsamplingLevel);
+    setFrameMetadataAtIndex(index, subsamplingLevel);
 }
 
-void ImageFrameCache::setFrameMetadata(size_t index, SubsamplingLevel subsamplingLevel)
+void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
@@ -208,34 +214,121 @@
         frame.m_duration = m_decoder->frameDurationAtIndex(index);
 }
 
-const ImageFrame& ImageFrameCache::frameAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageFrame::Caching caching)
+void ImageFrameCache::replaceFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
-    if (!isDecoderAvailable() || caching == ImageFrame::Caching::Empty)
-        return frame;
+
+    if (!frame.hasValidNativeImage(subsamplingLevel)) {
+        // Clear the current image frame and update the observer with this clearance.
+        unsigned decodedSize = frame.clear();
+        decodedSizeDecreased(decodedSize);
+    }
+
+    // Do not cache the NativeImage if adding its frameByes to the MemoryCache will cause numerical overflow.
+    size_t frameBytes = size().unclampedArea() * sizeof(RGBA32);
+    if (!WTF::isInBounds<unsigned>(frameBytes + decodedSize()))
+        return;
+
+    // Copy the new image to the cache.
+    setFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel);
+
+    // Update the observer with the new image frame bytes.
+    decodedSizeIncreased(frame.frameBytes());
+}
+
+void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
+{
+    if (!isDecoderAvailable())
+        return;
+
+    // Clean the old native image and set a new one
+    replaceFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel);
+
+    // Notify the image with the readiness of the new frame NativeImage.
+    if (m_image)
+        m_image->newFrameNativeImageAvailableAtIndex(index);
+}
+
+Ref<WorkQueue> ImageFrameCache::decodingQueue()
+{
+    if (!m_decodingQueue)
+        m_decodingQueue = WorkQueue::create("org.webkit.ImageDecoder", WorkQueue::Type::Serial, WorkQueue::QOS::UserInteractive);
     
+    return *m_decodingQueue;
+}
+
+void ImageFrameCache::startAsyncDecodingQueue()
+{
+    if (hasDecodingQueue() || !isDecoderAvailable())
+        return;
+
+    Ref<ImageFrameCache> protectedThis = Ref<ImageFrameCache>(*this);
+    Ref<WorkQueue> protectedQueue = decodingQueue();
+
+    // We need to protect this and m_decodingQueue from being deleted while we are in the decoding loop.
+    decodingQueue()->dispatch([this, protectedThis = WTFMove(protectedThis), protectedQueue = WTFMove(protectedQueue)] {
+        ImageFrameRequest frameRequest;
+
+        while (m_frameRequestQueue.dequeue(frameRequest)) {
+            // Get the frame NativeImage on the decoding thread.
+            NativeImagePtr nativeImage = m_decoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, DecodingMode::Immediate);
+
+            // Update the cached frames on the main thread to avoid updating the MemoryCache from a different thread.
+            callOnMainThread([this, nativeImage, frameRequest] () mutable {
+                // The queue may be closed if after we got the frame NativeImage, stopAsyncDecodingQueue() was called
+                if (hasDecodingQueue())
+                    cacheFrameNativeImageAtIndex(WTFMove(nativeImage), frameRequest.index, frameRequest.subsamplingLevel);
+            });
+        }
+    });
+}
+
+void ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+{
+    if (!isDecoderAvailable())
+        return;
+
+    if (!hasDecodingQueue())
+        startAsyncDecodingQueue();
+
+    ASSERT(index < m_frames.size());
+    ImageFrame& frame = m_frames[index];
+    
     if (subsamplingLevel == SubsamplingLevel::Undefinded)
         subsamplingLevel = frame.subsamplingLevel();
     
-    if (frame.hasInvalidNativeImage(subsamplingLevel)) {
-        unsigned decodedSize = frame.clear();
-        decodedSizeDecreased(decodedSize);
-    }
+    if (frame.hasValidNativeImage(subsamplingLevel))
+        return;
     
-    if (!frame.isComplete() && caching == ImageFrame::Caching::Metadata)
-        setFrameMetadata(index, subsamplingLevel);
+    frame.setDecoding(ImageFrame::Decoding::BeingDecoded);
+    m_frameRequestQueue.enqueue({ index, subsamplingLevel });
+}
+
+void ImageFrameCache::stopAsyncDecodingQueue()
+{
+    if (!hasDecodingQueue())
+        return;
     
-    if (!frame.hasNativeImage() && caching == ImageFrame::Caching::MetadataAndImage) {
-        size_t frameBytes = size().unclampedArea() * sizeof(RGBA32);
+    m_frameRequestQueue.close();
+    m_decodingQueue = nullptr;
+}
 
-        // Do not create the NativeImage if adding its frameByes to the MemoryCache will cause numerical overflow.
-        if (WTF::isInBounds<unsigned>(frameBytes + decodedSize())) {
-            setFrameNativeImage(m_decoder->createFrameImageAtIndex(index, subsamplingLevel), index, subsamplingLevel);
-            decodedSizeIncreased(frame.frameBytes());
-        }
-    }
+const ImageFrame& ImageFrameCache::frameAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageFrame::Caching caching)
+{
+    ASSERT(index < m_frames.size());
+    ImageFrame& frame = m_frames[index];
+    if (!isDecoderAvailable() || frame.isBeingDecoded() || caching == ImageFrame::Caching::Empty)
+        return frame;
     
+    if (subsamplingLevel == SubsamplingLevel::Undefinded)
+        subsamplingLevel = frame.subsamplingLevel();
+
+    if (!frame.isComplete() && caching == ImageFrame::Caching::Metadata)
+        setFrameMetadataAtIndex(index, subsamplingLevel);
+    else if (!frame.hasValidNativeImage(subsamplingLevel) && caching == ImageFrame::Caching::MetadataAndImage)
+        replaceFrameNativeImageAtIndex(m_decoder->createFrameImageAtIndex(index, subsamplingLevel), index, subsamplingLevel);
+
     return frame;
 }
 
@@ -327,6 +420,11 @@
     return frameCount() == 1 ? frameMetadataAtIndex<Color, (&ImageFrame::singlePixelSolidColor)>(0, SubsamplingLevel::Undefinded, ImageFrame::Caching::MetadataAndImage, &m_singlePixelSolidColor) : Color();
 }
 
+bool ImageFrameCache::frameIsBeingDecodedAtIndex(size_t index)
+{
+    return frameMetadataAtIndex<bool, (&ImageFrame::isBeingDecoded)>(index);
+}
+
 bool ImageFrameCache::frameIsCompleteAtIndex(size_t index)
 {
     return frameMetadataAtIndex<bool, (&ImageFrame::isComplete)>(index);
@@ -342,9 +440,9 @@
     return frameMetadataAtIndex<bool, (&ImageFrame::hasNativeImage)>(index);
 }
 
-bool ImageFrameCache::frameHasInvalidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+bool ImageFrameCache::frameHasValidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
 {
-    return frameHasImageAtIndex(index) && subsamplingLevel < frameSubsamplingLevelAtIndex(index);
+    return frameHasImageAtIndex(index) && subsamplingLevel >= frameSubsamplingLevelAtIndex(index);
 }
 
 SubsamplingLevel ImageFrameCache::frameSubsamplingLevelAtIndex(size_t index)

Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.h (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.h	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.h	2016-11-04 02:57:49 UTC (rev 208365)
@@ -30,6 +30,9 @@
 
 #include <wtf/Forward.h>
 #include <wtf/Optional.h>
+#include <wtf/SynchronizedFixedQueue.h>
+#include <wtf/WorkQueue.h>
+#include <wtf/threads/BinarySemaphore.h>
 
 namespace WebCore {
 
@@ -37,12 +40,21 @@
 class Image;
 class ImageDecoder;
 
-class ImageFrameCache {
+class ImageFrameCache : public RefCounted<ImageFrameCache> {
     friend class ImageSource;
 public:
-    ImageFrameCache(Image*);
-    ImageFrameCache(NativeImagePtr&&);
+    static Ref<ImageFrameCache> create(Image* image)
+    {
+        return adoptRef(*new ImageFrameCache(image));
+    }
 
+    static Ref<ImageFrameCache> create(NativeImagePtr&& nativeImage)
+    {
+        return adoptRef(*new ImageFrameCache(WTFMove(nativeImage)));
+    }
+
+    ~ImageFrameCache();
+
     void setDecoder(ImageDecoder* decoder) { m_decoder = decoder; }
     ImageDecoder* decoder() const { return m_decoder; }
 
@@ -53,6 +65,12 @@
 
     void growFrames();
     void clearMetadata();
+    
+    // Asynchronous image decoding
+    void startAsyncDecodingQueue();
+    void requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel);
+    void stopAsyncDecodingQueue();
+    bool hasDecodingQueue() { return m_decodingQueue; }
 
     // Image metadata which is calculated either by the ImageDecoder or directly
     // from the NativeImage if this class was created for a memory image.
@@ -69,10 +87,11 @@
     Color singlePixelSolidColor();
 
     // ImageFrame metadata which does not require caching the ImageFrame.
+    bool frameIsBeingDecodedAtIndex(size_t);
     bool frameIsCompleteAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t);
     bool frameHasImageAtIndex(size_t);
-    bool frameHasInvalidNativeImageAtIndex(size_t, SubsamplingLevel);
+    bool frameHasValidNativeImageAtIndex(size_t, SubsamplingLevel);
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t);
     
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
@@ -83,6 +102,9 @@
     NativeImagePtr frameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
 
 private:
+    ImageFrameCache(Image*);
+    ImageFrameCache(NativeImagePtr&&);
+
     template<typename T, T (ImageDecoder::*functor)() const>
     T metadata(const T& defaultValue, Optional<T>* cachedValue = nullptr);
 
@@ -97,8 +119,13 @@
     void decodedSizeReset(unsigned decodedSize);
 
     void setNativeImage(NativeImagePtr&&);
-    void setFrameNativeImage(NativeImagePtr&&, size_t, SubsamplingLevel);
-    void setFrameMetadata(size_t, SubsamplingLevel);
+    void setFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel);
+    void setFrameMetadataAtIndex(size_t, SubsamplingLevel);
+    void replaceFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel);
+    void cacheFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel);
+
+    Ref<WorkQueue> decodingQueue();
+
     const ImageFrame& frameAtIndex(size_t, SubsamplingLevel, ImageFrame::Caching);
 
     // Animated images over a certain size are considered large enough that we'll only hang on to one frame at a time.
@@ -115,6 +142,16 @@
 
     Vector<ImageFrame, 1> m_frames;
 
+    // Asynchronous image decoding.
+    struct ImageFrameRequest {
+        size_t index;
+        SubsamplingLevel subsamplingLevel;
+    };
+    static const int BufferSize = 8;
+    using FrameRequestQueue = SynchronizedFixedQueue<ImageFrameRequest, BufferSize>;
+    FrameRequestQueue m_frameRequestQueue;
+    RefPtr<WorkQueue> m_decodingQueue;
+
     // Image metadata.
     Optional<bool> m_isSizeAvailable;
     Optional<size_t> m_frameCount;

Modified: trunk/Source/WebCore/platform/graphics/ImageSource.cpp (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/ImageSource.cpp	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.cpp	2016-11-04 02:57:49 UTC (rev 208365)
@@ -46,12 +46,12 @@
 namespace WebCore {
 
 ImageSource::ImageSource(NativeImagePtr&& nativeImage)
-    : m_frameCache(WTFMove(nativeImage))
+    : m_frameCache(ImageFrameCache::create(WTFMove(nativeImage)))
 {
 }
     
 ImageSource::ImageSource(Image* image, AlphaOption alphaOption, GammaAndColorProfileOption gammaAndColorProfileOption)
-    : m_frameCache(image)
+    : m_frameCache(ImageFrameCache::create(image))
     , m_alphaOption(alphaOption)
     , m_gammaAndColorProfileOption(gammaAndColorProfileOption)
 {
@@ -72,19 +72,19 @@
 {
     // There's no need to throw away the decoder unless we're explicitly asked
     // to destroy all of the frames.
-    if (!destroyAll) {
+    if (!destroyAll || m_frameCache->hasDecodingQueue()) {
         clearFrameBufferCache(count);
         return;
     }
 
     m_decoder = nullptr;
-    m_frameCache.setDecoder(nullptr);
+    m_frameCache->setDecoder(nullptr);
     setData(data, isAllDataReceived());
 }
 
 void ImageSource::destroyDecodedData(SharedBuffer* data, bool destroyAll, size_t count)
 {
-    m_frameCache.destroyDecodedData(destroyAll, count);
+    m_frameCache->destroyDecodedData(destroyAll, count);
     clear(destroyAll, count, data);
 }
 
@@ -92,10 +92,10 @@
 {
     // If we have decoded frames but there is no encoded data, we shouldn't destroy
     // the decoded image since we won't be able to reconstruct it later.
-    if (!data && m_frameCache.frameCount())
+    if (!data && m_frameCache->frameCount())
         return false;
 
-    if (!m_frameCache.destroyDecodedDataIfNecessary(destroyAll, count))
+    if (!m_frameCache->destroyDecodedDataIfNecessary(destroyAll, count))
         return false;
 
     clear(destroyAll, count, data);
@@ -111,7 +111,7 @@
     if (!isDecoderAvailable())
         return false;
 
-    m_frameCache.setDecoder(m_decoder.get());
+    m_frameCache->setDecoder(m_decoder.get());
     return true;
 }
 
@@ -138,7 +138,7 @@
 
 bool ImageSource::dataChanged(SharedBuffer* data, bool allDataReceived)
 {
-    m_frameCache.destroyIncompleteDecodedData();
+    m_frameCache->destroyIncompleteDecodedData();
 
 #if PLATFORM(IOS)
     // FIXME: We should expose a setting to enable/disable progressive loading and make this
@@ -162,19 +162,25 @@
     setData(data, allDataReceived);
 #endif
 
-    m_frameCache.clearMetadata();
+    m_frameCache->clearMetadata();
     if (!isSizeAvailable())
         return false;
 
-    m_frameCache.growFrames();
+    m_frameCache->growFrames();
     return true;
 }
 
 bool ImageSource::isAllDataReceived()
 {
-    return isDecoderAvailable() ? m_decoder->isAllDataReceived() : m_frameCache.frameCount();
+    return isDecoderAvailable() ? m_decoder->isAllDataReceived() : m_frameCache->frameCount();
 }
 
+bool ImageSource::isAsyncDecodingRequired()
+{
+    // FIXME: figure out the best heuristic for enabling async image decoding.
+    return size().area() * sizeof(RGBA32) >= 100 * KB;
+}
+
 SubsamplingLevel ImageSource::maximumSubsamplingLevel()
 {
     if (m_maximumSubsamplingLevel)
@@ -216,7 +222,7 @@
 {
     setDecoderTargetContext(targetContext);
 
-    return m_frameCache.frameImageAtIndex(index, subsamplingLevel);
+    return m_frameCache->frameImageAtIndex(index, subsamplingLevel);
 }
 
 void ImageSource::dump(TextStream& ts)

Modified: trunk/Source/WebCore/platform/graphics/ImageSource.h (208364 => 208365)


--- trunk/Source/WebCore/platform/graphics/ImageSource.h	2016-11-04 02:37:45 UTC (rev 208364)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.h	2016-11-04 02:57:49 UTC (rev 208365)
@@ -62,33 +62,38 @@
     void setData(SharedBuffer* data, bool allDataReceived);
     bool dataChanged(SharedBuffer* data, bool allDataReceived);
 
-    unsigned decodedSize() const { return m_frameCache.decodedSize(); }
+    unsigned decodedSize() const { return m_frameCache->decodedSize(); }
     bool isAllDataReceived();
 
+    bool isAsyncDecodingRequired();
+    void requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel); }
+    void stopAsyncDecodingQueue() { m_frameCache->stopAsyncDecodingQueue(); }
+
     // Image metadata which is calculated by the decoder or can deduced by the case of the memory NativeImage.
-    bool isSizeAvailable() { return m_frameCache.isSizeAvailable(); }
-    size_t frameCount() { return m_frameCache.frameCount(); }
-    RepetitionCount repetitionCount() { return m_frameCache.repetitionCount(); }
-    String filenameExtension() { return m_frameCache.filenameExtension(); }
-    Optional<IntPoint> hotSpot() { return m_frameCache.hotSpot(); }
+    bool isSizeAvailable() { return m_frameCache->isSizeAvailable(); }
+    size_t frameCount() { return m_frameCache->frameCount(); }
+    RepetitionCount repetitionCount() { return m_frameCache->repetitionCount(); }
+    String filenameExtension() { return m_frameCache->filenameExtension(); }
+    Optional<IntPoint> hotSpot() { return m_frameCache->hotSpot(); }
 
     // Image metadata which is calculated from the first ImageFrame.
-    IntSize size() { return m_frameCache.size(); }
-    IntSize sizeRespectingOrientation() { return m_frameCache.sizeRespectingOrientation(); }
-    Color singlePixelSolidColor() { return m_frameCache.singlePixelSolidColor(); }
+    IntSize size() { return m_frameCache->size(); }
+    IntSize sizeRespectingOrientation() { return m_frameCache->sizeRespectingOrientation(); }
+    Color singlePixelSolidColor() { return m_frameCache->singlePixelSolidColor(); }
 
     // ImageFrame metadata which does not require caching the ImageFrame.
-    bool frameIsCompleteAtIndex(size_t index) { return m_frameCache.frameIsCompleteAtIndex(index); }
-    bool frameHasAlphaAtIndex(size_t index) { return m_frameCache.frameHasAlphaAtIndex(index); }
-    bool frameHasImageAtIndex(size_t index) { return m_frameCache.frameHasImageAtIndex(index); }
-    bool frameHasInvalidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_frameCache.frameHasInvalidNativeImageAtIndex(index, subsamplingLevel); }
-    SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) { return m_frameCache.frameSubsamplingLevelAtIndex(index); }
+    bool frameIsBeingDecodedAtIndex(size_t index) { return m_frameCache->frameIsBeingDecodedAtIndex(index); }
+    bool frameIsCompleteAtIndex(size_t index) { return m_frameCache->frameIsCompleteAtIndex(index); }
+    bool frameHasAlphaAtIndex(size_t index) { return m_frameCache->frameHasAlphaAtIndex(index); }
+    bool frameHasImageAtIndex(size_t index) { return m_frameCache->frameHasImageAtIndex(index); }
+    bool frameHasValidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_frameCache->frameHasValidNativeImageAtIndex(index, subsamplingLevel); }
+    SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) { return m_frameCache->frameSubsamplingLevelAtIndex(index); }
 
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
-    IntSize frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache.frameSizeAtIndex(index, subsamplingLevel); }
-    unsigned frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache.frameBytesAtIndex(index, subsamplingLevel); }
-    float frameDurationAtIndex(size_t index) { return m_frameCache.frameDurationAtIndex(index); }
-    ImageOrientation frameOrientationAtIndex(size_t index) { return m_frameCache.frameOrientationAtIndex(index); }
+    IntSize frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameSizeAtIndex(index, subsamplingLevel); }
+    unsigned frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameBytesAtIndex(index, subsamplingLevel); }
+    float frameDurationAtIndex(size_t index) { return m_frameCache->frameDurationAtIndex(index); }
+    ImageOrientation frameOrientationAtIndex(size_t index) { return m_frameCache->frameOrientationAtIndex(index); }
     NativeImagePtr frameImageAtIndex(size_t index, SubsamplingLevel = SubsamplingLevel::Default, const GraphicsContext* targetContext = nullptr);
 
     SubsamplingLevel maximumSubsamplingLevel();
@@ -103,7 +108,7 @@
 
     void setDecoderTargetContext(const GraphicsContext*);
 
-    ImageFrameCache m_frameCache;
+    Ref<ImageFrameCache> m_frameCache;
     std::unique_ptr<ImageDecoder> m_decoder;
 
     Optional<SubsamplingLevel> m_maximumSubsamplingLevel;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to