Clean up image subsampling code, make it less iOS-specific
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Aug 2014 18:55:51 +0000 (18:55 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Aug 2014 18:55:51 +0000 (18:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=134916

Reviewed by Dean Jackson.

Compile the image subsampling code on both Mac and iOS, and make it more platform
neutral in general. Add a setting to allow it to be enabled on Mac for testing.

The most significant changes are in ImageSourceCG and BitmapImageCG. CG's ImageSource
is no longer stateful with respect to subsampling; its functions take a SubsamplingLevel
when appropriate. CG's BitmapImage now determines which level of subsampling to use
for a given frame, storing the subsampling level in the frame data. It can replace
an aggressively subsampled frame with a less subsampled frame if necessary.

To reduce the chances of subsampling affecting rendering, BitmapImage::size() now
always returns the non-subsampled size; subsampling is strictly internal to BitmapImage.
BitmapImage::draw() takes care of scaling the srcRect for subsampled images.

iOS had a code path that enabled caching of frame metadata in BitmapImage without
actually decoding the frame; make this cross-platform.

Fix a couple of issues in the original pathc: remove a log, and ImageSource::allowSubsamplingOfFrameAtIndex()
return false.

* WebCore.exp.in: Changed signature for GraphicsContext::drawNativeImage().
* WebCore.xcodeproj/project.pbxproj: Added ImageSource.cpp, which is not built
for Cocoa but useful for reference.
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::imageSizeForRenderer): Remove iOS-specific subsampling code.
(WebCore::CachedImage::createImage): Call setAllowSubsampling() on the image if we
can get to Settings (m_loader is null for image documents).
(WebCore::CachedImage::currentFrameKnownToBeOpaque): This forced decode always
caused creation of the non-subsampled image, so remove it. There's no reason to
eagerly decode the frame here.
* loader/cache/CachedImage.h: Fix comment.
* page/Settings.cpp: Add defaultImageSubsamplingEnabled, true for iOS and false for Mac.
* page/Settings.in: Added imageSubsamplingEnabled.
* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::BitmapImage): Init some more things. Default m_allowSubsampling to
true for iOS to catch images created in code paths where we can't get to Settings.
(WebCore::BitmapImage::haveFrameAtIndex): Handy helper.
(WebCore::BitmapImage::cacheFrame): Now takes the subsampling level and whether to cache
just metadata, or also the frame.
(WebCore::BitmapImage::didDecodeProperties): No need to store originalSize.
(WebCore::BitmapImage::updateSize): When we get the size for the first time, call
determineMinimumSubsamplingLevel() to choose a reasonable subsampling level which takes
platform-specific limits into account.
(WebCore::BitmapImage::dataChanged): Comment.
(WebCore::BitmapImage::ensureFrameIsCached): Take ImageFrameCaching into account.
(WebCore::BitmapImage::frameAtIndex): Choose a subsampling level given the scale,
then determine if we can use the currently cached frame, or whether we should resample.
(WebCore::BitmapImage::frameIsCompleteAtIndex): Caching m_isComplete is now done when caching
frame metadata.
(WebCore::BitmapImage::frameDurationAtIndex):
(WebCore::BitmapImage::frameHasAlphaAtIndex): The 'true' return is the safe return value.
(WebCore::BitmapImage::frameOrientationAtIndex): Caching m_orientation is now done when caching
frame metadata.
(WebCore::BitmapImage::cacheFrameInfo): Deleted.
(WebCore::BitmapImage::originalSize): Deleted.
(WebCore::BitmapImage::originalSizeRespectingOrientation): Deleted.
(WebCore::BitmapImage::currentFrameSize): Deleted.
(WebCore::BitmapImage::ensureFrameInfoIsCached): Deleted.
* platform/graphics/BitmapImage.h:
(WebCore::FrameData::FrameData):
* platform/graphics/GraphicsContext.h: No need to pass a scale param now.
* platform/graphics/ImageSource.cpp: Non-Cocoa changes.
(WebCore::ImageSource::subsamplingLevelForScale):
(WebCore::ImageSource::allowSubsamplingOfFrameAtIndex):
(WebCore::ImageSource::size):
(WebCore::ImageSource::frameSizeAtIndex):
(WebCore::ImageSource::createFrameAtIndex):
(WebCore::ImageSource::frameBytesAtIndex):
* platform/graphics/ImageSource.h: No longer stores subsampling state.
(WebCore::ImageSource::isSubsampled): Deleted.
* platform/graphics/cairo/BitmapImageCairo.cpp:
(WebCore::BitmapImage::determineMinimumSubsamplingLevel):
* platform/graphics/cg/BitmapImageCG.cpp:
(WebCore::FrameData::clear):
(WebCore::BitmapImage::BitmapImage): Init more members.
(WebCore::BitmapImage::determineMinimumSubsamplingLevel): Choose a minimum subsampling
level for the platform (subsample until the image area falls under a threshold).
(WebCore::BitmapImage::checkForSolidColor): Don't bother decoding frames if the image
is not 1x1. Also take care not to decode a non-subsampled image.
(WebCore::BitmapImage::draw): The actual bug fix is here; remove logic that
computed srcRectForCurrentFrame from m_size and m_originalSize; for some callers
srcRect was computed using the pre-subsampled size, and for others it was the subsampled size.
Instead, scale srcRect by mapping between the non-subsampled size, and the size of the CGImageRef
which is affected by subsampling.
(WebCore::BitmapImage::copyUnscaledFrameAtIndex):
* platform/graphics/cg/GraphicsContext3DCG.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::extractImage): Remove #ifdeffed code.
(WebCore::GraphicsContext3D::paintToCanvas):
* platform/graphics/cg/GraphicsContextCG.cpp:
(WebCore::GraphicsContext::drawNativeImage): No more weird scaling!
* platform/graphics/cg/ImageBufferCG.cpp:
(WebCore::ImageBuffer::draw):
* platform/graphics/cg/ImageSourceCG.cpp:
(WebCore::ImageSource::ImageSource):
(WebCore::createImageSourceOptions): Helper that always returns a new CFDictionaryRef.
(WebCore::imageSourceOptions): If not subsampling, return the cached CFDictionaryRef, otherwise
make a new options dict and return it.
(WebCore::ImageSource::subsamplingLevelForScale): Helper that returns a subsampling level
between 0 and 3 given a scale.
(WebCore::ImageSource::isSizeAvailable): SkipMetadata is a default value for the param now.
(WebCore::ImageSource::allowSubsamplingOfFrameAtIndex): We turn off subsampling for progressive
JPEGs because of a bug, so need this to know if a frame should be subsampled.
(WebCore::ImageSource::frameSizeAtIndex): The looping to find a subsampling level is now in BitmapImageCG.
(WebCore::ImageSource::orientationAtIndex):
(WebCore::ImageSource::size): Always use a subsampling level of 0 for size().
(WebCore::ImageSource::getHotSpot):
(WebCore::ImageSource::repetitionCount):
(WebCore::ImageSource::createFrameAtIndex): The caller mapped a scale to a level.
(WebCore::ImageSource::frameDurationAtIndex):
(WebCore::ImageSource::frameBytesAtIndex):
(WebCore::ImageSource::imageSourceOptions): Deleted.
(WebCore::ImageSource::originalSize): Deleted.
* platform/graphics/mac/ImageMac.mm:
(WebCore::BitmapImage::invalidatePlatformData): 0 -> nullptr
* platform/graphics/wince/ImageWinCE.cpp:
(WebCore::BitmapImage::determineMinimumSubsamplingLevel):

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

20 files changed:
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/loader/cache/CachedImage.cpp
Source/WebCore/loader/cache/CachedImage.h
Source/WebCore/page/Settings.cpp
Source/WebCore/page/Settings.in
Source/WebCore/platform/graphics/BitmapImage.cpp
Source/WebCore/platform/graphics/BitmapImage.h
Source/WebCore/platform/graphics/GraphicsContext.h
Source/WebCore/platform/graphics/ImageSource.cpp
Source/WebCore/platform/graphics/ImageSource.h
Source/WebCore/platform/graphics/cairo/BitmapImageCairo.cpp
Source/WebCore/platform/graphics/cg/BitmapImageCG.cpp
Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp
Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp
Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp
Source/WebCore/platform/graphics/cg/ImageSourceCG.cpp
Source/WebCore/platform/graphics/mac/ImageMac.mm
Source/WebCore/platform/graphics/wince/ImageWinCE.cpp

index 0dea9a4..8852cfd 100644 (file)
@@ -1,3 +1,126 @@
+2014-08-08  Simon Fraser  <simon.fraser@apple.com>
+
+        Clean up image subsampling code, make it less iOS-specific
+        https://bugs.webkit.org/show_bug.cgi?id=134916
+
+        Reviewed by Dean Jackson.
+
+        Compile the image subsampling code on both Mac and iOS, and make it more platform
+        neutral in general. Add a setting to allow it to be enabled on Mac for testing.
+        
+        The most significant changes are in ImageSourceCG and BitmapImageCG. CG's ImageSource
+        is no longer stateful with respect to subsampling; its functions take a SubsamplingLevel
+        when appropriate. CG's BitmapImage now determines which level of subsampling to use
+        for a given frame, storing the subsampling level in the frame data. It can replace
+        an aggressively subsampled frame with a less subsampled frame if necessary.
+        
+        To reduce the chances of subsampling affecting rendering, BitmapImage::size() now
+        always returns the non-subsampled size; subsampling is strictly internal to BitmapImage.
+        BitmapImage::draw() takes care of scaling the srcRect for subsampled images.
+        
+        iOS had a code path that enabled caching of frame metadata in BitmapImage without
+        actually decoding the frame; make this cross-platform.
+        
+        Fix a couple of issues in the original pathc: remove a log, and ImageSource::allowSubsamplingOfFrameAtIndex()
+        return false.
+
+        * WebCore.exp.in: Changed signature for GraphicsContext::drawNativeImage().
+        * WebCore.xcodeproj/project.pbxproj: Added ImageSource.cpp, which is not built
+        for Cocoa but useful for reference.
+        * loader/cache/CachedImage.cpp:
+        (WebCore::CachedImage::imageSizeForRenderer): Remove iOS-specific subsampling code.
+        (WebCore::CachedImage::createImage): Call setAllowSubsampling() on the image if we
+        can get to Settings (m_loader is null for image documents).
+        (WebCore::CachedImage::currentFrameKnownToBeOpaque): This forced decode always
+        caused creation of the non-subsampled image, so remove it. There's no reason to
+        eagerly decode the frame here.
+        * loader/cache/CachedImage.h: Fix comment.
+        * page/Settings.cpp: Add defaultImageSubsamplingEnabled, true for iOS and false for Mac.
+        * page/Settings.in: Added imageSubsamplingEnabled.
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::BitmapImage): Init some more things. Default m_allowSubsampling to
+        true for iOS to catch images created in code paths where we can't get to Settings.
+        (WebCore::BitmapImage::haveFrameAtIndex): Handy helper.
+        (WebCore::BitmapImage::cacheFrame): Now takes the subsampling level and whether to cache
+        just metadata, or also the frame.
+        (WebCore::BitmapImage::didDecodeProperties): No need to store originalSize.
+        (WebCore::BitmapImage::updateSize): When we get the size for the first time, call
+        determineMinimumSubsamplingLevel() to choose a reasonable subsampling level which takes
+        platform-specific limits into account.
+        (WebCore::BitmapImage::dataChanged): Comment.
+        (WebCore::BitmapImage::ensureFrameIsCached): Take ImageFrameCaching into account.
+        (WebCore::BitmapImage::frameAtIndex): Choose a subsampling level given the scale,
+        then determine if we can use the currently cached frame, or whether we should resample.
+        (WebCore::BitmapImage::frameIsCompleteAtIndex): Caching m_isComplete is now done when caching
+        frame metadata.
+        (WebCore::BitmapImage::frameDurationAtIndex):
+        (WebCore::BitmapImage::frameHasAlphaAtIndex): The 'true' return is the safe return value.
+        (WebCore::BitmapImage::frameOrientationAtIndex): Caching m_orientation is now done when caching
+        frame metadata.
+        (WebCore::BitmapImage::cacheFrameInfo): Deleted.
+        (WebCore::BitmapImage::originalSize): Deleted.
+        (WebCore::BitmapImage::originalSizeRespectingOrientation): Deleted.
+        (WebCore::BitmapImage::currentFrameSize): Deleted.
+        (WebCore::BitmapImage::ensureFrameInfoIsCached): Deleted.
+        * platform/graphics/BitmapImage.h:
+        (WebCore::FrameData::FrameData):
+        * platform/graphics/GraphicsContext.h: No need to pass a scale param now.
+        * platform/graphics/ImageSource.cpp: Non-Cocoa changes.
+        (WebCore::ImageSource::subsamplingLevelForScale):
+        (WebCore::ImageSource::allowSubsamplingOfFrameAtIndex):
+        (WebCore::ImageSource::size):
+        (WebCore::ImageSource::frameSizeAtIndex):
+        (WebCore::ImageSource::createFrameAtIndex):
+        (WebCore::ImageSource::frameBytesAtIndex):
+        * platform/graphics/ImageSource.h: No longer stores subsampling state.
+        (WebCore::ImageSource::isSubsampled): Deleted.
+        * platform/graphics/cairo/BitmapImageCairo.cpp:
+        (WebCore::BitmapImage::determineMinimumSubsamplingLevel):
+        * platform/graphics/cg/BitmapImageCG.cpp:
+        (WebCore::FrameData::clear):
+        (WebCore::BitmapImage::BitmapImage): Init more members.
+        (WebCore::BitmapImage::determineMinimumSubsamplingLevel): Choose a minimum subsampling
+        level for the platform (subsample until the image area falls under a threshold).
+        (WebCore::BitmapImage::checkForSolidColor): Don't bother decoding frames if the image
+        is not 1x1. Also take care not to decode a non-subsampled image.
+        (WebCore::BitmapImage::draw): The actual bug fix is here; remove logic that
+        computed srcRectForCurrentFrame from m_size and m_originalSize; for some callers
+        srcRect was computed using the pre-subsampled size, and for others it was the subsampled size.
+        Instead, scale srcRect by mapping between the non-subsampled size, and the size of the CGImageRef
+        which is affected by subsampling.
+        (WebCore::BitmapImage::copyUnscaledFrameAtIndex):
+        * platform/graphics/cg/GraphicsContext3DCG.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage): Remove #ifdeffed code.
+        (WebCore::GraphicsContext3D::paintToCanvas):
+        * platform/graphics/cg/GraphicsContextCG.cpp:
+        (WebCore::GraphicsContext::drawNativeImage): No more weird scaling!
+        * platform/graphics/cg/ImageBufferCG.cpp:
+        (WebCore::ImageBuffer::draw):
+        * platform/graphics/cg/ImageSourceCG.cpp:
+        (WebCore::ImageSource::ImageSource):
+        (WebCore::createImageSourceOptions): Helper that always returns a new CFDictionaryRef.
+        (WebCore::imageSourceOptions): If not subsampling, return the cached CFDictionaryRef, otherwise
+        make a new options dict and return it.
+        (WebCore::ImageSource::subsamplingLevelForScale): Helper that returns a subsampling level
+        between 0 and 3 given a scale.
+        (WebCore::ImageSource::isSizeAvailable): SkipMetadata is a default value for the param now.
+        (WebCore::ImageSource::allowSubsamplingOfFrameAtIndex): We turn off subsampling for progressive
+        JPEGs because of a bug, so need this to know if a frame should be subsampled.
+        (WebCore::ImageSource::frameSizeAtIndex): The looping to find a subsampling level is now in BitmapImageCG.
+        (WebCore::ImageSource::orientationAtIndex):
+        (WebCore::ImageSource::size): Always use a subsampling level of 0 for size().
+        (WebCore::ImageSource::getHotSpot):
+        (WebCore::ImageSource::repetitionCount):
+        (WebCore::ImageSource::createFrameAtIndex): The caller mapped a scale to a level.
+        (WebCore::ImageSource::frameDurationAtIndex):
+        (WebCore::ImageSource::frameBytesAtIndex):
+        (WebCore::ImageSource::imageSourceOptions): Deleted.
+        (WebCore::ImageSource::originalSize): Deleted.
+        * platform/graphics/mac/ImageMac.mm:
+        (WebCore::BitmapImage::invalidatePlatformData): 0 -> nullptr
+        * platform/graphics/wince/ImageWinCE.cpp:
+        (WebCore::BitmapImage::determineMinimumSubsamplingLevel):
+
 2014-08-08  Alex Christensen  <achristensen@webkit.org>
 
         Progress towards using CMake on Mac.
 2014-08-08  Alex Christensen  <achristensen@webkit.org>
 
         Progress towards using CMake on Mac.
index c7e2912..72d16dd 100644 (file)
@@ -521,6 +521,7 @@ __ZN7WebCore15GraphicsContext10strokeRectERKNS_9FloatRectEf
 __ZN7WebCore15GraphicsContext11clearShadowEv
 __ZN7WebCore15GraphicsContext12setFillColorERKNS_5ColorENS_10ColorSpaceE
 __ZN7WebCore15GraphicsContext14setStrokeColorERKNS_5ColorENS_10ColorSpaceE
 __ZN7WebCore15GraphicsContext11clearShadowEv
 __ZN7WebCore15GraphicsContext12setFillColorERKNS_5ColorENS_10ColorSpaceE
 __ZN7WebCore15GraphicsContext14setStrokeColorERKNS_5ColorENS_10ColorSpaceE
+__ZN7WebCore15GraphicsContext15drawNativeImageEP7CGImageRKNS_9FloatSizeENS_10ColorSpaceERKNS_9FloatRectES9_NS_17CompositeOperatorENS_9BlendModeENS_16ImageOrientationE
 __ZN7WebCore15GraphicsContext15setFillGradientEN3WTF10PassRefPtrINS_8GradientEEE
 __ZN7WebCore15GraphicsContext18setShouldAntialiasEb
 __ZN7WebCore15GraphicsContext19setIsCALayerContextEb
 __ZN7WebCore15GraphicsContext15setFillGradientEN3WTF10PassRefPtrINS_8GradientEEE
 __ZN7WebCore15GraphicsContext18setShouldAntialiasEb
 __ZN7WebCore15GraphicsContext19setIsCALayerContextEb
@@ -2240,7 +2241,6 @@ __ZN7WebCore12EventHandler9mouseDownEP7NSEvent
 __ZN7WebCore13getRawCookiesERKNS_21NetworkStorageSessionERKNS_3URLES5_RN3WTF6VectorINS_6CookieELm0ENS6_15CrashOnOverflowEEE
 __ZN7WebCore13toDeviceSpaceERKNS_9FloatRectEP8NSWindow
 __ZN7WebCore14cookiesEnabledERKNS_21NetworkStorageSessionERKNS_3URLES5_
 __ZN7WebCore13getRawCookiesERKNS_21NetworkStorageSessionERKNS_3URLES5_RN3WTF6VectorINS_6CookieELm0ENS6_15CrashOnOverflowEEE
 __ZN7WebCore13toDeviceSpaceERKNS_9FloatRectEP8NSWindow
 __ZN7WebCore14cookiesEnabledERKNS_21NetworkStorageSessionERKNS_3URLES5_
-__ZN7WebCore15GraphicsContext15drawNativeImageEP7CGImageRKNS_9FloatSizeENS_10ColorSpaceERKNS_9FloatRectES9_fNS_17CompositeOperatorENS_9BlendModeENS_16ImageOrientationE
 __ZN7WebCore15GraphicsContextC1EP9CGContext
 __ZN7WebCore15ResourceRequest41updateFromDelegatePreservingOldPropertiesERKS0_
 __ZN7WebCore16FontPlatformDataC1EP6NSFontfbbbNS_15FontOrientationENS_16FontWidthVariantE
 __ZN7WebCore15GraphicsContextC1EP9CGContext
 __ZN7WebCore15ResourceRequest41updateFromDelegatePreservingOldPropertiesERKS0_
 __ZN7WebCore16FontPlatformDataC1EP6NSFontfbbbNS_15FontOrientationENS_16FontWidthVariantE
@@ -2591,7 +2591,6 @@ __ZN7WebCore15DatabaseTracker44emptyDatabaseFilesRemovalTaskWillBeScheduledEv
 __ZN7WebCore15DatabaseTracker7trackerEv
 __ZN7WebCore15GraphicsContext12drawBidiTextERKNS_4FontERKNS_7TextRunERKNS_10FloatPointENS1_24CustomFontNotReadyActionEPNS_10BidiStatusEi
 __ZN7WebCore15GraphicsContext15drawLineForTextERKNS_10FloatPointEfbb
 __ZN7WebCore15DatabaseTracker7trackerEv
 __ZN7WebCore15GraphicsContext12drawBidiTextERKNS_4FontERKNS_7TextRunERKNS_10FloatPointENS1_24CustomFontNotReadyActionEPNS_10BidiStatusEi
 __ZN7WebCore15GraphicsContext15drawLineForTextERKNS_10FloatPointEfbb
-__ZN7WebCore15GraphicsContext15drawNativeImageEP7CGImageRKNS_9FloatSizeENS_10ColorSpaceERKNS_9FloatRectES9_fNS_17CompositeOperatorENS_9BlendModeENS_16ImageOrientationE
 __ZN7WebCore15GraphicsContext22setEmojiDrawingEnabledEb
 __ZN7WebCore15GraphicsContext23setIsAcceleratedContextEb
 __ZN7WebCore15GraphicsContextC1EP9CGContextb
 __ZN7WebCore15GraphicsContext22setEmojiDrawingEnabledEb
 __ZN7WebCore15GraphicsContext23setIsAcceleratedContextEb
 __ZN7WebCore15GraphicsContextC1EP9CGContextb
index e8d51ec..3923235 100644 (file)
                0F1774801378B772009DA76A /* ScrollAnimatorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F17747E1378B771009DA76A /* ScrollAnimatorIOS.h */; };
                0F1774811378B772009DA76A /* ScrollAnimatorIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F17747F1378B772009DA76A /* ScrollAnimatorIOS.mm */; };
                0F29C16E1300C2E2002D794E /* AccessibilityAllInOne.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F29C16D1300C2E2002D794E /* AccessibilityAllInOne.cpp */; };
                0F1774801378B772009DA76A /* ScrollAnimatorIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F17747E1378B771009DA76A /* ScrollAnimatorIOS.h */; };
                0F1774811378B772009DA76A /* ScrollAnimatorIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F17747F1378B772009DA76A /* ScrollAnimatorIOS.mm */; };
                0F29C16E1300C2E2002D794E /* AccessibilityAllInOne.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F29C16D1300C2E2002D794E /* AccessibilityAllInOne.cpp */; };
+               0F3C725E1974874B00AEDD0C /* ImageSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3C725D1974874B00AEDD0C /* ImageSource.cpp */; };
                0F3DD44F12F5EA1B000D9190 /* ShadowBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */; };
                0F3DD45012F5EA1B000D9190 /* ShadowBlur.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */; };
                0F3F0E59157030C3006DA57F /* RenderGeometryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3F0E57157030C3006DA57F /* RenderGeometryMap.cpp */; };
                0F3DD44F12F5EA1B000D9190 /* ShadowBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */; };
                0F3DD45012F5EA1B000D9190 /* ShadowBlur.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */; };
                0F3F0E59157030C3006DA57F /* RenderGeometryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3F0E57157030C3006DA57F /* RenderGeometryMap.cpp */; };
                0F17747E1378B771009DA76A /* ScrollAnimatorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScrollAnimatorIOS.h; path = ios/ScrollAnimatorIOS.h; sourceTree = "<group>"; };
                0F17747F1378B772009DA76A /* ScrollAnimatorIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ScrollAnimatorIOS.mm; path = ios/ScrollAnimatorIOS.mm; sourceTree = "<group>"; };
                0F29C16D1300C2E2002D794E /* AccessibilityAllInOne.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityAllInOne.cpp; sourceTree = "<group>"; };
                0F17747E1378B771009DA76A /* ScrollAnimatorIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ScrollAnimatorIOS.h; path = ios/ScrollAnimatorIOS.h; sourceTree = "<group>"; };
                0F17747F1378B772009DA76A /* ScrollAnimatorIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ScrollAnimatorIOS.mm; path = ios/ScrollAnimatorIOS.mm; sourceTree = "<group>"; };
                0F29C16D1300C2E2002D794E /* AccessibilityAllInOne.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AccessibilityAllInOne.cpp; sourceTree = "<group>"; };
+               0F3C725D1974874B00AEDD0C /* ImageSource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageSource.cpp; sourceTree = "<group>"; };
                0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowBlur.cpp; sourceTree = "<group>"; };
                0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowBlur.h; sourceTree = "<group>"; };
                0F3F0E57157030C3006DA57F /* RenderGeometryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderGeometryMap.cpp; sourceTree = "<group>"; };
                0F3DD44D12F5EA1B000D9190 /* ShadowBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShadowBlur.cpp; sourceTree = "<group>"; };
                0F3DD44E12F5EA1B000D9190 /* ShadowBlur.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowBlur.h; sourceTree = "<group>"; };
                0F3F0E57157030C3006DA57F /* RenderGeometryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderGeometryMap.cpp; sourceTree = "<group>"; };
                                A8748D6612CC3763001FBA41 /* ImageOrientation.h */,
                                49291E4A134172C800E753DE /* ImageRenderingMode.h */,
                                B27535430B053814002CE64F /* ImageSource.h */,
                                A8748D6612CC3763001FBA41 /* ImageOrientation.h */,
                                49291E4A134172C800E753DE /* ImageRenderingMode.h */,
                                B27535430B053814002CE64F /* ImageSource.h */,
+                               0F3C725D1974874B00AEDD0C /* ImageSource.cpp */,
                                07941793166EA04E009416C2 /* InbandTextTrackPrivate.h */,
                                07CE77D416712A6A00C55A47 /* InbandTextTrackPrivateClient.h */,
                                2D46F04D17B96FBD005647F0 /* IntPoint.cpp */,
                                07941793166EA04E009416C2 /* InbandTextTrackPrivate.h */,
                                07CE77D416712A6A00C55A47 /* InbandTextTrackPrivateClient.h */,
                                2D46F04D17B96FBD005647F0 /* IntPoint.cpp */,
                                97BC6A441505F081001B74AC /* SQLResultSetRowList.cpp in Sources */,
                                97BC6A471505F081001B74AC /* SQLStatement.cpp in Sources */,
                                FE8A674716CDD19E00930BF8 /* SQLStatementBackend.cpp in Sources */,
                                97BC6A441505F081001B74AC /* SQLResultSetRowList.cpp in Sources */,
                                97BC6A471505F081001B74AC /* SQLStatement.cpp in Sources */,
                                FE8A674716CDD19E00930BF8 /* SQLStatementBackend.cpp in Sources */,
+                               0F3C725E1974874B00AEDD0C /* ImageSource.cpp in Sources */,
                                97BC6A4D1505F081001B74AC /* SQLStatementSync.cpp in Sources */,
                                97BC6A4F1505F081001B74AC /* SQLTransaction.cpp in Sources */,
                                FEE1811316C319E800084849 /* SQLTransactionBackend.cpp in Sources */,
                                97BC6A4D1505F081001B74AC /* SQLStatementSync.cpp in Sources */,
                                97BC6A4F1505F081001B74AC /* SQLTransaction.cpp in Sources */,
                                FEE1811316C319E800084849 /* SQLTransactionBackend.cpp in Sources */,
index f5feabd..2c76fd0 100644 (file)
@@ -289,16 +289,7 @@ LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float
     }
 #else
     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
     }
 #else
     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
-#if !PLATFORM(IOS)
         imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation());
         imageSize = LayoutSize(toBitmapImage(m_image.get())->sizeRespectingOrientation());
-#else
-    {
-        // On iOS, the image may have been subsampled to accommodate our size restrictions. However
-        // we should tell the renderer what the original size was.
-        imageSize = LayoutSize(toBitmapImage(m_image.get())->originalSizeRespectingOrientation());
-    } else if (m_image->isBitmapImage())
-        imageSize = LayoutSize(toBitmapImage(m_image.get())->originalSize());
-#endif // !PLATFORM(IOS)
 #endif // ENABLE(CSS_IMAGE_ORIENTATION)
 
     else if (m_image->isSVGImage() && sizeType == UsedSize) {
 #endif // ENABLE(CSS_IMAGE_ORIENTATION)
 
     else if (m_image->isSVGImage() && sizeType == UsedSize) {
@@ -352,6 +343,7 @@ inline void CachedImage::createImage()
     // Create the image if it doesn't yet exist.
     if (m_image)
         return;
     // Create the image if it doesn't yet exist.
     if (m_image)
         return;
+
 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
     else if (m_response.mimeType() == "application/pdf")
         m_image = PDFDocumentImage::create(this);
 #if USE(CG) && !USE(WEBKIT_IMAGE_DECODERS)
     else if (m_response.mimeType() == "application/pdf")
         m_image = PDFDocumentImage::create(this);
@@ -360,8 +352,10 @@ inline void CachedImage::createImage()
         RefPtr<SVGImage> svgImage = SVGImage::create(this);
         m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get());
         m_image = svgImage.release();
         RefPtr<SVGImage> svgImage = SVGImage::create(this);
         m_svgImageCache = std::make_unique<SVGImageCache>(svgImage.get());
         m_image = svgImage.release();
-    } else
+    } else {
         m_image = BitmapImage::create(this);
         m_image = BitmapImage::create(this);
+        toBitmapImage(m_image.get())->setAllowSubsampling(m_loader && m_loader->frameLoader()->frame().settings().imageSubsamplingEnabled());
+    }
 
     if (m_image) {
         // Send queued container size requests.
 
     if (m_image) {
         // Send queued container size requests.
@@ -518,8 +512,6 @@ void CachedImage::changedInRect(const Image* image, const IntRect& rect)
 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
 {
     Image* image = imageForRenderer(renderer);
 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
 {
     Image* image = imageForRenderer(renderer);
-    if (image->isBitmapImage())
-        image->nativeImageForCurrentFrame(); // force decode
     return image->currentFrameKnownToBeOpaque();
 }
 
     return image->currentFrameKnownToBeOpaque();
 }
 
index 7fe56bf..93ae148 100644 (file)
@@ -59,7 +59,7 @@ public:
     Image* image(); // Returns the nullImage() if the image is not available yet.
     Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet.
     bool hasImage() const { return m_image.get(); }
     Image* image(); // Returns the nullImage() if the image is not available yet.
     Image* imageForRenderer(const RenderObject*); // Returns the nullImage() if the image is not available yet.
     bool hasImage() const { return m_image.get(); }
-    bool currentFrameKnownToBeOpaque(const RenderElement*); // Side effect: ensures decoded image is in cache, therefore should only be called when about to draw the image.
+    bool currentFrameKnownToBeOpaque(const RenderElement*);
 
     std::pair<Image*, float> brokenImage(float deviceScaleFactor) const; // Returns an image and the image's resolution scale factor.
     bool willPaintBrokenImage() const; 
 
     std::pair<Image*, float> brokenImage(float deviceScaleFactor) const; // Returns an image and the image's resolution scale factor.
     bool willPaintBrokenImage() const; 
index e8653df..d713412 100644 (file)
@@ -127,6 +127,7 @@ static const bool defaultAcceleratedCompositingForFixedPositionEnabled = true;
 static const bool defaultMediaPlaybackAllowsInline = false;
 static const bool defaultMediaPlaybackRequiresUserGesture = true;
 static const bool defaultShouldRespectImageOrientation = true;
 static const bool defaultMediaPlaybackAllowsInline = false;
 static const bool defaultMediaPlaybackRequiresUserGesture = true;
 static const bool defaultShouldRespectImageOrientation = true;
+static const bool defaultImageSubsamplingEnabled = true;
 static const bool defaultScrollingTreeIncludesFrames = true;
 #else
 static const bool defaultFixedPositionCreatesStackingContext = false;
 static const bool defaultScrollingTreeIncludesFrames = true;
 #else
 static const bool defaultFixedPositionCreatesStackingContext = false;
@@ -135,6 +136,7 @@ static const bool defaultAcceleratedCompositingForFixedPositionEnabled = false;
 static const bool defaultMediaPlaybackAllowsInline = true;
 static const bool defaultMediaPlaybackRequiresUserGesture = false;
 static const bool defaultShouldRespectImageOrientation = false;
 static const bool defaultMediaPlaybackAllowsInline = true;
 static const bool defaultMediaPlaybackRequiresUserGesture = false;
 static const bool defaultShouldRespectImageOrientation = false;
+static const bool defaultImageSubsamplingEnabled = false;
 static const bool defaultScrollingTreeIncludesFrames = false;
 #endif
 
 static const bool defaultScrollingTreeIncludesFrames = false;
 #endif
 
index 890bb3f..87d43d0 100644 (file)
@@ -141,6 +141,7 @@ notificationsEnabled initial=true
 needsIsLoadingInAPISenseQuirk initial=false
 
 shouldRespectImageOrientation initial=defaultShouldRespectImageOrientation
 needsIsLoadingInAPISenseQuirk initial=false
 
 shouldRespectImageOrientation initial=defaultShouldRespectImageOrientation
+imageSubsamplingEnabled initial=defaultImageSubsamplingEnabled
 wantsBalancedSetDefersLoadingBehavior initial=false
 requestAnimationFrameEnabled initial=true
 deviceSupportsTouch initial=false
 wantsBalancedSetDefersLoadingBehavior initial=false
 requestAnimationFrameEnabled initial=true
 deviceSupportsTouch initial=false
index a77d5af..6603146 100644 (file)
 
 namespace WebCore {
 
 
 namespace WebCore {
 
-// FIXME: We should better integrate the iOS and non-iOS code in this class. Unlike other ports, the
-// iOS port caches the metadata for a frame without decoding the image.
 BitmapImage::BitmapImage(ImageObserver* observer)
     : Image(observer)
 BitmapImage::BitmapImage(ImageObserver* observer)
     : Image(observer)
+    , m_minimumSubsamplingLevel(0)
+    , m_imageOrientation(OriginTopLeft)
+    , m_shouldRespectImageOrientation(false)
     , m_currentFrame(0)
     , m_currentFrame(0)
-    , m_frames(0)
     , m_repetitionCount(cAnimationNone)
     , m_repetitionCountStatus(Unknown)
     , m_repetitionsComplete(0)
     , m_repetitionCount(cAnimationNone)
     , m_repetitionCountStatus(Unknown)
     , m_repetitionsComplete(0)
@@ -61,6 +61,9 @@ BitmapImage::BitmapImage(ImageObserver* observer)
     // FIXME: We should expose a setting to enable/disable progressive loading remove the PLATFORM(IOS)-guard.
     , m_progressiveLoadChunkTime(0)
     , m_progressiveLoadChunkCount(0)
     // FIXME: We should expose a setting to enable/disable progressive loading remove the PLATFORM(IOS)-guard.
     , m_progressiveLoadChunkTime(0)
     , m_progressiveLoadChunkCount(0)
+    , m_allowSubsampling(true)
+#else
+    , m_allowSubsampling(false)
 #endif
     , m_isSolidColor(false)
     , m_checkedForSolidColor(false)
 #endif
     , m_isSolidColor(false)
     , m_checkedForSolidColor(false)
@@ -80,6 +83,17 @@ BitmapImage::~BitmapImage()
     stopAnimation();
 }
 
     stopAnimation();
 }
 
+bool BitmapImage::haveFrameAtIndex(size_t index)
+{
+    if (index >= frameCount())
+        return false;
+
+    if (index >= m_frames.size())
+        return false;
+
+    return m_frames[index].m_frame;
+}
+
 bool BitmapImage::hasSingleSecurityOrigin() const
 {
     return true;
 bool BitmapImage::hasSingleSecurityOrigin() const
 {
     return true;
@@ -152,11 +166,7 @@ void BitmapImage::destroyMetadataAndNotify(unsigned frameBytesCleared)
         imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesCleared));
 }
 
         imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesCleared));
 }
 
-#if PLATFORM(IOS)
-void BitmapImage::cacheFrame(size_t index, float scaleHint)
-#else
-void BitmapImage::cacheFrame(size_t index)
-#endif
+void BitmapImage::cacheFrame(size_t index, SubsamplingLevel subsamplingLevel, ImageFrameCaching frameCaching)
 {
     size_t numFrames = frameCount();
     ASSERT(m_decodedSize == 0 || numFrames > 1);
 {
     size_t numFrames = frameCount();
     ASSERT(m_decodedSize == 0 || numFrames > 1);
@@ -164,26 +174,27 @@ void BitmapImage::cacheFrame(size_t index)
     if (m_frames.size() < numFrames)
         m_frames.grow(numFrames);
 
     if (m_frames.size() < numFrames)
         m_frames.grow(numFrames);
 
-#if PLATFORM(IOS)
-    m_frames[index].m_frame = m_source.createFrameAtIndex(index, &scaleHint);
-    m_frames[index].m_subsamplingScale = scaleHint;
-#else
-    m_frames[index].m_frame = m_source.createFrameAtIndex(index);
-#endif
-    if (numFrames == 1 && m_frames[index].m_frame)
-        checkForSolidColor();
+    if (frameCaching == CacheMetadataAndFrame) {
+        m_frames[index].m_frame = m_source.createFrameAtIndex(index, subsamplingLevel);
+        m_frames[index].m_subsamplingLevel = subsamplingLevel;
+        if (numFrames == 1 && m_frames[index].m_frame)
+            checkForSolidColor();
+    }
 
     m_frames[index].m_orientation = m_source.orientationAtIndex(index);
     m_frames[index].m_haveMetadata = true;
     m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
 
     m_frames[index].m_orientation = m_source.orientationAtIndex(index);
     m_frames[index].m_haveMetadata = true;
     m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
+
     if (repetitionCount(false) != cAnimationNone)
         m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
     if (repetitionCount(false) != cAnimationNone)
         m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
+
     m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
     m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
-    m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index);
+    m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index, subsamplingLevel);
 
 
-    const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size);
-    if (frameSize != m_size)
+    const IntSize frameSize(index ? m_source.frameSizeAtIndex(index, subsamplingLevel) : m_size);
+    if (!subsamplingLevel && frameSize != m_size)
         m_hasUniformFrameSize = false;
         m_hasUniformFrameSize = false;
+
     if (m_frames[index].m_frame) {
         int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes);
         m_decodedSize += deltaBytes;
     if (m_frames[index].m_frame) {
         int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes);
         m_decodedSize += deltaBytes;
@@ -196,30 +207,15 @@ void BitmapImage::cacheFrame(size_t index)
     }
 }
 
     }
 }
 
-#if PLATFORM(IOS)
-void BitmapImage::cacheFrameInfo(size_t index)
-{
-    size_t numFrames = frameCount();
-
-    if (m_frames.size() < numFrames)
-        m_frames.resize(numFrames);
-
-    ASSERT(!m_frames[index].m_haveInfo);
-
-    if (shouldAnimate())
-        m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
-    m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
-    m_frames[index].m_haveInfo = true;
-}
-#endif
-
 void BitmapImage::didDecodeProperties() const
 {
     if (m_decodedSize)
         return;
 void BitmapImage::didDecodeProperties() const
 {
     if (m_decodedSize)
         return;
+
     size_t updatedSize = m_source.bytesDecodedToDetermineProperties();
     if (m_decodedPropertiesSize == updatedSize)
         return;
     size_t updatedSize = m_source.bytesDecodedToDetermineProperties();
     if (m_decodedPropertiesSize == updatedSize)
         return;
+
     int deltaBytes = updatedSize - m_decodedPropertiesSize;
 #if !ASSERT_DISABLED
     bool overflow = updatedSize > m_decodedPropertiesSize && deltaBytes < 0;
     int deltaBytes = updatedSize - m_decodedPropertiesSize;
 #if !ASSERT_DISABLED
     bool overflow = updatedSize > m_decodedPropertiesSize && deltaBytes < 0;
@@ -238,13 +234,13 @@ void BitmapImage::updateSize(ImageOrientationDescription description) const
 
     m_size = m_source.size(description);
     m_sizeRespectingOrientation = m_source.size(ImageOrientationDescription(RespectImageOrientation, description.imageOrientation()));
 
     m_size = m_source.size(description);
     m_sizeRespectingOrientation = m_source.size(ImageOrientationDescription(RespectImageOrientation, description.imageOrientation()));
+
     m_imageOrientation = static_cast<unsigned>(description.imageOrientation());
     m_shouldRespectImageOrientation = static_cast<unsigned>(description.respectImageOrientation());
     m_imageOrientation = static_cast<unsigned>(description.imageOrientation());
     m_shouldRespectImageOrientation = static_cast<unsigned>(description.respectImageOrientation());
-#if PLATFORM(IOS)
-    m_originalSize = m_source.originalSize();
-    m_originalSizeRespectingOrientation = m_source.originalSize(RespectImageOrientation);
-#endif
+
     m_haveSize = true;
     m_haveSize = true;
+
+    determineMinimumSubsamplingLevel();
     didDecodeProperties();
 }
 
     didDecodeProperties();
 }
 
@@ -260,29 +256,6 @@ IntSize BitmapImage::sizeRespectingOrientation(ImageOrientationDescription descr
     return m_sizeRespectingOrientation;
 }
 
     return m_sizeRespectingOrientation;
 }
 
-#if PLATFORM(IOS)
-FloatSize BitmapImage::originalSize() const
-{
-    updateSize();
-    return m_originalSize;
-}
-
-IntSize BitmapImage::originalSizeRespectingOrientation() const
-{
-    updateSize();
-    return m_originalSizeRespectingOrientation;
-}
-#endif
-
-IntSize BitmapImage::currentFrameSize() const
-{
-    if (!m_currentFrame || m_hasUniformFrameSize)
-        return IntSize(size());
-    IntSize frameSize = m_source.frameSizeAtIndex(m_currentFrame);
-    didDecodeProperties();
-    return frameSize;
-}
-
 bool BitmapImage::getHotSpot(IntPoint& hotSpot) const
 {
     bool result = m_source.getHotSpot(hotSpot);
 bool BitmapImage::getHotSpot(IntPoint& hotSpot) const
 {
     bool result = m_source.getHotSpot(hotSpot);
@@ -323,6 +296,7 @@ bool BitmapImage::dataChanged(bool allDataReceived)
     }
     destroyMetadataAndNotify(frameBytesCleared);
 #else
     }
     destroyMetadataAndNotify(frameBytesCleared);
 #else
+    // FIXME: why is this different for iOS?
     int deltaBytes = 0;
     if (!m_frames.isEmpty()) {
         int bytes = m_frames[m_frames.size() - 1].m_frameBytes;
     int deltaBytes = 0;
     if (!m_frames.isEmpty()) {
         int bytes = m_frames[m_frames.size() - 1].m_frameBytes;
@@ -390,85 +364,58 @@ bool BitmapImage::isSizeAvailable()
     return m_sizeAvailable;
 }
 
     return m_sizeAvailable;
 }
 
-#if !PLATFORM(IOS)
-bool BitmapImage::ensureFrameIsCached(size_t index)
+bool BitmapImage::ensureFrameIsCached(size_t index, ImageFrameCaching frameCaching)
 {
     if (index >= frameCount())
         return false;
 
 {
     if (index >= frameCount())
         return false;
 
-    if (index >= m_frames.size() || !m_frames[index].m_frame)
-        cacheFrame(index);
-    return true;
-}
-#else
-bool BitmapImage::ensureFrameInfoIsCached(size_t index)
-{
-    if (index >= frameCount())
-        return false;
+    if (index >= m_frames.size()
+        || (frameCaching == CacheMetadataAndFrame && !m_frames[index].m_frame)
+        || (frameCaching == CacheMetadataOnly && !m_frames[index].m_haveMetadata))
+        cacheFrame(index, 0, frameCaching);
 
 
-    if (index >= m_frames.size() || !m_frames[index].m_haveInfo)
-        cacheFrameInfo(index);
     return true;
 }
     return true;
 }
-#endif
-
-PassNativeImagePtr BitmapImage::frameAtIndex(size_t index)
-{
-#if PLATFORM(IOS)
-    return frameAtIndex(index, 1.0f);
-#else
-    if (!ensureFrameIsCached(index))
-        return nullptr;
-    return m_frames[index].m_frame;
-#endif
-}
 
 
-#if PLATFORM(IOS)
-PassNativeImagePtr BitmapImage::frameAtIndex(size_t index, float scaleHint)
+PassNativeImagePtr BitmapImage::frameAtIndex(size_t index, float presentationScaleHint)
 {
     if (index >= frameCount())
         return nullptr;
 
 {
     if (index >= frameCount())
         return nullptr;
 
-    if (index >= m_frames.size() || !m_frames[index].m_frame)
-        cacheFrame(index, scaleHint);
-    else if (std::min(1.0f, scaleHint) > m_frames[index].m_subsamplingScale) {
+    SubsamplingLevel subsamplingLevel = std::min(m_source.subsamplingLevelForScale(presentationScaleHint), m_minimumSubsamplingLevel);
+
+    // We may have cached a frame with a higher subsampling level, in which case we need to
+    // re-decode with a lower level.
+    if (index < m_frames.size() && m_frames[index].m_frame && subsamplingLevel < m_frames[index].m_subsamplingLevel) {
         // If the image is already cached, but at too small a size, re-decode a larger version.
         int sizeChange = -m_frames[index].m_frameBytes;
         // If the image is already cached, but at too small a size, re-decode a larger version.
         int sizeChange = -m_frames[index].m_frameBytes;
-        ASSERT(static_cast<int>(m_decodedSize) + sizeChange >= 0);
         m_frames[index].clear(true);
         invalidatePlatformData();
         m_decodedSize += sizeChange;
         if (imageObserver())
             imageObserver()->decodedSizeChanged(this, sizeChange);
         m_frames[index].clear(true);
         invalidatePlatformData();
         m_decodedSize += sizeChange;
         if (imageObserver())
             imageObserver()->decodedSizeChanged(this, sizeChange);
-
-        cacheFrame(index, scaleHint);
     }
     }
+
+    // If we haven't fetched a frame yet, do so.
+    if (index >= m_frames.size() || !m_frames[index].m_frame)
+        cacheFrame(index, subsamplingLevel, CacheMetadataAndFrame);
+
     return m_frames[index].m_frame;
 }
     return m_frames[index].m_frame;
 }
-#endif
 
 bool BitmapImage::frameIsCompleteAtIndex(size_t index)
 {
 
 bool BitmapImage::frameIsCompleteAtIndex(size_t index)
 {
-#if PLATFORM(IOS)
-    // FIXME: cacheFrameInfo does not set m_isComplete. Should it?
-    if (!ensureFrameInfoIsCached(index))
-        return false;
-#else
-    if (!ensureFrameIsCached(index))
+    if (!ensureFrameIsCached(index, CacheMetadataOnly))
         return false;
         return false;
-#endif
+
     return m_frames[index].m_isComplete;
 }
 
 float BitmapImage::frameDurationAtIndex(size_t index)
 {
     return m_frames[index].m_isComplete;
 }
 
 float BitmapImage::frameDurationAtIndex(size_t index)
 {
-#if PLATFORM(IOS)
-    if (!ensureFrameInfoIsCached(index))
+    if (!ensureFrameIsCached(index, CacheMetadataOnly))
         return 0;
         return 0;
-#else
-    if (!ensureFrameIsCached(index))
-        return 0;
-#endif
+
     return m_frames[index].m_duration;
 }
 
     return m_frames[index].m_duration;
 }
 
@@ -479,13 +426,9 @@ PassNativeImagePtr BitmapImage::nativeImageForCurrentFrame()
 
 bool BitmapImage::frameHasAlphaAtIndex(size_t index)
 {
 
 bool BitmapImage::frameHasAlphaAtIndex(size_t index)
 {
-#if PLATFORM(IOS)
-    if (!ensureFrameInfoIsCached(index))
-        return true; // FIXME: Why would an invalid index return true here?
-#else
-    if (m_frames.size() <= index)
+    if (!ensureFrameIsCached(index, CacheMetadataOnly))
         return true;
         return true;
-#endif
+
     if (m_frames[index].m_haveMetadata)
         return m_frames[index].m_hasAlpha;
 
     if (m_frames[index].m_haveMetadata)
         return m_frames[index].m_hasAlpha;
 
@@ -499,14 +442,8 @@ bool BitmapImage::currentFrameKnownToBeOpaque()
 
 ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index)
 {
 
 ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index)
 {
-#if PLATFORM(IOS)
-    // FIXME: cacheFrameInfo does not set m_orientation. Should it?
-    if (!ensureFrameInfoIsCached(index))
+    if (!ensureFrameIsCached(index, CacheMetadataOnly))
         return DefaultImageOrientation;
         return DefaultImageOrientation;
-#else
-    if (!ensureFrameIsCached(index))
-        return DefaultImageOrientation;
-#endif
 
     if (m_frames[index].m_haveMetadata)
         return m_frames[index].m_orientation;
 
     if (m_frames[index].m_haveMetadata)
         return m_frames[index].m_orientation;
index 2301848..96410d4 100644 (file)
@@ -70,10 +70,7 @@ public:
     FrameData()
         : m_frame(0)
         , m_orientation(DefaultImageOrientation)
     FrameData()
         : m_frame(0)
         , m_orientation(DefaultImageOrientation)
-#if PLATFORM(IOS)
-        , m_subsamplingScale(0)
-        , m_haveInfo(false)
-#endif
+        , m_subsamplingLevel(0)
         , m_duration(0)
         , m_haveMetadata(false)
         , m_isComplete(false)
         , m_duration(0)
         , m_haveMetadata(false)
         , m_isComplete(false)
@@ -93,10 +90,7 @@ public:
 
     NativeImagePtr m_frame;
     ImageOrientation m_orientation;
 
     NativeImagePtr m_frame;
     ImageOrientation m_orientation;
-#if PLATFORM(IOS)
-    float m_subsamplingScale;
-    bool m_haveInfo;
-#endif
+    SubsamplingLevel m_subsamplingLevel;
     float m_duration;
     bool m_haveMetadata : 1;
     bool m_isComplete : 1;
     float m_duration;
     bool m_haveMetadata : 1;
     bool m_isComplete : 1;
@@ -108,9 +102,6 @@ public:
 // BitmapImage Class
 // =================================================
 
 // BitmapImage Class
 // =================================================
 
-// FIXME: We should better integrate the iOS and non-iOS code in this class. Unlike other ports, the
-// iOS port caches the metadata for a frame without decoding the image.
-
 class BitmapImage final : public Image {
     friend class GeneratedImage;
     friend class CrossfadeGeneratedImage;
 class BitmapImage final : public Image {
     friend class GeneratedImage;
     friend class CrossfadeGeneratedImage;
@@ -137,11 +128,7 @@ public:
     // FloatSize due to override.
     virtual FloatSize size() const override;
     IntSize sizeRespectingOrientation(ImageOrientationDescription = ImageOrientationDescription()) const;
     // FloatSize due to override.
     virtual FloatSize size() const override;
     IntSize sizeRespectingOrientation(ImageOrientationDescription = ImageOrientationDescription()) const;
-#if PLATFORM(IOS)
-    virtual FloatSize originalSize() const override;
-    IntSize originalSizeRespectingOrientation() const;
-#endif
-    IntSize currentFrameSize() const;
+
     virtual bool getHotSpot(IntPoint&) const override;
 
     unsigned decodedSize() const { return m_decodedSize; }
     virtual bool getHotSpot(IntPoint&) const override;
 
     unsigned decodedSize() const { return m_decodedSize; }
@@ -194,8 +181,12 @@ public:
     
     bool canAnimate();
 
     
     bool canAnimate();
 
+    bool allowSubsampling() const { return m_allowSubsampling; }
+    void setAllowSubsampling(bool allowSubsampling) { m_allowSubsampling = allowSubsampling; }
+    
 private:
     void updateSize(ImageOrientationDescription = ImageOrientationDescription()) const;
 private:
     void updateSize(ImageOrientationDescription = ImageOrientationDescription()) const;
+    void determineMinimumSubsamplingLevel() const;
 
 protected:
     enum RepetitionCountStatus {
 
 protected:
     enum RepetitionCountStatus {
@@ -218,30 +209,24 @@ protected:
 #endif
 
     size_t currentFrame() const { return m_currentFrame; }
 #endif
 
     size_t currentFrame() const { return m_currentFrame; }
-#if PLATFORM(IOS)
-    PassNativeImagePtr frameAtIndex(size_t, float scaleHint);
-    PassNativeImagePtr copyUnscaledFrameAtIndex(size_t);
-#endif
     size_t frameCount();
     size_t frameCount();
-    PassNativeImagePtr frameAtIndex(size_t);
+
+    PassNativeImagePtr frameAtIndex(size_t, float presentationScaleHint = 1);
+    PassNativeImagePtr copyUnscaledFrameAtIndex(size_t);
+
+    bool haveFrameAtIndex(size_t);
+
     bool frameIsCompleteAtIndex(size_t);
     float frameDurationAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t);
     ImageOrientation frameOrientationAtIndex(size_t);
 
     // Decodes and caches a frame. Never accessed except internally.
     bool frameIsCompleteAtIndex(size_t);
     float frameDurationAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t);
     ImageOrientation frameOrientationAtIndex(size_t);
 
     // Decodes and caches a frame. Never accessed except internally.
-#if PLATFORM(IOS)
-    void cacheFrame(size_t index, float scaleHint);
+    enum ImageFrameCaching { CacheMetadataOnly, CacheMetadataAndFrame };
+    void cacheFrame(size_t index, SubsamplingLevel, ImageFrameCaching = CacheMetadataAndFrame);
 
 
-    // Cache frame metadata without decoding image.
-    void cacheFrameInfo(size_t index);
     // Called before accessing m_frames[index] for info without decoding. Returns false on index out of bounds.
     // Called before accessing m_frames[index] for info without decoding. Returns false on index out of bounds.
-    bool ensureFrameInfoIsCached(size_t index);
-#else
-    void cacheFrame(size_t index);
-    // Called before accessing m_frames[index]. Returns false on index out of bounds.
-    bool ensureFrameIsCached(size_t index);
-#endif
+    bool ensureFrameIsCached(size_t index, ImageFrameCaching = CacheMetadataAndFrame);
 
     // Called to invalidate cached data.  When |destroyAll| is true, we wipe out
     // the entire frame buffer cache and tell the image source to destroy
 
     // Called to invalidate cached data.  When |destroyAll| is true, we wipe out
     // the entire frame buffer cache and tell the image source to destroy
@@ -301,13 +286,12 @@ private:
     ImageSource m_source;
     mutable IntSize m_size; // The size to use for the overall image (will just be the size of the first image).
     mutable IntSize m_sizeRespectingOrientation;
     ImageSource m_source;
     mutable IntSize m_size; // The size to use for the overall image (will just be the size of the first image).
     mutable IntSize m_sizeRespectingOrientation;
+
+    mutable SubsamplingLevel m_minimumSubsamplingLevel;
+
     mutable unsigned m_imageOrientation : 4; // ImageOrientationEnum
     mutable unsigned m_shouldRespectImageOrientation : 1; // RespectImageOrientationEnum
 
     mutable unsigned m_imageOrientation : 4; // ImageOrientationEnum
     mutable unsigned m_shouldRespectImageOrientation : 1; // RespectImageOrientationEnum
 
-#if PLATFORM(IOS)
-    mutable IntSize m_originalSize; // The size of the unsubsampled image.
-    mutable IntSize m_originalSizeRespectingOrientation;
-#endif
     size_t m_currentFrame; // The index of the current frame of animation.
     Vector<FrameData, 1> m_frames; // An array of the cached frames of the animation. We have to ref frames to pin them in the cache.
 
     size_t m_currentFrame; // The index of the current frame of animation.
     Vector<FrameData, 1> m_frames; // An array of the cached frames of the animation. We have to ref frames to pin them in the cache.
 
@@ -336,6 +320,7 @@ private:
     uint16_t m_progressiveLoadChunkCount;
 #endif
 
     uint16_t m_progressiveLoadChunkCount;
 #endif
 
+    bool m_allowSubsampling : 1; // Whether we should attempt subsampling if this image is very large.
     bool m_isSolidColor : 1; // Whether or not we are a 1x1 solid image.
     bool m_checkedForSolidColor : 1; // Whether we've checked the frame for solid color.
 
     bool m_isSolidColor : 1; // Whether or not we are a 1x1 solid image.
     bool m_checkedForSolidColor : 1; // Whether we've checked the frame for solid color.
 
index ac1e050..79d5118 100644 (file)
@@ -283,7 +283,7 @@ namespace WebCore {
         void applyFillPattern();
         void drawPath(const Path&);
 
         void applyFillPattern();
         void drawPath(const Path&);
 
-        void drawNativeImage(PassNativeImagePtr, const FloatSize& selfSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, float scale = 1, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = DefaultImageOrientation);
+        void drawNativeImage(PassNativeImagePtr, const FloatSize& selfSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = DefaultImageOrientation);
 
         // Allow font smoothing (LCD antialiasing). Not part of the graphics state.
         void setAllowsFontSmoothing(bool);
 
         // Allow font smoothing (LCD antialiasing). Not part of the graphics state.
         void setAllowsFontSmoothing(bool);
index f6b765f..74ef053 100644 (file)
@@ -29,6 +29,8 @@
 #include "config.h"
 #include "ImageSource.h"
 
 #include "config.h"
 #include "ImageSource.h"
 
+#if !USE(CG)
+
 #include "ImageDecoder.h"
 
 #include "ImageOrientation.h"
 #include "ImageDecoder.h"
 
 #include "ImageOrientation.h"
@@ -94,6 +96,16 @@ String ImageSource::filenameExtension() const
     return m_decoder ? m_decoder->filenameExtension() : String();
 }
 
     return m_decoder ? m_decoder->filenameExtension() : String();
 }
 
+SubsamplingLevel ImageSource::subsamplingLevelForScale(float) const
+{
+    return 0;
+}
+
+bool ImageSource::allowSubsamplingOfFrameAtIndex(size_t) const
+{
+    return false;
+}
+
 bool ImageSource::isSizeAvailable()
 {
     return m_decoder && m_decoder->isSizeAvailable();
 bool ImageSource::isSizeAvailable()
 {
     return m_decoder && m_decoder->isSizeAvailable();
@@ -101,10 +113,10 @@ bool ImageSource::isSizeAvailable()
 
 IntSize ImageSource::size(ImageOrientationDescription description) const
 {
 
 IntSize ImageSource::size(ImageOrientationDescription description) const
 {
-    return frameSizeAtIndex(0, description);
+    return frameSizeAtIndex(0, 0, description);
 }
 
 }
 
-IntSize ImageSource::frameSizeAtIndex(size_t index, ImageOrientationDescription description) const
+IntSize ImageSource::frameSizeAtIndex(size_t index, SubsamplingLevel, ImageOrientationDescription description) const
 {
     if (!m_decoder)
         return IntSize();
 {
     if (!m_decoder)
         return IntSize();
@@ -136,10 +148,8 @@ size_t ImageSource::frameCount() const
     return m_decoder ? m_decoder->frameCount() : 0;
 }
 
     return m_decoder ? m_decoder->frameCount() : 0;
 }
 
-PassNativeImagePtr ImageSource::createFrameAtIndex(size_t index, float* scale)
+PassNativeImagePtr ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel)
 {
 {
-    UNUSED_PARAM(scale);
-
     if (!m_decoder)
         return 0;
 
     if (!m_decoder)
         return 0;
 
@@ -197,7 +207,7 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index)
     return buffer && buffer->status() == ImageFrame::FrameComplete;
 }
 
     return buffer && buffer->status() == ImageFrame::FrameComplete;
 }
 
-unsigned ImageSource::frameBytesAtIndex(size_t index) const
+unsigned ImageSource::frameBytesAtIndex(size_t index, SubsamplingLevel) const
 {
     if (!m_decoder)
         return 0;
 {
     if (!m_decoder)
         return 0;
@@ -205,3 +215,5 @@ unsigned ImageSource::frameBytesAtIndex(size_t index) const
 }
 
 }
 }
 
 }
+
+#endif // USE(CG)
index c4ee98f..281f61c 100644 (file)
@@ -77,6 +77,9 @@ const int cAnimationLoopOnce = 0;
 const int cAnimationLoopInfinite = -1;
 const int cAnimationNone = -2;
 
 const int cAnimationLoopInfinite = -1;
 const int cAnimationNone = -2;
 
+// SubsamplingLevel. 0 is no subsampling, 1 is half dimensions on each axis etc.
+typedef short SubsamplingLevel;
+
 class ImageSource {
     WTF_MAKE_NONCOPYABLE(ImageSource);
 public:
 class ImageSource {
     WTF_MAKE_NONCOPYABLE(ImageSource);
 public:
@@ -131,14 +134,14 @@ public:
     void setData(SharedBuffer* data, bool allDataReceived);
     String filenameExtension() const;
 
     void setData(SharedBuffer* data, bool allDataReceived);
     String filenameExtension() const;
 
+    SubsamplingLevel subsamplingLevelForScale(float) const;
+    bool allowSubsamplingOfFrameAtIndex(size_t) const;
+
     bool isSizeAvailable();
     bool isSizeAvailable();
+    // Always original size, without subsampling.
     IntSize size(ImageOrientationDescription = ImageOrientationDescription()) const;
     IntSize size(ImageOrientationDescription = ImageOrientationDescription()) const;
-    IntSize frameSizeAtIndex(size_t, ImageOrientationDescription = ImageOrientationDescription()) const;
-
-#if PLATFORM(IOS)
-    IntSize originalSize(RespectImageOrientationEnum = DoNotRespectImageOrientation) const;
-    bool isSubsampled() const { return m_baseSubsampling; }
-#endif
+    // Size of optionally subsampled frame.
+    IntSize frameSizeAtIndex(size_t, SubsamplingLevel = 0, ImageOrientationDescription = ImageOrientationDescription()) const;
 
     bool getHotSpot(IntPoint&) const;
 
 
     bool getHotSpot(IntPoint&) const;
 
@@ -150,7 +153,7 @@ public:
 
     // Callers should not call this after calling clear() with a higher index;
     // see comments on clear() above.
 
     // Callers should not call this after calling clear() with a higher index;
     // see comments on clear() above.
-    PassNativeImagePtr createFrameAtIndex(size_t, float* scale = 0);
+    PassNativeImagePtr createFrameAtIndex(size_t, SubsamplingLevel = 0);
 
     float frameDurationAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha.
 
     float frameDurationAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha.
@@ -159,7 +162,7 @@ public:
 
     // Return the number of bytes in the decoded frame. If the frame is not yet
     // decoded then return 0.
 
     // Return the number of bytes in the decoded frame. If the frame is not yet
     // decoded then return 0.
-    unsigned frameBytesAtIndex(size_t) const;
+    unsigned frameBytesAtIndex(size_t, SubsamplingLevel = 0) const;
 
 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
     static unsigned maxPixelsPerDecodedImage() { return s_maxPixelsPerDecodedImage; }
 
 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
     static unsigned maxPixelsPerDecodedImage() { return s_maxPixelsPerDecodedImage; }
@@ -176,11 +179,6 @@ private:
 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
     static unsigned s_maxPixelsPerDecodedImage;
 #endif
 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
     static unsigned s_maxPixelsPerDecodedImage;
 #endif
-#if PLATFORM(IOS)
-    mutable int m_baseSubsampling;
-    mutable bool m_isProgressive;
-    CFDictionaryRef imageSourceOptions(ShouldSkipMetadata, int subsampling = 0) const;
-#endif
 };
 
 }
 };
 
 }
index aeb76e0..0802257 100644 (file)
@@ -119,6 +119,11 @@ void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const Flo
         imageObserver()->didDraw(this);
 }
 
         imageObserver()->didDraw(this);
 }
 
+void BitmapImage::determineMinimumSubsamplingLevel() const
+{
+    m_minimumSubsamplingLevel = 0;
+}
+
 void BitmapImage::checkForSolidColor()
 {
     m_isSolidColor = false;
 void BitmapImage::checkForSolidColor()
 {
     m_isSolidColor = false;
index b042765..8a4a13e 100644 (file)
@@ -29,6 +29,7 @@
 #if USE(CG)
 
 #include "FloatConversion.h"
 #if USE(CG)
 
 #include "FloatConversion.h"
+#include "GeometryUtilities.h"
 #include "GraphicsContextCG.h"
 #include "ImageObserver.h"
 #include "SubimageCacheWithTimer.h"
 #include "GraphicsContextCG.h"
 #include "ImageObserver.h"
 #include "SubimageCacheWithTimer.h"
@@ -58,12 +59,7 @@ bool FrameData::clear(bool clearMetadata)
         m_haveMetadata = false;
 
     m_orientation = DefaultImageOrientation;
         m_haveMetadata = false;
 
     m_orientation = DefaultImageOrientation;
-
-#if PLATFORM(IOS)
-    m_frameBytes = 0;
-    m_subsamplingScale = 1;
-    m_haveInfo = false;
-#endif
+    m_subsamplingLevel = 0;
 
     if (m_frame) {
 #if CACHE_SUBIMAGES
 
     if (m_frame) {
 #if CACHE_SUBIMAGES
@@ -78,8 +74,10 @@ bool FrameData::clear(bool clearMetadata)
 
 BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer)
     : Image(observer)
 
 BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer)
     : Image(observer)
+    , m_minimumSubsamplingLevel(0)
+    , m_imageOrientation(OriginTopLeft)
+    , m_shouldRespectImageOrientation(false)
     , m_currentFrame(0)
     , m_currentFrame(0)
-    , m_frames(0)
     , m_repetitionCount(cAnimationNone)
     , m_repetitionCountStatus(Unknown)
     , m_repetitionsComplete(0)
     , m_repetitionCount(cAnimationNone)
     , m_repetitionCountStatus(Unknown)
     , m_repetitionsComplete(0)
@@ -103,50 +101,62 @@ BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer)
     // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0.
     m_sizeRespectingOrientation = m_size;
 
     // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0.
     m_sizeRespectingOrientation = m_size;
 
-#if PLATFORM(IOS)
-    m_originalSize = m_size;
-    m_originalSizeRespectingOrientation = m_size;
-#endif
-
     m_frames.grow(1);
     m_frames[0].m_frame = CGImageRetain(cgImage);
     m_frames[0].m_hasAlpha = true;
     m_frames[0].m_haveMetadata = true;
 
     m_frames.grow(1);
     m_frames[0].m_frame = CGImageRetain(cgImage);
     m_frames[0].m_hasAlpha = true;
     m_frames[0].m_haveMetadata = true;
 
-#if PLATFORM(IOS)
-    m_frames[0].m_subsamplingScale = 1;
-#endif
-
     checkForSolidColor();
 }
 
     checkForSolidColor();
 }
 
-// Drawing Routines
+void BitmapImage::determineMinimumSubsamplingLevel() const
+{
+    if (!m_allowSubsampling)
+        return;
+
+    if (!m_source.allowSubsamplingOfFrameAtIndex(0))
+        return;
+
+    // Values chosen to be appropriate for iOS.
+    const int cMaximumImageAreaBeforeSubsampling = 5 * 1024 * 1024;
+    const SubsamplingLevel maxSubsamplingLevel = 3;
+
+    SubsamplingLevel currentLevel = 0;
+    for ( ; currentLevel <= maxSubsamplingLevel; ++currentLevel) {
+        IntSize frameSize = m_source.frameSizeAtIndex(0, currentLevel);
+        if (frameSize.area() < cMaximumImageAreaBeforeSubsampling)
+            break;
+    }
+
+    m_minimumSubsamplingLevel = currentLevel;
+}
 
 void BitmapImage::checkForSolidColor()
 {
     m_checkedForSolidColor = true;
 
 void BitmapImage::checkForSolidColor()
 {
     m_checkedForSolidColor = true;
-    if (frameCount() > 1) {
-        m_isSolidColor = false;
+    m_isSolidColor = false;
+
+    if (frameCount() > 1)
         return;
         return;
+
+    if (!haveFrameAtIndex(0)) {
+        IntSize size = m_source.frameSizeAtIndex(0, 0);
+        if (size.width() != 1 || size.height() != 1)
+            return;
+
+        if (!ensureFrameIsCached(0))
+            return;
     }
 
     }
 
-#if !PLATFORM(IOS)
-    CGImageRef image = frameAtIndex(0);
-#else
-    // Note, checkForSolidColor() may be called from frameAtIndex(). On iOS frameAtIndex() gets passed a scaleHint
-    // argument which it uses to tell CG to create a scaled down image. Since we don't know the scaleHint here, if
-    // we call frameAtIndex() again, we would pass it the default scale of 1 and would end up recreating the image.
-    // So we do a quick check and call frameAtIndex(0) only if we haven't yet created an image.
     CGImageRef image = nullptr;
     if (m_frames.size())
         image = m_frames[0].m_frame;
 
     if (!image)
     CGImageRef image = nullptr;
     if (m_frames.size())
         image = m_frames[0].m_frame;
 
     if (!image)
-        image = frameAtIndex(0);
-#endif
+        return;
 
     // Currently we only check for solid color in the important special case of a 1x1 image.
 
     // Currently we only check for solid color in the important special case of a 1x1 image.
-    if (image && CGImageGetWidth(image) == 1 && CGImageGetHeight(image) == 1) {
+    if (CGImageGetWidth(image) == 1 && CGImageGetHeight(image) == 1) {
         unsigned char pixel[4]; // RGBA
         RetainPtr<CGContextRef> bitmapContext = adoptCF(CGBitmapContextCreate(pixel, 1, 1, 8, sizeof(pixel), deviceRGBColorSpaceRef(),
             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big));
         unsigned char pixel[4]; // RGBA
         RetainPtr<CGContextRef> bitmapContext = adoptCF(CGBitmapContextCreate(pixel, 1, 1, 8, sizeof(pixel), deviceRGBColorSpaceRef(),
             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big));
@@ -198,30 +208,23 @@ RetainPtr<CFArrayRef> BitmapImage::getCGImageArray()
 
 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription description)
 {
 
 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription description)
 {
-    CGImageRef image;
-    FloatRect srcRectForCurrentFrame = srcRect;
-
 #if PLATFORM(IOS)
 #if PLATFORM(IOS)
-    if (m_originalSize != m_size && !m_originalSize.isEmpty())
-        srcRectForCurrentFrame.scale(m_size.width() / static_cast<float>(m_originalSize.width()), m_size.height() / static_cast<float>(m_originalSize.height()));
-
     startAnimation(DoNotCatchUp);
     startAnimation(DoNotCatchUp);
-
-    CGRect transformedDestinationRect = CGRectApplyAffineTransform(destRect, CGContextGetCTM(ctxt->platformContext()));
-    RetainPtr<CGImageRef> imagePossiblyCopied;
-    // Never use subsampled images for drawing into PDF contexts.
-    if (CGContextGetType(ctxt->platformContext()) == kCGContextTypePDF)
-        imagePossiblyCopied = adoptCF(copyUnscaledFrameAtIndex(m_currentFrame));
-    else
-        imagePossiblyCopied = frameAtIndex(m_currentFrame, std::min<float>(1.0f, std::max(transformedDestinationRect.size.width  / srcRectForCurrentFrame.width(), transformedDestinationRect.size.height / srcRectForCurrentFrame.height())));
-
-    image = imagePossiblyCopied.get();
 #else
     startAnimation();
 #else
     startAnimation();
-
-    image = frameAtIndex(m_currentFrame);
 #endif
 
 #endif
 
+    RetainPtr<CGImageRef> image;
+    // Never use subsampled images for drawing into PDF contexts.
+    if (wkCGContextIsPDFContext(ctxt->platformContext()))
+        image = adoptCF(copyUnscaledFrameAtIndex(m_currentFrame));
+    else {
+        CGRect transformedDestinationRect = CGRectApplyAffineTransform(destRect, CGContextGetCTM(ctxt->platformContext()));
+        float subsamplingScale = std::min<float>(1, std::max(transformedDestinationRect.size.width / srcRect.width(), transformedDestinationRect.size.height / srcRect.height()));
+
+        image = frameAtIndex(m_currentFrame, subsamplingScale);
+    }
+
     if (!image) // If it's too early we won't have an image yet.
         return;
     
     if (!image) // If it's too early we won't have an image yet.
         return;
     
@@ -230,37 +233,40 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F
         return;
     }
 
         return;
     }
 
-    float scale = 1;
-#if PLATFORM(IOS)
-    scale = m_frames[m_currentFrame].m_subsamplingScale;
-#endif
-    FloatSize selfSize = currentFrameSize();
+    // Subsampling may have given us an image that is smaller than size().
+    IntSize imageSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get()));
+    
+    // srcRect is in the coordinates of the unsubsampled image, so we have to map it to the subsampled image.
+    FloatRect scaledSrcRect = srcRect;
+    if (imageSize != m_size) {
+        FloatRect originalImageBounds(FloatPoint(), m_size);
+        FloatRect subsampledImageBounds(FloatPoint(), imageSize);
+        scaledSrcRect = mapRect(srcRect, originalImageBounds, subsampledImageBounds);
+    }
+    
     ImageOrientation orientation;
     ImageOrientation orientation;
-
     if (description.respectImageOrientation() == RespectImageOrientation)
         orientation = frameOrientationAtIndex(m_currentFrame);
 
     if (description.respectImageOrientation() == RespectImageOrientation)
         orientation = frameOrientationAtIndex(m_currentFrame);
 
-    ctxt->drawNativeImage(image, selfSize, styleColorSpace, destRect, srcRectForCurrentFrame, scale, compositeOp, blendMode, orientation);
+    ctxt->drawNativeImage(image.get(), imageSize, styleColorSpace, destRect, scaledSrcRect, compositeOp, blendMode, orientation);
 
     if (imageObserver())
         imageObserver()->didDraw(this);
 }
 
 
     if (imageObserver())
         imageObserver()->didDraw(this);
 }
 
-#if PLATFORM(IOS)
 PassNativeImagePtr BitmapImage::copyUnscaledFrameAtIndex(size_t index)
 {
     if (index >= frameCount())
         return nullptr;
 
     if (index >= m_frames.size() || !m_frames[index].m_frame)
 PassNativeImagePtr BitmapImage::copyUnscaledFrameAtIndex(size_t index)
 {
     if (index >= frameCount())
         return nullptr;
 
     if (index >= m_frames.size() || !m_frames[index].m_frame)
-        cacheFrame(index, 1);
+        cacheFrame(index, 0);
 
 
-    if (m_frames[index].m_subsamplingScale == 1 && !m_source.isSubsampled())
+    if (!m_frames[index].m_subsamplingLevel)
         return CGImageRetain(m_frames[index].m_frame);
 
     return m_source.createFrameAtIndex(index);
 }
         return CGImageRetain(m_frames[index].m_frame);
 
     return m_source.createFrameAtIndex(index);
 }
-#endif
 
 }
 
 
 }
 
index f026db8..f3f3fba 100644 (file)
@@ -332,17 +332,8 @@ bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool
         decoder.setData(m_image->data(), true);
         if (!decoder.frameCount())
             return false;
         decoder.setData(m_image->data(), true);
         if (!decoder.frameCount())
             return false;
-#if PLATFORM(IOS)
-        float scaleHint = 1;
-        if (m_image->isBitmapImage()) {
-            FloatSize originalSize = toBitmapImage(m_image)->originalSize();
-            if (!originalSize.isEmpty())
-                scaleHint = std::min<float>(1, std::max(m_image->size().width() / originalSize.width(), m_image->size().width() / originalSize.height()));
-        }
-        m_decodedImage = adoptCF(decoder.createFrameAtIndex(0, &scaleHint));
-#else
+
         m_decodedImage = adoptCF(decoder.createFrameAtIndex(0));
         m_decodedImage = adoptCF(decoder.createFrameAtIndex(0));
-#endif
         m_cgImage = m_decodedImage.get();
     } else
         m_cgImage = m_image->nativeImageForCurrentFrame();
         m_cgImage = m_decodedImage.get();
     } else
         m_cgImage = m_image->nativeImageForCurrentFrame();
@@ -545,7 +536,7 @@ void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imag
     context->scale(FloatSize(1, -1));
     context->translate(0, -imageHeight);
     context->setImageInterpolationQuality(InterpolationNone);
     context->scale(FloatSize(1, -1));
     context->translate(0, -imageHeight);
     context->setImageInterpolationQuality(InterpolationNone);
-    context->drawNativeImage(cgImage.get(), imageSize, ColorSpaceDeviceRGB, canvasRect, FloatRect(FloatPoint(), imageSize), 1, CompositeCopy);
+    context->drawNativeImage(cgImage.get(), imageSize, ColorSpaceDeviceRGB, canvasRect, FloatRect(FloatPoint(), imageSize), CompositeCopy);
 }
 
 } // namespace WebCore
 }
 
 } // namespace WebCore
index 2eb0e13..b0a05d5 100644 (file)
@@ -169,18 +169,11 @@ void GraphicsContext::restorePlatformState()
     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
-void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSize& imageSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, float scale, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSize& imageSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
 {
     RetainPtr<CGImageRef> image(imagePtr);
 
     float currHeight = orientation.usesWidthAsHeight() ? CGImageGetWidth(image.get()) : CGImageGetHeight(image.get());
 {
     RetainPtr<CGImageRef> image(imagePtr);
 
     float currHeight = orientation.usesWidthAsHeight() ? CGImageGetWidth(image.get()) : CGImageGetHeight(image.get());
-#if PLATFORM(IOS)
-    // Unapply the scaling since we are getting this from a scaled bitmap.
-    currHeight /= scale;
-#else
-    UNUSED_PARAM(scale);
-#endif
-
     if (currHeight <= srcRect.y())
         return;
 
     if (currHeight <= srcRect.y())
         return;
 
@@ -221,9 +214,6 @@ void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSi
             subimageRect.setHeight(ceilf(subimageRect.height() + topPadding));
             adjustedDestRect.setHeight(subimageRect.height() / yScale);
 
             subimageRect.setHeight(ceilf(subimageRect.height() + topPadding));
             adjustedDestRect.setHeight(subimageRect.height() / yScale);
 
-#if PLATFORM(IOS)
-            subimageRect.scale(scale, scale);
-#endif
 #if CACHE_SUBIMAGES
             image = subimageCache().getSubimage(image.get(), subimageRect);
 #else
 #if CACHE_SUBIMAGES
             image = subimageCache().getSubimage(image.get(), subimageRect);
 #else
index 503334c..b401207 100644 (file)
@@ -301,7 +301,7 @@ void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace,
 
     FloatRect adjustedSrcRect = srcRect;
     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
 
     FloatRect adjustedSrcRect = srcRect;
     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
-    destContext->drawNativeImage(image.get(), m_data.m_backingStoreSize, colorSpace, destRect, adjustedSrcRect, 1, op, blendMode);
+    destContext->drawNativeImage(image.get(), m_data.m_backingStoreSize, colorSpace, destRect, adjustedSrcRect, op, blendMode);
 }
 
 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
 }
 
 void ImageBuffer::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode)
index 0d75524..85aac85 100644 (file)
 #include <ApplicationServices/ApplicationServices.h>
 #else
 #include <CoreGraphics/CGImagePrivate.h>
 #include <ApplicationServices/ApplicationServices.h>
 #else
 #include <CoreGraphics/CGImagePrivate.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <ImageIO/CGImageSourcePrivate.h>
 #include <ImageIO/ImageIO.h>
 #include <ImageIO/ImageIO.h>
+#include <wtf/NeverDestroyed.h>
 #include <wtf/RetainPtr.h>
 #endif
 
 #include <wtf/RetainPtr.h>
 #endif
 
+#if __has_include(<ImageIO/CGImageSourcePrivate.h>)
+#import <ImageIO/CGImageSourcePrivate.h>
+#else
+const CFStringRef kCGImageSourceSubsampleFactor = CFSTR("kCGImageSourceSubsampleFactor");
+#endif
+
 namespace WebCore {
 
 const CFStringRef WebCoreCGImagePropertyAPNGUnclampedDelayTime = CFSTR("UnclampedDelayTime");
 namespace WebCore {
 
 const CFStringRef WebCoreCGImagePropertyAPNGUnclampedDelayTime = CFSTR("UnclampedDelayTime");
@@ -76,10 +81,6 @@ void sharedBufferRelease(void* info)
 
 ImageSource::ImageSource(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption)
     : m_decoder(0)
 
 ImageSource::ImageSource(ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption)
     : m_decoder(0)
-#if PLATFORM(IOS)
-    , m_baseSubsampling(0)
-    , m_isProgressive(false)
-#endif
 {
     // FIXME: AlphaOption and GammaAndColorProfileOption are ignored.
 }
 {
     // FIXME: AlphaOption and GammaAndColorProfileOption are ignored.
 }
@@ -105,39 +106,35 @@ void ImageSource::clear(bool destroyAllFrames, size_t, SharedBuffer* data, bool
         setData(data, allDataReceived);
 }
 
         setData(data, allDataReceived);
 }
 
-#if !PLATFORM(IOS)
-static CFDictionaryRef imageSourceOptions(ImageSource::ShouldSkipMetadata skipMetaData)
+static CFDictionaryRef createImageSourceOptions(ImageSource::ShouldSkipMetadata skipMetaData, SubsamplingLevel subsamplingLevel)
 {
 {
-    static CFDictionaryRef options;
-
-    if (!options) {
+    const CFBooleanRef imageSourceSkipMetadata = (skipMetaData == ImageSource::SkipMetadata) ? kCFBooleanTrue : kCFBooleanFalse;
+    
+    if (!subsamplingLevel) {
         const unsigned numOptions = 3;
         const unsigned numOptions = 3;
-        const CFBooleanRef imageSourceSkipMetadata = (skipMetaData == ImageSource::SkipMetadata) ? kCFBooleanTrue : kCFBooleanFalse;
         const void* keys[numOptions] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32, kCGImageSourceSkipMetadata };
         const void* values[numOptions] = { kCFBooleanTrue, kCFBooleanTrue, imageSourceSkipMetadata };
         const void* keys[numOptions] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32, kCGImageSourceSkipMetadata };
         const void* values[numOptions] = { kCFBooleanTrue, kCFBooleanTrue, imageSourceSkipMetadata };
-        options = CFDictionaryCreate(NULL, keys, values, numOptions, 
-            &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        return CFDictionaryCreate(nullptr, keys, values, numOptions, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     }
     }
-    return options;
+
+    short constrainedSubsamplingLevel = std::min<short>(3, std::max<short>(0, subsamplingLevel));
+    int subsampleInt = 1 << constrainedSubsamplingLevel; // [0..3] => [1, 2, 4, 8]
+
+    RetainPtr<CFNumberRef> subsampleNumber = adoptCF(CFNumberCreate(nullptr,  kCFNumberIntType,  &subsampleInt));
+    const CFIndex numOptions = 4;
+    const void* keys[numOptions] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32, kCGImageSourceSkipMetadata, kCGImageSourceSubsampleFactor };
+    const void* values[numOptions] = { kCFBooleanTrue, kCFBooleanTrue, imageSourceSkipMetadata, subsampleNumber.get() };
+    return CFDictionaryCreate(nullptr, keys, values, numOptions, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 }
 }
-#else
-CFDictionaryRef ImageSource::imageSourceOptions(ShouldSkipMetadata skipMetaData, int requestedSubsampling) const
+
+static CFDictionaryRef imageSourceOptions(ImageSource::ShouldSkipMetadata skipMetadata = ImageSource::SkipMetadata, SubsamplingLevel subsamplingLevel = 0)
 {
 {
-    static CFDictionaryRef options[4] = {nullptr, nullptr, nullptr, nullptr};
-    int subsampling = std::min(3, m_isProgressive || requestedSubsampling < 0 ? 0 : (requestedSubsampling + m_baseSubsampling));
-
-    if (!options[subsampling]) {
-        int subsampleInt = 1 << subsampling; // [0..3] => [1, 2, 4, 8]
-        RetainPtr<CFNumberRef> subsampleNumber = adoptCF(CFNumberCreate(nullptr,  kCFNumberIntType,  &subsampleInt));
-        const CFIndex numOptions = 4;
-        const CFBooleanRef imageSourceSkipMetaData = (skipMetaData == ImageSource::SkipMetadata) ? kCFBooleanTrue : kCFBooleanFalse;
-        const void* keys[numOptions] = { kCGImageSourceShouldCache, kCGImageSourceShouldPreferRGB32, kCGImageSourceSubsampleFactor, kCGImageSourceSkipMetadata };
-        const void* values[numOptions] = { kCFBooleanTrue, kCFBooleanTrue, subsampleNumber.get(), imageSourceSkipMetaData };
-        options[subsampling] = CFDictionaryCreate(nullptr, keys, values, numOptions, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    }
-    return options[subsampling];
+    if (subsamplingLevel)
+        return createImageSourceOptions(skipMetadata, subsamplingLevel);
+
+    static CFDictionaryRef options = createImageSourceOptions(skipMetadata, 0);
+    return options;
 }
 }
-#endif
 
 bool ImageSource::initialized() const
 {
 
 bool ImageSource::initialized() const
 {
@@ -182,6 +179,15 @@ String ImageSource::filenameExtension() const
     return WebCore::preferredExtensionForImageSourceType(imageSourceType);
 }
 
     return WebCore::preferredExtensionForImageSourceType(imageSourceType);
 }
 
+SubsamplingLevel ImageSource::subsamplingLevelForScale(float scale) const
+{
+    // There are four subsampling levels: 0 = 1x, 1 = 0.5x, 2 = 0.25x, 3 = 0.125x.
+    float clampedScale = std::max<float>(0.125, std::min<float>(1, scale));
+    int result = ceilf(log2f(1 / clampedScale));
+    ASSERT(result >=0 && result <= 3);
+    return result;
+}
+
 bool ImageSource::isSizeAvailable()
 {
     bool result = false;
 bool ImageSource::isSizeAvailable()
 {
     bool result = false;
@@ -189,7 +195,7 @@ bool ImageSource::isSizeAvailable()
 
     // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
     if (imageSourceStatus >= kCGImageStatusIncomplete) {
 
     // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
     if (imageSourceStatus >= kCGImageStatusIncomplete) {
-        RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions(SkipMetadata)));
+        RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
         if (image0Properties) {
             CFNumberRef widthNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelWidth);
             CFNumberRef heightNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelHeight);
         if (image0Properties) {
             CFNumberRef widthNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelWidth);
             CFNumberRef heightNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelHeight);
@@ -212,101 +218,67 @@ static ImageOrientation orientationFromProperties(CFDictionaryRef imagePropertie
     return ImageOrientation::fromEXIFValue(exifValue);
 }
 
     return ImageOrientation::fromEXIFValue(exifValue);
 }
 
-IntSize ImageSource::frameSizeAtIndex(size_t index, ImageOrientationDescription description) const
+bool ImageSource::allowSubsamplingOfFrameAtIndex(size_t) const
 {
 {
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata)));
-
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
     if (!properties)
     if (!properties)
-        return IntSize();
-
-    int w = 0, h = 0;
-    CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
-    if (num)
-        CFNumberGetValue(num, kCFNumberIntType, &w);
-    num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight);
-    if (num)
-        CFNumberGetValue(num, kCFNumberIntType, &h);
+        return false;
 
 
-#if PLATFORM(IOS)
-    if (!m_isProgressive) {
-        CFDictionaryRef jfifProperties = static_cast<CFDictionaryRef>(CFDictionaryGetValue(properties.get(), kCGImagePropertyJFIFDictionary));
-        if (jfifProperties) {
-            CFBooleanRef isProgCFBool = static_cast<CFBooleanRef>(CFDictionaryGetValue(jfifProperties, kCGImagePropertyJFIFIsProgressive));
-            if (isProgCFBool)
-                m_isProgressive = CFBooleanGetValue(isProgCFBool);
+    CFDictionaryRef jfifProperties = static_cast<CFDictionaryRef>(CFDictionaryGetValue(properties.get(), kCGImagePropertyJFIFDictionary));
+    if (jfifProperties) {
+        CFBooleanRef isProgCFBool = static_cast<CFBooleanRef>(CFDictionaryGetValue(jfifProperties, kCGImagePropertyJFIFIsProgressive));
+        if (isProgCFBool) {
+            bool isProgressive = CFBooleanGetValue(isProgCFBool);
             // Workaround for <rdar://problem/5184655> - Hang rendering very large progressive JPEG. Decoding progressive
             // images hangs for a very long time right now. Until this is fixed, don't sub-sample progressive images. This
             // will cause them to fail our large image check and they won't be decoded.
             // FIXME: Remove once underlying issue is fixed (<rdar://problem/5191418>)
             // Workaround for <rdar://problem/5184655> - Hang rendering very large progressive JPEG. Decoding progressive
             // images hangs for a very long time right now. Until this is fixed, don't sub-sample progressive images. This
             // will cause them to fail our large image check and they won't be decoded.
             // FIXME: Remove once underlying issue is fixed (<rdar://problem/5191418>)
+            return !isProgressive;
         }
     }
 
         }
     }
 
-    if ((m_baseSubsampling == 0) && !m_isProgressive) {
-        IntSize subsampledSize(w, h);
-        const int cMaximumImageSizeBeforeSubsampling = 5 * 1024 * 1024;
-        while ((m_baseSubsampling < 3) && subsampledSize.width() * subsampledSize.height() > cMaximumImageSizeBeforeSubsampling) {
-            // We know the size, but the actual image is very large and should be sub-sampled.
-            // Increase the base subsampling and ask for the size again. If the image can be subsampled, the size will be
-            // greatly reduced. 4x sub-sampling will make us support up to 320MP (5MP * 4^3) images, which should be plenty.
-            // There's no callback from ImageIO when the size is available, so we do the check when we happen
-            // to check the size and its non - zero.
-            // Note: Some clients of this class don't call isSizeAvailable() so we can't rely on that.
-            ++m_baseSubsampling;
-            subsampledSize = frameSizeAtIndex(index, description.respectImageOrientation());
-        }
-        w = subsampledSize.width();
-        h = subsampledSize.height();
-    }
-#endif
-
-    if ((description.respectImageOrientation() == RespectImageOrientation) && orientationFromProperties(properties.get()).usesWidthAsHeight())
-        return IntSize(h, w);
-
-    return IntSize(w, h);
-}
-
-ImageOrientation ImageSource::orientationAtIndex(size_t index) const
-{
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata)));
-    if (!properties)
-        return DefaultImageOrientation;
-
-    return orientationFromProperties(properties.get());
+    return true;
 }
 
 }
 
-#if PLATFORM(IOS)
-IntSize ImageSource::originalSize(RespectImageOrientationEnum shouldRespectOrientation) const
+IntSize ImageSource::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageOrientationDescription description) const
 {
 {
-    frameSizeAtIndex(0, shouldRespectOrientation);
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions(SkipMetadata, -1)));
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsamplingLevel)));
 
     if (!properties)
         return IntSize();
 
     int width = 0;
     int height = 0;
 
     if (!properties)
         return IntSize();
 
     int width = 0;
     int height = 0;
-    CFNumberRef number = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
-    if (number)
-        CFNumberGetValue(number, kCFNumberIntType, &width);
-    number = static_cast<CFNumberRef>(CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight));
-    if (number)
-        CFNumberGetValue(number, kCFNumberIntType, &height);
-
-    if ((shouldRespectOrientation == RespectImageOrientation) && orientationFromProperties(properties.get()).usesWidthAsHeight())
+    CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
+    if (num)
+        CFNumberGetValue(num, kCFNumberIntType, &width);
+    num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight);
+    if (num)
+        CFNumberGetValue(num, kCFNumberIntType, &height);
+
+    if ((description.respectImageOrientation() == RespectImageOrientation) && orientationFromProperties(properties.get()).usesWidthAsHeight())
         return IntSize(height, width);
 
     return IntSize(width, height);
 }
         return IntSize(height, width);
 
     return IntSize(width, height);
 }
-#endif
+
+ImageOrientation ImageSource::orientationAtIndex(size_t index) const
+{
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()));
+    if (!properties)
+        return DefaultImageOrientation;
+
+    return orientationFromProperties(properties.get());
+}
 
 IntSize ImageSource::size(ImageOrientationDescription description) const
 {
 
 IntSize ImageSource::size(ImageOrientationDescription description) const
 {
-    return frameSizeAtIndex(0, description);
+    return frameSizeAtIndex(0, 0, description);
 }
 
 bool ImageSource::getHotSpot(IntPoint& hotSpot) const
 {
 }
 
 bool ImageSource::getHotSpot(IntPoint& hotSpot) const
 {
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions(SkipMetadata)));
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
     if (!properties)
         return false;
 
     if (!properties)
         return false;
 
@@ -342,7 +314,7 @@ int ImageSource::repetitionCount()
     if (!initialized())
         return cAnimationLoopOnce;
 
     if (!initialized())
         return cAnimationLoopOnce;
 
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_decoder, imageSourceOptions(SkipMetadata)));
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_decoder, imageSourceOptions()));
     if (!properties)
         return cAnimationLoopOnce;
 
     if (!properties)
         return cAnimationLoopOnce;
 
@@ -381,22 +353,14 @@ size_t ImageSource::frameCount() const
     return m_decoder ? CGImageSourceGetCount(m_decoder) : 0;
 }
 
     return m_decoder ? CGImageSourceGetCount(m_decoder) : 0;
 }
 
-CGImageRef ImageSource::createFrameAtIndex(size_t index, float* scale)
+CGImageRef ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
 {
 {
-    UNUSED_PARAM(scale);
-
     if (!initialized())
         return 0;
 
     if (!initialized())
         return 0;
 
-#if !PLATFORM(IOS)
-    UNUSED_PARAM(scale);
-    RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata)));
-#else
-    // Subsampling can be 1, 2 or 3, which means quarter-, sixteenth- and sixty-fourth-size, respectively.
-    // A zero or negative value means no subsampling.
-    int subsampling = scale ? static_cast<int>(log2f(1.0f / std::max(0.1f, std::min(1.0f, *scale)))) : -1;
-    RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsampling)));
+    RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsamplingLevel)));
 
 
+#if PLATFORM(IOS)
     // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient
     // which caused a performance regression for us since the images had to be resampled/recreated every time we called
     // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> -
     // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient
     // which caused a performance regression for us since the images had to be resampled/recreated every time we called
     // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> -
@@ -409,15 +373,8 @@ CGImageRef ImageSource::createFrameAtIndex(size_t index, float* scale)
 #if COMPILER(CLANG)
 #pragma clang diagnostic pop
 #endif
 #if COMPILER(CLANG)
 #pragma clang diagnostic pop
 #endif
-    if (scale) {
-        if (subsampling > 0)
-            *scale = static_cast<float>(CGImageGetWidth(image.get())) / size(DoNotRespectImageOrientation).width();
-        else {
-            ASSERT(static_cast<int>(CGImageGetWidth(image.get())) == size(DoNotRespectImageOrientation).width());
-            *scale = 1;
-        }
-    }
 #endif // !PLATFORM(IOS)
 #endif // !PLATFORM(IOS)
+
     CFStringRef imageUTI = CGImageSourceGetType(m_decoder);
     static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image");
     if (!imageUTI || !CFEqual(imageUTI, xbmUTI))
     CFStringRef imageUTI = CGImageSourceGetType(m_decoder);
     static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image");
     if (!imageUTI || !CFEqual(imageUTI, xbmUTI))
@@ -485,7 +442,7 @@ float ImageSource::frameDurationAtIndex(size_t index)
     // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
     // for more information.
     if (duration < 0.011f)
     // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
     // for more information.
     if (duration < 0.011f)
-        return 0.100f;
+        return 0.1f;
     return duration;
 }
 
     return duration;
 }
 
@@ -510,9 +467,9 @@ bool ImageSource::frameHasAlphaAtIndex(size_t index)
     return true;
 }
 
     return true;
 }
 
-unsigned ImageSource::frameBytesAtIndex(size_t index) const
+unsigned ImageSource::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
 {
 {
-    IntSize frameSize = frameSizeAtIndex(index, ImageOrientationDescription(RespectImageOrientation));
+    IntSize frameSize = frameSizeAtIndex(index, subsamplingLevel, ImageOrientationDescription(RespectImageOrientation));
     return frameSize.width() * frameSize.height() * 4;
 }
 
     return frameSize.width() * frameSize.height() * 4;
 }
 
index 9e046d6..ff07131 100644 (file)
@@ -57,9 +57,9 @@ void BitmapImage::invalidatePlatformData()
         return;
 
 #if USE(APPKIT)
         return;
 
 #if USE(APPKIT)
-    m_nsImage = 0;
+    m_nsImage = nullptr;
 #endif
 #endif
-    m_tiffRep = 0;
+    m_tiffRep = nullptr;
 }
 
 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
 }
 
 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
index 6066c45..bc0a963 100644 (file)
@@ -148,6 +148,11 @@ void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRectIn
     bmp->drawPattern(ctxt, tileRectIn, patternTransform, phase, styleColorSpace, op, destRect, m_source.size());
 }
 
     bmp->drawPattern(ctxt, tileRectIn, patternTransform, phase, styleColorSpace, op, destRect, m_source.size());
 }
 
+void BitmapImage::determineMinimumSubsamplingLevel() const
+{
+    m_minimumSubsamplingLevel = 0;
+}
+
 void BitmapImage::checkForSolidColor()
 {
     if (m_checkedForSolidColor)
 void BitmapImage::checkForSolidColor()
 {
     if (m_checkedForSolidColor)