REGRESSION(213764): Large images should not be decoded asynchronously when they are...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Mar 2017 02:23:10 +0000 (02:23 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Mar 2017 02:23:10 +0000 (02:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=169771

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

Source/WebCore:

Sometimes we have to draw the image immediately like when a canvas calls
drawImage. In this case we have to decode the image synchronously to guarantee
the drawing. Other times we need to decode with the native size of the image.
The animated images have to be decoded with native size always. Otherwise
the frame cache will be messed up if the same image is animated with different
sizes. Currently we always decode asynchronously with sizeForDrawing. We need
to decouple the decoding mode from the sizeForDrawing.

This patch introduce the DecodingOptions class which can store and compare the
following four cases:
    -- Synchronous: The frame has be decoded with native size only.
    -- Asynchronous + anySize: This is passed from the Image::draw() callers.
    -- Asynchronous + fullSize: The image has to be decoded with its full size.
    -- Asynchronous + sizeForDrawing: The image can be decoded with sizeForDrawing unless
    it was decoded with either a full size or sizeForDrawing which is larger than the
    requested sizeForDrawing.

A new argument of type DecodingMode will be added to Image::draw() function.
Only when the drawing comes from the render tree, it will be Asynchronous.
Otherwise it will be Synchronous.

Tests: fast/images/animated-image-different-dest-size.html
       fast/images/async-image-background-image.html
       fast/images/async-image-canvas-draw-image.html

* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::frameImageAtIndexCacheIfNeeded): Gets the frame image, cache it synchronously if
the current one is invalid. frameImageAtIndex() returns whatever stored in the cache.
(WebCore::BitmapImage::nativeImage): Call frameImageAtIndexCacheIfNeeded() instead of frameImageAtIndex().
(WebCore::BitmapImage::nativeImageForCurrentFrame): Ditto.
(WebCore::BitmapImage::nativeImageOfSize): Ditto.
(WebCore::BitmapImage::framesNativeImages): Ditto.
(WebCore::BitmapImage::draw): Change the logic to do the following:
-- The animated image has to be decoded with its full size.
-- The animated image expects the current frame to be ready for drawing.
-- The large image decoding does not need to call internalStartAnimation().
-- The large image has to request async image decoding but draw the current one if it exists.
(WebCore::BitmapImage::drawPattern): Draw the pattern synchronously.
(WebCore::BitmapImage::shouldUseAsyncDecodingForLargeImages): Delete the call to shouldUseAsyncDecodingForTesting()
since it is only applied for animated images.
(WebCore::BitmapImage::shouldUseAsyncDecodingForAnimatedImages): Call shouldUseAsyncDecodingForAnimatedImageForTesting().
(WebCore::BitmapImage::internalStartAnimation): Request decoding with the full size.
(WebCore::BitmapImage::advanceAnimation): Call shouldUseAsyncDecodingForAnimatedImageForTesting().
(WebCore::BitmapImage::internalAdvanceAnimation): Assert the current frame is not being decoding asynchronously for any size.
(WebCore::BitmapImage::frameImageAtIndex): Deleted. Moved to the header file but with a new purpose: return
the current frame from the frame cache as is; do not cache a new one.
(WebCore::BitmapImage::shouldUseAsyncDecodingForLargeImage): Deleted. Function was renamed to shouldUseAsyncDecodingForLargeImages.
(WebCore::BitmapImage::shouldUseAsyncDecodingForAnimatedImage): Deleted. Function was renamed to shouldUseAsyncDecodingForAnimatedImages.
* platform/graphics/BitmapImage.h:
* platform/graphics/CrossfadeGeneratedImage.cpp:
(WebCore::CrossfadeGeneratedImage::draw): Add a new argument of type DecodingMode.
* platform/graphics/CrossfadeGeneratedImage.h:
* platform/graphics/DecodingOptions.h: Added.
(WebCore::DecodingOptions::DecodingOptions): Default constructor: Synchronous mode.
(WebCore::DecodingOptions::operator==): Compares two DecodingOptions for equality.
(WebCore::DecodingOptions::isSynchronous): Is the frame decoded synchronously?
(WebCore::DecodingOptions::isAsynchronous): Is the frame decoded asynchronously?
(WebCore::DecodingOptions::isAsynchronousCompatibleWith): Is this DecodingOptions compatible with another one?
(WebCore::DecodingOptions::hasFullSize): Is the decoding mode asynchronous but for the image full size?
(WebCore::DecodingOptions::hasSizeForDrawing): Is this decoding mode asynchronous but for a sizeForDrawing?
(WebCore::DecodingOptions::sizeForDrawing): Returns the sizeForDrawing. m_decodingModeOrSize has to hold an IntSize.
(WebCore::DecodingOptions::maxDimension): Moved form ImageFrame.cpp.
(WebCore::DecodingOptions::has): A helper function.
(WebCore::DecodingOptions::hasDecodingMode): Does m_decodingModeOrSize a DecodingMode?
(WebCore::DecodingOptions::hasSize): Does m_decodingModeOrSize an IntSize?
* platform/graphics/GeneratedImage.h: Add a new argument of type DecodingMode.
* platform/graphics/GradientImage.cpp:
(WebCore::GradientImage::draw): Ditto.
* platform/graphics/GradientImage.h: Ditto.
* platform/graphics/GraphicsContext.cpp:
(WebCore::GraphicsContext::drawImage): Pass the ImagePaintingOptions::m_DecodingMode to Image::draw().
* platform/graphics/GraphicsContext.h:
(WebCore::ImagePaintingOptions::ImagePaintingOptions): Add a new member of type DecodingMode to ImagePaintingOptions.
* platform/graphics/Image.cpp:
(WebCore::Image::drawTiled): Pass DecodingMode::Synchronous to Image::draw().
* platform/graphics/Image.h: Add a new argument of type DecodingMode to Image::draw().
* platform/graphics/ImageFrame.cpp:
(WebCore::ImageFrame::operator=): Replace m_sizeForDrawing by m_decodingOptions.
(WebCore::ImageFrame::hasNativeImage): Check if m_nativeImage is valid and the subsamplingLevels match.
(WebCore::ImageFrame::hasFullSizeNativeImage): Checks hasNativeImage() and whether the image frame was
decoded for the image full size.
(WebCore::ImageFrame::hasDecodedNativeImageCompatibleWithOptions): Checks hasNativeImage() and the DecodingOptions match.
(WebCore::maxDimension): Deleted. Moved to DecodingOptions.h.
(WebCore::ImageFrame::isBeingDecoded): Deleted. The check for having an ImageFrame being decoded is
moved to ImageFrameCache.
(WebCore::ImageFrame::hasValidNativeImage): Deleted. No need to this function.
* platform/graphics/ImageFrame.h:
(WebCore::ImageFrame::hasNativeImage): Add an std::optional<SubsamplingLevel> argument.
(WebCore::ImageFrame::hasFullSizeNativeImage): Checks whether the ImageFrame was decoded for the image full size.
(WebCore::ImageFrame::enqueueSizeForDecoding): Deleted.
(WebCore::ImageFrame::dequeueSizeForDecoding): Deleted.
(WebCore::ImageFrame::clearSizeForDecoding): Deleted.
(WebCore::ImageFrame::isBeingDecoded): Deleted.
(WebCore::ImageFrame::sizeForDrawing): Deleted.
(WebCore::ImageFrame::hasDecodedNativeImage): Deleted.
The logic of knowing whether an ImageFrame is being decoded is moved to ImageFrameCache.
* platform/graphics/ImageFrameCache.cpp:
(WebCore::ImageFrameCache::cacheFrameMetadataAtIndex): Caches the metadata of an ImageFrame. If the NativeImage
was decoded for a sizeForDrawing, the size of the ImageFrame will be the nativeImageSize(). Otherwise, the
frameSizeAtIndex() will be called.
(WebCore::ImageFrameCache::cacheFrameNativeImageAtIndex): Cache a new NativeImage which involves caching new
metadata and updating the memory cache. No need to check if the existing native image is valid or not for the
DecodingOptions. Actually it would be a bug if it happens. This is why cacheNativeImageForFrameRequest() asserts
!frame.hasAsyncNativeImage() before calling cacheFrameNativeImageAtIndex().
(WebCore::ImageFrameCache::cacheAsyncFrameNativeImageAtIndex): Cache new NativeImage which was decoded asynchronously.
(WebCore::ImageFrameCache::startAsyncDecodingQueue): Call cacheAsyncFrameNativeImageAtIndex() instead of
cacheNativeImageForFrameRequest() for clarity.
(WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Call hasAsyncNativeImage() instead of hasValidNativeImage()
Call frameIsDecodingCompatibleWithOptionsAtIndex() instead of frame.isBeingDecoded(). Replace the call to enqueueSizeForDecoding()
by appending the same ImageFrameRequest to m_frameCommitQueue.
(WebCore::ImageFrameCache::isAsyncDecodingQueueIdle): Use m_frameCommitQueue to answer the question whether the decodingQueue is idle.
(WebCore::ImageFrameCache::stopAsyncDecodingQueue): Use m_frameCommitQueue to loop through all the ImageFrames which are currently being decoded.
(WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded): For getting the metadata, this function needs a valid frame. If it is requested
to decode the nativeImage, it has to do it synchronously.
(WebCore::ImageFrameCache::singlePixelSolidColor): Don't cache the frame if it is an animated image or the size is not a single pixel.
(WebCore::ImageFrameCache::frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex): Use m_frameCommitQueue to answer the question whether an ImageFrame
is being decoded and is compatible with DecodingOptions.
(WebCore::ImageFrameCache::frameHasFullSizeNativeImageAtIndex): Calls ImageFrame::hasFullNativeImage() for a frame.
(WebCore::ImageFrameCache::frameHasDecodedNativeImageCompatibleWithOptionsAtIndex): Calls ImageFrame::hasDecodedNativeImageCompatibleWithOptions() for a frame.
(WebCore::ImageFrameCache::frameImageAtIndex): Returns the current NativeImage without caching.
(WebCore::ImageFrameCache::frameImageAtIndexCacheIfNeeded): Returns the current NativeImage but cache it synchronously if needed.
(WebCore::ImageFrameCache::setFrameNativeImageAtIndex): Deleted.
(WebCore::ImageFrameCache::setFrameMetadataAtIndex): Deleted.
(WebCore::ImageFrameCache::replaceFrameNativeImageAtIndex): Deleted.
(WebCore::ImageFrameCache::frameIsBeingDecodedAtIndex): Deleted.
(WebCore::ImageFrameCache::frameHasImageAtIndex): Deleted.
(WebCore::ImageFrameCache::frameHasValidNativeImageAtIndex): Deleted.
(WebCore::ImageFrameCache::frameHasDecodedNativeImage): Deleted.
* platform/graphics/ImageFrameCache.h: Two ImageFrameRequest queues will be used.
-- The existing one m_frameRequestQueue which is shared between the main thread and decoding thread. The requests will be
dequeued from it before starting the decoding. The decoded NativeImage will be cached only on the main thread. The decoding
thread is not blocked by the callOnMainThread(). This means there might be multiple ImageFrameRequests which were dequeued
while their NativeImages have not been cached yet.
-- A new one m_frameCommitQueue which is track all the ImageFrameRequests whose NativeImages have not been cached yet.
(WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded): Be explicit about caching the image frame. frameImageAtIndex()
returns the current image frame without caching. frameAtIndexCacheIfNeeded(). returns the current image frame but cache
it if needed.
(WebCore::ImageFrameCache::ImageFrameRequest::operator==): Compares two ImageFrameRequests for equality.
* platform/graphics/ImageSource.cpp:
(WebCore::ImageSource::frameImageAtIndexCacheIfNeeded):
(WebCore::ImageSource::frameImageAtIndex): Deleted.
* platform/graphics/ImageSource.h:
(WebCore::ImageSource::requestFrameAsyncDecodingAtIndex): Change the type of the argument from IntSize to be const std::optional<IntSize>.
(WebCore::ImageSource::frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex): Rename of frameIsBeingDecodedAtIndex(). Replace the argument of type
std::optional<IntSize> by an argument of type DecodingOptions.
(WebCore::ImageSource::frameHasFullSizeNativeImageAtIndex): A wrapper around the ImageFrameCache function.
(WebCore::ImageSource::frameHasDecodedNativeImageCompatibleWithOptionsAtIndex): Ditto.
(WebCore::ImageSource::frameImageAtIndex): Ditto.
(WebCore::ImageSource::frameIsBeingDecodedAtIndex): Deleted.
(WebCore::ImageSource::frameHasValidNativeImageAtIndex): Deleted.
(WebCore::ImageSource::frameHasDecodedNativeImage): Deleted.
* platform/graphics/NamedImageGeneratedImage.cpp:
(WebCore::NamedImageGeneratedImage::draw): Add a new argument of type DecodingMode.
* platform/graphics/NamedImageGeneratedImage.h: Ditto.
* platform/graphics/cairo/ImageBufferCairo.cpp:
(WebCore::ImageBuffer::draw): Add a new argument of type DecodingMode.
* platform/graphics/cg/ImageDecoderCG.cpp:
(WebCore::ImageDecoder::createFrameImageAtIndex): Replace the sizeForDrawing argument by a DecodingMode argument. Add a new handling
for decoding asynchronously for the image full size.
* platform/graphics/cg/ImageDecoderCG.h: Change the prototype of the function.
* platform/graphics/cg/PDFDocumentImage.cpp:
(WebCore::PDFDocumentImage::draw): Add a new argument of type DecodingMode.
* platform/graphics/cg/PDFDocumentImage.h:
* platform/graphics/win/ImageCGWin.cpp:
(WebCore::BitmapImage::getHBITMAPOfSize): Pass DecodingMode::Synchronous to Image::draw().
(WebCore::BitmapImage::drawFrameMatchingSourceSize): Ditto.
* platform/graphics/win/ImageDecoderDirect2D.cpp:
(WebCore::ImageDecoder::createFrameImageAtIndex): Replace the sizeForDrawing argument by a DecodingMode argument.
* platform/graphics/win/ImageDecoderDirect2D.h: Change the prototype of the function.
* platform/image-decoders/ImageDecoder.cpp:
(WebCore::ImageDecoder::createFrameImageAtIndex): Replace the sizeForDrawing argument by a DecodingMode argument.
* platform/image-decoders/ImageDecoder.h: Change the prototype of the function.
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::paintFillLayerExtended): Draw the background image asynchronously if the image size is large.
* rendering/RenderImage.cpp:
(WebCore::RenderImage::paintIntoRect): Draw the background image element asynchronously if the image size is large.
* svg/graphics/SVGImage.cpp:
(WebCore::SVGImage::drawForContainer): Pass DecodingMode::Synchronous to draw().
(WebCore::SVGImage::nativeImageForCurrentFrame): Ditto.
(WebCore::SVGImage::nativeImage): Ditto.
(WebCore::SVGImage::draw): Add a new argument of type DecodingMode.
* svg/graphics/SVGImage.h: Change the prototype of the function.
* svg/graphics/SVGImageForContainer.cpp:
(WebCore::SVGImageForContainer::draw): Add a new argument of type DecodingMode.
* svg/graphics/SVGImageForContainer.h: Change the prototype of the function.

LayoutTests:

* fast/images/animated-image-different-dest-size-expected.html: Added.
* fast/images/animated-image-different-dest-size.html: Added.
This test crashes without this patch.

* fast/images/animated-image-loop-count.html:
Clear the memory cache so the test can be not flaky. Running it with -repeat-each was failing.

* fast/images/async-image-background-image-expected.html: Added.
* fast/images/async-image-background-image.html: Added.
Ensures the background image can be drawn asynchronously if it is large.

* fast/images/async-image-canvas-draw-image-expected.html: Added.
* fast/images/async-image-canvas-draw-image.html: Added.
Ensures the image is drawn synchronously on the canvas regardless of its size.

* fast/images/ordered-animated-image-frames.html:
Clear the memory cache so the test can be not flaky. Running it with -repeat-each was failing.

* fast/images/reset-image-animation-expected.txt:
* fast/images/reset-image-animation.html:
Change how the steps of the test are ordered so the test can be not flaky.
Running it with -repeat-each was failing.

* fast/images/resources/red-green-blue-900-300.png: Added.

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

50 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/images/animated-image-different-dest-size-expected.html [new file with mode: 0644]
LayoutTests/fast/images/animated-image-different-dest-size.html [new file with mode: 0644]
LayoutTests/fast/images/animated-image-loop-count.html
LayoutTests/fast/images/async-image-background-image-expected.html [new file with mode: 0644]
LayoutTests/fast/images/async-image-background-image.html [new file with mode: 0644]
LayoutTests/fast/images/async-image-canvas-draw-image-expected.html [new file with mode: 0644]
LayoutTests/fast/images/async-image-canvas-draw-image.html [new file with mode: 0644]
LayoutTests/fast/images/ordered-animated-image-frames.html
LayoutTests/fast/images/reset-image-animation-expected.txt
LayoutTests/fast/images/reset-image-animation.html
LayoutTests/fast/images/resources/red-green-blue-900-300.png [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/graphics/BitmapImage.cpp
Source/WebCore/platform/graphics/BitmapImage.h
Source/WebCore/platform/graphics/CrossfadeGeneratedImage.cpp
Source/WebCore/platform/graphics/CrossfadeGeneratedImage.h
Source/WebCore/platform/graphics/DecodingOptions.h [new file with mode: 0644]
Source/WebCore/platform/graphics/GeneratedImage.h
Source/WebCore/platform/graphics/GradientImage.cpp
Source/WebCore/platform/graphics/GradientImage.h
Source/WebCore/platform/graphics/GraphicsContext.cpp
Source/WebCore/platform/graphics/GraphicsContext.h
Source/WebCore/platform/graphics/Image.cpp
Source/WebCore/platform/graphics/Image.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/ImageSource.cpp
Source/WebCore/platform/graphics/ImageSource.h
Source/WebCore/platform/graphics/NamedImageGeneratedImage.cpp
Source/WebCore/platform/graphics/NamedImageGeneratedImage.h
Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp
Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp
Source/WebCore/platform/graphics/cg/ImageDecoderCG.h
Source/WebCore/platform/graphics/cg/PDFDocumentImage.cpp
Source/WebCore/platform/graphics/cg/PDFDocumentImage.h
Source/WebCore/platform/graphics/win/ImageCGWin.cpp
Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.cpp
Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.h
Source/WebCore/platform/image-decoders/ImageDecoder.cpp
Source/WebCore/platform/image-decoders/ImageDecoder.h
Source/WebCore/rendering/RenderBoxModelObject.cpp
Source/WebCore/rendering/RenderImage.cpp
Source/WebCore/svg/graphics/SVGImage.cpp
Source/WebCore/svg/graphics/SVGImage.h
Source/WebCore/svg/graphics/SVGImageForContainer.cpp
Source/WebCore/svg/graphics/SVGImageForContainer.h

index 72bc347..4ebb766 100644 (file)
@@ -1,3 +1,35 @@
+2017-03-27  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        REGRESSION(213764): Large images should not be decoded asynchronously when they are drawn on a canvas
+        https://bugs.webkit.org/show_bug.cgi?id=169771
+
+        Reviewed by Simon Fraser.
+
+        * fast/images/animated-image-different-dest-size-expected.html: Added.
+        * fast/images/animated-image-different-dest-size.html: Added.
+        This test crashes without this patch.
+
+        * fast/images/animated-image-loop-count.html:
+        Clear the memory cache so the test can be not flaky. Running it with -repeat-each was failing.
+
+        * fast/images/async-image-background-image-expected.html: Added.
+        * fast/images/async-image-background-image.html: Added.
+        Ensures the background image can be drawn asynchronously if it is large.
+
+        * fast/images/async-image-canvas-draw-image-expected.html: Added.
+        * fast/images/async-image-canvas-draw-image.html: Added.
+        Ensures the image is drawn synchronously on the canvas regardless of its size.
+
+        * fast/images/ordered-animated-image-frames.html:
+        Clear the memory cache so the test can be not flaky. Running it with -repeat-each was failing.
+
+        * fast/images/reset-image-animation-expected.txt:
+        * fast/images/reset-image-animation.html:
+        Change how the steps of the test are ordered so the test can be not flaky.
+        Running it with -repeat-each was failing.
+
+        * fast/images/resources/red-green-blue-900-300.png: Added.
+
 2017-03-27  Youenn Fablet  <youenn@apple.com>
 
         addIceCandidate should not throw if passed null or undefined
diff --git a/LayoutTests/fast/images/animated-image-different-dest-size-expected.html b/LayoutTests/fast/images/animated-image-different-dest-size-expected.html
new file mode 100644 (file)
index 0000000..444ab95
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<style>
+    .small-box {
+        width: 100px;
+        height: 100px;
+        display: inline-block;
+    }
+    .large-box {
+        width: 200px;
+        height: 200px;
+        display: inline-block;
+    }
+</style>    
+<body>
+    <div>
+        <div class="small-box" style="background-color: red;"></div>
+        <div class="small-box" style="background-color: green;"></div>
+        <div class="small-box" style="background-color: blue;"></div>
+    </div>
+    <div>
+        <div class="large-box" style="background-color: red;"></div>
+        <div class="large-box" style="background-color: green;"></div>
+        <div class="large-box" style="background-color: blue;"></div>
+    </div>
+</body>
+</html>
diff --git a/LayoutTests/fast/images/animated-image-different-dest-size.html b/LayoutTests/fast/images/animated-image-different-dest-size.html
new file mode 100644 (file)
index 0000000..0e1f0ba
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <div>
+        <canvas width="100" height="100" class="small-canvas" id="canvas-1"></canvas>
+        <canvas width="100" height="100" class="small-canvas" id="canvas-2"></canvas>
+        <canvas width="100" height="100" class="small-canvas" id="canvas-3"></canvas>
+    </div>
+    <div>
+        <canvas width="200" height="200" class="large-canvas" id="canvas-a"></canvas>
+        <canvas width="200" height="200" class="large-canvas" id="canvas-b"></canvas>
+        <canvas width="200" height="200" class="large-canvas" id="canvas-c"></canvas>
+    </div>
+    <script>
+        function drawFrame(image, canvasIds) {
+            return new Promise((resolve) => {
+                var newCanvasIds = [];
+                for (canvasId of canvasIds) {
+                    let canvas = document.getElementById("canvas-" + canvasId);
+                    let context = canvas.getContext("2d");
+                    context.drawImage(image, 0, 0, 100, 100, 0, 0, canvas.width, canvas.height);
+                    newCanvasIds.push(String.fromCharCode(canvasId.charCodeAt() + 1))
+                }
+                setTimeout(() => {
+                    resolve(newCanvasIds);
+                }, 40);
+            });
+        }
+
+        function drawImage(image, canvasIds, frameCount) {
+            let promise = drawFrame(image, canvasIds);
+            for (let frame = 1; frame < frameCount; ++frame) {
+                promise = promise.then((canvasIds) => {
+                    return drawFrame(image, canvasIds);
+                });
+            }
+            return promise;
+        }
+
+        function loadImage(src, canvasIds, frameCount) {
+            return new Promise((resolve) => {
+                let image = new Image;
+                image.onload = (() => {
+                    if (window.internals)
+                        internals.setImageFrameDecodingDuration(image, 0.030);
+                    drawImage(image, canvasIds, frameCount).then(resolve);
+                });
+                image.src = src;
+            });
+        }
+
+        (function() {
+            if (window.internals)
+                internals.clearMemoryCache();
+
+            if (window.testRunner)
+                testRunner.waitUntilDone();
+                
+            var promise = loadImage("resources/animated-red-green-blue.gif", ['1', 'a'], 3);
+
+            promise.then(() => {
+                if (window.testRunner)
+                    testRunner.notifyDone();
+            });
+        })();
+    </script>
+</body>
+</html>
index dba1fdb..4a5b74e 100644 (file)
@@ -57,6 +57,9 @@
         }
 
         (function() {
+            if (window.internals)
+                internals.clearMemoryCache();
+
             if (window.testRunner)
                 testRunner.waitUntilDone();
 
diff --git a/LayoutTests/fast/images/async-image-background-image-expected.html b/LayoutTests/fast/images/async-image-background-image-expected.html
new file mode 100644 (file)
index 0000000..ab450a4
--- /dev/null
@@ -0,0 +1,10 @@
+<style>
+    div {
+        width: 300px;
+        height: 300px;
+        background-color: green;
+    }
+</style>
+<body>
+    <div></div>
+</body>
diff --git a/LayoutTests/fast/images/async-image-background-image.html b/LayoutTests/fast/images/async-image-background-image.html
new file mode 100644 (file)
index 0000000..9bcda44
--- /dev/null
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<style>
+    div {
+        width: 300px;
+        height: 300px;
+        background-image: url(resources/red-green-blue-900-300.png);
+        background-position: -300px 0px;
+    }
+</style>
+<script>
+    if (window.internals)
+        internals.settings.setLargeImageAsyncDecodingEnabled(true);
+</script>
+<body>
+    <div></div>
+</body>
+</html>
diff --git a/LayoutTests/fast/images/async-image-canvas-draw-image-expected.html b/LayoutTests/fast/images/async-image-canvas-draw-image-expected.html
new file mode 100644 (file)
index 0000000..ab450a4
--- /dev/null
@@ -0,0 +1,10 @@
+<style>
+    div {
+        width: 300px;
+        height: 300px;
+        background-color: green;
+    }
+</style>
+<body>
+    <div></div>
+</body>
diff --git a/LayoutTests/fast/images/async-image-canvas-draw-image.html b/LayoutTests/fast/images/async-image-canvas-draw-image.html
new file mode 100644 (file)
index 0000000..6914493
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<style>
+    canvas {
+        width: 300px;
+        height: 300px;
+    }
+</style>    
+<body>
+    <canvas id="canvas"></canvas>
+    <script>
+        if (window.internals)
+            internals.settings.setLargeImageAsyncDecodingEnabled(true);
+
+        let image = new Image;
+        image.onload = (() => {
+            let canvas = document.getElementById("canvas");
+            let context = canvas.getContext("2d");
+            context.drawImage(image, 300, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
+        });
+        image.src = "resources/red-green-blue-900-300.png";
+    </script>
+</body>
+</html>
index d685417..77c3ecf 100644 (file)
@@ -9,6 +9,9 @@
 <body>
     <canvas id="canvas"></canvas>
     <script>
+        if (window.internals)
+            internals.clearMemoryCache();
+
         if (window.testRunner)
             testRunner.waitUntilDone();
 
index f39623e..1f1d887 100644 (file)
@@ -3,6 +3,10 @@ Ensure the image animation is played in order after the animation is reset.
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS internals.imageFrameIndex(image) is 0
+PASS internals.imageFrameIndex(image) is 1
+The animation of the image was reset.
+PASS internals.imageFrameIndex(image) is 0
 PASS internals.imageFrameIndex(image) is 1
 PASS internals.imageFrameIndex(image) is 2
 PASS successfullyParsed is true
index 39bfd95..b9113b6 100644 (file)
@@ -6,56 +6,56 @@
 <body>
     <canvas id="canvas"></canvas>
     <script>
-        description("Ensure the image animation is played in order after the animation is reset.");
-        jsTestIsAsync = true;
-
-        internals.clearMemoryCache();
-
         var image = new Image;
-        image.onload = imageLoaded;
-        image.src = "resources/animated-red-green-blue.gif";
-        
-        function imageLoaded()
-        {
-            if (!window.internals)
-                return;
-            internals.setImageFrameDecodingDuration(image, 0.040);
-            drawImage();
-            drawLoop();
+                
+        function drawFrame(expectedFrame) {
+            return new Promise((resolve) => {
+                let canvas = document.getElementById("canvas");
+                let context = canvas.getContext("2d");
+                context.drawImage(image, 0, 0, canvas.width, canvas.height);
+                shouldBe("internals.imageFrameIndex(image)", expectedFrame.toString());
+                setTimeout(() => {
+                    resolve(expectedFrame + 1);
+                }, 30);
+            });
         }
 
-        function drawImage()
-        {
-            if (drawImage.count == undefined)
-                drawImage.count = 0;
-            var canvas = document.getElementById("canvas");
-            var ctx = canvas.getContext("2d");
-            ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
-            return ++drawImage.count;
+        function drawImage(frameCount, expectedFrame) {
+            let promise = drawFrame(expectedFrame);
+            for (let frame = 1; frame < frameCount; ++frame) {
+                promise = promise.then((expectedFrame) => {
+                    return drawFrame(expectedFrame);
+                });
+            }
+            return promise;
         }
-                
-        function drawLoop()
-        {
-            setTimeout(function() {
-                switch (drawImage()) {
-                case 2:
-                    shouldBe("internals.imageFrameIndex(image)", "1");
-                    internals.resetImageAnimation(image);
-                    drawLoop();
-                    break;
 
-                case 5:
-                    // The animation was reset at drawCount = 2. Three more
-                    // drawings should make current_frame = 2.
-                    shouldBe("internals.imageFrameIndex(image)", "2");
-                    finishJSTest();
-                    break;
-                    
-                default:
-                    drawLoop();
-                }
-            }, 50);
+        function loadImage(src, frameCount) {
+            return new Promise((resolve) => {
+                image.onload = (() => {
+                    if (window.internals)
+                        internals.setImageFrameDecodingDuration(image, 0.020);
+                    drawImage(Math.ceil(frameCount / 2), 0).then(() => {
+                        internals.resetImageAnimation(image);
+                        debug("The animation of the image was reset.");
+                        drawImage(frameCount, 0).then(resolve);
+                    });
+                });
+                image.src = src;
+            });
         }
+
+        (function() {
+            if (window.internals)
+                internals.clearMemoryCache();
+
+            description("Ensure the image animation is played in order after the animation is reset.");
+            jsTestIsAsync = true;
+                
+            loadImage("resources/animated-red-green-blue.gif", 3).then(() => {
+                finishJSTest();
+            });
+        })();
     </script>
     <script src="../../resources/js-test-post.js"></script>
 </body>
diff --git a/LayoutTests/fast/images/resources/red-green-blue-900-300.png b/LayoutTests/fast/images/resources/red-green-blue-900-300.png
new file mode 100644 (file)
index 0000000..0b3a3e3
Binary files /dev/null and b/LayoutTests/fast/images/resources/red-green-blue-900-300.png differ
index 03efef9..848b60a 100644 (file)
@@ -1,3 +1,197 @@
+2017-03-27  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        REGRESSION(213764): Large images should not be decoded asynchronously when they are drawn on a canvas
+        https://bugs.webkit.org/show_bug.cgi?id=169771
+
+        Reviewed by Simon Fraser.
+
+        Sometimes we have to draw the image immediately like when a canvas calls  
+        drawImage. In this case we have to decode the image synchronously to guarantee
+        the drawing. Other times we need to decode with the native size of the image.
+        The animated images have to be decoded with native size always. Otherwise
+        the frame cache will be messed up if the same image is animated with different
+        sizes. Currently we always decode asynchronously with sizeForDrawing. We need
+        to decouple the decoding mode from the sizeForDrawing.
+
+        This patch introduce the DecodingOptions class which can store and compare the
+        following four cases:
+            -- Synchronous: The frame has be decoded with native size only.
+            -- Asynchronous + anySize: This is passed from the Image::draw() callers.
+            -- Asynchronous + fullSize: The image has to be decoded with its full size.
+            -- Asynchronous + sizeForDrawing: The image can be decoded with sizeForDrawing unless
+            it was decoded with either a full size or sizeForDrawing which is larger than the
+            requested sizeForDrawing.
+            
+        A new argument of type DecodingMode will be added to Image::draw() function.
+        Only when the drawing comes from the render tree, it will be Asynchronous.
+        Otherwise it will be Synchronous.
+
+        Tests: fast/images/animated-image-different-dest-size.html
+               fast/images/async-image-background-image.html
+               fast/images/async-image-canvas-draw-image.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::frameImageAtIndexCacheIfNeeded): Gets the frame image, cache it synchronously if
+        the current one is invalid. frameImageAtIndex() returns whatever stored in the cache.
+        (WebCore::BitmapImage::nativeImage): Call frameImageAtIndexCacheIfNeeded() instead of frameImageAtIndex().
+        (WebCore::BitmapImage::nativeImageForCurrentFrame): Ditto.
+        (WebCore::BitmapImage::nativeImageOfSize): Ditto.
+        (WebCore::BitmapImage::framesNativeImages): Ditto.
+        (WebCore::BitmapImage::draw): Change the logic to do the following:
+        -- The animated image has to be decoded with its full size.
+        -- The animated image expects the current frame to be ready for drawing.
+        -- The large image decoding does not need to call internalStartAnimation().
+        -- The large image has to request async image decoding but draw the current one if it exists.
+        (WebCore::BitmapImage::drawPattern): Draw the pattern synchronously.
+        (WebCore::BitmapImage::shouldUseAsyncDecodingForLargeImages): Delete the call to shouldUseAsyncDecodingForTesting()
+        since it is only applied for animated images.
+        (WebCore::BitmapImage::shouldUseAsyncDecodingForAnimatedImages): Call shouldUseAsyncDecodingForAnimatedImageForTesting().
+        (WebCore::BitmapImage::internalStartAnimation): Request decoding with the full size.
+        (WebCore::BitmapImage::advanceAnimation): Call shouldUseAsyncDecodingForAnimatedImageForTesting().
+        (WebCore::BitmapImage::internalAdvanceAnimation): Assert the current frame is not being decoding asynchronously for any size.
+        (WebCore::BitmapImage::frameImageAtIndex): Deleted. Moved to the header file but with a new purpose: return
+        the current frame from the frame cache as is; do not cache a new one.
+        (WebCore::BitmapImage::shouldUseAsyncDecodingForLargeImage): Deleted. Function was renamed to shouldUseAsyncDecodingForLargeImages.
+        (WebCore::BitmapImage::shouldUseAsyncDecodingForAnimatedImage): Deleted. Function was renamed to shouldUseAsyncDecodingForAnimatedImages.
+        * platform/graphics/BitmapImage.h:
+        * platform/graphics/CrossfadeGeneratedImage.cpp:
+        (WebCore::CrossfadeGeneratedImage::draw): Add a new argument of type DecodingMode.
+        * platform/graphics/CrossfadeGeneratedImage.h:
+        * platform/graphics/DecodingOptions.h: Added.
+        (WebCore::DecodingOptions::DecodingOptions): Default constructor: Synchronous mode.
+        (WebCore::DecodingOptions::operator==): Compares two DecodingOptions for equality.
+        (WebCore::DecodingOptions::isSynchronous): Is the frame decoded synchronously?
+        (WebCore::DecodingOptions::isAsynchronous): Is the frame decoded asynchronously?
+        (WebCore::DecodingOptions::isAsynchronousCompatibleWith): Is this DecodingOptions compatible with another one?
+        (WebCore::DecodingOptions::hasFullSize): Is the decoding mode asynchronous but for the image full size?
+        (WebCore::DecodingOptions::hasSizeForDrawing): Is this decoding mode asynchronous but for a sizeForDrawing?
+        (WebCore::DecodingOptions::sizeForDrawing): Returns the sizeForDrawing. m_decodingModeOrSize has to hold an IntSize.
+        (WebCore::DecodingOptions::maxDimension): Moved form ImageFrame.cpp.
+        (WebCore::DecodingOptions::has): A helper function.
+        (WebCore::DecodingOptions::hasDecodingMode): Does m_decodingModeOrSize a DecodingMode?
+        (WebCore::DecodingOptions::hasSize): Does m_decodingModeOrSize an IntSize?
+        * platform/graphics/GeneratedImage.h: Add a new argument of type DecodingMode.
+        * platform/graphics/GradientImage.cpp:
+        (WebCore::GradientImage::draw): Ditto.
+        * platform/graphics/GradientImage.h: Ditto.
+        * platform/graphics/GraphicsContext.cpp:
+        (WebCore::GraphicsContext::drawImage): Pass the ImagePaintingOptions::m_DecodingMode to Image::draw().
+        * platform/graphics/GraphicsContext.h:
+        (WebCore::ImagePaintingOptions::ImagePaintingOptions): Add a new member of type DecodingMode to ImagePaintingOptions.
+        * platform/graphics/Image.cpp:
+        (WebCore::Image::drawTiled): Pass DecodingMode::Synchronous to Image::draw().
+        * platform/graphics/Image.h: Add a new argument of type DecodingMode to Image::draw().
+        * platform/graphics/ImageFrame.cpp:
+        (WebCore::ImageFrame::operator=): Replace m_sizeForDrawing by m_decodingOptions.
+        (WebCore::ImageFrame::hasNativeImage): Check if m_nativeImage is valid and the subsamplingLevels match.
+        (WebCore::ImageFrame::hasFullSizeNativeImage): Checks hasNativeImage() and whether the image frame was
+        decoded for the image full size.
+        (WebCore::ImageFrame::hasDecodedNativeImageCompatibleWithOptions): Checks hasNativeImage() and the DecodingOptions match.
+        (WebCore::maxDimension): Deleted. Moved to DecodingOptions.h.
+        (WebCore::ImageFrame::isBeingDecoded): Deleted. The check for having an ImageFrame being decoded is
+        moved to ImageFrameCache.
+        (WebCore::ImageFrame::hasValidNativeImage): Deleted. No need to this function.
+        * platform/graphics/ImageFrame.h:
+        (WebCore::ImageFrame::hasNativeImage): Add an std::optional<SubsamplingLevel> argument.
+        (WebCore::ImageFrame::hasFullSizeNativeImage): Checks whether the ImageFrame was decoded for the image full size.
+        (WebCore::ImageFrame::enqueueSizeForDecoding): Deleted.
+        (WebCore::ImageFrame::dequeueSizeForDecoding): Deleted.
+        (WebCore::ImageFrame::clearSizeForDecoding): Deleted.
+        (WebCore::ImageFrame::isBeingDecoded): Deleted.
+        (WebCore::ImageFrame::sizeForDrawing): Deleted.
+        (WebCore::ImageFrame::hasDecodedNativeImage): Deleted.
+        The logic of knowing whether an ImageFrame is being decoded is moved to ImageFrameCache.
+        * platform/graphics/ImageFrameCache.cpp:
+        (WebCore::ImageFrameCache::cacheFrameMetadataAtIndex): Caches the metadata of an ImageFrame. If the NativeImage
+        was decoded for a sizeForDrawing, the size of the ImageFrame will be the nativeImageSize(). Otherwise, the
+        frameSizeAtIndex() will be called.
+        (WebCore::ImageFrameCache::cacheFrameNativeImageAtIndex): Cache a new NativeImage which involves caching new
+        metadata and updating the memory cache. No need to check if the existing native image is valid or not for the
+        DecodingOptions. Actually it would be a bug if it happens. This is why cacheNativeImageForFrameRequest() asserts
+        !frame.hasAsyncNativeImage() before calling cacheFrameNativeImageAtIndex().
+        (WebCore::ImageFrameCache::cacheAsyncFrameNativeImageAtIndex): Cache new NativeImage which was decoded asynchronously.
+        (WebCore::ImageFrameCache::startAsyncDecodingQueue): Call cacheAsyncFrameNativeImageAtIndex() instead of
+        cacheNativeImageForFrameRequest() for clarity.
+        (WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Call hasAsyncNativeImage() instead of hasValidNativeImage()
+        Call frameIsDecodingCompatibleWithOptionsAtIndex() instead of frame.isBeingDecoded(). Replace the call to enqueueSizeForDecoding()
+        by appending the same ImageFrameRequest to m_frameCommitQueue.
+        (WebCore::ImageFrameCache::isAsyncDecodingQueueIdle): Use m_frameCommitQueue to answer the question whether the decodingQueue is idle.
+        (WebCore::ImageFrameCache::stopAsyncDecodingQueue): Use m_frameCommitQueue to loop through all the ImageFrames which are currently being decoded.
+        (WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded): For getting the metadata, this function needs a valid frame. If it is requested
+        to decode the nativeImage, it has to do it synchronously.
+        (WebCore::ImageFrameCache::singlePixelSolidColor): Don't cache the frame if it is an animated image or the size is not a single pixel.
+        (WebCore::ImageFrameCache::frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex): Use m_frameCommitQueue to answer the question whether an ImageFrame
+        is being decoded and is compatible with DecodingOptions.
+        (WebCore::ImageFrameCache::frameHasFullSizeNativeImageAtIndex): Calls ImageFrame::hasFullNativeImage() for a frame. 
+        (WebCore::ImageFrameCache::frameHasDecodedNativeImageCompatibleWithOptionsAtIndex): Calls ImageFrame::hasDecodedNativeImageCompatibleWithOptions() for a frame.
+        (WebCore::ImageFrameCache::frameImageAtIndex): Returns the current NativeImage without caching.
+        (WebCore::ImageFrameCache::frameImageAtIndexCacheIfNeeded): Returns the current NativeImage but cache it synchronously if needed.
+        (WebCore::ImageFrameCache::setFrameNativeImageAtIndex): Deleted.
+        (WebCore::ImageFrameCache::setFrameMetadataAtIndex): Deleted.
+        (WebCore::ImageFrameCache::replaceFrameNativeImageAtIndex): Deleted.
+        (WebCore::ImageFrameCache::frameIsBeingDecodedAtIndex): Deleted.
+        (WebCore::ImageFrameCache::frameHasImageAtIndex): Deleted.
+        (WebCore::ImageFrameCache::frameHasValidNativeImageAtIndex): Deleted.
+        (WebCore::ImageFrameCache::frameHasDecodedNativeImage): Deleted.
+        * platform/graphics/ImageFrameCache.h: Two ImageFrameRequest queues will be used.
+        -- The existing one m_frameRequestQueue which is shared between the main thread and decoding thread. The requests will be
+        dequeued from it before starting the decoding. The decoded NativeImage will be cached only on the main thread. The decoding
+        thread is not blocked by the callOnMainThread(). This means there might be multiple ImageFrameRequests which were dequeued
+        while their NativeImages have not been cached yet.
+        -- A new one m_frameCommitQueue which is track all the ImageFrameRequests whose NativeImages have not been cached yet.
+        (WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded): Be explicit about caching the image frame. frameImageAtIndex()
+        returns the current image frame without caching. frameAtIndexCacheIfNeeded(). returns the current image frame but cache
+        it if needed.
+        (WebCore::ImageFrameCache::ImageFrameRequest::operator==): Compares two ImageFrameRequests for equality.
+        * platform/graphics/ImageSource.cpp:
+        (WebCore::ImageSource::frameImageAtIndexCacheIfNeeded): 
+        (WebCore::ImageSource::frameImageAtIndex): Deleted.
+        * platform/graphics/ImageSource.h:
+        (WebCore::ImageSource::requestFrameAsyncDecodingAtIndex): Change the type of the argument from IntSize to be const std::optional<IntSize>.
+        (WebCore::ImageSource::frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex): Rename of frameIsBeingDecodedAtIndex(). Replace the argument of type
+        std::optional<IntSize> by an argument of type DecodingOptions.
+        (WebCore::ImageSource::frameHasFullSizeNativeImageAtIndex): A wrapper around the ImageFrameCache function.
+        (WebCore::ImageSource::frameHasDecodedNativeImageCompatibleWithOptionsAtIndex): Ditto.
+        (WebCore::ImageSource::frameImageAtIndex): Ditto.
+        (WebCore::ImageSource::frameIsBeingDecodedAtIndex): Deleted.
+        (WebCore::ImageSource::frameHasValidNativeImageAtIndex): Deleted.
+        (WebCore::ImageSource::frameHasDecodedNativeImage): Deleted.
+        * platform/graphics/NamedImageGeneratedImage.cpp:
+        (WebCore::NamedImageGeneratedImage::draw): Add a new argument of type DecodingMode.
+        * platform/graphics/NamedImageGeneratedImage.h: Ditto.
+        * platform/graphics/cairo/ImageBufferCairo.cpp:
+        (WebCore::ImageBuffer::draw): Add a new argument of type DecodingMode.
+        * platform/graphics/cg/ImageDecoderCG.cpp:
+        (WebCore::ImageDecoder::createFrameImageAtIndex): Replace the sizeForDrawing argument by a DecodingMode argument. Add a new handling
+        for decoding asynchronously for the image full size.
+        * platform/graphics/cg/ImageDecoderCG.h: Change the prototype of the function.
+        * platform/graphics/cg/PDFDocumentImage.cpp:
+        (WebCore::PDFDocumentImage::draw): Add a new argument of type DecodingMode.
+        * platform/graphics/cg/PDFDocumentImage.h:
+        * platform/graphics/win/ImageCGWin.cpp:
+        (WebCore::BitmapImage::getHBITMAPOfSize): Pass DecodingMode::Synchronous to Image::draw().
+        (WebCore::BitmapImage::drawFrameMatchingSourceSize): Ditto.
+        * platform/graphics/win/ImageDecoderDirect2D.cpp:
+        (WebCore::ImageDecoder::createFrameImageAtIndex): Replace the sizeForDrawing argument by a DecodingMode argument.
+        * platform/graphics/win/ImageDecoderDirect2D.h: Change the prototype of the function.
+        * platform/image-decoders/ImageDecoder.cpp: 
+        (WebCore::ImageDecoder::createFrameImageAtIndex): Replace the sizeForDrawing argument by a DecodingMode argument.
+        * platform/image-decoders/ImageDecoder.h: Change the prototype of the function.
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::RenderBoxModelObject::paintFillLayerExtended): Draw the background image asynchronously if the image size is large.
+        * rendering/RenderImage.cpp:
+        (WebCore::RenderImage::paintIntoRect): Draw the background image element asynchronously if the image size is large.
+        * svg/graphics/SVGImage.cpp:
+        (WebCore::SVGImage::drawForContainer): Pass DecodingMode::Synchronous to draw().
+        (WebCore::SVGImage::nativeImageForCurrentFrame): Ditto.
+        (WebCore::SVGImage::nativeImage): Ditto.
+        (WebCore::SVGImage::draw): Add a new argument of type DecodingMode.
+        * svg/graphics/SVGImage.h: Change the prototype of the function.
+        * svg/graphics/SVGImageForContainer.cpp:
+        (WebCore::SVGImageForContainer::draw): Add a new argument of type DecodingMode.
+        * svg/graphics/SVGImageForContainer.h: Change the prototype of the function.
+
 2017-03-27  Youenn Fablet  <youenn@apple.com>
 
         Activate release libwebrtc logging when WebRTC log channel is on
index 5e3f04a..2a4aa8e 100644 (file)
                53EF766C16531994004CBE49 /* SettingsMacros.h in Copy Generated Headers */ = {isa = PBXBuildFile; fileRef = 53EF766A16530A61004CBE49 /* SettingsMacros.h */; };
                550A0BC9085F6039007353D6 /* QualifiedName.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 550A0BC7085F6039007353D6 /* QualifiedName.cpp */; };
                550A0BCA085F6039007353D6 /* QualifiedName.h in Headers */ = {isa = PBXBuildFile; fileRef = 550A0BC8085F6039007353D6 /* QualifiedName.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               555130011E7CCCCB00A69E38 /* DecodingOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 555130001E7CCCCA00A69E38 /* DecodingOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
                555B87EC1CAAF0AB00349425 /* ImageDecoderCG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 555B87EA1CAAF0AB00349425 /* ImageDecoderCG.cpp */; };
                555B87ED1CAAF0AB00349425 /* ImageDecoderCG.h in Headers */ = {isa = PBXBuildFile; fileRef = 555B87EB1CAAF0AB00349425 /* ImageDecoderCG.h */; };
                5576A5641D88A70800CCC04C /* ImageFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5576A5621D88A70800CCC04C /* ImageFrame.cpp */; };
                53EF766A16530A61004CBE49 /* SettingsMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsMacros.h; sourceTree = "<group>"; };
                550A0BC7085F6039007353D6 /* QualifiedName.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QualifiedName.cpp; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                550A0BC8085F6039007353D6 /* QualifiedName.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = QualifiedName.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
+               555130001E7CCCCA00A69E38 /* DecodingOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DecodingOptions.h; sourceTree = "<group>"; };
                555B87EA1CAAF0AB00349425 /* ImageDecoderCG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDecoderCG.cpp; sourceTree = "<group>"; };
                555B87EB1CAAF0AB00349425 /* ImageDecoderCG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageDecoderCG.h; sourceTree = "<group>"; };
                5576A5621D88A70800CCC04C /* ImageFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFrame.cpp; sourceTree = "<group>"; };
                                2D2FC0541460CD6F00263633 /* CrossfadeGeneratedImage.cpp */,
                                2D2FC0551460CD6F00263633 /* CrossfadeGeneratedImage.h */,
                                A8CB41020E85B8A50032C4F0 /* DashArray.h */,
+                               555130001E7CCCCA00A69E38 /* DecodingOptions.h */,
                                49FC7A4F1444AF5F00A5D864 /* DisplayRefreshMonitor.cpp */,
                                49AF2D6814435D050016A784 /* DisplayRefreshMonitor.h */,
                                2D29ECC1192ECC8300984B78 /* DisplayRefreshMonitorClient.cpp */,
                                A12538D413F9B60A00024754 /* LayoutRepainter.h in Headers */,
                                141DC054164834B900371E5A /* LayoutSize.h in Headers */,
                                2D9066070BE141D400956998 /* LayoutState.h in Headers */,
+                               555130011E7CCCCB00A69E38 /* DecodingOptions.h in Headers */,
                                141DC0481648348F00371E5A /* LayoutUnit.h in Headers */,
                                CDE8B5ED1A69777300B4B66A /* LegacyCDMPrivateClearKey.h in Headers */,
                                CDF4B7121E0087AE00E235A2 /* LegacyCDMSession.h in Headers */,
index 5d4b46a..843edfa 100644 (file)
@@ -102,24 +102,24 @@ bool BitmapImage::dataChanged(bool allDataReceived)
     return m_source.dataChanged(data(), allDataReceived);
 }
 
-NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext)
+NativeImagePtr BitmapImage::frameImageAtIndexCacheIfNeeded(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
 {
-    if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing)) {
+    if (!frameHasFullSizeNativeImageAtIndex(index, subsamplingLevel)) {
         LOG(Images, "BitmapImage::%s - %p - url: %s [subsamplingLevel was %d, resampling]", __FUNCTION__, this, sourceURL().utf8().data(), static_cast<int>(frameSubsamplingLevelAtIndex(index)));
         invalidatePlatformData();
     }
 
-    return m_source.frameImageAtIndex(index, subsamplingLevel, sizeForDrawing, targetContext);
+    return m_source.frameImageAtIndexCacheIfNeeded(index, subsamplingLevel, targetContext);
 }
 
 NativeImagePtr BitmapImage::nativeImage(const GraphicsContext* targetContext)
 {
-    return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext);
+    return frameImageAtIndexCacheIfNeeded(0, SubsamplingLevel::Default, targetContext);
 }
 
 NativeImagePtr BitmapImage::nativeImageForCurrentFrame(const GraphicsContext* targetContext)
 {
-    return frameImageAtIndex(m_currentFrame, SubsamplingLevel::Default, { }, targetContext);
+    return frameImageAtIndexCacheIfNeeded(m_currentFrame, SubsamplingLevel::Default, targetContext);
 }
 
 #if USE(CG)
@@ -128,13 +128,13 @@ NativeImagePtr BitmapImage::nativeImageOfSize(const IntSize& size, const Graphic
     size_t count = frameCount();
 
     for (size_t i = 0; i < count; ++i) {
-        auto image = frameImageAtIndex(i, SubsamplingLevel::Default, { }, targetContext);
+        auto image = frameImageAtIndexCacheIfNeeded(i, SubsamplingLevel::Default, targetContext);
         if (image && nativeImageSize(image) == size)
             return image;
     }
 
     // Fallback to the first frame image if we can't find the right size
-    return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext);
+    return frameImageAtIndexCacheIfNeeded(0, SubsamplingLevel::Default, targetContext);
 }
 
 Vector<NativeImagePtr> BitmapImage::framesNativeImages()
@@ -143,7 +143,7 @@ Vector<NativeImagePtr> BitmapImage::framesNativeImages()
     size_t count = frameCount();
 
     for (size_t i = 0; i < count; ++i) {
-        if (auto image = frameImageAtIndex(i))
+        if (auto image = frameImageAtIndexCacheIfNeeded(i))
             images.append(image);
     }
 
@@ -158,55 +158,58 @@ bool BitmapImage::notSolidColor()
 }
 #endif
 
-void BitmapImage::draw(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode mode, ImageOrientationDescription description)
+void BitmapImage::draw(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode mode, DecodingMode decodingMode, ImageOrientationDescription description)
 {
     if (destRect.isEmpty() || srcRect.isEmpty())
         return;
 
     float scale = subsamplingScale(context, destRect, srcRect);
     m_currentSubsamplingLevel = allowSubsampling() ? m_source.subsamplingLevelForScale(scale) : SubsamplingLevel::Default;
-    m_sizeForDrawing = enclosingIntRect(context.getCTM().mapRect(destRect)).size();
+    IntSize sizeForDrawing = enclosingIntRect(context.getCTM().mapRect(destRect)).size();
 
     LOG(Images, "BitmapImage::%s - %p - url: %s [subsamplingLevel = %d scale = %.4f]", __FUNCTION__, this, sourceURL().utf8().data(), static_cast<int>(m_currentSubsamplingLevel), scale);
 
-    StartAnimationResult result = internalStartAnimation();
-    if (result == StartAnimationResult::DecodingActive && showDebugBackground()) {
-        fillWithSolidColor(context, destRect, Color(Color::yellow).colorWithAlpha(0.5), op);
-        return;
-    }
-
-    ASSERT_IMPLIES(result == StartAnimationResult::DecodingActive, m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing));
-
     NativeImagePtr image;
-    if (shouldUseAsyncDecodingForLargeImage()) {
-        if (m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing))
-            image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing, &context);
-        else {
-            ASSERT(!canAnimate() && !m_currentFrame);
-            if (!frameIsBeingDecodedAtIndex(m_currentFrame, m_sizeForDrawing)) {
-                m_source.requestFrameAsyncDecodingAtIndex(0, m_currentSubsamplingLevel, m_sizeForDrawing);
-                LOG(Images, "BitmapImage::%s - %p - url: %s [requesting large async decoding]", __FUNCTION__, this, sourceURL().utf8().data());
-            }
+    if (decodingMode == DecodingMode::Asynchronous && shouldUseAsyncDecodingForLargeImages()) {
+        ASSERT(!canAnimate() && !m_currentFrame);
 
-            if (!frameHasDecodedNativeImage(m_currentFrame)) {
-                if (showDebugBackground())
-                    fillWithSolidColor(context, destRect, Color(Color::yellow).colorWithAlpha(0.5), op);
-                return;
-            }
+        if (!frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(m_currentFrame, m_currentSubsamplingLevel, DecodingOptions(sizeForDrawing))
+            && !frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(m_currentFrame, DecodingOptions(sizeForDrawing))) {
+            m_source.requestFrameAsyncDecodingAtIndex(0, m_currentSubsamplingLevel, sizeForDrawing);
+            LOG(Images, "BitmapImage::%s - %p - url: %s [requesting large async decoding]", __FUNCTION__, this, sourceURL().utf8().data());
+        }
 
-            image = frameImageAtIndex(m_currentFrame);
+        if (!frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(m_currentFrame, m_currentSubsamplingLevel, DecodingMode::Asynchronous)) {
+            if (showDebugBackground())
+                fillWithSolidColor(context, destRect, Color(Color::yellow).colorWithAlpha(0.5), op);
+            return;
         }
+
+        image = frameImageAtIndex(m_currentFrame);
     } else {
-        ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame, m_sizeForDrawing));
-        if (shouldUseAsyncDecodingForAnimatedImage())
-            image = frameImageAtIndex(m_currentFrame);
-        else
-            image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, { }, &context);
-    }
+        StartAnimationStatus status = internalStartAnimation();
+        ASSERT_IMPLIES(status == StartAnimationStatus::DecodingActive, frameHasFullSizeNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel));
 
-    if (!image) // If it's too early we won't have an image yet.
-        return;
+        if (status == StartAnimationStatus::DecodingActive && showDebugBackground()) {
+            fillWithSolidColor(context, destRect, Color(Color::yellow).colorWithAlpha(0.5), op);
+            return;
+        }
+
+        if (frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(m_currentFrame, DecodingMode::Asynchronous)) {
+            // FIXME: instead of showing the yellow rectangle and returning we need to wait for this the frame to finish decoding.
+            if (showDebugBackground()) {
+                fillWithSolidColor(context, destRect, Color(Color::yellow).colorWithAlpha(0.5), op);
+                LOG(Images, "BitmapImage::%s - %p - url: %s [waiting for async decoding to finish]", __FUNCTION__, this, sourceURL().utf8().data());
+            }
+            return;
+        }
+
+        image = frameImageAtIndexCacheIfNeeded(m_currentFrame, m_currentSubsamplingLevel, &context);
+        if (!image) // If it's too early we won't have an image yet.
+            return;
+    }
 
+    ASSERT(image);
     Color color = singlePixelSolidColor();
     if (color.isValid()) {
         fillWithSolidColor(context, destRect, color, op);
@@ -243,7 +246,7 @@ void BitmapImage::drawPattern(GraphicsContext& ctxt, const FloatRect& destRect,
         // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout.
         setImageObserver(nullptr);
 
-        draw(buffer->context(), tileRect, tileRect, op, blendMode, ImageOrientationDescription());
+        draw(buffer->context(), tileRect, tileRect, op, blendMode, DecodingMode::Synchronous, ImageOrientationDescription());
 
         setImageObserver(observer);
         buffer->convertToLuminanceMask();
@@ -267,14 +270,14 @@ bool BitmapImage::canAnimate()
     return shouldAnimate() && frameCount() > 1;
 }
 
-bool BitmapImage::shouldUseAsyncDecodingForLargeImage()
+bool BitmapImage::shouldUseAsyncDecodingForLargeImages()
 {
-    return !canAnimate() && allowLargeImageAsyncDecoding() && (shouldUseAsyncDecodingForTesting() || m_source.shouldUseAsyncDecoding());
+    return !canAnimate() && allowLargeImageAsyncDecoding() && m_source.shouldUseAsyncDecoding();
 }
 
-bool BitmapImage::shouldUseAsyncDecodingForAnimatedImage()
+bool BitmapImage::shouldUseAsyncDecodingForAnimatedImages()
 {
-    return canAnimate() && allowAnimatedImageAsyncDecoding() && (shouldUseAsyncDecodingForTesting() || m_source.shouldUseAsyncDecoding());
+    return canAnimate() && allowAnimatedImageAsyncDecoding() && (shouldUseAsyncDecodingForAnimatedImagesForTesting() || m_source.shouldUseAsyncDecoding());
 }
 
 void BitmapImage::clearTimer()
@@ -289,19 +292,19 @@ void BitmapImage::startTimer(double delay)
     m_frameTimer->startOneShot(delay);
 }
 
-BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
+BitmapImage::StartAnimationStatus BitmapImage::internalStartAnimation()
 {
     if (!canAnimate())
-        return StartAnimationResult::CannotStart;
+        return StartAnimationStatus::CannotStart;
 
     if (m_frameTimer)
-        return StartAnimationResult::TimerActive;
+        return StartAnimationStatus::TimerActive;
 
     // Don't start a new animation until we draw the frame that is currently being decoded.
     size_t nextFrame = (m_currentFrame + 1) % frameCount();
-    if (frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing)) {
+    if (frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(nextFrame, DecodingMode::Asynchronous)) {
         LOG(Images, "BitmapImage::%s - %p - url: %s [nextFrame = %ld is being decoded]", __FUNCTION__, this, sourceURL().utf8().data(), nextFrame);
-        return StartAnimationResult::DecodingActive;
+        return StartAnimationStatus::DecodingActive;
     }
 
     if (m_currentFrame >= frameCount() - 1) {
@@ -310,7 +313,7 @@ BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
         // in a GIF can potentially come after all the rest of the image data, so
         // wait on it.
         if (!m_source.isAllDataReceived() && repetitionCount() == RepetitionCountOnce)
-            return StartAnimationResult::IncompleteData;
+            return StartAnimationStatus::IncompleteData;
 
         ++m_repetitionsComplete;
 
@@ -318,7 +321,7 @@ BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
         if (repetitionCount() != RepetitionCountInfinite && m_repetitionsComplete >= repetitionCount()) {
             m_animationFinished = true;
             destroyDecodedDataIfNecessary(false);
-            return StartAnimationResult::CannotStart;
+            return StartAnimationStatus::CannotStart;
         }
 
         destroyDecodedDataIfNecessary(true);
@@ -326,7 +329,7 @@ BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
 
     // Don't advance the animation to an incomplete frame.
     if (!m_source.isAllDataReceived() && !frameIsCompleteAtIndex(nextFrame))
-        return StartAnimationResult::IncompleteData;
+        return StartAnimationStatus::IncompleteData;
 
     double time = monotonicallyIncreasingTime();
 
@@ -341,8 +344,8 @@ BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
     // it will be decoded on a separate work queue. When decoding nextFrame finishes, we will be notified
     // through the callback newFrameNativeImageAvailableAtIndex(). Otherwise, advanceAnimation() will be called
     // when the timer fires and m_currentFrame will be advanced to nextFrame since it is not being decoded.
-    if (shouldUseAsyncDecodingForAnimatedImage()) {
-        bool isAsyncDecode = m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel, m_sizeForDrawing);
+    if (shouldUseAsyncDecodingForAnimatedImages()) {
+        bool isAsyncDecode = m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel);
 
 #if !LOG_DISABLED
         if (isAsyncDecode)
@@ -360,7 +363,7 @@ BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
 
     ASSERT(!m_frameTimer);
     startTimer(m_desiredFrameStartTime - time);
-    return StartAnimationResult::Started;
+    return StartAnimationStatus::Started;
 }
 
 void BitmapImage::advanceAnimation()
@@ -369,7 +372,7 @@ void BitmapImage::advanceAnimation()
 
     // Pretend as if decoding nextFrame has taken m_frameDecodingDurationForTesting from
     // the time this decoding was requested.
-    if (shouldUseAsyncDecodingForTesting()) {
+    if (shouldUseAsyncDecodingForAnimatedImagesForTesting()) {
         double time = monotonicallyIncreasingTime();
         // Start a timer with the remaining time from now till the m_desiredFrameDecodeTime.
         if (m_desiredFrameDecodeTimeForTesting > std::max(time, m_desiredFrameStartTime)) {
@@ -380,7 +383,7 @@ void BitmapImage::advanceAnimation()
 
     // Don't advance to nextFrame unless its decoding has finished or was not required.
     size_t nextFrame = (m_currentFrame + 1) % frameCount();
-    if (!frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing))
+    if (!frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(nextFrame, DecodingMode::Asynchronous))
         internalAdvanceAnimation();
     else {
         // Force repaint if showDebugBackground() is on.
@@ -393,7 +396,7 @@ void BitmapImage::advanceAnimation()
 void BitmapImage::internalAdvanceAnimation()
 {
     m_currentFrame = (m_currentFrame + 1) % frameCount();
-    ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame, m_sizeForDrawing));
+    ASSERT(!frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(m_currentFrame, DecodingMode::Asynchronous));
 
     destroyDecodedDataIfNecessary(false);
 
index 2e08c4d..54795ce 100644 (file)
@@ -81,12 +81,13 @@ public:
     FloatSize size() const override { return m_source.size(); }
     IntSize sizeRespectingOrientation() const { return m_source.sizeRespectingOrientation(); }
     Color singlePixelSolidColor() const override { return m_source.singlePixelSolidColor(); }
-
-    bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameIsBeingDecodedAtIndex(index, sizeForDrawing); }
-    bool frameHasDecodedNativeImage(size_t index) const { return m_source.frameHasDecodedNativeImage(index); }
+    bool frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t index, const DecodingOptions& decodingOptions) const { return m_source.frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(index, decodingOptions); }
     bool frameIsCompleteAtIndex(size_t index) const { return m_source.frameIsCompleteAtIndex(index); }
     bool frameHasAlphaAtIndex(size_t index) const { return m_source.frameHasAlphaAtIndex(index); }
-    bool frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); }
+
+    bool frameHasFullSizeNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_source.frameHasFullSizeNativeImageAtIndex(index, subsamplingLevel); }
+    bool frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const DecodingOptions& decodingOptions) { return m_source.frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(index, subsamplingLevel, decodingOptions); }
+
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) const { return m_source.frameSubsamplingLevelAtIndex(index); }
 
     float frameDurationAtIndex(size_t index) const { return m_source.frameDurationAtIndex(index); }
@@ -96,10 +97,10 @@ public:
     bool currentFrameKnownToBeOpaque() const override { return !frameHasAlphaAtIndex(currentFrame()); }
     ImageOrientation orientationForCurrentFrame() const override { return frameOrientationAtIndex(currentFrame()); }
 
-    bool shouldUseAsyncDecodingForTesting() const { return m_frameDecodingDurationForTesting > 0; }
+    bool shouldUseAsyncDecodingForAnimatedImagesForTesting() const { return m_frameDecodingDurationForTesting > 0; }
     void setFrameDecodingDurationForTesting(float duration) { m_frameDecodingDurationForTesting = duration; }
-    bool shouldUseAsyncDecodingForLargeImage();
-    bool shouldUseAsyncDecodingForAnimatedImage();
+    bool shouldUseAsyncDecodingForLargeImages();
+    bool shouldUseAsyncDecodingForAnimatedImages();
     void setClearDecoderAfterAsyncFrameRequestForTesting(bool value) { m_clearDecoderAfterAsyncFrameRequestForTesting = value; }
 
     // Accessors for native image formats.
@@ -132,7 +133,8 @@ protected:
     WEBCORE_EXPORT BitmapImage(NativeImagePtr&&, ImageObserver* = nullptr);
     WEBCORE_EXPORT BitmapImage(ImageObserver* = nullptr);
 
-    NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* = nullptr);
+    NativeImagePtr frameImageAtIndex(size_t index) { return m_source.frameImageAtIndex(index); }
+    NativeImagePtr frameImageAtIndexCacheIfNeeded(size_t, SubsamplingLevel = SubsamplingLevel::Default, const GraphicsContext* = nullptr);
 
     String sourceURL() const { return imageObserver() ? imageObserver()->sourceUrl().string() : emptyString(); }
     bool allowSubsampling() const { return imageObserver() && imageObserver()->allowSubsampling(); }
@@ -152,19 +154,19 @@ protected:
     // |destroyAll| along.
     void destroyDecodedDataIfNecessary(bool destroyAll = true);
 
-    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) override;
     void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendModeNormal) override;
 #if PLATFORM(WIN)
     void drawFrameMatchingSourceSize(GraphicsContext&, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) override;
 #endif
 
     // Animation.
-    enum class StartAnimationResult { CannotStart, IncompleteData, TimerActive, DecodingActive, Started };
+    enum class StartAnimationStatus { CannotStart, IncompleteData, TimerActive, DecodingActive, Started };
     bool isAnimated() const override { return m_source.frameCount() > 1; }
     bool shouldAnimate();
     bool canAnimate();
     void startAnimation() override { internalStartAnimation(); }
-    StartAnimationResult internalStartAnimation();
+    StartAnimationStatus internalStartAnimation();
     void advanceAnimation();
     void internalAdvanceAnimation();
 
@@ -203,7 +205,6 @@ private:
 
     size_t m_currentFrame { 0 }; // The index of the current frame of animation.
     SubsamplingLevel m_currentSubsamplingLevel { SubsamplingLevel::Default };
-    IntSize m_sizeForDrawing;
     std::unique_ptr<Timer> m_frameTimer;
     RepetitionCount m_repetitionsComplete { RepetitionCountNone }; // How many repetitions we've finished.
     double m_desiredFrameStartTime { 0 }; // The system time at which we hope to see the next call to startAnimation().
index 3a229c6..3cc770c 100644 (file)
@@ -86,7 +86,7 @@ void CrossfadeGeneratedImage::drawCrossfade(GraphicsContext& context)
     context.endTransparencyLayer();
 }
 
-void CrossfadeGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+void CrossfadeGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, DecodingMode, ImageOrientationDescription)
 {
     GraphicsContextStateSaver stateSaver(context);
     context.setCompositeOperation(compositeOp, blendMode);
index c7849a7..87dda17 100644 (file)
@@ -49,7 +49,7 @@ public:
     FloatSize size() const override { return m_crossfadeSize; }
 
 protected:
-    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) override;
     void drawPattern(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override;
 
     CrossfadeGeneratedImage(Image& fromImage, Image& toImage, float percentage, const FloatSize& crossfadeSize, const FloatSize&);
diff --git a/Source/WebCore/platform/graphics/DecodingOptions.h b/Source/WebCore/platform/graphics/DecodingOptions.h
new file mode 100644 (file)
index 0000000..31c8815
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * 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 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
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IntSize.h"
+#include <wtf/Variant.h>
+
+namespace WebCore {
+
+enum class DecodingMode {
+    Synchronous,
+    Asynchronous
+};
+
+class DecodingOptions {
+public:
+    DecodingOptions(DecodingMode decodingMode = DecodingMode::Synchronous)
+        : m_decodingModeOrSize(decodingMode)
+    {
+    }
+
+    DecodingOptions(const std::optional<IntSize>& sizeForDrawing)
+        : m_decodingModeOrSize(sizeForDrawing)
+    {
+    }
+
+    bool operator==(const DecodingOptions& other) const
+    {
+        return m_decodingModeOrSize == other.m_decodingModeOrSize;
+    }
+
+    bool isSynchronous() const
+    {
+        return hasDecodingMode() && WTF::get<DecodingMode>(m_decodingModeOrSize) == DecodingMode::Synchronous;
+    }
+
+    bool isAsynchronous() const
+    {
+        return hasDecodingMode() && WTF::get<DecodingMode>(m_decodingModeOrSize) == DecodingMode::Asynchronous;
+    }
+
+    bool isAsynchronousCompatibleWith(const DecodingOptions& DecodingOptions) const
+    {
+        if (!hasSize() || DecodingOptions.isSynchronous())
+            return false;
+
+        if (DecodingOptions.isAsynchronous())
+            return true;
+
+        ASSERT(DecodingOptions.hasSize());
+        if (DecodingOptions.hasFullSize())
+            return hasFullSize();
+
+        ASSERT(DecodingOptions.hasSizeForDrawing());
+        if (hasFullSize())
+            return true;
+
+        ASSERT(hasSizeForDrawing());
+        return maxDimension(*sizeForDrawing()) >= maxDimension(*DecodingOptions.sizeForDrawing());
+    }
+
+    bool hasFullSize() const
+    {
+        return hasSize() && !sizeForDrawing();
+    }
+
+    bool hasSizeForDrawing() const
+    {
+        return hasSize() && sizeForDrawing();
+    }
+
+    std::optional<IntSize> sizeForDrawing() const
+    {
+        ASSERT(hasSize());
+        return WTF::get<std::optional<IntSize>>(m_decodingModeOrSize);
+    }
+
+private:
+    static int maxDimension(const IntSize& size)
+    {
+        return std::max(size.width(), size.height());
+    }
+
+    template<typename T>
+    bool has() const
+    {
+        return WTF::holds_alternative<T>(m_decodingModeOrSize);
+    }
+
+    bool hasDecodingMode() const
+    {
+        return has<DecodingMode>();
+    }
+
+    bool hasSize() const
+    {
+        return has<std::optional<IntSize>>();
+    }
+
+    // Four states of the decoding:
+    // - Synchronous: DecodingMode::Synchronous
+    // - Asynchronous + anySize: DecodingMode::Asynchronous
+    // - Asynchronous + intrinsicSize: an empty std::optional<IntSize>>
+    // - Asynchronous + sizeForDrawing: a none empty std::optional<IntSize>>
+    using DecodingModeOrSize = Variant<DecodingMode, std::optional<IntSize>>;
+    DecodingModeOrSize m_decodingModeOrSize;
+};
+
+}
index 7d9fe35..3ef7e5f 100644 (file)
@@ -47,7 +47,7 @@ public:
     void destroyDecodedData(bool /*destroyAll*/ = true) override { }
 
 protected:
-    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override = 0;
+    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) override = 0;
     void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
         const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override = 0;
 
index 50b9d77..8251440 100644 (file)
@@ -41,7 +41,7 @@ GradientImage::~GradientImage()
 {
 }
 
-void GradientImage::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+void GradientImage::draw(GraphicsContext& destContext, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, DecodingMode, ImageOrientationDescription)
 {
     GraphicsContextStateSaver stateSaver(destContext);
     destContext.setCompositeOperation(compositeOp, blendMode);
index c494639..4b925d1 100644 (file)
@@ -44,7 +44,7 @@ public:
 private:
     GradientImage(Gradient&, const FloatSize&);
 
-    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) final;
+    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) final;
     void drawPattern(GraphicsContext&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) final;
     bool isGradientImage() const final { return true; }
     void dump(TextStream&) const final;
index 1bf59c3..b5c24b6 100644 (file)
@@ -728,7 +728,7 @@ void GraphicsContext::drawImage(Image& image, const FloatRect& destination, cons
     }
 
     InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_interpolationQuality);
-    image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription);
+    image.draw(*this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_decodingMode, imagePaintingOptions.m_orientationDescription);
 }
 
 void GraphicsContext::drawTiledImage(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
index 82b9612..ec3b82d 100644 (file)
@@ -188,25 +188,28 @@ struct GraphicsContextState {
 };
 
 struct ImagePaintingOptions {
-    ImagePaintingOptions(CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), InterpolationQuality interpolationQuality = InterpolationDefault)
+    ImagePaintingOptions(CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal, DecodingMode decodingMode = DecodingMode::Synchronous, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), InterpolationQuality interpolationQuality = InterpolationDefault)
         : m_compositeOperator(compositeOperator)
         , m_blendMode(blendMode)
+        , m_decodingMode(decodingMode)
         , m_orientationDescription(orientationDescription)
         , m_interpolationQuality(interpolationQuality)
     {
     }
 
-    ImagePaintingOptions(ImageOrientationDescription orientationDescription, InterpolationQuality interpolationQuality = InterpolationDefault, CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal)
+    ImagePaintingOptions(ImageOrientationDescription orientationDescription, InterpolationQuality interpolationQuality = InterpolationDefault, CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal, DecodingMode decodingMode = DecodingMode::Synchronous)
         : m_compositeOperator(compositeOperator)
         , m_blendMode(blendMode)
+        , m_decodingMode(decodingMode)
         , m_orientationDescription(orientationDescription)
         , m_interpolationQuality(interpolationQuality)
     {
     }
 
-    ImagePaintingOptions(InterpolationQuality interpolationQuality, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal)
+    ImagePaintingOptions(InterpolationQuality interpolationQuality, ImageOrientationDescription orientationDescription = ImageOrientationDescription(), CompositeOperator compositeOperator = CompositeSourceOver, BlendMode blendMode = BlendModeNormal, DecodingMode decodingMode = DecodingMode::Synchronous)
         : m_compositeOperator(compositeOperator)
         , m_blendMode(blendMode)
+        , m_decodingMode(decodingMode)
         , m_orientationDescription(orientationDescription)
         , m_interpolationQuality(interpolationQuality)
     {
@@ -216,6 +219,7 @@ struct ImagePaintingOptions {
 
     CompositeOperator m_compositeOperator;
     BlendMode m_blendMode;
+    DecodingMode m_decodingMode;
     ImageOrientationDescription m_orientationDescription;
     InterpolationQuality m_interpolationQuality;
 };
index bff9854..c0470d0 100644 (file)
@@ -126,7 +126,7 @@ void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const Fl
         visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height());
         visibleSrcRect.setWidth(destRect.width() / scale.width());
         visibleSrcRect.setHeight(destRect.height() / scale.height());
-        draw(ctxt, destRect, visibleSrcRect, op, blendMode, ImageOrientationDescription());
+        draw(ctxt, destRect, visibleSrcRect, op, blendMode, DecodingMode::Synchronous, ImageOrientationDescription());
         return;
     }
 
@@ -139,7 +139,7 @@ void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const Fl
             visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height());
             visibleSrcRect.setWidth(1);
             visibleSrcRect.setHeight(destRect.height() / scale.height());
-            draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription());
+            draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
             return;
         }
         if (size().height() == 1 && intersection(oneTileRect, destRect).width() == destRect.width()) {
@@ -148,7 +148,7 @@ void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const Fl
             visibleSrcRect.setY(0);
             visibleSrcRect.setWidth(destRect.width() / scale.width());
             visibleSrcRect.setHeight(1);
-            draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription());
+            draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
             return;
         }
     }
@@ -180,7 +180,7 @@ void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const Fl
                 FloatRect fromRect(toFloatPoint(currentTileRect.location() - oneTileRect.location()), currentTileRect.size());
                 fromRect.scale(1 / scale.width(), 1 / scale.height());
 
-                draw(ctxt, toRect, fromRect, op, BlendModeNormal, ImageOrientationDescription());
+                draw(ctxt, toRect, fromRect, op, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
                 toX += currentTileRect.width();
                 currentTileRect.shiftXEdgeTo(oneTileRect.x());
             }
index ab414c0..bc98bd9 100644 (file)
@@ -28,6 +28,7 @@
 #define Image_h
 
 #include "Color.h"
+#include "DecodingOptions.h"
 #include "FloatRect.h"
 #include "FloatSize.h"
 #include "GraphicsTypes.h"
@@ -184,7 +185,7 @@ protected:
 #if PLATFORM(WIN)
     virtual void drawFrameMatchingSourceSize(GraphicsContext&, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator) { }
 #endif
-    virtual void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) = 0;
+    virtual void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) = 0;
     void drawTiled(GraphicsContext&, const FloatRect& dstRect, const FloatPoint& srcPoint, const FloatSize& tileSize, const FloatSize& spacing, CompositeOperator, BlendMode);
     void drawTiled(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator);
 
index ea40ef5..acaec6f 100644 (file)
@@ -63,7 +63,7 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other)
 
     m_nativeImage = other.m_nativeImage;
     m_subsamplingLevel = other.m_subsamplingLevel;
-    m_sizeForDrawing = other.m_sizeForDrawing;
+    m_decodingOptions = other.m_decodingOptions;
 
     m_orientation = other.m_orientation;
     m_duration = other.m_duration;
@@ -85,6 +85,7 @@ unsigned ImageFrame::clearImage()
 
     clearNativeImageSubimages(m_nativeImage);
     m_nativeImage = nullptr;
+    m_decodingOptions = { };
 
     return frameBytes;
 }
@@ -125,43 +126,19 @@ IntSize ImageFrame::size() const
     return m_size;
 }
     
-static int maxDimension(const IntSize& size)
+bool ImageFrame::hasNativeImage(const std::optional<SubsamplingLevel>& subsamplingLevel) const
 {
-    return std::max(size.width(), size.height());
+    return m_nativeImage && (!subsamplingLevel || *subsamplingLevel >= m_subsamplingLevel);
 }
 
-bool ImageFrame::isBeingDecoded(const std::optional<IntSize>& sizeForDrawing) const
+bool ImageFrame::hasFullSizeNativeImage(const std::optional<SubsamplingLevel>& subsamplingLevel) const
 {
-    if (!m_sizeForDecoding.size())
-        return false;
-    
-    if (!sizeForDrawing)
-        return true;
-
-    // Return true if the ImageFrame will be decoded eventually with a suitable sizeForDecoding.
-    return maxDimension(m_sizeForDecoding.last()) >= maxDimension(*sizeForDrawing);
+    return hasNativeImage(subsamplingLevel) && (m_decodingOptions.isSynchronous() || m_decodingOptions.hasFullSize());
 }
-    
-bool ImageFrame::hasValidNativeImage(const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const
-{
-    ASSERT_IMPLIES(!subsamplingLevel, !sizeForDrawing);
-
-    if (!hasNativeImage())
-        return false;
-
-    // The caller does not care about subsamplingLevel or sizeForDrawing. The current NativeImage is fine.
-    if (!subsamplingLevel)
-        return true;
-
-    if (*subsamplingLevel < m_subsamplingLevel)
-        return false;
-
-    // The NativeImage was decoded with the native size. So it is valid for any size.
-    if (!m_sizeForDrawing)
-        return true;
 
-    // The NativeImage was decoded for a specific size. The two sizeForDrawings have to match.
-    return sizeForDrawing && maxDimension(*m_sizeForDrawing) >= maxDimension(*sizeForDrawing);
+bool ImageFrame::hasDecodedNativeImageCompatibleWithOptions(const std::optional<SubsamplingLevel>& subsamplingLevel, const DecodingOptions& decodingOptions) const
+{
+    return hasNativeImage(subsamplingLevel) && m_decodingOptions.isAsynchronousCompatibleWith(decodingOptions);
 }
 
 Color ImageFrame::singlePixelSolidColor() const
index 60593a4..80324b6 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "Color.h"
+#include "DecodingOptions.h"
 #include "ImageBackingStore.h"
 #include "ImageOrientation.h"
 #include "IntSize.h"
@@ -98,12 +99,8 @@ public:
 
     void setDecoding(Decoding decoding) { m_decoding = decoding; }
     Decoding decoding() const { return m_decoding; }
-    void enqueueSizeForDecoding(const IntSize& sizeForDecoding) { m_sizeForDecoding.append(sizeForDecoding); }
-    void dequeueSizeForDecoding() { m_sizeForDecoding.removeFirst(); }
-    void clearSizeForDecoding() { m_sizeForDecoding.clear(); }
 
     bool isEmpty() const { return m_decoding == Decoding::None; }
-    bool isBeingDecoded(const std::optional<IntSize>& sizeForDrawing = { }) const;
     bool isPartial() const { return m_decoding == Decoding::Partial; }
     bool isComplete() const { return m_decoding == Decoding::Complete; }
 
@@ -111,7 +108,6 @@ public:
     IntSize sizeRespectingOrientation() const { return !m_orientation.usesWidthAsHeight() ? size() : size().transposedSize(); }
     unsigned frameBytes() const { return hasNativeImage() ? (size().area() * sizeof(RGBA32)).unsafeGet() : 0; }
     SubsamplingLevel subsamplingLevel() const { return m_subsamplingLevel; }
-    std::optional<IntSize> sizeForDrawing() const { return m_sizeForDrawing; }
 
 #if !USE(CG)
     enum class DisposalMethod { Unspecified, DoNotDispose, RestoreToBackground, RestoreToPrevious };
@@ -130,9 +126,9 @@ public:
     void setHasAlpha(bool hasAlpha) { m_hasAlpha = hasAlpha; }
     bool hasAlpha() const { return !hasMetadata() || m_hasAlpha; }
 
-    bool hasNativeImage() const { return m_nativeImage; }
-    bool hasValidNativeImage(const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing) const;
-    bool hasDecodedNativeImage() const { return hasNativeImage() && sizeForDrawing(); }
+    bool hasNativeImage(const std::optional<SubsamplingLevel>& = { }) const;
+    bool hasFullSizeNativeImage(const std::optional<SubsamplingLevel>& = { }) const;
+    bool hasDecodedNativeImageCompatibleWithOptions(const std::optional<SubsamplingLevel>&, const DecodingOptions&) const;
     bool hasMetadata() const { return !size().isEmpty(); }
 
 #if !USE(CG)
@@ -153,8 +149,7 @@ private:
 
     NativeImagePtr m_nativeImage;
     SubsamplingLevel m_subsamplingLevel { SubsamplingLevel::Default };
-    std::optional<IntSize> m_sizeForDrawing;
-    Deque<IntSize, 4> m_sizeForDecoding;
+    DecodingOptions m_decodingOptions;
 
     ImageOrientation m_orientation { DefaultImageOrientation };
     float m_duration { 0 };
index f0f5a09..846cebe 100644 (file)
@@ -192,18 +192,7 @@ void ImageFrameCache::setNativeImage(NativeImagePtr&& nativeImage)
     frame.m_hasAlpha = nativeImageHasAlpha(frame.m_nativeImage);
 }
 
-void ImageFrameCache::setFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
-{
-    ASSERT(index < m_frames.size());
-    ImageFrame& frame = m_frames[index];
-
-    ASSERT(isDecoderAvailable());
-
-    frame.m_nativeImage = WTFMove(nativeImage);
-    setFrameMetadataAtIndex(index, subsamplingLevel, sizeForDrawing);
-}
-
-void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+void ImageFrameCache::cacheFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
@@ -215,14 +204,11 @@ void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel sub
     
     frame.m_subsamplingLevel = subsamplingLevel;
 
-    if (!sizeForDrawing) {
-        frame.m_size = m_decoder->frameSizeAtIndex(index, frame.m_subsamplingLevel);
-        frame.m_sizeForDrawing = { };
-    } else {
-        ASSERT(frame.nativeImage());
+    if (frame.m_decodingOptions.hasSizeForDrawing()) {
+        ASSERT(frame.hasNativeImage());
         frame.m_size = nativeImageSize(frame.nativeImage());
-        frame.m_sizeForDrawing = sizeForDrawing;
-    }
+    } else
+        frame.m_size = m_decoder->frameSizeAtIndex(index, subsamplingLevel);
 
     frame.m_orientation = m_decoder->frameOrientationAtIndex(index);
     frame.m_hasAlpha = m_decoder->frameHasAlphaAtIndex(index);
@@ -231,40 +217,38 @@ void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel sub
         frame.m_duration = m_decoder->frameDurationAtIndex(index);
 }
 
-void ImageFrameCache::replaceFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
 
-    if (!frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing)) {
-        // Clear the current image frame and update the observer with this clearance.
-        unsigned decodedSize = frame.clear();
-        decodedSizeDecreased(decodedSize);
-    }
+    // Clear the current image frame and update the observer with this clearance.
+    decodedSizeDecreased(frame.clear());
 
     // 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, sizeForDrawing);
+    // Move the new image to the cache.
+    frame.m_nativeImage = WTFMove(nativeImage);
+    frame.m_decodingOptions = decodingOptions;
+    cacheFrameMetadataAtIndex(index, subsamplingLevel);
 
     // Update the observer with the new image frame bytes.
     decodedSizeIncreased(frame.frameBytes());
 }
 
-void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
+void ImageFrameCache::cacheAsyncFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions)
 {
     if (!isDecoderAvailable())
         return;
 
     ASSERT(index < m_frames.size());
-    ASSERT(m_frames[index].isBeingDecoded(sizeForDrawing));
+    ASSERT(!frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(index, subsamplingLevel, decodingOptions));
 
     // Clean the old native image and set a new one
-    replaceFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing);
-    m_frames[index].dequeueSizeForDecoding();
+    cacheFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, decodingOptions);
 
     // Notify the image with the readiness of the new frame NativeImage.
     if (m_image)
@@ -298,50 +282,48 @@ void ImageFrameCache::startAsyncDecodingQueue()
             TraceScope tracingScope(AsyncImageDecodeStart, AsyncImageDecodeEnd);
 
             // Get the frame NativeImage on the decoding thread.
-            NativeImagePtr nativeImage = protectedDecoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing);
+            NativeImagePtr nativeImage = protectedDecoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, frameRequest.decodingOptions);
 
             // Update the cached frames on the main thread to avoid updating the MemoryCache from a different thread.
             callOnMainThread([this, protectedQueue = protectedQueue.copyRef(), nativeImage, frameRequest] () mutable {
                 // The queue may be closed if after we got the frame NativeImage, stopAsyncDecodingQueue() was called
-                if (protectedQueue.ptr() == m_decodingQueue)
-                    cacheFrameNativeImageAtIndex(WTFMove(nativeImage), frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing);
+                if (protectedQueue.ptr() == m_decodingQueue) {
+                    ASSERT(m_frameCommitQueue.first() == frameRequest);
+                    m_frameCommitQueue.removeFirst();
+                    cacheAsyncFrameNativeImageAtIndex(WTFMove(nativeImage), frameRequest.index, frameRequest.subsamplingLevel, frameRequest.decodingOptions);
+                }
             });
         }
     });
 }
 
-bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
+bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
 {
     if (!isDecoderAvailable())
         return false;
 
     ASSERT(index < m_frames.size());
-    ImageFrame& frame = m_frames[index];
 
     // We need to coalesce multiple requests for decoding the same ImageFrame while it
     // is still being decoded. This may happen if the image rectangle is repainted
     // multiple times while the ImageFrame has not finished decoding.
-    if (frame.isBeingDecoded(sizeForDrawing))
+    if (frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(index, sizeForDrawing))
         return true;
 
-    if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing))
+    if (frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(index, subsamplingLevel, sizeForDrawing))
         return false;
 
     if (!hasAsyncDecodingQueue())
         startAsyncDecodingQueue();
-    
-    frame.enqueueSizeForDecoding(sizeForDrawing);
+
     m_frameRequestQueue.enqueue({ index, subsamplingLevel, sizeForDrawing });
+    m_frameCommitQueue.append({ index, subsamplingLevel, sizeForDrawing });
     return true;
 }
 
 bool ImageFrameCache::isAsyncDecodingQueueIdle() const
 {
-    for (const ImageFrame& frame : m_frames) {
-        if (frame.isBeingDecoded())
-            return false;
-    }
-    return true;
+    return m_frameCommitQueue.isEmpty();
 }
     
 void ImageFrameCache::stopAsyncDecodingQueue()
@@ -349,22 +331,22 @@ void ImageFrameCache::stopAsyncDecodingQueue()
     if (!hasAsyncDecodingQueue())
         return;
     
+    std::for_each(m_frameCommitQueue.begin(), m_frameCommitQueue.end(), [this](const ImageFrameRequest& frameRequest) {
+        ImageFrame& frame = m_frames[frameRequest.index];
+        if (!frame.isEmpty())
+            frame.clear();
+    });
+
     m_frameRequestQueue.close();
+    m_frameCommitQueue.clear();
     m_decodingQueue = nullptr;
-
-    for (ImageFrame& frame : m_frames) {
-        if (frame.isBeingDecoded()) {
-            frame.clearSizeForDecoding();
-            frame.clear();
-        }
-    }
 }
 
-const ImageFrame& ImageFrameCache::frameAtIndexCacheIfNeeded(size_t index, ImageFrame::Caching caching, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+const ImageFrame& ImageFrameCache::frameAtIndexCacheIfNeeded(size_t index, ImageFrame::Caching caching, const std::optional<SubsamplingLevel>& subsamplingLevel)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
-    if (!isDecoderAvailable() || frame.isBeingDecoded(sizeForDrawing))
+    if (!isDecoderAvailable() || frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(index, DecodingMode::Asynchronous))
         return frame;
     
     SubsamplingLevel subsamplingLevelValue = subsamplingLevel ? subsamplingLevel.value() : frame.subsamplingLevel();
@@ -374,16 +356,17 @@ const ImageFrame& ImageFrameCache::frameAtIndexCacheIfNeeded(size_t index, Image
         // Retrieve the metadata from ImageDecoder if the ImageFrame isn't complete.
         if (frame.isComplete())
             break;
-        setFrameMetadataAtIndex(index, subsamplingLevelValue, frame.sizeForDrawing());
+        cacheFrameMetadataAtIndex(index, subsamplingLevelValue);
         break;
             
     case ImageFrame::Caching::MetadataAndImage:
         // Cache the image and retrieve the metadata from ImageDecoder only if there was not valid image stored.
-        if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing))
+        if (frame.hasFullSizeNativeImage(subsamplingLevel))
             break;
-        // We have to perform synchronous image decoding in this code path regardless of the sizeForDrawing value.
-        // So pass an empty sizeForDrawing to create an ImageFrame with the native size.
-        replaceFrameNativeImageAtIndex(m_decoder->createFrameImageAtIndex(index, subsamplingLevelValue, { }), index, subsamplingLevelValue, { });
+        // We have to perform synchronous image decoding in this code. 
+        NativeImagePtr nativeImage = m_decoder->createFrameImageAtIndex(index, subsamplingLevelValue);
+        // Clean the old native image and set a new one.
+        cacheFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevelValue, DecodingMode::Synchronous);
         break;
     }
 
@@ -481,12 +464,21 @@ IntSize ImageFrameCache::sizeRespectingOrientation()
 
 Color ImageFrameCache::singlePixelSolidColor()
 {
-    return frameCount() == 1 ? frameMetadataAtIndexCacheIfNeeded<Color>(0, (&ImageFrame::singlePixelSolidColor), &m_singlePixelSolidColor, ImageFrame::Caching::MetadataAndImage) : Color();
+    if (!m_singlePixelSolidColor && (size() != IntSize(1, 1) || frameCount() != 1))
+        m_singlePixelSolidColor = Color();
+
+    if (m_singlePixelSolidColor)
+        return m_singlePixelSolidColor.value();
+
+    return frameMetadataAtIndexCacheIfNeeded<Color>(0, (&ImageFrame::singlePixelSolidColor), &m_singlePixelSolidColor, ImageFrame::Caching::MetadataAndImage);
 }
 
-bool ImageFrameCache::frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing)
+bool ImageFrameCache::frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t index, const DecodingOptions& decodingOptions)
 {
-    return frameMetadataAtIndex<bool>(index, (&ImageFrame::isBeingDecoded), sizeForDrawing);
+    auto it = std::find_if(m_frameCommitQueue.begin(), m_frameCommitQueue.end(), [index, &decodingOptions](const ImageFrameRequest& frameRequest) {
+        return frameRequest.index == index && frameRequest.decodingOptions.isAsynchronousCompatibleWith(decodingOptions);
+    });
+    return it != m_frameCommitQueue.end();
 }
 
 bool ImageFrameCache::frameIsCompleteAtIndex(size_t index)
@@ -499,21 +491,16 @@ bool ImageFrameCache::frameHasAlphaAtIndex(size_t index)
     return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasAlpha));
 }
 
-bool ImageFrameCache::frameHasImageAtIndex(size_t index)
+bool ImageFrameCache::frameHasFullSizeNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel)
 {
-    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasNativeImage));
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasFullSizeNativeImage), subsamplingLevel);
 }
 
-bool ImageFrameCache::frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+bool ImageFrameCache::frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const DecodingOptions& decodingOptions)
 {
-    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasValidNativeImage), subsamplingLevel, sizeForDrawing);
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasDecodedNativeImageCompatibleWithOptions), subsamplingLevel, decodingOptions);
 }
     
-bool ImageFrameCache::frameHasDecodedNativeImage(size_t index)
-{
-    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasDecodedNativeImage));
-}
-
 SubsamplingLevel ImageFrameCache::frameSubsamplingLevelAtIndex(size_t index)
 {
     return frameMetadataAtIndex<SubsamplingLevel>(index, (&ImageFrame::subsamplingLevel));
@@ -539,9 +526,14 @@ ImageOrientation ImageFrameCache::frameOrientationAtIndex(size_t index)
     return frameMetadataAtIndexCacheIfNeeded<ImageOrientation>(index, (&ImageFrame::orientation), nullptr, ImageFrame::Caching::Metadata);
 }
 
-NativeImagePtr ImageFrameCache::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+NativeImagePtr ImageFrameCache::frameImageAtIndex(size_t index)
+{
+    return frameMetadataAtIndex<NativeImagePtr>(index, (&ImageFrame::nativeImage));
+}
+
+NativeImagePtr ImageFrameCache::frameImageAtIndexCacheIfNeeded(size_t index, SubsamplingLevel subsamplingLevel)
 {
-    return frameMetadataAtIndexCacheIfNeeded<NativeImagePtr>(index, (&ImageFrame::nativeImage), nullptr, ImageFrame::Caching::MetadataAndImage, subsamplingLevel, sizeForDrawing);
+    return frameMetadataAtIndexCacheIfNeeded<NativeImagePtr>(index, (&ImageFrame::nativeImage), nullptr, ImageFrame::Caching::MetadataAndImage, subsamplingLevel);
 }
 
 }
index bdd7bae..d831792 100644 (file)
@@ -69,7 +69,7 @@ public:
     
     // Asynchronous image decoding
     void startAsyncDecodingQueue();
-    bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel, const IntSize&);
+    bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel, const std::optional<IntSize>&);
     void stopAsyncDecodingQueue();
     bool hasAsyncDecodingQueue() const { return m_decodingQueue; }
     bool isAsyncDecodingQueueIdle() const;
@@ -89,12 +89,12 @@ public:
     Color singlePixelSolidColor();
 
     // ImageFrame metadata which does not require caching the ImageFrame.
-    bool frameIsBeingDecodedAtIndex(size_t, const std::optional<IntSize>& sizeForDrawing);
+    bool frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t, const DecodingOptions&);
     bool frameIsCompleteAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t);
     bool frameHasImageAtIndex(size_t);
-    bool frameHasValidNativeImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing);
-    bool frameHasDecodedNativeImage(size_t);
+    bool frameHasFullSizeNativeImageAtIndex(size_t, const std::optional<SubsamplingLevel>&);
+    bool frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(size_t, const std::optional<SubsamplingLevel>&, const DecodingOptions&);
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t);
     
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
@@ -102,7 +102,9 @@ public:
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
     float frameDurationAtIndex(size_t);
     ImageOrientation frameOrientationAtIndex(size_t);
-    NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing);
+
+    NativeImagePtr frameImageAtIndex(size_t);
+    NativeImagePtr frameImageAtIndexCacheIfNeeded(size_t, SubsamplingLevel);
 
 private:
     ImageFrameCache(Image*);
@@ -126,14 +128,13 @@ private:
     void decodedSizeReset(unsigned decodedSize);
 
     void setNativeImage(NativeImagePtr&&);
-    void setFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing);
-    void setFrameMetadataAtIndex(size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing);
-    void replaceFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const std::optional<IntSize>& sizeForDrawing);
-    void cacheFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const IntSize& sizeForDrawing);
+    void cacheFrameMetadataAtIndex(size_t, SubsamplingLevel);
+    void cacheFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&);
+    void cacheAsyncFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel, const DecodingOptions&);
 
     Ref<WorkQueue> decodingQueue();
 
-    const ImageFrame& frameAtIndexCacheIfNeeded(size_t, ImageFrame::Caching, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { });
+    const ImageFrame& frameAtIndexCacheIfNeeded(size_t, ImageFrame::Caching, const std::optional<SubsamplingLevel>& = { });
 
     Image* m_image { nullptr };
     RefPtr<ImageDecoder> m_decoder;
@@ -146,11 +147,17 @@ private:
     struct ImageFrameRequest {
         size_t index;
         SubsamplingLevel subsamplingLevel;
-        IntSize sizeForDrawing;
+        DecodingOptions decodingOptions;
+        bool operator==(const ImageFrameRequest& other) const
+        {
+            return index == other.index && subsamplingLevel == other.subsamplingLevel && decodingOptions == other.decodingOptions;
+        }
     };
     static const int BufferSize = 8;
     using FrameRequestQueue = SynchronizedFixedQueue<ImageFrameRequest, BufferSize>;
+    using FrameCommitQueue = Deque<ImageFrameRequest, BufferSize>;
     FrameRequestQueue m_frameRequestQueue;
+    FrameCommitQueue m_frameCommitQueue;
     RefPtr<WorkQueue> m_decodingQueue;
 
     // Image metadata.
index c83eca7..2219169 100644 (file)
@@ -191,10 +191,10 @@ NativeImagePtr ImageSource::createFrameImageAtIndex(size_t index, SubsamplingLev
     return isDecoderAvailable() ? m_decoder->createFrameImageAtIndex(index, subsamplingLevel) : nullptr;
 }
 
-NativeImagePtr ImageSource::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext)
+NativeImagePtr ImageSource::frameImageAtIndexCacheIfNeeded(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
 {
     setDecoderTargetContext(targetContext);
-    return m_frameCache->frameImageAtIndex(index, subsamplingLevel, sizeForDrawing);
+    return m_frameCache->frameImageAtIndexCacheIfNeeded(index, subsamplingLevel);
 }
 
 void ImageSource::dump(TextStream& ts)
index b347356..7e03995 100644 (file)
@@ -69,7 +69,7 @@ public:
     bool isAllDataReceived();
 
     bool shouldUseAsyncDecoding();
-    bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing) { return m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel, sizeForDrawing); }
+    bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing = { }) { return m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel, sizeForDrawing); }
     bool hasAsyncDecodingQueue() const { return m_frameCache->hasAsyncDecodingQueue(); }
     bool isAsyncDecodingQueueIdle() const  { return m_frameCache->isAsyncDecodingQueueIdle(); }
     void stopAsyncDecodingQueue() { m_frameCache->stopAsyncDecodingQueue(); }
@@ -87,12 +87,12 @@ public:
     Color singlePixelSolidColor() { return m_frameCache->singlePixelSolidColor(); }
 
     // ImageFrame metadata which does not require caching the ImageFrame.
-    bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameIsBeingDecodedAtIndex(index, sizeForDrawing); }
+    bool frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(size_t index, const DecodingOptions& decodingOptions) { return m_frameCache->frameIsBeingDecodedAndIsCompatibleWithOptionsAtIndex(index, decodingOptions); }
     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, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); }
-    bool frameHasDecodedNativeImage(size_t index) { return m_frameCache->frameHasDecodedNativeImage(index); }
+    bool frameHasFullSizeNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel) { return m_frameCache->frameHasFullSizeNativeImageAtIndex(index, subsamplingLevel); }
+    bool frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const DecodingOptions& decodingOptions) { return m_frameCache->frameHasDecodedNativeImageCompatibleWithOptionsAtIndex(index, subsamplingLevel, decodingOptions); }
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) { return m_frameCache->frameSubsamplingLevelAtIndex(index); }
 
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
@@ -100,7 +100,9 @@ public:
     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, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* targetContext = nullptr);
+
+    NativeImagePtr frameImageAtIndex(size_t index) { return m_frameCache->frameImageAtIndex(index); }
+    NativeImagePtr frameImageAtIndexCacheIfNeeded(size_t, SubsamplingLevel = SubsamplingLevel::Default, const GraphicsContext* = nullptr);
 
     SubsamplingLevel maximumSubsamplingLevel();
     SubsamplingLevel subsamplingLevelForScale(float);
index 591b4dd..7a56b5a 100644 (file)
@@ -40,7 +40,7 @@ NamedImageGeneratedImage::NamedImageGeneratedImage(String name, const FloatSize&
     setContainerSize(size);
 }
 
-void NamedImageGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+void NamedImageGeneratedImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, DecodingMode, ImageOrientationDescription)
 {
 #if USE(NEW_THEME) || PLATFORM(IOS)
     GraphicsContextStateSaver stateSaver(context);
index 1bac56a..4858d52 100644 (file)
@@ -40,7 +40,7 @@ public:
     }
 
 protected:
-    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) override;
     void drawPattern(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode) override;
 
     NamedImageGeneratedImage(String name, const FloatSize&);
index 89a3093..74768e3 100644 (file)
@@ -283,7 +283,7 @@ void ImageBuffer::draw(GraphicsContext& destinationContext, const FloatRect& des
 {
     BackingStoreCopy copyMode = &destinationContext == &context() ? CopyBackingStore : DontCopyBackingStore;
     RefPtr<Image> image = copyImage(copyMode);
-    destinationContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, ImageOrientationDescription()));
+    destinationContext.drawImage(*image, destRect, srcRect, ImagePaintingOptions(op, blendMode, DecodingMode::Synchronous, ImageOrientationDescription()));
 }
 
 void ImageBuffer::drawPattern(GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform& patternTransform,
index 9bc7ac8..a58bab9 100644 (file)
@@ -366,34 +366,38 @@ unsigned ImageDecoder::frameBytesAtIndex(size_t index, SubsamplingLevel subsampl
     return (frameSize.area() * 4).unsafeGet();
 }
 
-NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const
+NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions) const
 {
     LOG(Images, "ImageDecoder %p createFrameImageAtIndex %lu", this, index);
     RetainPtr<CFDictionaryRef> options;
     RetainPtr<CGImageRef> image;
 
-    if (!sizeForDrawing) {
-        // Decode an image synchronously for its native size.
-        options = imageSourceOptions(subsamplingLevel);
-        image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
-    } else {
-        // CGImageSourceCreateThumbnailAtIndex() returns a CGImage with the image native size
-        // regardless of the subsamplingLevel unless kCGImageSourceSubsampleFactor is passed.
-        // Here we are trying to see which size is smaller: the image native size or the
-        // sizeForDrawing. If we want a CGImage with the image native size, sizeForDrawing will
-        // not passed. So we need to get the image native size with the default subsampling and
-        // then compare it with sizeForDrawing.
-        IntSize size = frameSizeAtIndex(index, SubsamplingLevel::Default);
-
-        if (size.unclampedArea() < sizeForDrawing.value().unclampedArea()) {
-            // Decode an image asynchronously for its native size.
+    if (!decodingOptions.isSynchronous()) {
+        if (decodingOptions.hasSizeForDrawing()) {
+            // CGImageSourceCreateThumbnailAtIndex() returns a CGImage with the image native size
+            // regardless of the subsamplingLevel unless kCGImageSourceSubsampleFactor is passed.
+            // Here we are trying to see which size is smaller: the image native size or the
+            // sizeForDrawing. If we want a CGImage with the image native size, sizeForDrawing will
+            // not passed. So we need to get the image native size with the default subsampling and
+            // then compare it with sizeForDrawing.
+            IntSize size = frameSizeAtIndex(index, SubsamplingLevel::Default);
+            std::optional<IntSize> sizeForDrawing = decodingOptions.sizeForDrawing();
+
+            if (size.unclampedArea() < sizeForDrawing.value().unclampedArea()) {
+                // Decode an image asynchronously for its native size.
+                options = imageSourceAsyncOptions(subsamplingLevel);
+            } else {
+                // Decode an image asynchronously for sizeForDrawing since it is smaller than the image native size.
+                options = imageSourceAsyncOptions(subsamplingLevel, sizeForDrawing);
+            }
+        } else
             options = imageSourceAsyncOptions(subsamplingLevel);
-        } else {
-            // Decode an image asynchronously for sizeForDrawing since it is smaller than the image native size.
-            options = imageSourceAsyncOptions(subsamplingLevel, sizeForDrawing);
-        }
         
         image = adoptCF(CGImageSourceCreateThumbnailAtIndex(m_nativeDecoder.get(), index, options.get()));
+    } else {
+        // Decode an image synchronously for its native size.
+        options = imageSourceOptions(subsamplingLevel);
+        image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
     }
     
 #if PLATFORM(IOS)
index c0071a4..53df266 100644 (file)
@@ -62,7 +62,7 @@ public:
     bool frameAllowSubsamplingAtIndex(size_t) const;
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default) const;
     
-    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDrawing = { }) const;
+    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const DecodingOptions& = DecodingMode::Synchronous) const;
     
     void setData(SharedBuffer&, bool allDataReceived);
     bool isAllDataReceived() const { return m_isAllDataReceived; }
index 0ad50fd..795e4e5 100644 (file)
@@ -263,7 +263,7 @@ void PDFDocumentImage::updateCachedImageIfNeeded(GraphicsContext& context, const
     decodedSizeChanged(internalSize.unclampedArea() * 4);
 }
 
-void PDFDocumentImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator op, BlendMode, ImageOrientationDescription)
+void PDFDocumentImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator op, BlendMode, DecodingMode, ImageOrientationDescription)
 {
     if (!m_document || !m_hasPage)
         return;
index fb9fb0a..47b9c94 100644 (file)
@@ -72,7 +72,7 @@ private:
     void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio) override;
     FloatSize size() const override;
 
-    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientationDescription) override;
+    void draw(GraphicsContext&, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) override;
 
     // FIXME: Implement this to be less conservative.
     bool currentFrameKnownToBeOpaque() const override { return false; }
index d11253a..7e4a0b2 100644 (file)
@@ -80,7 +80,7 @@ bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, const IntSize* size)
     if (size)
         drawFrameMatchingSourceSize(gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), *size, CompositeCopy);
     else
-        draw(gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), CompositeCopy, BlendModeNormal, ImageOrientationDescription());
+        draw(gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), CompositeCopy, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
 
     // Do cleanup
     CGContextRelease(cgContext);
@@ -96,7 +96,7 @@ void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext& ctxt, const Float
         if (image && CGImageGetHeight(image) == static_cast<size_t>(srcSize.height()) && CGImageGetWidth(image) == static_cast<size_t>(srcSize.width())) {
             size_t currentFrame = m_currentFrame;
             m_currentFrame = i;
-            draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, srcSize.width(), srcSize.height()), compositeOp, BlendModeNormal, ImageOrientationDescription());
+            draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, srcSize.width(), srcSize.height()), compositeOp, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
             m_currentFrame = currentFrame;
             return;
         }
@@ -104,7 +104,7 @@ void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext& ctxt, const Float
 
     // No image of the correct size was found, fallback to drawing the current frame
     FloatSize imageSize = BitmapImage::size();
-    draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), compositeOp, BlendModeNormal, ImageOrientationDescription());
+    draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), compositeOp, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
 }
 
 } // namespace WebCore
index 568792f..73f1d30 100644 (file)
@@ -180,7 +180,7 @@ void ImageDecoder::setTargetContext(ID2D1RenderTarget* renderTarget)
     m_renderTarget = renderTarget;
 }
 
-NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>&) const
+NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions&) const
 {
     if (!m_nativeDecoder || !m_renderTarget)
         return nullptr;
index 301ffad..5fe0f83 100644 (file)
@@ -67,7 +67,7 @@ public:
     bool frameAllowSubsamplingAtIndex(size_t) const;
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default) const;
     
-    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDraw = { }) const;
+    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const DecodingOptions& = DecodingMode::Synchronous) const;
     
     void setData(SharedBuffer&, bool allDataReceived);
     bool isAllDataReceived() const { return m_isAllDataReceived; }
index ad6e402..baf9e46 100644 (file)
@@ -207,7 +207,7 @@ float ImageDecoder::frameDurationAtIndex(size_t index)
     return duration;
 }
 
-NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, const std::optional<IntSize>&)
+NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, const DecodingOptions&)
 {
     // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail.
     if (size().isEmpty())
index 758987c..58162f1 100644 (file)
@@ -136,7 +136,7 @@ namespace WebCore {
         
         float frameDurationAtIndex(size_t);
         
-        NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDraw = { });
+        NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const DecodingOptions& = DecodingMode::Synchronous);
 
         void setIgnoreGammaAndColorProfile(bool flag) { m_ignoreGammaAndColorProfile = flag; }
         bool ignoresGammaAndColorProfile() const { return m_ignoreGammaAndColorProfile; }
index f487ac9..a063e18 100644 (file)
@@ -878,7 +878,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
             context.setDrawLuminanceMask(bgLayer.maskSourceType() == MaskLuminance);
 
             auto interpolation = chooseInterpolationQuality(context, *image, &bgLayer, geometry.tileSize());
-            context.drawTiledImage(*image, geometry.destRect(), toLayoutPoint(geometry.relativePhase()), geometry.tileSize(), geometry.spaceSize(), ImagePaintingOptions(compositeOp, bgLayer.blendMode(), ImageOrientationDescription(), interpolation));
+            context.drawTiledImage(*image, geometry.destRect(), toLayoutPoint(geometry.relativePhase()), geometry.tileSize(), geometry.spaceSize(), ImagePaintingOptions(compositeOp, bgLayer.blendMode(), DecodingMode::Asynchronous, ImageOrientationDescription(), interpolation));
         }
     }
 
index 73aa530..ffb2112 100644 (file)
@@ -581,7 +581,7 @@ void RenderImage::paintIntoRect(GraphicsContext& context, const FloatRect& rect)
 #endif
 
     ImageOrientationDescription orientationDescription(shouldRespectImageOrientation(), style().imageOrientation());
-    context.drawImage(*img, rect, ImagePaintingOptions(compositeOperator, BlendModeNormal, orientationDescription, interpolation));
+    context.drawImage(*img, rect, ImagePaintingOptions(compositeOperator, BlendModeNormal, DecodingMode::Asynchronous, orientationDescription, interpolation));
 }
 
 bool RenderImage::boxShadowShouldBeAppliedToBackground(const LayoutPoint& paintOffset, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox*) const
index 746113c..6884a1b 100644 (file)
@@ -188,7 +188,7 @@ void SVGImage::drawForContainer(GraphicsContext& context, const FloatSize contai
     adjustedSrcSize.scale(roundedContainerSize.width() / containerSize.width(), roundedContainerSize.height() / containerSize.height());
     scaledSrc.setSize(adjustedSrcSize);
 
-    draw(context, dstRect, scaledSrc, compositeOp, blendMode, ImageOrientationDescription());
+    draw(context, dstRect, scaledSrc, compositeOp, blendMode, DecodingMode::Synchronous, ImageOrientationDescription());
 
     setImageObserver(observer);
 }
@@ -206,7 +206,7 @@ NativeImagePtr SVGImage::nativeImageForCurrentFrame(const GraphicsContext*)
     if (!buffer) // failed to allocate image
         return nullptr;
 
-    draw(buffer->context(), rect(), rect(), CompositeSourceOver, BlendModeNormal, ImageOrientationDescription());
+    draw(buffer->context(), rect(), rect(), CompositeSourceOver, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
 
     // FIXME: WK(Bug 113657): We should use DontCopyBackingStore here.
     return buffer->copyImage(CopyBackingStore)->nativeImageForCurrentFrame();
@@ -230,7 +230,7 @@ NativeImagePtr SVGImage::nativeImage(const GraphicsContext* targetContext)
 
     GraphicsContext localContext(nativeImageTarget.get());
 
-    draw(localContext, rect(), rect(), CompositeSourceOver, BlendModeNormal, ImageOrientationDescription());
+    draw(localContext, rect(), rect(), CompositeSourceOver, BlendModeNormal, DecodingMode::Synchronous, ImageOrientationDescription());
 
     COMPtr<ID2D1Bitmap> nativeImage;
     hr = nativeImageTarget->GetBitmap(&nativeImage);
@@ -276,7 +276,7 @@ void SVGImage::drawPatternForContainer(GraphicsContext& context, const FloatSize
     image->drawPattern(context, dstRect, scaledSrcRect, unscaledPatternTransform, phase, spacing, compositeOp, blendMode);
 }
 
-void SVGImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+void SVGImage::draw(GraphicsContext& context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, DecodingMode, ImageOrientationDescription)
 {
     if (!m_page)
         return;
index 13f9ed8..844c910 100644 (file)
@@ -96,7 +96,7 @@ private:
     void dump(TextStream&) const final;
 
     SVGImage(ImageObserver&, const URL&);
-    void draw(GraphicsContext&, const FloatRect& fromRect, const FloatRect& toRect, CompositeOperator, BlendMode, ImageOrientationDescription) final;
+    void draw(GraphicsContext&, const FloatRect& fromRect, const FloatRect& toRect, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) final;
     void drawForContainer(GraphicsContext&, const FloatSize, float, const FloatRect&, const FloatRect&, CompositeOperator, BlendMode);
     void drawPatternForContainer(GraphicsContext&, const FloatSize& containerSize, float zoom, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing,
         CompositeOperator, const FloatRect&, BlendMode);
index 482c394..4fb1bff 100644 (file)
@@ -35,7 +35,7 @@ FloatSize SVGImageForContainer::size() const
 }
 
 void SVGImageForContainer::draw(GraphicsContext& context, const FloatRect& dstRect,
-    const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription)
+    const FloatRect& srcRect, CompositeOperator compositeOp, BlendMode blendMode, DecodingMode, ImageOrientationDescription)
 {
     m_image->drawForContainer(context, m_containerSize, m_zoom, dstRect, srcRect, compositeOp, blendMode);
 }
index aa1988a..83c1e25 100644 (file)
@@ -55,7 +55,7 @@ public:
         m_image->computeIntrinsicDimensions(intrinsicWidth, intrinsicHeight, intrinsicRatio);
     }
 
-    void draw(GraphicsContext&, const FloatRect&, const FloatRect&, CompositeOperator, BlendMode, ImageOrientationDescription) final;
+    void draw(GraphicsContext&, const FloatRect&, const FloatRect&, CompositeOperator, BlendMode, DecodingMode, ImageOrientationDescription) final;
 
     void drawPattern(GraphicsContext&, const FloatRect&, const FloatRect&, const AffineTransform&, const FloatPoint&, const FloatSize&, CompositeOperator, BlendMode) final;