Unreviewed, rolling out r171957.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Aug 2014 05:10:46 +0000 (05:10 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Aug 2014 05:10:46 +0000 (05:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=135538

Caused GTK assertions and test failures (Requested by smfr on
#webkit).

Reverted changeset:

"Clean up image subsampling code, make it less iOS-specific"
https://bugs.webkit.org/show_bug.cgi?id=134916
http://trac.webkit.org/changeset/171957

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@171964 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 834c81f..56c4b7d 100644 (file)
@@ -1,3 +1,17 @@
+2014-08-01  Commit Queue  <commit-queue@webkit.org>
+
+        Unreviewed, rolling out r171957.
+        https://bugs.webkit.org/show_bug.cgi?id=135538
+
+        Caused GTK assertions and test failures (Requested by smfr on
+        #webkit).
+
+        Reverted changeset:
+
+        "Clean up image subsampling code, make it less iOS-specific"
+        https://bugs.webkit.org/show_bug.cgi?id=134916
+        http://trac.webkit.org/changeset/171957
+
 2014-08-01  Myles C. Maxfield  <litherum@gmail.com>
 
         [CMake] Allow CMake to find GLib on FreeBSD
index bf07cb7..cca7878 100644 (file)
@@ -521,7 +521,6 @@ __ZN7WebCore15GraphicsContext10strokeRectERKNS_9FloatRectEf
 __ZN7WebCore15GraphicsContext11clearShadowEv
 __ZN7WebCore15GraphicsContext12setFillColorERKNS_5ColorENS_10ColorSpaceE
 __ZN7WebCore15GraphicsContext14setStrokeColorERKNS_5ColorENS_10ColorSpaceE
-__ZN7WebCore15GraphicsContext15drawNativeImageEP7CGImageRKNS_9FloatSizeENS_10ColorSpaceERKNS_9FloatRectES9_NS_17CompositeOperatorENS_9BlendModeENS_16ImageOrientationE
 __ZN7WebCore15GraphicsContext15setFillGradientEN3WTF10PassRefPtrINS_8GradientEEE
 __ZN7WebCore15GraphicsContext18setShouldAntialiasEb
 __ZN7WebCore15GraphicsContext19setIsCALayerContextEb
@@ -2238,6 +2237,7 @@ __ZN7WebCore12EventHandler9mouseDownEP7NSEvent
 __ZN7WebCore13getRawCookiesERKNS_21NetworkStorageSessionERKNS_3URLES5_RN3WTF6VectorINS_6CookieELm0ENS6_15CrashOnOverflowEEE
 __ZN7WebCore13toDeviceSpaceERKNS_9FloatRectEP8NSWindow
 __ZN7WebCore14cookiesEnabledERKNS_21NetworkStorageSessionERKNS_3URLES5_
+__ZN7WebCore15GraphicsContext15drawNativeImageEP7CGImageRKNS_9FloatSizeENS_10ColorSpaceERKNS_9FloatRectES9_fNS_17CompositeOperatorENS_9BlendModeENS_16ImageOrientationE
 __ZN7WebCore15GraphicsContextC1EP9CGContext
 __ZN7WebCore15ResourceRequest41updateFromDelegatePreservingOldPropertiesERKS0_
 __ZN7WebCore16FontPlatformDataC1EP6NSFontfbbbNS_15FontOrientationENS_16FontWidthVariantE
@@ -2588,6 +2588,7 @@ __ZN7WebCore15DatabaseTracker44emptyDatabaseFilesRemovalTaskWillBeScheduledEv
 __ZN7WebCore15DatabaseTracker7trackerEv
 __ZN7WebCore15GraphicsContext12drawBidiTextERKNS_4FontERKNS_7TextRunERKNS_10FloatPointENS1_24CustomFontNotReadyActionEPNS_10BidiStatusEi
 __ZN7WebCore15GraphicsContext15drawLineForTextERKNS_10FloatPointEfbb
+__ZN7WebCore15GraphicsContext15drawNativeImageEP7CGImageRKNS_9FloatSizeENS_10ColorSpaceERKNS_9FloatRectES9_fNS_17CompositeOperatorENS_9BlendModeENS_16ImageOrientationE
 __ZN7WebCore15GraphicsContext22setEmojiDrawingEnabledEb
 __ZN7WebCore15GraphicsContext23setIsAcceleratedContextEb
 __ZN7WebCore15GraphicsContextC1EP9CGContextb
index 157a6ee..09a789d 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 */; };
-               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 */; };
                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>"; };
                                A8748D6612CC3763001FBA41 /* ImageOrientation.h */,
                                49291E4A134172C800E753DE /* ImageRenderingMode.h */,
                                B27535430B053814002CE64F /* ImageSource.h */,
-                               0F3C725D1974874B00AEDD0C /* ImageSource.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 */,
-                               0F3C725E1974874B00AEDD0C /* ImageSource.cpp in Sources */,
                                97BC6A4D1505F081001B74AC /* SQLStatementSync.cpp in Sources */,
                                97BC6A4F1505F081001B74AC /* SQLTransaction.cpp in Sources */,
                                FEE1811316C319E800084849 /* SQLTransactionBackend.cpp in Sources */,
index 2c76fd0..f5feabd 100644 (file)
@@ -289,7 +289,16 @@ LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float
     }
 #else
     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
+#if !PLATFORM(IOS)
         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) {
@@ -343,7 +352,6 @@ inline void CachedImage::createImage()
     // 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);
@@ -352,10 +360,8 @@ inline void CachedImage::createImage()
         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);
-        toBitmapImage(m_image.get())->setAllowSubsampling(m_loader && m_loader->frameLoader()->frame().settings().imageSubsamplingEnabled());
-    }
 
     if (m_image) {
         // Send queued container size requests.
@@ -512,6 +518,8 @@ void CachedImage::changedInRect(const Image* image, const IntRect& rect)
 bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
 {
     Image* image = imageForRenderer(renderer);
+    if (image->isBitmapImage())
+        image->nativeImageForCurrentFrame(); // force decode
     return image->currentFrameKnownToBeOpaque();
 }
 
index 93ae148..7fe56bf 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(); }
-    bool currentFrameKnownToBeOpaque(const RenderElement*);
+    bool currentFrameKnownToBeOpaque(const RenderElement*); // Side effect: ensures decoded image is in cache, therefore should only be called when about to draw the image.
 
     std::pair<Image*, float> brokenImage(float deviceScaleFactor) const; // Returns an image and the image's resolution scale factor.
     bool willPaintBrokenImage() const; 
index d713412..e8653df 100644 (file)
@@ -127,7 +127,6 @@ static const bool defaultAcceleratedCompositingForFixedPositionEnabled = 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;
@@ -136,7 +135,6 @@ static const bool defaultAcceleratedCompositingForFixedPositionEnabled = 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
 
index 87d43d0..890bb3f 100644 (file)
@@ -141,7 +141,6 @@ notificationsEnabled initial=true
 needsIsLoadingInAPISenseQuirk initial=false
 
 shouldRespectImageOrientation initial=defaultShouldRespectImageOrientation
-imageSubsamplingEnabled initial=defaultImageSubsamplingEnabled
 wantsBalancedSetDefersLoadingBehavior initial=false
 requestAnimationFrameEnabled initial=true
 deviceSupportsTouch initial=false
index 10a6694..a77d5af 100644 (file)
 
 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)
-    , m_minimumSubsamplingLevel(0)
-    , m_imageOrientation(OriginTopLeft)
-    , m_shouldRespectImageOrientation(false)
     , m_currentFrame(0)
+    , m_frames(0)
     , m_repetitionCount(cAnimationNone)
     , m_repetitionCountStatus(Unknown)
     , m_repetitionsComplete(0)
@@ -61,9 +61,6 @@ 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)
-    , m_allowSubsampling(true)
-#else
-    , m_allowSubsampling(false)
 #endif
     , m_isSolidColor(false)
     , m_checkedForSolidColor(false)
@@ -83,17 +80,6 @@ BitmapImage::~BitmapImage()
     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;
@@ -166,7 +152,11 @@ void BitmapImage::destroyMetadataAndNotify(unsigned frameBytesCleared)
         imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesCleared));
 }
 
-void BitmapImage::cacheFrame(size_t index, SubsamplingLevel subsamplingLevel, ImageFrameCaching frameCaching)
+#if PLATFORM(IOS)
+void BitmapImage::cacheFrame(size_t index, float scaleHint)
+#else
+void BitmapImage::cacheFrame(size_t index)
+#endif
 {
     size_t numFrames = frameCount();
     ASSERT(m_decodedSize == 0 || numFrames > 1);
@@ -174,27 +164,26 @@ void BitmapImage::cacheFrame(size_t index, SubsamplingLevel subsamplingLevel, Im
     if (m_frames.size() < numFrames)
         m_frames.grow(numFrames);
 
-    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();
-    }
+#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();
 
     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);
-
     m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
-    m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index, subsamplingLevel);
+    m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index);
 
-    const IntSize frameSize(index ? m_source.frameSizeAtIndex(index, subsamplingLevel) : m_size);
-    if (!subsamplingLevel && frameSize != m_size)
+    const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size);
+    if (frameSize != m_size)
         m_hasUniformFrameSize = false;
-
     if (m_frames[index].m_frame) {
         int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes);
         m_decodedSize += deltaBytes;
@@ -207,15 +196,30 @@ void BitmapImage::cacheFrame(size_t index, SubsamplingLevel subsamplingLevel, Im
     }
 }
 
+#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;
-
     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;
@@ -234,13 +238,13 @@ void BitmapImage::updateSize(ImageOrientationDescription description) const
 
     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());
-
+#if PLATFORM(IOS)
+    m_originalSize = m_source.originalSize();
+    m_originalSizeRespectingOrientation = m_source.originalSize(RespectImageOrientation);
+#endif
     m_haveSize = true;
-
-    determineMinimumSubsamplingLevel();
     didDecodeProperties();
 }
 
@@ -256,6 +260,29 @@ IntSize BitmapImage::sizeRespectingOrientation(ImageOrientationDescription descr
     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);
@@ -296,7 +323,6 @@ bool BitmapImage::dataChanged(bool allDataReceived)
     }
     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;
@@ -364,59 +390,85 @@ bool BitmapImage::isSizeAvailable()
     return m_sizeAvailable;
 }
 
-bool BitmapImage::ensureFrameIsCached(size_t index, ImageFrameCaching frameCaching)
+#if !PLATFORM(IOS)
+bool BitmapImage::ensureFrameIsCached(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_frame)
+        cacheFrame(index);
+    return true;
+}
+#else
+bool BitmapImage::ensureFrameInfoIsCached(size_t index)
+{
+    if (index >= frameCount())
+        return false;
 
+    if (index >= m_frames.size() || !m_frames[index].m_haveInfo)
+        cacheFrameInfo(index);
     return true;
 }
+#endif
 
-PassNativeImagePtr BitmapImage::frameAtIndex(size_t index, float presentationScaleHint)
+PassNativeImagePtr BitmapImage::frameAtIndex(size_t index)
 {
-    if (index >= frameCount())
+#if PLATFORM(IOS)
+    return frameAtIndex(index, 1.0f);
+#else
+    if (!ensureFrameIsCached(index))
         return nullptr;
+    return m_frames[index].m_frame;
+#endif
+}
 
-    SubsamplingLevel subsamplingLevel = std::min(m_source.subsamplingLevelForScale(presentationScaleHint), m_minimumSubsamplingLevel);
+#if PLATFORM(IOS)
+PassNativeImagePtr BitmapImage::frameAtIndex(size_t index, float scaleHint)
+{
+    if (index >= frameCount())
+        return nullptr;
 
-    // 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 (index >= m_frames.size() || !m_frames[index].m_frame)
+        cacheFrame(index, scaleHint);
+    else if (std::min(1.0f, scaleHint) > m_frames[index].m_subsamplingScale) {
         // 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;
-        WTFLogAlways("BitmapImage %p frameAtIndex recaching, m_decodedSize now %u (size change %d)", this, m_decodedSize, sizeChange);
         if (imageObserver())
             imageObserver()->decodedSizeChanged(this, sizeChange);
-    }
-
-    // If we haven't fetched a frame yet, do so.
-    if (index >= m_frames.size() || !m_frames[index].m_frame)
-        cacheFrame(index, subsamplingLevel, CacheMetadataAndFrame);
 
+        cacheFrame(index, scaleHint);
+    }
     return m_frames[index].m_frame;
 }
+#endif
 
 bool BitmapImage::frameIsCompleteAtIndex(size_t index)
 {
-    if (!ensureFrameIsCached(index, CacheMetadataOnly))
+#if PLATFORM(IOS)
+    // FIXME: cacheFrameInfo does not set m_isComplete. Should it?
+    if (!ensureFrameInfoIsCached(index))
         return false;
-
+#else
+    if (!ensureFrameIsCached(index))
+        return false;
+#endif
     return m_frames[index].m_isComplete;
 }
 
 float BitmapImage::frameDurationAtIndex(size_t index)
 {
-    if (!ensureFrameIsCached(index, CacheMetadataOnly))
+#if PLATFORM(IOS)
+    if (!ensureFrameInfoIsCached(index))
         return 0;
-
+#else
+    if (!ensureFrameIsCached(index))
+        return 0;
+#endif
     return m_frames[index].m_duration;
 }
 
@@ -427,9 +479,13 @@ PassNativeImagePtr BitmapImage::nativeImageForCurrentFrame()
 
 bool BitmapImage::frameHasAlphaAtIndex(size_t index)
 {
-    if (!ensureFrameIsCached(index, CacheMetadataOnly))
+#if PLATFORM(IOS)
+    if (!ensureFrameInfoIsCached(index))
+        return true; // FIXME: Why would an invalid index return true here?
+#else
+    if (m_frames.size() <= index)
         return true;
-
+#endif
     if (m_frames[index].m_haveMetadata)
         return m_frames[index].m_hasAlpha;
 
@@ -443,8 +499,14 @@ bool BitmapImage::currentFrameKnownToBeOpaque()
 
 ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index)
 {
-    if (!ensureFrameIsCached(index, CacheMetadataOnly))
+#if PLATFORM(IOS)
+    // FIXME: cacheFrameInfo does not set m_orientation. Should it?
+    if (!ensureFrameInfoIsCached(index))
         return DefaultImageOrientation;
+#else
+    if (!ensureFrameIsCached(index))
+        return DefaultImageOrientation;
+#endif
 
     if (m_frames[index].m_haveMetadata)
         return m_frames[index].m_orientation;
index 96410d4..2301848 100644 (file)
@@ -70,7 +70,10 @@ public:
     FrameData()
         : m_frame(0)
         , m_orientation(DefaultImageOrientation)
-        , m_subsamplingLevel(0)
+#if PLATFORM(IOS)
+        , m_subsamplingScale(0)
+        , m_haveInfo(false)
+#endif
         , m_duration(0)
         , m_haveMetadata(false)
         , m_isComplete(false)
@@ -90,7 +93,10 @@ public:
 
     NativeImagePtr m_frame;
     ImageOrientation m_orientation;
-    SubsamplingLevel m_subsamplingLevel;
+#if PLATFORM(IOS)
+    float m_subsamplingScale;
+    bool m_haveInfo;
+#endif
     float m_duration;
     bool m_haveMetadata : 1;
     bool m_isComplete : 1;
@@ -102,6 +108,9 @@ public:
 // 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;
@@ -128,7 +137,11 @@ public:
     // 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; }
@@ -181,12 +194,8 @@ public:
     
     bool canAnimate();
 
-    bool allowSubsampling() const { return m_allowSubsampling; }
-    void setAllowSubsampling(bool allowSubsampling) { m_allowSubsampling = allowSubsampling; }
-    
 private:
     void updateSize(ImageOrientationDescription = ImageOrientationDescription()) const;
-    void determineMinimumSubsamplingLevel() const;
 
 protected:
     enum RepetitionCountStatus {
@@ -209,24 +218,30 @@ protected:
 #endif
 
     size_t currentFrame() const { return m_currentFrame; }
-    size_t frameCount();
-
-    PassNativeImagePtr frameAtIndex(size_t, float presentationScaleHint = 1);
+#if PLATFORM(IOS)
+    PassNativeImagePtr frameAtIndex(size_t, float scaleHint);
     PassNativeImagePtr copyUnscaledFrameAtIndex(size_t);
-
-    bool haveFrameAtIndex(size_t);
-
+#endif
+    size_t frameCount();
+    PassNativeImagePtr frameAtIndex(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.
-    enum ImageFrameCaching { CacheMetadataOnly, CacheMetadataAndFrame };
-    void cacheFrame(size_t index, SubsamplingLevel, ImageFrameCaching = CacheMetadataAndFrame);
+#if PLATFORM(IOS)
+    void cacheFrame(size_t index, float scaleHint);
 
+    // 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.
-    bool ensureFrameIsCached(size_t index, ImageFrameCaching = CacheMetadataAndFrame);
+    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
 
     // Called to invalidate cached data.  When |destroyAll| is true, we wipe out
     // the entire frame buffer cache and tell the image source to destroy
@@ -286,12 +301,13 @@ 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;
-
-    mutable SubsamplingLevel m_minimumSubsamplingLevel;
-
     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.
 
@@ -320,7 +336,6 @@ private:
     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.
 
index 79d5118..ac1e050 100644 (file)
@@ -283,7 +283,7 @@ namespace WebCore {
         void applyFillPattern();
         void drawPath(const Path&);
 
-        void drawNativeImage(PassNativeImagePtr, const FloatSize& selfSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = DefaultImageOrientation);
+        void drawNativeImage(PassNativeImagePtr, const FloatSize& selfSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, float scale = 1, CompositeOperator = CompositeSourceOver, BlendMode = BlendModeNormal, ImageOrientation = DefaultImageOrientation);
 
         // Allow font smoothing (LCD antialiasing). Not part of the graphics state.
         void setAllowsFontSmoothing(bool);
index aa3df8f..f6b765f 100644 (file)
@@ -29,8 +29,6 @@
 #include "config.h"
 #include "ImageSource.h"
 
-#if !USE(CG)
-
 #include "ImageDecoder.h"
 
 #include "ImageOrientation.h"
@@ -96,16 +94,6 @@ String ImageSource::filenameExtension() const
     return m_decoder ? m_decoder->filenameExtension() : String();
 }
 
-SubsamplingLevel ImageSource::subsamplingLevelForScale(float) const
-{
-    return 0;
-}
-
-bool ImageSource::allowSubsamplingOfFrameAtIndex(size_t) const
-{
-    return true;
-}
-
 bool ImageSource::isSizeAvailable()
 {
     return m_decoder && m_decoder->isSizeAvailable();
@@ -113,10 +101,10 @@ bool ImageSource::isSizeAvailable()
 
 IntSize ImageSource::size(ImageOrientationDescription description) const
 {
-    return frameSizeAtIndex(0, 0, description);
+    return frameSizeAtIndex(0, description);
 }
 
-IntSize ImageSource::frameSizeAtIndex(size_t index, SubsamplingLevel, ImageOrientationDescription description) const
+IntSize ImageSource::frameSizeAtIndex(size_t index, ImageOrientationDescription description) const
 {
     if (!m_decoder)
         return IntSize();
@@ -148,8 +136,10 @@ size_t ImageSource::frameCount() const
     return m_decoder ? m_decoder->frameCount() : 0;
 }
 
-PassNativeImagePtr ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel)
+PassNativeImagePtr ImageSource::createFrameAtIndex(size_t index, float* scale)
 {
+    UNUSED_PARAM(scale);
+
     if (!m_decoder)
         return 0;
 
@@ -207,7 +197,7 @@ bool ImageSource::frameIsCompleteAtIndex(size_t index)
     return buffer && buffer->status() == ImageFrame::FrameComplete;
 }
 
-unsigned ImageSource::frameBytesAtIndex(size_t index, SubsamplingLevel) const
+unsigned ImageSource::frameBytesAtIndex(size_t index) const
 {
     if (!m_decoder)
         return 0;
@@ -215,5 +205,3 @@ unsigned ImageSource::frameBytesAtIndex(size_t index, SubsamplingLevel) const
 }
 
 }
-
-#endif // USE(CG)
index 281f61c..c4ee98f 100644 (file)
@@ -77,9 +77,6 @@ const int cAnimationLoopOnce = 0;
 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:
@@ -134,14 +131,14 @@ public:
     void setData(SharedBuffer* data, bool allDataReceived);
     String filenameExtension() const;
 
-    SubsamplingLevel subsamplingLevelForScale(float) const;
-    bool allowSubsamplingOfFrameAtIndex(size_t) const;
-
     bool isSizeAvailable();
-    // Always original size, without subsampling.
     IntSize size(ImageOrientationDescription = ImageOrientationDescription()) const;
-    // Size of optionally subsampled frame.
-    IntSize frameSizeAtIndex(size_t, SubsamplingLevel = 0, 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
 
     bool getHotSpot(IntPoint&) const;
 
@@ -153,7 +150,7 @@ public:
 
     // Callers should not call this after calling clear() with a higher index;
     // see comments on clear() above.
-    PassNativeImagePtr createFrameAtIndex(size_t, SubsamplingLevel = 0);
+    PassNativeImagePtr createFrameAtIndex(size_t, float* scale = 0);
 
     float frameDurationAtIndex(size_t);
     bool frameHasAlphaAtIndex(size_t); // Whether or not the frame actually used any alpha.
@@ -162,7 +159,7 @@ public:
 
     // Return the number of bytes in the decoded frame. If the frame is not yet
     // decoded then return 0.
-    unsigned frameBytesAtIndex(size_t, SubsamplingLevel = 0) const;
+    unsigned frameBytesAtIndex(size_t) const;
 
 #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING)
     static unsigned maxPixelsPerDecodedImage() { return s_maxPixelsPerDecodedImage; }
@@ -179,6 +176,11 @@ private:
 #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 0802257..aeb76e0 100644 (file)
@@ -119,11 +119,6 @@ void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const Flo
         imageObserver()->didDraw(this);
 }
 
-void BitmapImage::determineMinimumSubsamplingLevel() const
-{
-    m_minimumSubsamplingLevel = 0;
-}
-
 void BitmapImage::checkForSolidColor()
 {
     m_isSolidColor = false;
index ecd7054..b042765 100644 (file)
@@ -29,7 +29,6 @@
 #if USE(CG)
 
 #include "FloatConversion.h"
-#include "GeometryUtilities.h"
 #include "GraphicsContextCG.h"
 #include "ImageObserver.h"
 #include "SubimageCacheWithTimer.h"
@@ -59,7 +58,12 @@ bool FrameData::clear(bool clearMetadata)
         m_haveMetadata = false;
 
     m_orientation = DefaultImageOrientation;
-    m_subsamplingLevel = 0;
+
+#if PLATFORM(IOS)
+    m_frameBytes = 0;
+    m_subsamplingScale = 1;
+    m_haveInfo = false;
+#endif
 
     if (m_frame) {
 #if CACHE_SUBIMAGES
@@ -74,10 +78,8 @@ bool FrameData::clear(bool clearMetadata)
 
 BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer)
     : Image(observer)
-    , m_minimumSubsamplingLevel(0)
-    , m_imageOrientation(OriginTopLeft)
-    , m_shouldRespectImageOrientation(false)
     , m_currentFrame(0)
+    , m_frames(0)
     , m_repetitionCount(cAnimationNone)
     , m_repetitionCountStatus(Unknown)
     , m_repetitionsComplete(0)
@@ -101,62 +103,50 @@ 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;
 
+#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;
 
+#if PLATFORM(IOS)
+    m_frames[0].m_subsamplingScale = 1;
+#endif
+
     checkForSolidColor();
 }
 
-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;
-}
+// Drawing Routines
 
 void BitmapImage::checkForSolidColor()
 {
     m_checkedForSolidColor = true;
-    m_isSolidColor = false;
-
-    if (frameCount() > 1)
+    if (frameCount() > 1) {
+        m_isSolidColor = false;
         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)
-        return;
+        image = frameAtIndex(0);
+#endif
 
     // Currently we only check for solid color in the important special case of a 1x1 image.
-    if (CGImageGetWidth(image) == 1 && CGImageGetHeight(image) == 1) {
+    if (image && 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));
@@ -208,22 +198,29 @@ RetainPtr<CFArrayRef> BitmapImage::getCGImageArray()
 
 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 (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);
-#else
-    startAnimation();
-#endif
 
-    RetainPtr<CGImageRef> image;
+    CGRect transformedDestinationRect = CGRectApplyAffineTransform(destRect, CGContextGetCTM(ctxt->platformContext()));
+    RetainPtr<CGImageRef> imagePossiblyCopied;
     // 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()));
+    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 = frameAtIndex(m_currentFrame, subsamplingScale);
-    }
+    image = imagePossiblyCopied.get();
+#else
+    startAnimation();
+
+    image = frameAtIndex(m_currentFrame);
+#endif
 
     if (!image) // If it's too early we won't have an image yet.
         return;
@@ -233,27 +230,23 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F
         return;
     }
 
-    // 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);
-    }
-    
+    float scale = 1;
+#if PLATFORM(IOS)
+    scale = m_frames[m_currentFrame].m_subsamplingScale;
+#endif
+    FloatSize selfSize = currentFrameSize();
     ImageOrientation orientation;
+
     if (description.respectImageOrientation() == RespectImageOrientation)
         orientation = frameOrientationAtIndex(m_currentFrame);
 
-    ctxt->drawNativeImage(image.get(), imageSize, styleColorSpace, destRect, scaledSrcRect, compositeOp, blendMode, orientation);
+    ctxt->drawNativeImage(image, selfSize, styleColorSpace, destRect, srcRectForCurrentFrame, scale, compositeOp, blendMode, orientation);
 
     if (imageObserver())
         imageObserver()->didDraw(this);
 }
 
+#if PLATFORM(IOS)
 PassNativeImagePtr BitmapImage::copyUnscaledFrameAtIndex(size_t index)
 {
     if (index >= frameCount())
@@ -262,11 +255,12 @@ PassNativeImagePtr BitmapImage::copyUnscaledFrameAtIndex(size_t index)
     if (index >= m_frames.size() || !m_frames[index].m_frame)
         cacheFrame(index, 1);
 
-    if (!m_frames[index].m_subsamplingLevel)
+    if (m_frames[index].m_subsamplingScale == 1 && !m_source.isSubsampled())
         return CGImageRetain(m_frames[index].m_frame);
 
     return m_source.createFrameAtIndex(index);
 }
+#endif
 
 }
 
index f3f3fba..f026db8 100644 (file)
@@ -332,8 +332,17 @@ bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool
         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));
+#endif
         m_cgImage = m_decodedImage.get();
     } else
         m_cgImage = m_image->nativeImageForCurrentFrame();
@@ -536,7 +545,7 @@ void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imag
     context->scale(FloatSize(1, -1));
     context->translate(0, -imageHeight);
     context->setImageInterpolationQuality(InterpolationNone);
-    context->drawNativeImage(cgImage.get(), imageSize, ColorSpaceDeviceRGB, canvasRect, FloatRect(FloatPoint(), imageSize), CompositeCopy);
+    context->drawNativeImage(cgImage.get(), imageSize, ColorSpaceDeviceRGB, canvasRect, FloatRect(FloatPoint(), imageSize), 1, CompositeCopy);
 }
 
 } // namespace WebCore
index 8250cf6..26599a1 100644 (file)
@@ -169,11 +169,18 @@ void GraphicsContext::restorePlatformState()
     m_data->m_userToDeviceTransformKnownToBeIdentity = false;
 }
 
-void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSize& imageSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
+void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSize& imageSize, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, float scale, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
 {
     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;
 
@@ -214,6 +221,9 @@ void GraphicsContext::drawNativeImage(PassNativeImagePtr imagePtr, const FloatSi
             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
index b401207..503334c 100644 (file)
@@ -301,7 +301,7 @@ void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace,
 
     FloatRect adjustedSrcRect = srcRect;
     adjustedSrcRect.scale(m_resolutionScale, m_resolutionScale);
-    destContext->drawNativeImage(image.get(), m_data.m_backingStoreSize, colorSpace, destRect, adjustedSrcRect, op, blendMode);
+    destContext->drawNativeImage(image.get(), m_data.m_backingStoreSize, colorSpace, destRect, adjustedSrcRect, 1, 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)
index 85aac85..0d75524 100644 (file)
 #include <ApplicationServices/ApplicationServices.h>
 #else
 #include <CoreGraphics/CGImagePrivate.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <ImageIO/CGImageSourcePrivate.h>
 #include <ImageIO/ImageIO.h>
-#include <wtf/NeverDestroyed.h>
 #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");
@@ -81,6 +76,10 @@ void sharedBufferRelease(void* info)
 
 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.
 }
@@ -106,35 +105,39 @@ void ImageSource::clear(bool destroyAllFrames, size_t, SharedBuffer* data, bool
         setData(data, allDataReceived);
 }
 
-static CFDictionaryRef createImageSourceOptions(ImageSource::ShouldSkipMetadata skipMetaData, SubsamplingLevel subsamplingLevel)
+#if !PLATFORM(IOS)
+static CFDictionaryRef imageSourceOptions(ImageSource::ShouldSkipMetadata skipMetaData)
 {
-    const CFBooleanRef imageSourceSkipMetadata = (skipMetaData == ImageSource::SkipMetadata) ? kCFBooleanTrue : kCFBooleanFalse;
-    
-    if (!subsamplingLevel) {
+    static CFDictionaryRef options;
+
+    if (!options) {
         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 };
-        return CFDictionaryCreate(nullptr, keys, values, numOptions, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        options = CFDictionaryCreate(NULL, keys, values, numOptions, 
+            &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
     }
-
-    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);
+    return options;
 }
-
-static CFDictionaryRef imageSourceOptions(ImageSource::ShouldSkipMetadata skipMetadata = ImageSource::SkipMetadata, SubsamplingLevel subsamplingLevel = 0)
+#else
+CFDictionaryRef ImageSource::imageSourceOptions(ShouldSkipMetadata skipMetaData, int requestedSubsampling) const
 {
-    if (subsamplingLevel)
-        return createImageSourceOptions(skipMetadata, subsamplingLevel);
-
-    static CFDictionaryRef options = createImageSourceOptions(skipMetadata, 0);
-    return options;
+    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];
 }
+#endif
 
 bool ImageSource::initialized() const
 {
@@ -179,15 +182,6 @@ String ImageSource::filenameExtension() const
     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;
@@ -195,7 +189,7 @@ bool ImageSource::isSizeAvailable()
 
     // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
     if (imageSourceStatus >= kCGImageStatusIncomplete) {
-        RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
+        RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions(SkipMetadata)));
         if (image0Properties) {
             CFNumberRef widthNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelWidth);
             CFNumberRef heightNumber = (CFNumberRef)CFDictionaryGetValue(image0Properties.get(), kCGImagePropertyPixelHeight);
@@ -218,67 +212,101 @@ static ImageOrientation orientationFromProperties(CFDictionaryRef imagePropertie
     return ImageOrientation::fromEXIFValue(exifValue);
 }
 
-bool ImageSource::allowSubsamplingOfFrameAtIndex(size_t) const
+IntSize ImageSource::frameSizeAtIndex(size_t index, ImageOrientationDescription description) const
 {
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata)));
+
     if (!properties)
-        return false;
+        return IntSize();
 
-    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);
+    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);
+
+#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);
             // 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;
         }
     }
 
-    return true;
+    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());
 }
 
-IntSize ImageSource::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel, ImageOrientationDescription description) const
+#if PLATFORM(IOS)
+IntSize ImageSource::originalSize(RespectImageOrientationEnum shouldRespectOrientation) const
 {
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsamplingLevel)));
+    frameSizeAtIndex(0, shouldRespectOrientation);
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions(SkipMetadata, -1)));
 
     if (!properties)
         return IntSize();
 
     int width = 0;
     int height = 0;
-    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())
+    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())
         return IntSize(height, width);
 
     return IntSize(width, height);
 }
-
-ImageOrientation ImageSource::orientationAtIndex(size_t index) const
-{
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, index, imageSourceOptions()));
-    if (!properties)
-        return DefaultImageOrientation;
-
-    return orientationFromProperties(properties.get());
-}
+#endif
 
 IntSize ImageSource::size(ImageOrientationDescription description) const
 {
-    return frameSizeAtIndex(0, 0, description);
+    return frameSizeAtIndex(0, description);
 }
 
 bool ImageSource::getHotSpot(IntPoint& hotSpot) const
 {
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions()));
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_decoder, 0, imageSourceOptions(SkipMetadata)));
     if (!properties)
         return false;
 
@@ -314,7 +342,7 @@ int ImageSource::repetitionCount()
     if (!initialized())
         return cAnimationLoopOnce;
 
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_decoder, imageSourceOptions()));
+    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_decoder, imageSourceOptions(SkipMetadata)));
     if (!properties)
         return cAnimationLoopOnce;
 
@@ -353,14 +381,22 @@ size_t ImageSource::frameCount() const
     return m_decoder ? CGImageSourceGetCount(m_decoder) : 0;
 }
 
-CGImageRef ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
+CGImageRef ImageSource::createFrameAtIndex(size_t index, float* scale)
 {
+    UNUSED_PARAM(scale);
+
     if (!initialized())
         return 0;
 
-    RetainPtr<CGImageRef> image = adoptCF(CGImageSourceCreateImageAtIndex(m_decoder, index, imageSourceOptions(SkipMetadata, subsamplingLevel)));
+#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)));
 
-#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> -
@@ -373,8 +409,15 @@ CGImageRef ImageSource::createFrameAtIndex(size_t index, SubsamplingLevel subsam
 #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)
-
     CFStringRef imageUTI = CGImageSourceGetType(m_decoder);
     static const CFStringRef xbmUTI = CFSTR("public.xbitmap-image");
     if (!imageUTI || !CFEqual(imageUTI, xbmUTI))
@@ -442,7 +485,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)
-        return 0.1f;
+        return 0.100f;
     return duration;
 }
 
@@ -467,9 +510,9 @@ bool ImageSource::frameHasAlphaAtIndex(size_t index)
     return true;
 }
 
-unsigned ImageSource::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
+unsigned ImageSource::frameBytesAtIndex(size_t index) const
 {
-    IntSize frameSize = frameSizeAtIndex(index, subsamplingLevel, ImageOrientationDescription(RespectImageOrientation));
+    IntSize frameSize = frameSizeAtIndex(index, ImageOrientationDescription(RespectImageOrientation));
     return frameSize.width() * frameSize.height() * 4;
 }
 
index ff07131..9e046d6 100644 (file)
@@ -57,9 +57,9 @@ void BitmapImage::invalidatePlatformData()
         return;
 
 #if USE(APPKIT)
-    m_nsImage = nullptr;
+    m_nsImage = 0;
 #endif
-    m_tiffRep = nullptr;
+    m_tiffRep = 0;
 }
 
 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
index bc0a963..6066c45 100644 (file)
@@ -148,11 +148,6 @@ void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRectIn
     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)