REGRESSION(r219045): A partially loaded image may not be repainted when its complete...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 22 Jul 2017 12:12:33 +0000 (12:12 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 22 Jul 2017 12:12:33 +0000 (12:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174230

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2017-07-22
Reviewed by Simon Fraser.

Because of r219045, we now only repaint the CachedImageClinets which tried
to draw the image but they could not because they have to wait for the image
decoding to finish. This was done by keeping a HashSet of these clients
and make CachedImage own it. This HashSet is cleared once the image frame
finishes decoding and all the waited clients are repainted.

But Multiple asynchronous image decoding requests are allowed for the same
frame if new data is added to the image source. If we tried to draw the
same image twice before it finishes decoding the first request, we will
not be to record this second request since the HashSet will not add the
same client twice. When he second request finishes decoding, CachedImage
will not repaint any client since its HashSet is empty.

To fix this problem we can do the following. When an image frame finishes
decoding, CachedImage will keep its HashSet of pending drawing clients as
long as the image frame is a partially loaded frame.

* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::CachedImageObserver::imageFrameAvailable):
(WebCore::CachedImage::imageFrameAvailable):
* loader/cache/CachedImage.h:
* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::destroyDecodedData):
(WebCore::BitmapImage::dataChanged):
(WebCore::BitmapImage::setCurrentFrameDecodingStatusIfNecessary):
(WebCore::BitmapImage::draw):
(WebCore::BitmapImage::internalStartAnimation):
(WebCore::BitmapImage::internalAdvanceAnimation):
(WebCore::BitmapImage::imageFrameAvailableAtIndex):
* platform/graphics/BitmapImage.h:
* platform/graphics/ImageFrame.cpp:
(WebCore::ImageFrame::decodingStatus):
* platform/graphics/ImageFrame.h: Move DecodingStatus out of this class
to ImageTypes.h to avoid adding other header files to ImageObvsever.h
* platform/graphics/ImageFrameCache.cpp:
(WebCore::ImageFrameCache::setNativeImage):
(WebCore::ImageFrameCache::cacheMetadataAtIndex):
(WebCore::ImageFrameCache::cacheNativeImageAtIndex):
(WebCore::ImageFrameCache::cacheNativeImageAtIndexAsync):
(WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex):
(WebCore::ImageFrameCache::frameDecodingStatusAtIndex):
* platform/graphics/ImageFrameCache.h:
* platform/graphics/ImageObserver.h:
* platform/graphics/ImageSource.h:
(WebCore::ImageSource::frameDecodingStatusAtIndex):
* platform/graphics/ImageTypes.h:
* platform/image-decoders/bmp/BMPImageReader.cpp:
(WebCore::BMPImageReader::decodeBMP):
* platform/image-decoders/gif/GIFImageDecoder.cpp:
(WebCore::GIFImageDecoder::frameComplete):
(WebCore::GIFImageDecoder::initFrameBuffer):
* platform/image-decoders/jpeg/JPEGImageDecoder.cpp:
(WebCore::JPEGImageDecoder::outputScanlines):
(WebCore::JPEGImageDecoder::jpegComplete):
* platform/image-decoders/png/PNGImageDecoder.cpp:
(WebCore::PNGImageDecoder::rowAvailable):
(WebCore::PNGImageDecoder::pngComplete):
(WebCore::PNGImageDecoder::frameComplete):
* platform/image-decoders/webp/WEBPImageDecoder.cpp:
(WebCore::WEBPImageDecoder::decode):
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::decodingModeForImageDraw):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@219762 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
Source/WebCore/ChangeLog
Source/WebCore/loader/cache/CachedImage.cpp
Source/WebCore/loader/cache/CachedImage.h
Source/WebCore/platform/graphics/BitmapImage.cpp
Source/WebCore/platform/graphics/BitmapImage.h
Source/WebCore/platform/graphics/ImageFrame.cpp
Source/WebCore/platform/graphics/ImageFrame.h
Source/WebCore/platform/graphics/ImageFrameCache.cpp
Source/WebCore/platform/graphics/ImageFrameCache.h
Source/WebCore/platform/graphics/ImageObserver.h
Source/WebCore/platform/graphics/ImageSource.h
Source/WebCore/platform/graphics/ImageTypes.h
Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp
Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp
Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp
Source/WebCore/rendering/RenderBoxModelObject.cpp

index a82c770..9709df4 100644 (file)
@@ -1,3 +1,73 @@
+2017-07-22  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        REGRESSION(r219045): A partially loaded image may not be repainted when its complete frame finishes decoding
+        https://bugs.webkit.org/show_bug.cgi?id=174230
+
+        Reviewed by Simon Fraser.
+
+        Because of r219045, we now only repaint the CachedImageClinets which tried
+        to draw the image but they could not because they have to wait for the image
+        decoding to finish. This was done by keeping a HashSet of these clients 
+        and make CachedImage own it. This HashSet is cleared once the image frame
+        finishes decoding and all the waited clients are repainted.
+
+        But Multiple asynchronous image decoding requests are allowed for the same
+        frame if new data is added to the image source. If we tried to draw the 
+        same image twice before it finishes decoding the first request, we will
+        not be to record this second request since the HashSet will not add the
+        same client twice. When he second request finishes decoding, CachedImage
+        will not repaint any client since its HashSet is empty.
+
+        To fix this problem we can do the following. When an image frame finishes
+        decoding, CachedImage will keep its HashSet of pending drawing clients as
+        long as the image frame is a partially loaded frame.
+
+        * loader/cache/CachedImage.cpp:
+        (WebCore::CachedImage::CachedImageObserver::imageFrameAvailable):
+        (WebCore::CachedImage::imageFrameAvailable):
+        * loader/cache/CachedImage.h:
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::destroyDecodedData):
+        (WebCore::BitmapImage::dataChanged):
+        (WebCore::BitmapImage::setCurrentFrameDecodingStatusIfNecessary):
+        (WebCore::BitmapImage::draw):
+        (WebCore::BitmapImage::internalStartAnimation):
+        (WebCore::BitmapImage::internalAdvanceAnimation):
+        (WebCore::BitmapImage::imageFrameAvailableAtIndex):
+        * platform/graphics/BitmapImage.h:
+        * platform/graphics/ImageFrame.cpp:
+        (WebCore::ImageFrame::decodingStatus):
+        * platform/graphics/ImageFrame.h: Move DecodingStatus out of this class
+        to ImageTypes.h to avoid adding other header files to ImageObvsever.h
+        * platform/graphics/ImageFrameCache.cpp:
+        (WebCore::ImageFrameCache::setNativeImage):
+        (WebCore::ImageFrameCache::cacheMetadataAtIndex):
+        (WebCore::ImageFrameCache::cacheNativeImageAtIndex):
+        (WebCore::ImageFrameCache::cacheNativeImageAtIndexAsync):
+        (WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex):
+        (WebCore::ImageFrameCache::frameDecodingStatusAtIndex):
+        * platform/graphics/ImageFrameCache.h:
+        * platform/graphics/ImageObserver.h:
+        * platform/graphics/ImageSource.h:
+        (WebCore::ImageSource::frameDecodingStatusAtIndex):
+        * platform/graphics/ImageTypes.h:
+        * platform/image-decoders/bmp/BMPImageReader.cpp:
+        (WebCore::BMPImageReader::decodeBMP):
+        * platform/image-decoders/gif/GIFImageDecoder.cpp:
+        (WebCore::GIFImageDecoder::frameComplete):
+        (WebCore::GIFImageDecoder::initFrameBuffer):
+        * platform/image-decoders/jpeg/JPEGImageDecoder.cpp:
+        (WebCore::JPEGImageDecoder::outputScanlines):
+        (WebCore::JPEGImageDecoder::jpegComplete):
+        * platform/image-decoders/png/PNGImageDecoder.cpp:
+        (WebCore::PNGImageDecoder::rowAvailable):
+        (WebCore::PNGImageDecoder::pngComplete):
+        (WebCore::PNGImageDecoder::frameComplete):
+        * platform/image-decoders/webp/WEBPImageDecoder.cpp:
+        (WebCore::WEBPImageDecoder::decode):
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::RenderBoxModelObject::decodingModeForImageDraw):
+
 2017-07-22  Timothy Horton  <timothy_horton@apple.com>
 
         Drag and Drop preview image for Twitter link is the wrong shape
index 97026e6..413f041 100644 (file)
@@ -374,10 +374,10 @@ bool CachedImage::CachedImageObserver::canDestroyDecodedData(const Image& image)
     return true;
 }
 
-void CachedImage::CachedImageObserver::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect)
+void CachedImage::CachedImageObserver::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect, DecodingStatus decodingStatus)
 {
     for (auto cachedImage : m_cachedImages)
-        cachedImage->imageFrameAvailable(image, animatingState, changeRect);
+        cachedImage->imageFrameAvailable(image, animatingState, changeRect, decodingStatus);
 }
 
 void CachedImage::CachedImageObserver::changedInRect(const Image& image, const IntRect* rect)
@@ -548,7 +548,7 @@ bool CachedImage::canDestroyDecodedData(const Image& image)
     return true;
 }
 
-void CachedImage::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect)
+void CachedImage::imageFrameAvailable(const Image& image, ImageAnimatingState animatingState, const IntRect* changeRect, DecodingStatus decodingStatus)
 {
     if (&image != m_image)
         return;
@@ -567,7 +567,8 @@ void CachedImage::imageFrameAvailable(const Image& image, ImageAnimatingState an
     if (visibleState == VisibleInViewportState::No && animatingState == ImageAnimatingState::Yes)
         m_image->stopAnimation();
 
-    m_pendingImageDrawingClients.clear();
+    if (decodingStatus != DecodingStatus::Partial)
+        m_pendingImageDrawingClients.clear();
 }
 
 void CachedImage::changedInRect(const Image& image, const IntRect* rect)
index f095ac2..8ea6f2e 100644 (file)
@@ -134,7 +134,7 @@ private:
         void didDraw(const Image&) final;
 
         bool canDestroyDecodedData(const Image&) final;
-        void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* changeRect = nullptr) final;
+        void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* changeRect = nullptr, DecodingStatus = DecodingStatus::Invalid) final;
         void changedInRect(const Image&, const IntRect*) final;
 
         HashSet<CachedImage*> m_cachedImages;
@@ -143,7 +143,7 @@ private:
     void decodedSizeChanged(const Image&, long long delta);
     void didDraw(const Image&);
     bool canDestroyDecodedData(const Image&);
-    void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* changeRect = nullptr);
+    void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* changeRect = nullptr, DecodingStatus = DecodingStatus::Invalid);
     void changedInRect(const Image&, const IntRect*);
 
     void addIncrementalDataBuffer(SharedBuffer&);
index dd5fc8c..bc16b40 100644 (file)
@@ -79,7 +79,7 @@ void BitmapImage::destroyDecodedData(bool destroyAll)
         m_source.destroyAllDecodedDataExcludeFrame(m_currentFrame);
     else {
         m_source.destroyAllDecodedData();
-        m_currentFrameDecodingStatus = ImageFrame::DecodingStatus::Invalid;
+        m_currentFrameDecodingStatus = DecodingStatus::Invalid;
     }
 
     // There's no need to throw away the decoder unless we're explicitly asked
@@ -110,9 +110,19 @@ EncodedDataStatus BitmapImage::dataChanged(bool allDataReceived)
     if (m_source.decodedSize() && !canUseAsyncDecodingForLargeImages())
         m_source.destroyIncompleteDecodedData();
 
-    m_currentFrameDecodingStatus = ImageFrame::DecodingStatus::Invalid;
+    m_currentFrameDecodingStatus = DecodingStatus::Invalid;
     return m_source.dataChanged(data(), allDataReceived);
 }
+    
+void BitmapImage::setCurrentFrameDecodingStatusIfNecessary(DecodingStatus decodingStatus)
+{
+    // When new data is received, m_currentFrameDecodingStatus is set to DecodingStatus::Invalid
+    // to force decoding the frame when it's drawn. m_currentFrameDecodingStatus should not be
+    // changed in this case till draw() is called and sets its value to DecodingStatus::Decoding.
+    if (m_currentFrameDecodingStatus != DecodingStatus::Decoding)
+        return;
+    m_currentFrameDecodingStatus = decodingStatus;
+}
 
 NativeImagePtr BitmapImage::frameImageAtIndexCacheIfNeeded(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
 {
@@ -192,13 +202,13 @@ ImageDrawResult BitmapImage::draw(GraphicsContext& context, const FloatRect& des
 
         // If the current frame is incomplete, a new request for decoding this frame has to be made even if
         // it is currently being decoded. New data may have been received since the previous request was made.
-        if ((!frameIsCompatible && !frameIsBeingDecoded) || m_currentFrameDecodingStatus == ImageFrame::DecodingStatus::Invalid) {
+        if ((!frameIsCompatible && !frameIsBeingDecoded) || m_currentFrameDecodingStatus == DecodingStatus::Invalid) {
             LOG(Images, "BitmapImage::%s - %p - url: %s [requesting large async decoding]", __FUNCTION__, this, sourceURL().string().utf8().data());
             m_source.requestFrameAsyncDecodingAtIndex(m_currentFrame, m_currentSubsamplingLevel, sizeForDrawing);
-            m_currentFrameDecodingStatus = ImageFrame::DecodingStatus::Decoding;
+            m_currentFrameDecodingStatus = DecodingStatus::Decoding;
         }
 
-        if (m_currentFrameDecodingStatus == ImageFrame::DecodingStatus::Decoding)
+        if (m_currentFrameDecodingStatus == DecodingStatus::Decoding)
             result = ImageDrawResult::DidRequestDecoding;
 
         if (!frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(m_currentFrame, m_currentSubsamplingLevel, DecodingMode::Asynchronous)) {
@@ -231,7 +241,7 @@ ImageDrawResult BitmapImage::draw(GraphicsContext& context, const FloatRect& des
         if (!image) // If it's too early we won't have an image yet.
             return ImageDrawResult::DidNothing;
 
-        if (m_currentFrameDecodingStatus != ImageFrame::DecodingStatus::Complete)
+        if (m_currentFrameDecodingStatus != DecodingStatus::Complete)
             ++m_decodeCountForTesting;
     }
 
@@ -391,7 +401,7 @@ BitmapImage::StartAnimationStatus BitmapImage::internalStartAnimation()
             LOG(Images, "BitmapImage::%s - %p - url: %s [cachedFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().string().utf8().data(), ++m_cachedFrameCount, nextFrame);
         else {
             m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel);
-            m_currentFrameDecodingStatus = ImageFrame::DecodingStatus::Decoding;
+            m_currentFrameDecodingStatus = DecodingStatus::Decoding;
             LOG(Images, "BitmapImage::%s - %p - url: %s [requesting async decoding for nextFrame = %ld]", __FUNCTION__, this, sourceURL().string().utf8().data(), nextFrame);
         }
 
@@ -439,10 +449,11 @@ void BitmapImage::internalAdvanceAnimation()
 
     destroyDecodedDataIfNecessary(false);
 
-    if (m_currentFrameDecodingStatus == ImageFrame::DecodingStatus::Decoding)
-        m_currentFrameDecodingStatus = frameDecodingStatusAtIndex(m_currentFrame);
+    DecodingStatus decodingStatus = frameDecodingStatusAtIndex(m_currentFrame);
+    setCurrentFrameDecodingStatusIfNecessary(decodingStatus);
+    
     if (imageObserver())
-        imageObserver()->imageFrameAvailable(*this, ImageAnimatingState::Yes);
+        imageObserver()->imageFrameAvailable(*this, ImageAnimatingState::Yes, nullptr, decodingStatus);
 
     LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld]", __FUNCTION__, this, sourceURL().string().utf8().data(), m_currentFrame);
 }
@@ -497,14 +508,15 @@ void BitmapImage::imageFrameAvailableAtIndex(size_t index)
     ASSERT(index == m_currentFrame && !m_currentFrame);
     if (m_source.isAsyncDecodingQueueIdle())
         m_source.stopAsyncDecodingQueue();
-    if (m_currentFrameDecodingStatus == ImageFrame::DecodingStatus::Decoding)
-        m_currentFrameDecodingStatus = frameDecodingStatusAtIndex(m_currentFrame);
 
-    if (m_currentFrameDecodingStatus == ImageFrame::DecodingStatus::Complete)
+    DecodingStatus decodingStatus = frameDecodingStatusAtIndex(m_currentFrame);
+    setCurrentFrameDecodingStatusIfNecessary(decodingStatus);
+    
+    if (m_currentFrameDecodingStatus == DecodingStatus::Complete)
         ++m_decodeCountForTesting;
 
     if (imageObserver())
-        imageObserver()->imageFrameAvailable(*this, ImageAnimatingState::No);
+        imageObserver()->imageFrameAvailable(*this, ImageAnimatingState::No, nullptr, decodingStatus);
 }
 
 unsigned BitmapImage::decodeCountForTesting() const
index 44dd773..df9bb78 100644 (file)
@@ -85,8 +85,8 @@ public:
     IntSize sizeRespectingOrientation() const { return m_source.sizeRespectingOrientation(); }
     Color singlePixelSolidColor() const override { return m_source.singlePixelSolidColor(); }
     bool frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t index, const DecodingOptions& decodingOptions) const { return m_source.frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(index, decodingOptions); }
-    ImageFrame::DecodingStatus frameDecodingStatusAtIndex(size_t index) const { return m_source.frameDecodingStatusAtIndex(index); }
-    bool frameIsCompleteAtIndex(size_t index) const { return frameDecodingStatusAtIndex(index) == ImageFrame::DecodingStatus::Complete; }
+    DecodingStatus frameDecodingStatusAtIndex(size_t index) const { return m_source.frameDecodingStatusAtIndex(index); }
+    bool frameIsCompleteAtIndex(size_t index) const { return frameDecodingStatusAtIndex(index) == DecodingStatus::Complete; }
     bool frameHasAlphaAtIndex(size_t index) const { return m_source.frameHasAlphaAtIndex(index); }
 
     bool frameHasFullSizeNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_source.frameHasFullSizeNativeImageAtIndex(index, subsamplingLevel); }
@@ -193,6 +193,7 @@ private:
     void clearTimer();
     void startTimer(Seconds delay);
     bool canDestroyDecodedData();
+    void setCurrentFrameDecodingStatusIfNecessary(DecodingStatus);
     bool isBitmapImage() const override { return true; }
     void dump(TextStream&) const override;
 
@@ -203,7 +204,7 @@ private:
 
     size_t m_currentFrame { 0 }; // The index of the current frame of animation.
     SubsamplingLevel m_currentSubsamplingLevel { SubsamplingLevel::Default };
-    ImageFrame::DecodingStatus m_currentFrameDecodingStatus { ImageFrame::DecodingStatus::Invalid };
+    DecodingStatus m_currentFrameDecodingStatus { DecodingStatus::Invalid };
     std::unique_ptr<Timer> m_frameTimer;
     RepetitionCount m_repetitionsComplete { RepetitionCountNone }; // How many repetitions we've finished.
     MonotonicTime m_desiredFrameStartTime; // The system time at which we hope to see the next call to startAnimation().
index 8e78a66..8165486 100644 (file)
@@ -77,7 +77,7 @@ void ImageFrame::setDecodingStatus(DecodingStatus decodingStatus)
     m_decodingStatus = decodingStatus;
 }
 
-ImageFrame::DecodingStatus ImageFrame::decodingStatus() const
+DecodingStatus ImageFrame::decodingStatus() const
 {
     ASSERT(m_decodingStatus != DecodingStatus::Decoding);
     return m_decodingStatus;
index 212a2cb..1821863 100644 (file)
@@ -39,7 +39,6 @@ class ImageFrame {
     friend class ImageFrameCache;
 public:
     enum class Caching { Metadata, MetadataAndImage };
-    enum class DecodingStatus { Invalid, Partial, Complete, Decoding };
 
     ImageFrame();
     ImageFrame(const ImageFrame& other) { operator=(other); }
index 8c336f4..b564698 100644 (file)
@@ -190,19 +190,19 @@ void ImageFrameCache::setNativeImage(NativeImagePtr&& nativeImage)
 
     frame.m_nativeImage = WTFMove(nativeImage);
 
-    frame.m_decodingStatus = ImageFrame::DecodingStatus::Complete;
+    frame.m_decodingStatus = DecodingStatus::Complete;
     frame.m_size = nativeImageSize(frame.m_nativeImage);
     frame.m_hasAlpha = nativeImageHasAlpha(frame.m_nativeImage);
 }
 
-void ImageFrameCache::cacheMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageFrame::DecodingStatus decodingStatus)
+void ImageFrameCache::cacheMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, DecodingStatus decodingStatus)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
 
     ASSERT(isDecoderAvailable());
-    if (decodingStatus == ImageFrame::DecodingStatus::Invalid)
-        frame.m_decodingStatus = m_decoder->frameIsCompleteAtIndex(index) ? ImageFrame::DecodingStatus::Complete : ImageFrame::DecodingStatus::Partial;
+    if (decodingStatus == DecodingStatus::Invalid)
+        frame.m_decodingStatus = m_decoder->frameIsCompleteAtIndex(index) ? DecodingStatus::Complete : DecodingStatus::Partial;
     else
         frame.m_decodingStatus = decodingStatus;
 
@@ -224,7 +224,7 @@ void ImageFrameCache::cacheMetadataAtIndex(size_t index, SubsamplingLevel subsam
         frame.m_duration = m_decoder->frameDurationAtIndex(index);
 }
 
-void ImageFrameCache::cacheNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions, ImageFrame::DecodingStatus decodingStatus)
+void ImageFrameCache::cacheNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions, DecodingStatus decodingStatus)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
@@ -246,7 +246,7 @@ void ImageFrameCache::cacheNativeImageAtIndex(NativeImagePtr&& nativeImage, size
     decodedSizeIncreased(frame.frameBytes());
 }
 
-void ImageFrameCache::cacheNativeImageAtIndexAsync(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions, ImageFrame::DecodingStatus decodingStatus)
+void ImageFrameCache::cacheNativeImageAtIndexAsync(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions, DecodingStatus decodingStatus)
 {
     if (!isDecoderAvailable())
         return;
@@ -314,7 +314,7 @@ void ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, Subsampling
         startAsyncDecodingQueue();
     
     ASSERT(index < m_frames.size());
-    ImageFrame::DecodingStatus decodingStatus = m_decoder->frameIsCompleteAtIndex(index) ? ImageFrame::DecodingStatus::Complete : ImageFrame::DecodingStatus::Partial;
+    DecodingStatus decodingStatus = m_decoder->frameIsCompleteAtIndex(index) ? DecodingStatus::Complete : DecodingStatus::Partial;
 
     LOG(Images, "ImageFrameCache::%s - %p - url: %s [enqueuing frame %ld for decoding]", __FUNCTION__, this, sourceURL().string().utf8().data(), index);
     m_frameRequestQueue.enqueue({ index, subsamplingLevel, sizeForDrawing, decodingStatus });
@@ -499,9 +499,9 @@ bool ImageFrameCache::frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_
     return it != m_frameCommitQueue.end();
 }
 
-ImageFrame::DecodingStatus ImageFrameCache::frameDecodingStatusAtIndex(size_t index)
+DecodingStatus ImageFrameCache::frameDecodingStatusAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<ImageFrame::DecodingStatus>(index, (&ImageFrame::decodingStatus));
+    return frameMetadataAtIndex<DecodingStatus>(index, (&ImageFrame::decodingStatus));
 }
 
 bool ImageFrameCache::frameHasAlphaAtIndex(size_t index)
index c77b927..649abfc 100644 (file)
@@ -93,7 +93,7 @@ public:
 
     // ImageFrame metadata which does not require caching the ImageFrame.
     bool frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t, const DecodingOptions&);
-    ImageFrame::DecodingStatus frameDecodingStatusAtIndex(size_t);
+    DecodingStatus frameDecodingStatusAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t);
     bool frameHasImageAtIndex(size_t);
     bool frameHasFullSizeNativeImageAtIndex(size_t, const std::optional<SubsamplingLevel>&);
@@ -131,9 +131,9 @@ private:
     void decodedSizeReset(unsigned decodedSize);
 
     void setNativeImage(NativeImagePtr&&);
-    void cacheMetadataAtIndex(size_t, SubsamplingLevel, ImageFrame::DecodingStatus = ImageFrame::DecodingStatus::Invalid);
-    void cacheNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&, ImageFrame::DecodingStatus = ImageFrame::DecodingStatus::Invalid);
-    void cacheNativeImageAtIndexAsync(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&, ImageFrame::DecodingStatus);
+    void cacheMetadataAtIndex(size_t, SubsamplingLevel, DecodingStatus = DecodingStatus::Invalid);
+    void cacheNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&, DecodingStatus = DecodingStatus::Invalid);
+    void cacheNativeImageAtIndexAsync(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&, DecodingStatus);
 
     Ref<WorkQueue> decodingQueue();
 
@@ -151,7 +151,7 @@ private:
         size_t index;
         SubsamplingLevel subsamplingLevel;
         DecodingOptions decodingOptions;
-        ImageFrame::DecodingStatus decodingStatus;
+        DecodingStatus decodingStatus;
         bool operator==(const ImageFrameRequest& other) const
         {
             return index == other.index && subsamplingLevel == other.subsamplingLevel && decodingOptions == other.decodingOptions && decodingStatus == other.decodingStatus;
index 80062ca..a06851e 100644 (file)
@@ -46,7 +46,7 @@ public:
     virtual void didDraw(const Image&) = 0;
 
     virtual bool canDestroyDecodedData(const Image&) = 0;
-    virtual void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* changeRect = nullptr) = 0;
+    virtual void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* changeRect = nullptr, DecodingStatus = DecodingStatus::Invalid) = 0;
     virtual void changedInRect(const Image&, const IntRect* changeRect = nullptr) = 0;
 };
 
index b36c6aa..f4dd4d8 100644 (file)
@@ -91,7 +91,7 @@ public:
 
     // ImageFrame metadata which does not require caching the ImageFrame.
     bool frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t index, const DecodingOptions& decodingOptions) { return m_frameCache->frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(index, decodingOptions); }
-    ImageFrame::DecodingStatus frameDecodingStatusAtIndex(size_t index) { return m_frameCache->frameDecodingStatusAtIndex(index); }
+    DecodingStatus frameDecodingStatusAtIndex(size_t index) { return m_frameCache->frameDecodingStatusAtIndex(index); }
     bool frameHasAlphaAtIndex(size_t index) { return m_frameCache->frameHasAlphaAtIndex(index); }
     bool frameHasImageAtIndex(size_t index) { return m_frameCache->frameHasImageAtIndex(index); }
     bool frameHasFullSizeNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel) { return m_frameCache->frameHasFullSizeNativeImageAtIndex(index, subsamplingLevel); }
index ba7b254..0fbb99e 100644 (file)
@@ -78,6 +78,13 @@ enum class EncodedDataStatus {
     Complete
 };
 
+enum class DecodingStatus {
+    Invalid,
+    Partial,
+    Complete,
+    Decoding
+};
+
 enum class ImageDrawResult {
     DidNothing,
     DidRequestDecoding,
index 8d68a40..0168343 100644 (file)
@@ -81,7 +81,7 @@ bool BMPImageReader::decodeBMP(bool onlySize)
         if (!m_buffer->initialize(m_parent->size(), m_parent->premultiplyAlpha()))
             return m_parent->setFailed(); // Unable to allocate.
 
-        m_buffer->setDecodingStatus(ImageFrame::DecodingStatus::Partial);
+        m_buffer->setDecodingStatus(DecodingStatus::Partial);
         m_buffer->setHasAlpha(false);
 
         if (!m_isTopDown)
@@ -117,7 +117,7 @@ bool BMPImageReader::decodeBMP(bool onlySize)
     }
 
     // Done!
-    m_buffer->setDecodingStatus(ImageFrame::DecodingStatus::Complete);
+    m_buffer->setDecodingStatus(DecodingStatus::Complete);
     return true;
 }
 
index fd0cc75..9cac113 100644 (file)
@@ -247,7 +247,7 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration,
     if (buffer.isInvalid() && !initFrameBuffer(frameIndex))
         return false; // initFrameBuffer() has already called setFailed().
 
-    buffer.setDecodingStatus(ImageFrame::DecodingStatus::Complete);
+    buffer.setDecodingStatus(DecodingStatus::Complete);
     buffer.setDuration(frameDuration);
     buffer.setDisposalMethod(disposalMethod);
 
@@ -401,7 +401,7 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex)
     buffer->backingStore()->setFrameRect(IntRect(left, top, right - left, bottom - top));
 
     // Update our status to be partially complete.
-    buffer->setDecodingStatus(ImageFrame::DecodingStatus::Partial);
+    buffer->setDecodingStatus(DecodingStatus::Partial);
 
     // Reset the alpha pixel tracker for this frame.
     m_currentBufferSawAlpha = false;
index 551af6e..74ca111 100644 (file)
@@ -608,7 +608,7 @@ bool JPEGImageDecoder::outputScanlines()
     if (buffer.isInvalid()) {
         if (!buffer.initialize(scaledSize(), m_premultiplyAlpha))
             return setFailed();
-        buffer.setDecodingStatus(ImageFrame::DecodingStatus::Partial);
+        buffer.setDecodingStatus(DecodingStatus::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);
@@ -652,7 +652,7 @@ void JPEGImageDecoder::jpegComplete()
     // empty.
     ImageFrame& buffer = m_frameBufferCache[0];
     buffer.setHasAlpha(false);
-    buffer.setDecodingStatus(ImageFrame::DecodingStatus::Complete);
+    buffer.setDecodingStatus(DecodingStatus::Complete);
 }
 
 void JPEGImageDecoder::decode(bool onlySize, bool allDataReceived)
index 2fb6c44..ecda18d 100644 (file)
@@ -450,7 +450,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex,
             }
         }
 
-        buffer.setDecodingStatus(ImageFrame::DecodingStatus::Partial);
+        buffer.setDecodingStatus(DecodingStatus::Partial);
         buffer.setHasAlpha(false);
 
 #if ENABLE(APNG)
@@ -558,7 +558,7 @@ void PNGImageDecoder::pngComplete()
     }
 #endif
     if (!m_frameBufferCache.isEmpty())
-        m_frameBufferCache.first().setDecodingStatus(ImageFrame::DecodingStatus::Complete);
+        m_frameBufferCache.first().setDecodingStatus(DecodingStatus::Complete);
 }
 
 void PNGImageDecoder::decode(bool onlySize, unsigned haltAtFrame, bool allDataReceived)
@@ -825,7 +825,7 @@ void PNGImageDecoder::frameComplete()
         return;
 
     ImageFrame& buffer = m_frameBufferCache[m_currentFrame];
-    buffer.setDecodingStatus(ImageFrame::DecodingStatus::Complete);
+    buffer.setDecodingStatus(DecodingStatus::Complete);
 
     png_bytep interlaceBuffer = m_reader->interlaceBuffer();
 
index 44551dc..b3a632f 100644 (file)
@@ -119,7 +119,7 @@ bool WEBPImageDecoder::decode(bool onlySize, bool)
     if (buffer.isInvalid()) {
         if (!buffer.initialize(size(), m_premultiplyAlpha))
             return setFailed();
-        buffer.setDecodingStatus(ImageFrame::DecodingStatus::Partial);
+        buffer.setDecodingStatus(DecodingStatus::Partial);
         buffer.setHasAlpha(m_hasAlpha);
     }
 
@@ -137,7 +137,7 @@ bool WEBPImageDecoder::decode(bool onlySize, bool)
 
     switch (WebPIUpdate(m_decoder, dataBytes, dataSize)) {
     case VP8_STATUS_OK:
-        buffer.setDecodingStatus(ImageFrame::DecodingStatus::Complete);
+        buffer.setDecodingStatus(DecodingStatus::Complete);
         clear();
         return true;
     case VP8_STATUS_SUSPENDED:
index 01ed005..e39640b 100644 (file)
@@ -324,8 +324,6 @@ DecodingMode RenderBoxModelObject::decodingModeForImageDraw(const Image& image,
     if (IOSApplication::isIBooksStorytime())
         return DecodingMode::Synchronous;
 #endif
-    if (document().isImageDocument())
-        return DecodingMode::Synchronous;
     if (!settings().largeImageAsyncDecodingEnabled())
         return DecodingMode::Synchronous;
     if (!bitmapImage.canUseAsyncDecodingForLargeImages())