2011-07-22 Simon Fraser <simon.fraser@apple.com>
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Jul 2011 00:39:54 +0000 (00:39 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Jul 2011 00:39:54 +0000 (00:39 +0000)
        Avoiding painting backgrounds if they are fully obscures by an object's foreground
        https://bugs.webkit.org/show_bug.cgi?id=65030

        Reviewed by Dan Bernstein.

        Some pages use animated loading GIFs as the background of <img>,
        but WebKit keeps animating these after the <img> has loaded.

        Thwart this by avoiding the painting of such backgrounds, if we can
        determine that they are completely obscured by the border and content
        of the element.

        * platform/graphics/BitmapImage.h:
        (WebCore::BitmapImage::currentFrameHasAlpha): Utility method, since currentFrame()
        is protected.
        * rendering/RenderBox.cpp:
        (WebCore::RenderBox::paintBoxDecorations): Call paintBackground().
        (WebCore::RenderBox::paintBackground): New wrapper for the paintFillLayers() which
        paints the background layers, plus some code we call in a couple of places. This
        checks the new backgroundIsObscured() method before doing any painting.
        * rendering/RenderBox.h:
        (WebCore::RenderBox::backgroundIsObscured): New virtual method that determines
        whether any of the background is visible.
        * rendering/RenderBoxModelObject.h:
        * rendering/RenderBoxModelObject.cpp:
        (WebCore::BorderEdge::obscuresBackground): Returns true if this edge will
        entirely hide the background under it.
        (WebCore::RenderBoxModelObject::borderObscuresBackground): Determine whether
        the border hides the background.
        * rendering/RenderImage.cpp:
        (WebCore::RenderImage::backgroundIsObscured): Override the RenderBox method
        and return true if the image is a loaded, opaque bitmap image, and the background
        won't show in the border or padding areas.
        * rendering/RenderImage.h:
        * rendering/RenderTable.cpp:
        (WebCore::RenderTable::paintBoxDecorations): Use paintBackground().

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/BitmapImage.h
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/RenderBox.h
Source/WebCore/rendering/RenderBoxModelObject.cpp
Source/WebCore/rendering/RenderBoxModelObject.h
Source/WebCore/rendering/RenderImage.cpp
Source/WebCore/rendering/RenderImage.h
Source/WebCore/rendering/RenderTable.cpp

index 1488bd2..ace8d18 100644 (file)
@@ -1,3 +1,42 @@
+2011-07-22  Simon Fraser  <simon.fraser@apple.com>
+
+        Avoiding painting backgrounds if they are fully obscures by an object's foreground
+        https://bugs.webkit.org/show_bug.cgi?id=65030
+
+        Reviewed by Dan Bernstein.
+
+        Some pages use animated loading GIFs as the background of <img>,
+        but WebKit keeps animating these after the <img> has loaded.
+        
+        Thwart this by avoiding the painting of such backgrounds, if we can
+        determine that they are completely obscured by the border and content
+        of the element.
+
+        * platform/graphics/BitmapImage.h:
+        (WebCore::BitmapImage::currentFrameHasAlpha): Utility method, since currentFrame()
+        is protected.
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::paintBoxDecorations): Call paintBackground().
+        (WebCore::RenderBox::paintBackground): New wrapper for the paintFillLayers() which
+        paints the background layers, plus some code we call in a couple of places. This
+        checks the new backgroundIsObscured() method before doing any painting.
+        * rendering/RenderBox.h:
+        (WebCore::RenderBox::backgroundIsObscured): New virtual method that determines
+        whether any of the background is visible.
+        * rendering/RenderBoxModelObject.h:
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::BorderEdge::obscuresBackground): Returns true if this edge will
+        entirely hide the background under it.
+        (WebCore::RenderBoxModelObject::borderObscuresBackground): Determine whether
+        the border hides the background.
+        * rendering/RenderImage.cpp:
+        (WebCore::RenderImage::backgroundIsObscured): Override the RenderBox method
+        and return true if the image is a loaded, opaque bitmap image, and the background
+        won't show in the border or padding areas.
+        * rendering/RenderImage.h:
+        * rendering/RenderTable.cpp:
+        (WebCore::RenderTable::paintBoxDecorations): Use paintBackground().
+
 2011-07-22  Kenneth Russell  <kbr@google.com>
 
         HTMLImageElement::crossOrigin is hard to use because of caching
 2011-07-22  Kenneth Russell  <kbr@google.com>
 
         HTMLImageElement::crossOrigin is hard to use because of caching
index 4f61251..517489b 100644 (file)
@@ -158,7 +158,8 @@ public:
 #endif
 
     virtual NativeImagePtr nativeImageForCurrentFrame() { return frameAtIndex(currentFrame()); }
 #endif
 
     virtual NativeImagePtr nativeImageForCurrentFrame() { return frameAtIndex(currentFrame()); }
-    bool frameHasAlphaAtIndex(size_t); 
+    bool frameHasAlphaAtIndex(size_t);
+    bool currentFrameHasAlpha() { return frameHasAlphaAtIndex(currentFrame()); }
 
 #if !ASSERT_DISABLED
     bool notSolidColor()
 
 #if !ASSERT_DISABLED
     bool notSolidColor()
index 24e2765..ee30c39 100644 (file)
@@ -862,13 +862,8 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai
     // The theme will tell us whether or not we should also paint the CSS background.
     bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, paintRect);
     if (!themePainted) {
     // The theme will tell us whether or not we should also paint the CSS background.
     bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, paintRect);
     if (!themePainted) {
-        if (isRoot())
-            paintRootBoxFillLayers(paintInfo);
-        else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) {
-            // The <body> only paints its background if the root element has defined a background
-            // independent of the body.
-            paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance);
-        }
+        paintBackground(paintInfo, paintRect, bleedAvoidance);
+
         if (style()->hasAppearance())
             theme()->paintDecorations(this, paintInfo, paintRect);
     }
         if (style()->hasAppearance())
             theme()->paintDecorations(this, paintInfo, paintRect);
     }
@@ -882,6 +877,18 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai
         paintInfo.context->endTransparencyLayer();
 }
 
         paintInfo.context->endTransparencyLayer();
 }
 
+void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance)
+{
+    if (isRoot())
+        paintRootBoxFillLayers(paintInfo);
+    else if (!isBody() || document()->documentElement()->renderer()->hasBackground()) {
+        // The <body> only paints its background if the root element has defined a background
+        // independent of the body.
+        if (!backgroundIsObscured())
+            paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance);
+    }
+}
+
 void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
     if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
 void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
     if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
index 481521c..269e820 100644 (file)
@@ -417,6 +417,9 @@ protected:
     virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
     virtual void updateBoxModelInfoFromStyle();
 
     virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
     virtual void updateBoxModelInfoFromStyle();
 
+    virtual bool backgroundIsObscured() const { return false; }
+    void paintBackground(const PaintInfo&, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone);
+    
     void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, CompositeOperator, RenderObject* backgroundObject);
     void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0);
 
     void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, CompositeOperator, RenderObject* backgroundObject);
     void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance = BackgroundBleedNone, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0);
 
index a2af017..14b2fd7 100644 (file)
@@ -1095,6 +1095,16 @@ public:
 
         return true;
     }
 
         return true;
     }
+    bool obscuresBackground() const
+    {
+        if (!isPresent || isTransparent || color.hasAlpha() || style == BHIDDEN)
+            return false;
+
+        if (style == DOTTED || style == DASHED || style == DOUBLE)
+            return false;
+
+        return true;
+    }
 
     int usedWidth() const { return isPresent ? width : 0; }
     
 
     int usedWidth() const { return isPresent ? width : 0; }
     
@@ -2145,6 +2155,27 @@ bool RenderBoxModelObject::borderObscuresBackgroundEdge(const FloatSize& context
     return true;
 }
 
     return true;
 }
 
+bool RenderBoxModelObject::borderObscuresBackground() const
+{
+    if (!style()->hasBorder())
+        return false;
+
+    // Bail if we have any border-image for now. We could look at the image alpha to improve this.
+    if (style()->borderImage().image())
+        return false;
+
+    BorderEdge edges[4];
+    getBorderEdgeInfo(edges);
+
+    for (int i = BSTop; i <= BSLeft; ++i) {
+        const BorderEdge& currEdge = edges[i];
+        if (!currEdge.obscuresBackground())
+            return false;
+    }
+
+    return true;
+}
+
 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
 {
     IntRect bounds(holeRect);
 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
 {
     IntRect bounds(holeRect);
index e3d08cc..24905c1 100644 (file)
@@ -183,6 +183,7 @@ protected:
     void calculateBackgroundImageGeometry(const FillLayer*, const IntRect& paintRect, BackgroundImageGeometry&);
     void getBorderEdgeInfo(class BorderEdge[], bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const;
     bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const;
     void calculateBackgroundImageGeometry(const FillLayer*, const IntRect& paintRect, BackgroundImageGeometry&);
     void getBorderEdgeInfo(class BorderEdge[], bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const;
     bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const;
+    bool borderObscuresBackground() const;
 
     bool shouldPaintAtLowQuality(GraphicsContext*, Image*, const void*, const IntSize&);
 
 
     bool shouldPaintAtLowQuality(GraphicsContext*, Image*, const void*, const IntSize&);
 
index 733a219..04347d4 100644 (file)
@@ -27,6 +27,7 @@
 #include "config.h"
 #include "RenderImage.h"
 
 #include "config.h"
 #include "RenderImage.h"
 
+#include "BitmapImage.h"
 #include "FontCache.h"
 #include "Frame.h"
 #include "FrameSelection.h"
 #include "FontCache.h"
 #include "Frame.h"
 #include "FrameSelection.h"
@@ -387,6 +388,36 @@ void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect)
     context->drawImage(m_imageResource->image(rect.width(), rect.height()).get(), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling);
 }
 
     context->drawImage(m_imageResource->image(rect.width(), rect.height()).get(), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling);
 }
 
+bool RenderImage::backgroundIsObscured() const
+{
+    if (!m_imageResource->hasImage() || m_imageResource->errorOccurred())
+        return false;
+
+    if (m_imageResource->cachedImage() && !m_imageResource->cachedImage()->isLoaded())
+        return false;
+
+    EFillBox backgroundClip = style()->backgroundClip();
+
+    // Background paints under borders.
+    if (backgroundClip == BorderFillBox && style()->hasBorder() && !borderObscuresBackground())
+        return false;
+
+    // Background shows in padding area.
+    if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
+        return false;
+
+    // Check for bitmap image with alpha.
+    Image* image = m_imageResource->image().get();
+    if (!image || !image->isBitmapImage())
+        return false;
+        
+    BitmapImage* bitmapImage = static_cast<BitmapImage*>(image);
+    if (bitmapImage->currentFrameHasAlpha())
+        return false;
+
+    return true;
+}
+
 int RenderImage::minimumReplacedHeight() const
 {
     return m_imageResource->errorOccurred() ? intrinsicSize().height() : 0;
 int RenderImage::minimumReplacedHeight() const
 {
     return m_imageResource->errorOccurred() ? intrinsicSize().height() : 0;
index 7bed7c5..da895d2 100644 (file)
@@ -78,6 +78,8 @@ private:
 
     virtual void paintReplaced(PaintInfo&, const IntPoint&);
 
 
     virtual void paintReplaced(PaintInfo&, const IntPoint&);
 
+    virtual bool backgroundIsObscured() const;
+
     virtual int minimumReplacedHeight() const;
 
     virtual void notifyFinished(CachedResource*);
     virtual int minimumReplacedHeight() const;
 
     virtual void notifyFinished(CachedResource*);
index 68f6501..a2db33f 100644 (file)
@@ -558,14 +558,7 @@ void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& p
     subtractCaptionRect(rect);
 
     paintBoxShadow(paintInfo.context, rect, style(), Normal);
     subtractCaptionRect(rect);
 
     paintBoxShadow(paintInfo.context, rect, style(), Normal);
-    
-    if (isRoot())
-        paintRootBoxFillLayers(paintInfo);
-    else if (!isBody() || document()->documentElement()->renderer()->hasBackground())
-        // The <body> only paints its background if the root element has defined a background
-        // independent of the body.
-        paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), rect);
-
+    paintBackground(paintInfo, rect);
     paintBoxShadow(paintInfo.context, rect, style(), Inset);
 
     if (style()->hasBorder() && !collapseBorders())
     paintBoxShadow(paintInfo.context, rect, style(), Inset);
 
     if (style()->hasBorder() && !collapseBorders())