Skip to content
Permalink
Browse files
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().

Canonical link: https://commits.webkit.org/80806@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@91628 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
smfr committed Jul 23, 2011
1 parent 5391a0f commit 582e4e050ae9a69eece166a273ec79c39c2698a6
Showing 9 changed files with 124 additions and 16 deletions.
@@ -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
@@ -158,7 +158,8 @@ class BitmapImage : public Image {
#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()
@@ -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) {
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);
}
@@ -882,6 +877,18 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai
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())
@@ -417,6 +417,9 @@ class RenderBox : public RenderBoxModelObject {
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);

@@ -1095,6 +1095,16 @@ class BorderEdge {

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; }

@@ -2145,6 +2155,27 @@ bool RenderBoxModelObject::borderObscuresBackgroundEdge(const FloatSize& context
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);
@@ -183,6 +183,7 @@ class RenderBoxModelObject : public RenderObject {
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&);

@@ -27,6 +27,7 @@
#include "config.h"
#include "RenderImage.h"

#include "BitmapImage.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);
}

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;
@@ -78,6 +78,8 @@ class RenderImage : public RenderReplaced {

virtual void paintReplaced(PaintInfo&, const IntPoint&);

virtual bool backgroundIsObscured() const;

virtual int minimumReplacedHeight() const;

virtual void notifyFinished(CachedResource*);
@@ -558,14 +558,7 @@ void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& p
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())

0 comments on commit 582e4e0

Please sign in to comment.