Title: [213563] trunk/Source/WebCore
Revision
213563
Author
[email protected]
Date
2017-03-07 19:54:28 -0800 (Tue, 07 Mar 2017)

Log Message

Asynchronous image decoding should consider the drawing size if it is smaller than the size of the image
https://bugs.webkit.org/show_bug.cgi?id=168814

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

If the image destinationRect.size() is smaller than the imageSourceSize
(e.g. 3000x3000 pixels), CGImageSourceCreateThumbnailAtIndex() is slower
than CGImageSourceCreateImageAtIndex() in decoding this image. To overcome
this problem, the entry (kCGImageSourceThumbnailMaxPixelSize,
max(destinationRect.width, destinationRect.height)) is added to the options
dictionary when calling CGImageSourceCreateThumbnailAtIndex(). This will
avoid copying a large block of memory for the unscaled bitmap image.

An argument named 'sizeForDrawing' of type std::optional<IntSize> will be passed
all the way from BitmapImage to ImageDecoder. If bool(sizeForDrawing) equals
true that means we want async image decoding. Otherwise the image will be decoded
synchronously.

The subsamplingLevel argument will be passed as std::optional<SubsamplingLevel>.
to ImageFrame query functions. When combined with sizeForDrawing, the meaning of
these two arguments will be the following:
-- !bool(subsamplingLevel): No caching is required. return what is stored in ImageFrameCache.
-- bool(subsamplingLevel) && !bool(sizeForDrawing): Match subsamplingLevel only. Recache if it's different.
-- bool(subsamplingLevel) && bool(sizeForDrawing): Match both both. . Recache if one of them is different.

We are going to allow decoding the same ImageFrame for different sizeForDrawings.
The rule is a new decoding is allowed only if the maxPixelSize(sizeForDrawing) of
the last requested image decoding is less than the new request sizeForDrawing.

* loader/cache/CachedImage.h: Add a helper function which returns the URL of a CachedImage.

* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::frameImageAtIndex): Add a new argument for sizeForDrawing.
(WebCore::BitmapImage::nativeImage): Pass an empty sizeForDrawing to frameImageAtIndex(). We an image with the native size.
(WebCore::BitmapImage::nativeImageForCurrentFrame): Ditto.
(WebCore::BitmapImage::nativeImageOfSize): Ditto.
(WebCore::BitmapImage::draw): Pass the destRect.size() to internalStartAnimation().
(WebCore::BitmapImage::isAsyncDecodingRequired): A helper function to answer the question
whether the async image decoding is required. It takes into account the animated images, the
large image, and the image size.
(WebCore::BitmapImage::internalStartAnimation):  If async image decoding is requested for this frame m_sizeForDraw
will be set. If internalStartAnimation() is called from startAnimation(), sizeForDraw will be empty. In this
case no async image decoding will be requested. This happens only when startAnimation() is called from outside
BitmapImage::draw().
(WebCore::BitmapImage::advanceAnimation): Change the log message.
(WebCore::BitmapImage::newFrameNativeImageAvailableAtIndex): Ditto.
* platform/graphics/BitmapImage.h:

* platform/graphics/ImageFrame.cpp:
(WebCore::ImageFrame::operator=):  Include m_sizeForDraw in the properties of ImageFrame.
(WebCore::maxPixelSize): Returns the maximum of the width() and the height of an IntSize.
(WebCore::ImageFrame::isBeingDecoded): Returns true if the ImageFrame is currently being decoded for a specific sizeForDrawing.
(WebCore::ImageFrame::hasValidNativeImage): Ditto.
* platform/graphics/ImageFrame.h:
(WebCore::ImageFrame::enqueueSizeForDecoding): Adds a new sizeForDrawing; this sets the ImageFrame is being decoded for this sizeForDrawing.
(WebCore::ImageFrame::dequeueSizeForDecoding): Removes the first sizeForDrawing was enqueued; this marks this ImageFrame has finished decoding for this sizeForDrawing.
(WebCore::ImageFrame::clearSizeForDecoding): Clears the sizeForDecoding queue. Marks the ImageFrame for not being decoded.
(WebCore::ImageFrame::isEmpty): Replace Decoding::Empty by Decoding::None.
(WebCore::ImageFrame::sizeForDrawing): Returns the ImageFrame sizeForDraw.
(WebCore::ImageFrame::hasDecodedNativeImage): Returns true if the ImageFrame doesn't need decoding before drawing.
(WebCore::ImageFrame::hasValidNativeImage): Deleted. Moved to the source file.

* platform/graphics/ImageFrameCache.cpp:
(WebCore::ImageFrameCache::setFrameNativeImageAtIndex): Take a new argument for sizeForDraw.
(WebCore::ImageFrameCache::setFrameMetadataAtIndex):  When sizeForDraw is set, use the decoder to get the image
frame size. Otherwise, get the size of the nativeImage.
(WebCore::ImageFrameCache::replaceFrameNativeImageAtIndex): Take a new argument for sizeForDraw.
(WebCore::ImageFrameCache::cacheFrameNativeImageAtIndex): Ditto.
(WebCore::ImageFrameCache::startAsyncDecodingQueue): Pass the sizeForDraw as a new property of the ImageFrame.
(WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Store sizeForDraw in ImageFrameRequest. Delete unneeded check.
This function always receives a valid subsamplingLevel.
(WebCore::ImageFrameCache::stopAsyncDecodingQueue): Marks all the queued ImageFrames for not being decoded.
(WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded): Take a new argument for sizeForDraw. If this function fixes the
properties of ImageFrame properties, keep the old sizeForDraw and/or subsamplingLevel. If a new frame is
decoded, no async image decoding will be done in this code path. So pass an empty std::optional<IntSize> to
ImageDecoder::createFrameImageAtIndex() and store std::optional<IntSize> in ImageFrame.
(WebCore::ImageFrameCache::frameMetadataAtIndex): A new helper function which takes a variable number of arguments which
will be passed to the (ImageFrame::*functor).
(WebCore::ImageFrameCache::frameMetadataAtIndexCacheIfNeeded): Make this function takes a variable number of arguments which
will be passed to the frameAtIndexCacheIfNeeded().
(WebCore::ImageFrameCache::size): Pass an Metadata, valid SubsamplingLevel and empty sizeForDraw to frameMetadataAtIndexCacheIfNeeded().
(WebCore::ImageFrameCache::sizeRespectingOrientation): Ditto.
(WebCore::ImageFrameCache::singlePixelSolidColor): Pass MetadataAndImage, empty SubsamplingLevel and empty sizeForDraw to
frameMetadataAtIndexCacheIfNeeded(); we can use the current frame image regardless of its size.
(WebCore::ImageFrameCache::frameIsBeingDecodedAtIndex): Pass the ImageFrame method as a function argument instead of
passing it as a template argument.
(WebCore::ImageFrameCache::frameIsCompleteAtIndex): Ditto.
(WebCore::ImageFrameCache::frameHasAlphaAtIndex): Ditto.
(WebCore::ImageFrameCache::frameHasImageAtIndex): Ditto.
(WebCore::ImageFrameCache::frameHasValidNativeImageAtIndex): Pass subsamplingLevel and sizeForDrawing to frameMetadataAtIndex().
(WebCore::ImageFrameCache::frameHasDecodedNativeImage): New helper function to answer the question whether an ImageFrame will need
decoding when drawing or not.
(WebCore::ImageFrameCache::frameSubsamplingLevelAtIndex):  Pass the ImageFrame method as a function argument instead of
passing it as a template argument.
(WebCore::ImageFrameCache::frameSizeAtIndex): Ditto.
(WebCore::ImageFrameCache::frameBytesAtIndex): Ditto.
(WebCore::ImageFrameCache::frameDurationAtIndex): Ditto.
(WebCore::ImageFrameCache::frameOrientationAtIndex):
(WebCore::ImageFrameCache::frameImageAtIndex): Ditto.
(WebCore::ImageFrameCache::frameAtIndex): Deleted. Renamed to frameAtIndexCacheIfNeeded().
* platform/graphics/ImageFrameCache.h:
(WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded):

* platform/graphics/ImageObserver.h: Define a virtual function for image sourceUrl().

* platform/graphics/ImageSource.cpp:
(WebCore::ImageSource::frameImageAtIndex): Take a new argument for sizeForDrawing.
* platform/graphics/ImageSource.h:
(WebCore::ImageSource::requestFrameAsyncDecodingAtIndex): Take a new argument for sizeForDrawing.
(WebCore::ImageSource::frameHasValidNativeImageAtIndex): Ditto.
(WebCore::ImageSource::frameHasDecodedNativeImage): New helper function.
(WebCore::ImageSource::frameImageAtIndex): Ditto.

* platform/graphics/cg/ImageDecoderCG.cpp:
(WebCore::createImageSourceOptions): Create a dictionary with the basic image decoding options.
(WebCore::createImageSourceAsyncOptions): Create a dictionary with the basic asynchronous image decoding options.
(WebCore::appendImageSourceOption): Append the SubsamplingLevel or the MaxPixelSize option to an CGImageSource options dictionary.
(WebCore::appendImageSourceOptions): Append the SubsamplingLevel and the MaxPixelSize option to an CGImageSource options dictionary.
(WebCore::imageSourceOptions): Creates a dictionary for the synchronous image decoding options.
(WebCore::imageSourceAsyncOptions): Creates a dictionary for the asynchronous image decoding options.
(WebCore::ImageDecoder::createFrameImageAtIndex): Replace the DecodingMode argument by an std::optional<IntSize>.
* platform/graphics/cg/ImageDecoderCG.h: Change a prototype.

* platform/graphics/win/ImageDecoderDirect2D.cpp:
(WebCore::ImageDecoder::createFrameImageAtIndex): Replace the DecodingMode argument by an std::optional<IntSize>.
* platform/graphics/win/ImageDecoderDirect2D.h: Change a prototype.

* platform/image-decoders/ImageDecoder.cpp:
(WebCore::ImageDecoder::createFrameImageAtIndex): Replace the DecodingMode argument by an std::optional<IntSize>.
* platform/image-decoders/ImageDecoder.h: Change a prototype.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (213562 => 213563)


--- trunk/Source/WebCore/ChangeLog	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/ChangeLog	2017-03-08 03:54:28 UTC (rev 213563)
@@ -1,3 +1,136 @@
+2017-03-07  Said Abou-Hallawa  <[email protected]>
+
+        Asynchronous image decoding should consider the drawing size if it is smaller than the size of the image
+        https://bugs.webkit.org/show_bug.cgi?id=168814
+
+        Reviewed by Simon Fraser.
+
+        If the image destinationRect.size() is smaller than the imageSourceSize
+        (e.g. 3000x3000 pixels), CGImageSourceCreateThumbnailAtIndex() is slower
+        than CGImageSourceCreateImageAtIndex() in decoding this image. To overcome
+        this problem, the entry (kCGImageSourceThumbnailMaxPixelSize,
+        max(destinationRect.width, destinationRect.height)) is added to the options
+        dictionary when calling CGImageSourceCreateThumbnailAtIndex(). This will
+        avoid copying a large block of memory for the unscaled bitmap image.
+
+        An argument named 'sizeForDrawing' of type std::optional<IntSize> will be passed
+        all the way from BitmapImage to ImageDecoder. If bool(sizeForDrawing) equals
+        true that means we want async image decoding. Otherwise the image will be decoded
+        synchronously.
+
+        The subsamplingLevel argument will be passed as std::optional<SubsamplingLevel>.
+        to ImageFrame query functions. When combined with sizeForDrawing, the meaning of
+        these two arguments will be the following:
+        -- !bool(subsamplingLevel): No caching is required. return what is stored in ImageFrameCache.
+        -- bool(subsamplingLevel) && !bool(sizeForDrawing): Match subsamplingLevel only. Recache if it's different.
+        -- bool(subsamplingLevel) && bool(sizeForDrawing): Match both both. . Recache if one of them is different.
+
+        We are going to allow decoding the same ImageFrame for different sizeForDrawings.
+        The rule is a new decoding is allowed only if the maxPixelSize(sizeForDrawing) of
+        the last requested image decoding is less than the new request sizeForDrawing.
+
+        * loader/cache/CachedImage.h: Add a helper function which returns the URL of a CachedImage.
+
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::frameImageAtIndex): Add a new argument for sizeForDrawing.
+        (WebCore::BitmapImage::nativeImage): Pass an empty sizeForDrawing to frameImageAtIndex(). We an image with the native size.
+        (WebCore::BitmapImage::nativeImageForCurrentFrame): Ditto.
+        (WebCore::BitmapImage::nativeImageOfSize): Ditto.
+        (WebCore::BitmapImage::draw): Pass the destRect.size() to internalStartAnimation().
+        (WebCore::BitmapImage::isAsyncDecodingRequired): A helper function to answer the question
+        whether the async image decoding is required. It takes into account the animated images, the
+        large image, and the image size.
+        (WebCore::BitmapImage::internalStartAnimation):  If async image decoding is requested for this frame m_sizeForDraw
+        will be set. If internalStartAnimation() is called from startAnimation(), sizeForDraw will be empty. In this
+        case no async image decoding will be requested. This happens only when startAnimation() is called from outside
+        BitmapImage::draw().
+        (WebCore::BitmapImage::advanceAnimation): Change the log message.
+        (WebCore::BitmapImage::newFrameNativeImageAvailableAtIndex): Ditto.
+        * platform/graphics/BitmapImage.h:
+
+        * platform/graphics/ImageFrame.cpp:
+        (WebCore::ImageFrame::operator=):  Include m_sizeForDraw in the properties of ImageFrame.
+        (WebCore::maxPixelSize): Returns the maximum of the width() and the height of an IntSize.
+        (WebCore::ImageFrame::isBeingDecoded): Returns true if the ImageFrame is currently being decoded for a specific sizeForDrawing.
+        (WebCore::ImageFrame::hasValidNativeImage): Ditto.
+        * platform/graphics/ImageFrame.h:
+        (WebCore::ImageFrame::enqueueSizeForDecoding): Adds a new sizeForDrawing; this sets the ImageFrame is being decoded for this sizeForDrawing.
+        (WebCore::ImageFrame::dequeueSizeForDecoding): Removes the first sizeForDrawing was enqueued; this marks this ImageFrame has finished decoding for this sizeForDrawing.
+        (WebCore::ImageFrame::clearSizeForDecoding): Clears the sizeForDecoding queue. Marks the ImageFrame for not being decoded.
+        (WebCore::ImageFrame::isEmpty): Replace Decoding::Empty by Decoding::None.
+        (WebCore::ImageFrame::sizeForDrawing): Returns the ImageFrame sizeForDraw.
+        (WebCore::ImageFrame::hasDecodedNativeImage): Returns true if the ImageFrame doesn't need decoding before drawing.
+        (WebCore::ImageFrame::hasValidNativeImage): Deleted. Moved to the source file.
+
+        * platform/graphics/ImageFrameCache.cpp:
+        (WebCore::ImageFrameCache::setFrameNativeImageAtIndex): Take a new argument for sizeForDraw.
+        (WebCore::ImageFrameCache::setFrameMetadataAtIndex):  When sizeForDraw is set, use the decoder to get the image
+        frame size. Otherwise, get the size of the nativeImage.
+        (WebCore::ImageFrameCache::replaceFrameNativeImageAtIndex): Take a new argument for sizeForDraw.
+        (WebCore::ImageFrameCache::cacheFrameNativeImageAtIndex): Ditto.
+        (WebCore::ImageFrameCache::startAsyncDecodingQueue): Pass the sizeForDraw as a new property of the ImageFrame.
+        (WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Store sizeForDraw in ImageFrameRequest. Delete unneeded check.
+        This function always receives a valid subsamplingLevel. 
+        (WebCore::ImageFrameCache::stopAsyncDecodingQueue): Marks all the queued ImageFrames for not being decoded.
+        (WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded): Take a new argument for sizeForDraw. If this function fixes the
+        properties of ImageFrame properties, keep the old sizeForDraw and/or subsamplingLevel. If a new frame is 
+        decoded, no async image decoding will be done in this code path. So pass an empty std::optional<IntSize> to 
+        ImageDecoder::createFrameImageAtIndex() and store std::optional<IntSize> in ImageFrame.
+        (WebCore::ImageFrameCache::frameMetadataAtIndex): A new helper function which takes a variable number of arguments which
+        will be passed to the (ImageFrame::*functor).
+        (WebCore::ImageFrameCache::frameMetadataAtIndexCacheIfNeeded): Make this function takes a variable number of arguments which
+        will be passed to the frameAtIndexCacheIfNeeded().
+        (WebCore::ImageFrameCache::size): Pass an Metadata, valid SubsamplingLevel and empty sizeForDraw to frameMetadataAtIndexCacheIfNeeded().
+        (WebCore::ImageFrameCache::sizeRespectingOrientation): Ditto.
+        (WebCore::ImageFrameCache::singlePixelSolidColor): Pass MetadataAndImage, empty SubsamplingLevel and empty sizeForDraw to
+        frameMetadataAtIndexCacheIfNeeded(); we can use the current frame image regardless of its size.
+        (WebCore::ImageFrameCache::frameIsBeingDecodedAtIndex): Pass the ImageFrame method as a function argument instead of 
+        passing it as a template argument.
+        (WebCore::ImageFrameCache::frameIsCompleteAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameHasAlphaAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameHasImageAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameHasValidNativeImageAtIndex): Pass subsamplingLevel and sizeForDrawing to frameMetadataAtIndex().
+        (WebCore::ImageFrameCache::frameHasDecodedNativeImage): New helper function to answer the question whether an ImageFrame will need
+        decoding when drawing or not.
+        (WebCore::ImageFrameCache::frameSubsamplingLevelAtIndex):  Pass the ImageFrame method as a function argument instead of 
+        passing it as a template argument.
+        (WebCore::ImageFrameCache::frameSizeAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameBytesAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameDurationAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameOrientationAtIndex):
+        (WebCore::ImageFrameCache::frameImageAtIndex): Ditto.
+        (WebCore::ImageFrameCache::frameAtIndex): Deleted. Renamed to frameAtIndexCacheIfNeeded().
+        * platform/graphics/ImageFrameCache.h:
+        (WebCore::ImageFrameCache::frameAtIndexCacheIfNeeded):
+
+        * platform/graphics/ImageObserver.h: Define a virtual function for image sourceUrl().
+
+        * platform/graphics/ImageSource.cpp:
+        (WebCore::ImageSource::frameImageAtIndex): Take a new argument for sizeForDrawing.
+        * platform/graphics/ImageSource.h:
+        (WebCore::ImageSource::requestFrameAsyncDecodingAtIndex): Take a new argument for sizeForDrawing.
+        (WebCore::ImageSource::frameHasValidNativeImageAtIndex): Ditto.
+        (WebCore::ImageSource::frameHasDecodedNativeImage): New helper function.
+        (WebCore::ImageSource::frameImageAtIndex): Ditto.
+
+        * platform/graphics/cg/ImageDecoderCG.cpp:
+        (WebCore::createImageSourceOptions): Create a dictionary with the basic image decoding options.
+        (WebCore::createImageSourceAsyncOptions): Create a dictionary with the basic asynchronous image decoding options.
+        (WebCore::appendImageSourceOption): Append the SubsamplingLevel or the MaxPixelSize option to an CGImageSource options dictionary.
+        (WebCore::appendImageSourceOptions): Append the SubsamplingLevel and the MaxPixelSize option to an CGImageSource options dictionary.
+        (WebCore::imageSourceOptions): Creates a dictionary for the synchronous image decoding options.
+        (WebCore::imageSourceAsyncOptions): Creates a dictionary for the asynchronous image decoding options.
+        (WebCore::ImageDecoder::createFrameImageAtIndex): Replace the DecodingMode argument by an std::optional<IntSize>.
+        * platform/graphics/cg/ImageDecoderCG.h: Change a prototype.
+
+        * platform/graphics/win/ImageDecoderDirect2D.cpp: 
+        (WebCore::ImageDecoder::createFrameImageAtIndex): Replace the DecodingMode argument by an std::optional<IntSize>.
+        * platform/graphics/win/ImageDecoderDirect2D.h: Change a prototype.
+
+        * platform/image-decoders/ImageDecoder.cpp: 
+        (WebCore::ImageDecoder::createFrameImageAtIndex): Replace the DecodingMode argument by an std::optional<IntSize>.
+        * platform/image-decoders/ImageDecoder.h: Change a prototype.
+
 2017-03-07  Simon Fraser  <[email protected]>
 
         Parsing -webkit-hyphenate-character uses confusingly named consumeLocale()

Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (213562 => 213563)


--- trunk/Source/WebCore/loader/cache/CachedImage.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -126,6 +126,7 @@
         explicit CachedImageObserver(CachedImage&);
 
         // ImageObserver API
+        URL sourceUrl() const override { return m_cachedImages[0]->url(); }
         bool allowSubsampling() const final { return m_allowSubsampling; }
         bool allowLargeImageAsyncDecoding() const override { return m_allowLargeImageAsyncDecoding; }
         bool allowAnimatedImageAsyncDecoding() const override { return m_allowAnimatedImageAsyncDecoding; }

Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -100,24 +100,24 @@
     return m_source.dataChanged(data(), allDataReceived);
 }
 
-NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
+NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext)
 {
-    if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel)) {
-        LOG(Images, "BitmapImage %p %s - subsamplingLevel was %d, resampling", this, __FUNCTION__, static_cast<int>(frameSubsamplingLevelAtIndex(index)));
+    if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing)) {
+        LOG(Images, "BitmapImage::%s - %p - url: %s [subsamplingLevel was %d, resampling]", __FUNCTION__, this, sourceURL().characters8(), static_cast<int>(frameSubsamplingLevelAtIndex(index)));
         invalidatePlatformData();
     }
 
-    return m_source.frameImageAtIndex(index, subsamplingLevel, targetContext);
+    return m_source.frameImageAtIndex(index, subsamplingLevel, sizeForDrawing, targetContext);
 }
 
 NativeImagePtr BitmapImage::nativeImage(const GraphicsContext* targetContext)
 {
-    return frameImageAtIndex(0, SubsamplingLevel::Default, targetContext);
+    return frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext);
 }
 
 NativeImagePtr BitmapImage::nativeImageForCurrentFrame(const GraphicsContext* targetContext)
 {
-    return frameImageAtIndex(m_currentFrame, SubsamplingLevel::Default, targetContext);
+    return frameImageAtIndex(m_currentFrame, SubsamplingLevel::Default, { }, targetContext);
 }
 
 #if USE(CG)
@@ -126,13 +126,13 @@
     size_t count = frameCount();
 
     for (size_t i = 0; i < count; ++i) {
-        auto image = frameImageAtIndex(i, SubsamplingLevel::Default, targetContext);
+        auto image = frameImageAtIndex(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 frameImageAtIndex(0, SubsamplingLevel::Default, { }, targetContext);
 }
 
 Vector<NativeImagePtr> BitmapImage::framesNativeImages()
@@ -161,6 +161,7 @@
     if (destRect.isEmpty() || srcRect.isEmpty())
         return;
 
+    m_sizeForDrawing = enclosingIntRect(destRect).size();
     StartAnimationResult result = internalStartAnimation();
 
     Color color;
@@ -176,10 +177,10 @@
 
     float scale = subsamplingScale(context, destRect, srcRect);
     m_currentSubsamplingLevel = allowSubsampling() ? m_source.subsamplingLevelForScale(scale) : SubsamplingLevel::Default;
-    LOG(Images, "BitmapImage %p draw - subsamplingLevel %d at scale %.4f", this, static_cast<int>(m_currentSubsamplingLevel), scale);
+    LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld subsamplingLevel = %d scale = %.4f]", __FUNCTION__, this, sourceURL().characters8(), m_currentFrame, static_cast<int>(m_currentSubsamplingLevel), scale);
 
-    ASSERT_IMPLIES(result == StartAnimationResult::DecodingActive, m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel));
-    auto image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, &context);
+    ASSERT_IMPLIES(result == StartAnimationResult::DecodingActive, m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing));
+    auto image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, m_sizeForDrawing, &context);
     if (!image) // If it's too early we won't have an image yet.
         return;
 
@@ -237,6 +238,16 @@
     return shouldAnimate() && frameCount() > 1;
 }
 
+bool BitmapImage::isLargeImageAsyncDecodingRequired()
+{
+    return !canAnimate() && allowLargeImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired());
+}
+
+bool BitmapImage::isAnimatedImageAsyncDecodingRequired()
+{
+    return canAnimate() && allowAnimatedImageAsyncDecoding() && (isAsyncDecodingForcedForTesting() || m_source.isAsyncDecodingRequired());
+}
+
 void BitmapImage::clearTimer()
 {
     m_frameTimer = nullptr;
@@ -259,8 +270,10 @@
     
     // 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))
+    if (frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing)) {
+        LOG(Images, "BitmapImage::%s - %p - url: %s [nextFrame = %ld is being decoded]", __FUNCTION__, this, sourceURL().characters8(), nextFrame);
         return StartAnimationResult::DecodingActive;
+    }
 
     if (m_currentFrame >= frameCount() - 1) {
         // Don't advance past the last frame if we haven't decoded the whole image
@@ -299,9 +312,18 @@
     // 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 ((allowAnimatedImageAsyncDecoding() && m_source.isAsyncDecodingRequired()) || isAsyncDecodingForcedForTesting()) {
-        if (!m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel))
-            LOG(Images, "BitmapImage %p %s - cachedFrameCount %ld nextFrame %ld", this, __FUNCTION__, ++m_cachedFrameCount, nextFrame);
+    if (m_sizeForDrawing && isAnimatedImageAsyncDecodingRequired()) {
+        bool isAsyncDecode = m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel, *m_sizeForDrawing);
+
+#if !LOG_DISABLED
+        if (isAsyncDecode)
+            LOG(Images, "BitmapImage::%s - %p - url: %s [requesting async decoding for nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), nextFrame);
+        else
+            LOG(Images, "BitmapImage::%s - %p - url: %s [cachedFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_cachedFrameCount, nextFrame);
+#else
+        UNUSED_PARAM(isAsyncDecode);
+#endif
+
         m_desiredFrameDecodeTimeForTesting = time + std::max(m_frameDecodingDurationForTesting, 0.0f);
     }
 
@@ -327,13 +349,13 @@
     
     // Don't advance to nextFrame unless its decoding has finished or was not required.
     size_t nextFrame = (m_currentFrame + 1) % frameCount();
-    if (!frameIsBeingDecodedAtIndex(nextFrame))
+    if (!frameIsBeingDecodedAtIndex(nextFrame, m_sizeForDrawing))
         internalAdvanceAnimation();
     else {
         // Force repaint if showDebugBackground() is on.
         if (showDebugBackground())
             imageObserver()->changedInRect(this);
-        LOG(Images, "BitmapImage %p %s - lateFrameCount %ld nextFrame %ld", this, __FUNCTION__, ++m_lateFrameCount, nextFrame);
+        LOG(Images, "BitmapImage::%s - %p - url: %s [lateFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_lateFrameCount, nextFrame);
     }
 }
 
@@ -340,12 +362,14 @@
 void BitmapImage::internalAdvanceAnimation()
 {
     m_currentFrame = (m_currentFrame + 1) % frameCount();
-    ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame));
+    ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame, m_sizeForDrawing));
 
     destroyDecodedDataIfNecessary(false);
 
     if (imageObserver())
         imageObserver()->animationAdvanced(this);
+
+    LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), m_currentFrame);
 }
 
 void BitmapImage::stopAnimation()
@@ -377,7 +401,7 @@
     if (canAnimate() && !m_frameTimer)
         internalAdvanceAnimation();
     else
-        LOG(Images, "BitmapImage %p %s - earlyFrameCount %ld nextFrame %ld", this, __FUNCTION__, ++m_earlyFrameCount, index);
+        LOG(Images, "BitmapImage::%s - %p - url: %s [earlyFrameCount = %ld nextFrame = %ld]", __FUNCTION__, this, sourceURL().characters8(), ++m_earlyFrameCount, index);
 }
 
 void BitmapImage::dump(TextStream& ts) const

Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/BitmapImage.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -33,6 +33,7 @@
 #include "ImageOrientation.h"
 #include "ImageSource.h"
 #include "IntSize.h"
+#include "URL.h"
 
 #if USE(CG) || USE(APPKIT)
 #include <wtf/RetainPtr.h>
@@ -81,10 +82,11 @@
     IntSize sizeRespectingOrientation() const { return m_source.sizeRespectingOrientation(); }
     Color singlePixelSolidColor() const override { return m_source.singlePixelSolidColor(); }
 
-    bool frameIsBeingDecodedAtIndex(size_t index) const { return m_source.frameIsBeingDecodedAtIndex(index); }
+    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 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, SubsamplingLevel subsamplingLevel) const { return m_source.frameHasValidNativeImageAtIndex(index, subsamplingLevel); }
+    bool frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const { return m_source.frameHasValidNativeImageAtIndex(index, subsamplingLevel, sizeForDrawing); }
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) const { return m_source.frameSubsamplingLevelAtIndex(index); }
 
     float frameDurationAtIndex(size_t index) const { return m_source.frameDurationAtIndex(index); }
@@ -96,6 +98,8 @@
 
     bool isAsyncDecodingForcedForTesting() const { return m_frameDecodingDurationForTesting > 0; }
     void setFrameDecodingDurationForTesting(float duration) { m_frameDecodingDurationForTesting = duration; }
+    bool isLargeImageAsyncDecodingRequired();
+    bool isAnimatedImageAsyncDecodingRequired();
 
     // Accessors for native image formats.
 #if USE(APPKIT)
@@ -127,8 +131,9 @@
     WEBCORE_EXPORT BitmapImage(NativeImagePtr&&, ImageObserver* = nullptr);
     WEBCORE_EXPORT BitmapImage(ImageObserver* = nullptr);
 
-    NativeImagePtr frameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const GraphicsContext* = nullptr);
+    NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* = nullptr);
 
+    String sourceURL() const { return imageObserver() ? imageObserver()->sourceUrl().string() : emptyString(); }
     bool allowSubsampling() const { return imageObserver() && imageObserver()->allowSubsampling(); }
     bool allowLargeImageAsyncDecoding() const { return imageObserver() && imageObserver()->allowLargeImageAsyncDecoding(); }
     bool allowAnimatedImageAsyncDecoding() const { return imageObserver() && imageObserver()->allowAnimatedImageAsyncDecoding(); }
@@ -197,6 +202,7 @@
 
     size_t m_currentFrame { 0 }; // The index of the current frame of animation.
     SubsamplingLevel m_currentSubsamplingLevel { SubsamplingLevel::Default };
+    std::optional<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().

Modified: trunk/Source/WebCore/platform/graphics/ImageFrame.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageFrame.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageFrame.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -63,6 +63,7 @@
 
     m_nativeImage = other.m_nativeImage;
     m_subsamplingLevel = other.m_subsamplingLevel;
+    m_sizeForDrawing = other.m_sizeForDrawing;
 
     m_orientation = other.m_orientation;
     m_duration = other.m_duration;
@@ -123,7 +124,46 @@
 #endif
     return m_size;
 }
+    
+static int maxDimension(const IntSize& size)
+{
+    return std::max(size.width(), size.height());
+}
 
+bool ImageFrame::isBeingDecoded(const std::optional<IntSize>& sizeForDrawing) 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);
+}
+    
+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);
+}
+
 Color ImageFrame::singlePixelSolidColor() const
 {
     if (!hasNativeImage() || m_size != IntSize(1, 1))

Modified: trunk/Source/WebCore/platform/graphics/ImageFrame.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageFrame.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageFrame.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -30,6 +30,7 @@
 #include "ImageOrientation.h"
 #include "IntSize.h"
 #include "NativeImage.h"
+#include <wtf/Deque.h>
 
 namespace WebCore {
 
@@ -37,7 +38,6 @@
 
 // There are four subsampling levels: 0 = 1x, 1 = 0.5x, 2 = 0.25x, 3 = 0.125x.
 enum class SubsamplingLevel {
-    Undefinded = -1,
     First = 0,
     Default = First,
     Level0 = First,
@@ -73,16 +73,11 @@
     Ignored
 };
 
-enum class DecodingMode {
-    OnDemand,
-    Immediate
-};
-
 class ImageFrame {
     friend class ImageFrameCache;
 public:
-    enum class Caching { Empty, Metadata, MetadataAndImage };
-    enum class Decoding { Empty, BeingDecoded, Partial, Complete };
+    enum class Caching { Metadata, MetadataAndImage };
+    enum class Decoding { None, Partial, Complete };
 
     ImageFrame();
     ImageFrame(const ImageFrame& other) { operator=(other); }
@@ -103,8 +98,12 @@
 
     void setDecoding(Decoding decoding) { m_decoding = decoding; }
     Decoding decoding() const { return m_decoding; }
-    bool isEmpty() const { return m_decoding == Decoding::Empty; }
-    bool isBeingDecoded() const { return m_decoding == Decoding::BeingDecoded; }
+    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; }
 
@@ -112,6 +111,7 @@
     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 };
@@ -131,7 +131,8 @@
     bool hasAlpha() const { return !hasMetadata() || m_hasAlpha; }
 
     bool hasNativeImage() const { return m_nativeImage; }
-    bool hasValidNativeImage(SubsamplingLevel subsamplingLevel) const { return hasNativeImage() && subsamplingLevel >= m_subsamplingLevel; }
+    bool hasValidNativeImage(const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing) const;
+    bool hasDecodedNativeImage() const { return hasNativeImage() && sizeForDrawing(); }
     bool hasMetadata() const { return !size().isEmpty(); }
 
 #if !USE(CG)
@@ -142,7 +143,7 @@
     Color singlePixelSolidColor() const;
 
 private:
-    Decoding m_decoding { Decoding::Empty };
+    Decoding m_decoding { Decoding::None };
     IntSize m_size;
 
 #if !USE(CG)
@@ -152,6 +153,8 @@
 
     NativeImagePtr m_nativeImage;
     SubsamplingLevel m_subsamplingLevel { SubsamplingLevel::Default };
+    std::optional<IntSize> m_sizeForDrawing;
+    Deque<IntSize, 4> m_sizeForDecoding;
 
     ImageOrientation m_orientation { DefaultImageOrientation };
     float m_duration { 0 };

Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -174,7 +174,7 @@
     frame.m_hasAlpha = nativeImageHasAlpha(frame.m_nativeImage);
 }
 
-void ImageFrameCache::setFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
+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];
@@ -182,10 +182,10 @@
     ASSERT(isDecoderAvailable());
 
     frame.m_nativeImage = WTFMove(nativeImage);
-    setFrameMetadataAtIndex(index, subsamplingLevel);
+    setFrameMetadataAtIndex(index, subsamplingLevel, sizeForDrawing);
 }
 
-void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+void ImageFrameCache::setFrameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
@@ -196,20 +196,29 @@
         return;
     
     frame.m_subsamplingLevel = subsamplingLevel;
-    frame.m_size = m_decoder->frameSizeAtIndex(index, subsamplingLevel);
+
+    if (!sizeForDrawing) {
+        frame.m_size = m_decoder->frameSizeAtIndex(index, frame.m_subsamplingLevel);
+        frame.m_sizeForDrawing = { };
+    } else {
+        ASSERT(frame.nativeImage());
+        frame.m_size = nativeImageSize(frame.nativeImage());
+        frame.m_sizeForDrawing = sizeForDrawing;
+    }
+
     frame.m_orientation = m_decoder->frameOrientationAtIndex(index);
     frame.m_hasAlpha = m_decoder->frameHasAlphaAtIndex(index);
-    
+
     if (repetitionCount())
         frame.m_duration = m_decoder->frameDurationAtIndex(index);
 }
 
-void ImageFrameCache::replaceFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
+void ImageFrameCache::replaceFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
 
-    if (!frame.hasValidNativeImage(subsamplingLevel)) {
+    if (!frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing)) {
         // Clear the current image frame and update the observer with this clearance.
         unsigned decodedSize = frame.clear();
         decodedSizeDecreased(decodedSize);
@@ -221,22 +230,23 @@
         return;
 
     // Copy the new image to the cache.
-    setFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel);
+    setFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing);
 
     // Update the observer with the new image frame bytes.
     decodedSizeIncreased(frame.frameBytes());
 }
 
-void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel)
+void ImageFrameCache::cacheFrameNativeImageAtIndex(NativeImagePtr&& nativeImage, size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
 {
     if (!isDecoderAvailable())
         return;
 
     ASSERT(index < m_frames.size());
-    ASSERT(m_frames[index].isBeingDecoded());
+    ASSERT(m_frames[index].isBeingDecoded(sizeForDrawing));
 
     // Clean the old native image and set a new one
-    replaceFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel);
+    replaceFrameNativeImageAtIndex(WTFMove(nativeImage), index, subsamplingLevel, sizeForDrawing);
+    m_frames[index].dequeueSizeForDecoding();
 
     // Notify the image with the readiness of the new frame NativeImage.
     if (m_image)
@@ -267,26 +277,23 @@
 
         while (m_frameRequestQueue.dequeue(frameRequest)) {
             // Get the frame NativeImage on the decoding thread.
-            NativeImagePtr nativeImage = m_decoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, DecodingMode::Immediate);
+            NativeImagePtr nativeImage = m_decoder->createFrameImageAtIndex(frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing);
 
             // 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);
+                    cacheFrameNativeImageAtIndex(WTFMove(nativeImage), frameRequest.index, frameRequest.subsamplingLevel, frameRequest.sizeForDrawing);
             });
         }
     });
 }
 
-bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
 {
     if (!isDecoderAvailable())
         return false;
 
-    if (!hasDecodingQueue())
-        startAsyncDecodingQueue();
-
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
 
@@ -293,17 +300,17 @@
     // 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())
+    if (frame.isBeingDecoded(sizeForDrawing))
         return true;
 
-    if (subsamplingLevel == SubsamplingLevel::Undefinded)
-        subsamplingLevel = frame.subsamplingLevel();
-
-    if (frame.hasValidNativeImage(subsamplingLevel))
+    if (frame.hasValidNativeImage(subsamplingLevel, sizeForDrawing))
         return false;
 
-    frame.setDecoding(ImageFrame::Decoding::BeingDecoded);
-    m_frameRequestQueue.enqueue({ index, subsamplingLevel });
+    if (!hasDecodingQueue())
+        startAsyncDecodingQueue();
+    
+    frame.enqueueSizeForDecoding(sizeForDrawing);
+    m_frameRequestQueue.enqueue({ index, subsamplingLevel, sizeForDrawing });
     return true;
 }
 
@@ -316,25 +323,39 @@
     m_decodingQueue = nullptr;
 
     for (ImageFrame& frame : m_frames) {
-        if (frame.isBeingDecoded())
+        if (frame.isBeingDecoded()) {
+            frame.clearSizeForDecoding();
             frame.clear();
+        }
     }
 }
 
-const ImageFrame& ImageFrameCache::frameAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageFrame::Caching caching)
+const ImageFrame& ImageFrameCache::frameAtIndexCacheIfNeeded(size_t index, ImageFrame::Caching caching, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
 {
     ASSERT(index < m_frames.size());
     ImageFrame& frame = m_frames[index];
-    if (!isDecoderAvailable() || frame.isBeingDecoded() || caching == ImageFrame::Caching::Empty)
+    if (!isDecoderAvailable() || frame.isBeingDecoded(sizeForDrawing))
         return frame;
     
-    if (subsamplingLevel == SubsamplingLevel::Undefinded)
-        subsamplingLevel = frame.subsamplingLevel();
+    SubsamplingLevel subsamplingLevelValue = subsamplingLevel ? subsamplingLevel.value() : frame.subsamplingLevel();
 
-    if (!frame.isComplete() && caching == ImageFrame::Caching::Metadata)
-        setFrameMetadataAtIndex(index, subsamplingLevel);
-    else if (!frame.hasValidNativeImage(subsamplingLevel) && caching == ImageFrame::Caching::MetadataAndImage)
-        replaceFrameNativeImageAtIndex(m_decoder->createFrameImageAtIndex(index, subsamplingLevel), index, subsamplingLevel);
+    switch (caching) {
+    case ImageFrame::Caching::Metadata:
+        // Retrieve the metadata from ImageDecoder if the ImageFrame isn't complete.
+        if (frame.isComplete())
+            break;
+        setFrameMetadataAtIndex(index, subsamplingLevelValue, frame.sizeForDrawing());
+        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))
+            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, { });
+        break;
+    }
 
     return frame;
 }
@@ -362,18 +383,25 @@
     return cachedValue->value();
 }
 
-template<typename T, T (ImageFrame::*functor)() const>
-T ImageFrameCache::frameMetadataAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageFrame::Caching caching, std::optional<T>* cachedValue)
+template<typename T, typename... Args>
+T ImageFrameCache::frameMetadataAtIndex(size_t index, T (ImageFrame::*functor)(Args...) const, Args&&... args)
 {
+    const ImageFrame& frame = index < m_frames.size() ? m_frames[index] : ImageFrame::defaultFrame();
+    return (frame.*functor)(std::forward<Args>(args)...);
+}
+
+template<typename T, typename... Args>
+T ImageFrameCache::frameMetadataAtIndexCacheIfNeeded(size_t index, T (ImageFrame::*functor)() const, std::optional<T>* cachedValue, Args&&... args)
+{
     if (cachedValue && *cachedValue)
         return cachedValue->value();
-    
-    const ImageFrame& frame = index < m_frames.size() ? frameAtIndex(index, subsamplingLevel, caching) : ImageFrame::defaultFrame();
 
+    const ImageFrame& frame = index < m_frames.size() ? frameAtIndexCacheIfNeeded(index, std::forward<Args>(args)...) : ImageFrame::defaultFrame();
+
     // Don't cache any unavailable frame metadata.
     if (!frame.hasMetadata() || !cachedValue)
         return (frame.*functor)();
-    
+
     *cachedValue = (frame.*functor)();
     return cachedValue->value();
 }
@@ -413,72 +441,77 @@
 
 IntSize ImageFrameCache::size()
 {
-    return frameMetadataAtIndex<IntSize, (&ImageFrame::size)>(0, SubsamplingLevel::Default, ImageFrame::Caching::Metadata, &m_size);
+    return frameMetadataAtIndexCacheIfNeeded<IntSize>(0, (&ImageFrame::size), &m_size, ImageFrame::Caching::Metadata, SubsamplingLevel::Default);
 }
 
 IntSize ImageFrameCache::sizeRespectingOrientation()
 {
-    return frameMetadataAtIndex<IntSize, (&ImageFrame::sizeRespectingOrientation)>(0, SubsamplingLevel::Default, ImageFrame::Caching::Metadata, &m_sizeRespectingOrientation);
+    return frameMetadataAtIndexCacheIfNeeded<IntSize>(0, (&ImageFrame::sizeRespectingOrientation), &m_sizeRespectingOrientation, ImageFrame::Caching::Metadata, SubsamplingLevel::Default);
 }
 
 Color ImageFrameCache::singlePixelSolidColor()
 {
-    return frameCount() == 1 ? frameMetadataAtIndex<Color, (&ImageFrame::singlePixelSolidColor)>(0, SubsamplingLevel::Undefinded, ImageFrame::Caching::MetadataAndImage, &m_singlePixelSolidColor) : Color();
+    return frameCount() == 1 ? frameMetadataAtIndexCacheIfNeeded<Color>(0, (&ImageFrame::singlePixelSolidColor), &m_singlePixelSolidColor, ImageFrame::Caching::MetadataAndImage) : Color();
 }
 
-bool ImageFrameCache::frameIsBeingDecodedAtIndex(size_t index)
+bool ImageFrameCache::frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing)
 {
-    return frameMetadataAtIndex<bool, (&ImageFrame::isBeingDecoded)>(index);
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::isBeingDecoded), sizeForDrawing);
 }
 
 bool ImageFrameCache::frameIsCompleteAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<bool, (&ImageFrame::isComplete)>(index);
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::isComplete));
 }
 
 bool ImageFrameCache::frameHasAlphaAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<bool, (&ImageFrame::hasAlpha)>(index);
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasAlpha));
 }
 
 bool ImageFrameCache::frameHasImageAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<bool, (&ImageFrame::hasNativeImage)>(index);
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasNativeImage));
 }
 
-bool ImageFrameCache::frameHasValidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+bool ImageFrameCache::frameHasValidNativeImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
 {
-    return frameHasImageAtIndex(index) && subsamplingLevel >= frameSubsamplingLevelAtIndex(index);
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasValidNativeImage), subsamplingLevel, sizeForDrawing);
 }
+    
+bool ImageFrameCache::frameHasDecodedNativeImage(size_t index)
+{
+    return frameMetadataAtIndex<bool>(index, (&ImageFrame::hasDecodedNativeImage));
+}
 
 SubsamplingLevel ImageFrameCache::frameSubsamplingLevelAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<SubsamplingLevel, (&ImageFrame::subsamplingLevel)>(index);
+    return frameMetadataAtIndex<SubsamplingLevel>(index, (&ImageFrame::subsamplingLevel));
 }
 
 IntSize ImageFrameCache::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
 {
-    return frameMetadataAtIndex<IntSize, (&ImageFrame::size)>(index, subsamplingLevel, ImageFrame::Caching::Metadata);
+    return frameMetadataAtIndexCacheIfNeeded<IntSize>(index, (&ImageFrame::size), nullptr, ImageFrame::Caching::Metadata, subsamplingLevel);
 }
 
 unsigned ImageFrameCache::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
 {
-    return frameMetadataAtIndex<unsigned, (&ImageFrame::frameBytes)>(index, subsamplingLevel, ImageFrame::Caching::Metadata);
+    return frameMetadataAtIndexCacheIfNeeded<unsigned>(index, (&ImageFrame::frameBytes), nullptr, ImageFrame::Caching::Metadata, subsamplingLevel);
 }
 
 float ImageFrameCache::frameDurationAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<float, (&ImageFrame::duration)>(index, SubsamplingLevel::Undefinded, ImageFrame::Caching::Metadata);
+    return frameMetadataAtIndexCacheIfNeeded<float>(index, (&ImageFrame::duration), nullptr, ImageFrame::Caching::Metadata);
 }
 
 ImageOrientation ImageFrameCache::frameOrientationAtIndex(size_t index)
 {
-    return frameMetadataAtIndex<ImageOrientation, (&ImageFrame::orientation)>(index, SubsamplingLevel::Undefinded, ImageFrame::Caching::Metadata);
+    return frameMetadataAtIndexCacheIfNeeded<ImageOrientation>(index, (&ImageFrame::orientation), nullptr, ImageFrame::Caching::Metadata);
 }
 
-NativeImagePtr ImageFrameCache::frameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+NativeImagePtr ImageFrameCache::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
 {
-    return frameMetadataAtIndex<NativeImagePtr, (&ImageFrame::nativeImage)>(index, subsamplingLevel, ImageFrame::Caching::MetadataAndImage);
+    return frameMetadataAtIndexCacheIfNeeded<NativeImagePtr>(index, (&ImageFrame::nativeImage), nullptr, ImageFrame::Caching::MetadataAndImage, subsamplingLevel, sizeForDrawing);
 }
 
 }

Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -69,7 +69,7 @@
     
     // Asynchronous image decoding
     void startAsyncDecodingQueue();
-    bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel);
+    bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel, const IntSize&);
     void stopAsyncDecodingQueue();
     bool hasDecodingQueue() { return m_decodingQueue; }
 
@@ -88,11 +88,12 @@
     Color singlePixelSolidColor();
 
     // ImageFrame metadata which does not require caching the ImageFrame.
-    bool frameIsBeingDecodedAtIndex(size_t);
+    bool frameIsBeingDecodedAtIndex(size_t, const std::optional<IntSize>& sizeForDrawing);
     bool frameIsCompleteAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t);
     bool frameHasImageAtIndex(size_t);
-    bool frameHasValidNativeImageAtIndex(size_t, SubsamplingLevel);
+    bool frameHasValidNativeImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing);
+    bool frameHasDecodedNativeImage(size_t);
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t);
     
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
@@ -100,7 +101,7 @@
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
     float frameDurationAtIndex(size_t);
     ImageOrientation frameOrientationAtIndex(size_t);
-    NativeImagePtr frameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
+    NativeImagePtr frameImageAtIndex(size_t, const std::optional<SubsamplingLevel>&, const std::optional<IntSize>& sizeForDrawing);
 
 private:
     ImageFrameCache(Image*);
@@ -109,9 +110,12 @@
     template<typename T, T (ImageDecoder::*functor)() const>
     T metadata(const T& defaultValue, std::optional<T>* cachedValue = nullptr);
 
-    template<typename T, T (ImageFrame::*functor)() const>
-    T frameMetadataAtIndex(size_t index, SubsamplingLevel = SubsamplingLevel::Undefinded, ImageFrame::Caching = ImageFrame::Caching::Empty, std::optional<T>* = nullptr);
-
+    template<typename T, typename... Args>
+    T frameMetadataAtIndex(size_t, T (ImageFrame::*functor)(Args...) const, Args&&...);
+    
+    template<typename T, typename... Args>
+    T frameMetadataAtIndexCacheIfNeeded(size_t, T (ImageFrame::*functor)() const,  std::optional<T>* cachedValue, Args&&...);
+    
     bool isDecoderAvailable() const { return m_decoder; }
     void destroyDecodedData(size_t frameCount, size_t excludeFrame);
     void decodedSizeChanged(long long decodedSize);
@@ -121,14 +125,14 @@
     void decodedSizeReset(unsigned decodedSize);
 
     void setNativeImage(NativeImagePtr&&);
-    void setFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel);
-    void setFrameMetadataAtIndex(size_t, SubsamplingLevel);
-    void replaceFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel);
-    void cacheFrameNativeImageAtIndex(NativeImagePtr&&, size_t, SubsamplingLevel);
+    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);
 
     Ref<WorkQueue> decodingQueue();
 
-    const ImageFrame& frameAtIndex(size_t, SubsamplingLevel, ImageFrame::Caching);
+    const ImageFrame& frameAtIndexCacheIfNeeded(size_t, ImageFrame::Caching, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { });
 
     Image* m_image { nullptr };
     ImageDecoder* m_decoder { nullptr };
@@ -141,6 +145,7 @@
     struct ImageFrameRequest {
         size_t index;
         SubsamplingLevel subsamplingLevel;
+        IntSize sizeForDrawing;
     };
     static const int BufferSize = 8;
     using FrameRequestQueue = SynchronizedFixedQueue<ImageFrameRequest, BufferSize>;

Modified: trunk/Source/WebCore/platform/graphics/ImageObserver.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageObserver.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageObserver.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -30,6 +30,7 @@
 
 class Image;
 class IntRect;
+class URL;
 
 // Interface for notification about changes to an image, including decoding,
 // drawing, and animating.
@@ -37,6 +38,7 @@
 protected:
     virtual ~ImageObserver() {}
 public:
+    virtual URL sourceUrl() const = 0;
     virtual bool allowSubsampling() const = 0;
     virtual bool allowLargeImageAsyncDecoding() const = 0;
     virtual bool allowAnimatedImageAsyncDecoding() const = 0;

Modified: trunk/Source/WebCore/platform/graphics/ImageSource.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageSource.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -191,11 +191,10 @@
     return isDecoderAvailable() ? m_decoder->createFrameImageAtIndex(index, subsamplingLevel) : nullptr;
 }
 
-NativeImagePtr ImageSource::frameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
+NativeImagePtr ImageSource::frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& subsamplingLevel, const std::optional<IntSize>& sizeForDrawing, const GraphicsContext* targetContext)
 {
     setDecoderTargetContext(targetContext);
-
-    return m_frameCache->frameImageAtIndex(index, subsamplingLevel);
+    return m_frameCache->frameImageAtIndex(index, subsamplingLevel, sizeForDrawing);
 }
 
 void ImageSource::dump(TextStream& ts)

Modified: trunk/Source/WebCore/platform/graphics/ImageSource.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/ImageSource.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -69,7 +69,7 @@
     bool isAllDataReceived();
 
     bool isAsyncDecodingRequired();
-    bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel); }
+    bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing) { return m_frameCache->requestFrameAsyncDecodingAtIndex(index, subsamplingLevel, sizeForDrawing); }
     bool hasDecodingQueue() const { return m_frameCache->hasDecodingQueue(); }
     void stopAsyncDecodingQueue() { m_frameCache->stopAsyncDecodingQueue(); }
 
@@ -86,11 +86,12 @@
     Color singlePixelSolidColor() { return m_frameCache->singlePixelSolidColor(); }
 
     // ImageFrame metadata which does not require caching the ImageFrame.
-    bool frameIsBeingDecodedAtIndex(size_t index) { return m_frameCache->frameIsBeingDecodedAtIndex(index); }
+    bool frameIsBeingDecodedAtIndex(size_t index, const std::optional<IntSize>& sizeForDrawing) { return m_frameCache->frameIsBeingDecodedAtIndex(index, sizeForDrawing); }
     bool frameIsCompleteAtIndex(size_t index) { return m_frameCache->frameIsCompleteAtIndex(index); }
     bool frameHasAlphaAtIndex(size_t index) { return m_frameCache->frameHasAlphaAtIndex(index); }
     bool frameHasImageAtIndex(size_t index) { return m_frameCache->frameHasImageAtIndex(index); }
-    bool frameHasValidNativeImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_frameCache->frameHasValidNativeImageAtIndex(index, subsamplingLevel); }
+    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); }
     SubsamplingLevel frameSubsamplingLevelAtIndex(size_t index) { return m_frameCache->frameSubsamplingLevelAtIndex(index); }
 
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
@@ -98,7 +99,7 @@
     unsigned frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default) { return m_frameCache->frameBytesAtIndex(index, subsamplingLevel); }
     float frameDurationAtIndex(size_t index) { return m_frameCache->frameDurationAtIndex(index); }
     ImageOrientation frameOrientationAtIndex(size_t index) { return m_frameCache->frameOrientationAtIndex(index); }
-    NativeImagePtr frameImageAtIndex(size_t index, SubsamplingLevel = SubsamplingLevel::Default, const GraphicsContext* targetContext = nullptr);
+    NativeImagePtr frameImageAtIndex(size_t index, const std::optional<SubsamplingLevel>& = { }, const std::optional<IntSize>& sizeForDrawing = { }, const GraphicsContext* targetContext = nullptr);
 
     SubsamplingLevel maximumSubsamplingLevel();
     SubsamplingLevel subsamplingLevelForScale(float);

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -58,41 +58,68 @@
 const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
 const CFStringRef kCGImageSourceSkipMetadata = CFSTR("kCGImageSourceSkipMetadata");
 
-static RetainPtr<CFDictionaryRef> createImageSourceOptions(SubsamplingLevel subsamplingLevel, DecodingMode decodingMode)
+static RetainPtr<CFMutableDictionaryRef> createImageSourceOptions()
 {
     RetainPtr<CFMutableDictionaryRef> options = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
-
     CFDictionarySetValue(options.get(), kCGImageSourceShouldCache, kCFBooleanTrue);
     CFDictionarySetValue(options.get(), kCGImageSourceShouldPreferRGB32, kCFBooleanTrue);
     CFDictionarySetValue(options.get(), kCGImageSourceSkipMetadata, kCFBooleanTrue);
-
-    if (subsamplingLevel > SubsamplingLevel::First) {
-        RetainPtr<CFNumberRef> subsampleNumber;
-        subsamplingLevel = std::min(SubsamplingLevel::Last, std::max(SubsamplingLevel::First, subsamplingLevel));
-        int subsampleInt = 1 << static_cast<int>(subsamplingLevel); // [0..3] => [1, 2, 4, 8]
-        subsampleNumber = adoptCF(CFNumberCreate(nullptr,  kCFNumberIntType,  &subsampleInt));
-        CFDictionarySetValue(options.get(), kCGImageSourceSubsampleFactor, subsampleNumber.get());
-    }
-
-    if (decodingMode == DecodingMode::Immediate) {
-        CFDictionarySetValue(options.get(), kCGImageSourceShouldCacheImmediately, kCFBooleanTrue);
-        CFDictionarySetValue(options.get(), kCGImageSourceCreateThumbnailFromImageAlways, kCFBooleanTrue);
-    }
-
     return options;
 }
+    
+static RetainPtr<CFMutableDictionaryRef> createImageSourceAsyncOptions()
+{
+    RetainPtr<CFMutableDictionaryRef> options = createImageSourceOptions();
+    CFDictionarySetValue(options.get(), kCGImageSourceShouldCacheImmediately, kCFBooleanTrue);
+    CFDictionarySetValue(options.get(), kCGImageSourceCreateThumbnailFromImageAlways, kCFBooleanTrue);
+    return options;
+}
 
-static RetainPtr<CFDictionaryRef> imageSourceOptions(SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default, DecodingMode decodingMode = DecodingMode::OnDemand)
+static RetainPtr<CFMutableDictionaryRef> appendImageSourceOption(RetainPtr<CFMutableDictionaryRef>&& options, SubsamplingLevel subsamplingLevel)
 {
-    if (subsamplingLevel > SubsamplingLevel::First)
-        return createImageSourceOptions(subsamplingLevel, decodingMode);
+    RetainPtr<CFNumberRef> subsampleNumber;
+    subsamplingLevel = std::min(SubsamplingLevel::Last, std::max(SubsamplingLevel::First, subsamplingLevel));
+    int subsampleInt = 1 << static_cast<int>(subsamplingLevel); // [0..3] => [1, 2, 4, 8]
+    subsampleNumber = adoptCF(CFNumberCreate(nullptr,  kCFNumberIntType,  &subsampleInt));
+    CFDictionarySetValue(options.get(), kCGImageSourceSubsampleFactor, subsampleNumber.get());
+    return WTFMove(options);
+}
 
-    static NeverDestroyed<RetainPtr<CFDictionaryRef>> optionsOnDemand = createImageSourceOptions(SubsamplingLevel::First, DecodingMode::OnDemand);
-    static NeverDestroyed<RetainPtr<CFDictionaryRef>> optionsImmediate = createImageSourceOptions(SubsamplingLevel::First, DecodingMode::Immediate);
+static RetainPtr<CFMutableDictionaryRef> appendImageSourceOption(RetainPtr<CFMutableDictionaryRef>&& options, const IntSize& sizeForDrawing)
+{
+    unsigned maxPixelSize = std::max(sizeForDrawing.width(), sizeForDrawing.height());
+    RetainPtr<CFNumberRef> maxPixelSizeNumber = adoptCF(CFNumberCreate(nullptr, kCFNumberIntType, &maxPixelSize));
+    CFDictionarySetValue(options.get(), kCGImageSourceThumbnailMaxPixelSize, maxPixelSizeNumber.get());
+    return WTFMove(options);
+}
+    
+static RetainPtr<CFMutableDictionaryRef> appendImageSourceOptions(RetainPtr<CFMutableDictionaryRef>&& options, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing)
+{
+    if (subsamplingLevel != SubsamplingLevel::Default)
+        options = appendImageSourceOption(WTFMove(options), subsamplingLevel);
+    
+    if (sizeForDrawing)
+        options = appendImageSourceOption(WTFMove(options), sizeForDrawing.value());
 
-    return decodingMode == DecodingMode::OnDemand ? optionsOnDemand : optionsImmediate;
+    return WTFMove(options);
 }
+    
+static RetainPtr<CFDictionaryRef> imageSourceOptions(SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default)
+{
+    static NeverDestroyed<RetainPtr<CFMutableDictionaryRef>> options = createImageSourceOptions();
+    if (subsamplingLevel == SubsamplingLevel::Default)
+        return options.get();
+    return appendImageSourceOption(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options.get().get())), subsamplingLevel);
+}
 
+static RetainPtr<CFDictionaryRef> imageSourceAsyncOptions(SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDrawing = { })
+{
+    static NeverDestroyed<RetainPtr<CFMutableDictionaryRef>> options = createImageSourceAsyncOptions();
+    if (subsamplingLevel == SubsamplingLevel::Default && !sizeForDrawing)
+        return options.get();
+    return appendImageSourceOptions(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options.get().get())), subsamplingLevel, sizeForDrawing);
+}
+    
 static ImageOrientation orientationFromProperties(CFDictionaryRef imageProperties)
 {
     ASSERT(imageProperties);
@@ -339,17 +366,29 @@
     return (frameSize.area() * 4).unsafeGet();
 }
 
-NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, DecodingMode decodingMode) const
+NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>& sizeForDrawing) const
 {
     LOG(Images, "ImageDecoder %p createFrameImageAtIndex %lu", this, index);
-
-    RetainPtr<CFDictionaryRef> options = imageSourceOptions(subsamplingLevel, decodingMode);
+    RetainPtr<CFDictionaryRef> options;
     RetainPtr<CGImageRef> image;
 
-    if (decodingMode == DecodingMode::OnDemand)
+    if (!sizeForDrawing) {
+        // Decode an image synchronously for its native size.
+        options = imageSourceOptions(subsamplingLevel);
         image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
-    else
+    } else {
+        IntSize size = frameSizeAtIndex(index, subsamplingLevel);
+
+        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);
+        }
+        
         image = adoptCF(CGImageSourceCreateThumbnailAtIndex(m_nativeDecoder.get(), index, options.get()));
+    }
     
 #if PLATFORM(IOS)
     // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient

Modified: trunk/Source/WebCore/platform/graphics/cg/ImageDecoderCG.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/cg/ImageDecoderCG.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/cg/ImageDecoderCG.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -62,7 +62,7 @@
     bool frameAllowSubsamplingAtIndex(size_t) const;
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default) const;
     
-    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, DecodingMode = DecodingMode::OnDemand) const;
+    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDrawing = { }) const;
     
     void setData(SharedBuffer&, bool allDataReceived);
     bool isAllDataReceived() const { return m_isAllDataReceived; }

Modified: trunk/Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -180,7 +180,7 @@
     m_renderTarget = renderTarget;
 }
 
-NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, DecodingMode) const
+NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const std::optional<IntSize>&) const
 {
     if (!m_nativeDecoder || !m_renderTarget)
         return nullptr;

Modified: trunk/Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.h (213562 => 213563)


--- trunk/Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/graphics/win/ImageDecoderDirect2D.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -67,7 +67,7 @@
     bool frameAllowSubsamplingAtIndex(size_t) const;
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default) const;
     
-    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, DecodingMode = DecodingMode::OnDemand) const;
+    NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDraw = { }) const;
     
     void setData(SharedBuffer&, bool allDataReceived);
     bool isAllDataReceived() const { return m_isAllDataReceived; }

Modified: trunk/Source/WebCore/platform/image-decoders/ImageDecoder.cpp (213562 => 213563)


--- trunk/Source/WebCore/platform/image-decoders/ImageDecoder.cpp	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/image-decoders/ImageDecoder.cpp	2017-03-08 03:54:28 UTC (rev 213563)
@@ -207,7 +207,7 @@
     return duration;
 }
 
-NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, DecodingMode)
+NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel, const std::optional<IntSize>&)
 {
     // Zero-height images can cause problems for some ports. If we have an empty image dimension, just bail.
     if (size().isEmpty())

Modified: trunk/Source/WebCore/platform/image-decoders/ImageDecoder.h (213562 => 213563)


--- trunk/Source/WebCore/platform/image-decoders/ImageDecoder.h	2017-03-08 03:46:19 UTC (rev 213562)
+++ trunk/Source/WebCore/platform/image-decoders/ImageDecoder.h	2017-03-08 03:54:28 UTC (rev 213563)
@@ -137,7 +137,7 @@
         
         float frameDurationAtIndex(size_t);
         
-        NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, DecodingMode = DecodingMode::OnDemand);
+        NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const std::optional<IntSize>& sizeForDraw = { });
 
         void setIgnoreGammaAndColorProfile(bool flag) { m_ignoreGammaAndColorProfile = flag; }
         bool ignoresGammaAndColorProfile() const { return m_ignoreGammaAndColorProfile; }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to