<rdar://problem/10870238> Box shadow drawing takes an unnecessarily slow code path...
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Feb 2012 21:57:02 +0000 (21:57 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Feb 2012 21:57:02 +0000 (21:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=78728

In some cases, when there is only one normal box shadow, and the box has an opaque background,
it is possible to draw the box shadow by having the background cast it directly. This appears
to be faster than the generic code path that uses a separate drawing pass to cast the shadow,
clipping out the border box and the shadow-casting box.

Reviewed by Dave Hyatt.

No new tests, because behavior is unchanged.

* rendering/InlineFlowBox.cpp:
(WebCore::InlineFlowBox::paintBoxDecorations): Changed to not paint normal box shadows if
they are going to be cast by the background.
* rendering/RenderBox.cpp:
(WebCore::RenderBox::paintBoxDecorations): Ditto.
* rendering/RenderBox.h: Made determineBackgroundBleedAvoidance() protected.
* rendering/RenderBoxModelObject.cpp:
(WebCore::applyBoxShadowForBackground): Added this helper function, which applies the first
normal shadow from the given RenderStyle to the given GraphicsContext.
(WebCore::RenderBoxModelObject::paintFillLayerExtended): Added calls to
applyBoxShadowForBackground() before drawing the background color when needed.
(WebCore::RenderBoxModelObject::boxShadowShouldBeAppliedToBackground): Added. Returns true
in some of the cases where the box shadow can be cast by the background directly.
* rendering/RenderBoxModelObject.h:
* rendering/RenderFieldset.cpp:
(WebCore::RenderFieldset::paintBoxDecorations): Changed to not paint normal box shadows if
they are going to be cast by the background.
* rendering/RenderTable.cpp:
(WebCore::RenderTable::paintBoxDecorations): Ditto.
* rendering/RenderTableCell.cpp:
(WebCore::RenderTableCell::boxShadowShouldBeAppliedToBackground): Added this override that
always returns false, because table cells sometimes apply a clip before drawing the background.
* rendering/RenderTableCell.h:

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

Source/WebCore/ChangeLog
Source/WebCore/rendering/InlineFlowBox.cpp
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/RenderBox.h
Source/WebCore/rendering/RenderBoxModelObject.cpp
Source/WebCore/rendering/RenderBoxModelObject.h
Source/WebCore/rendering/RenderFieldset.cpp
Source/WebCore/rendering/RenderTable.cpp
Source/WebCore/rendering/RenderTableCell.cpp
Source/WebCore/rendering/RenderTableCell.h

index e9277da27890a6abeb3929dd33128b41b65778ec..215d20c825fb4bcc4f5c2086f6252208f956a1cf 100644 (file)
@@ -1,3 +1,41 @@
+2012-02-15  Dan Bernstein  <mitz@apple.com>
+
+        <rdar://problem/10870238> Box shadow drawing takes an unnecessarily slow code path in some single-shadow, opaque-background cases
+        https://bugs.webkit.org/show_bug.cgi?id=78728
+
+        In some cases, when there is only one normal box shadow, and the box has an opaque background,
+        it is possible to draw the box shadow by having the background cast it directly. This appears
+        to be faster than the generic code path that uses a separate drawing pass to cast the shadow,
+        clipping out the border box and the shadow-casting box.
+
+        Reviewed by Dave Hyatt.
+
+        No new tests, because behavior is unchanged.
+
+        * rendering/InlineFlowBox.cpp:
+        (WebCore::InlineFlowBox::paintBoxDecorations): Changed to not paint normal box shadows if
+        they are going to be cast by the background.
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::paintBoxDecorations): Ditto.
+        * rendering/RenderBox.h: Made determineBackgroundBleedAvoidance() protected.
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::applyBoxShadowForBackground): Added this helper function, which applies the first
+        normal shadow from the given RenderStyle to the given GraphicsContext.
+        (WebCore::RenderBoxModelObject::paintFillLayerExtended): Added calls to
+        applyBoxShadowForBackground() before drawing the background color when needed.
+        (WebCore::RenderBoxModelObject::boxShadowShouldBeAppliedToBackground): Added. Returns true
+        in some of the cases where the box shadow can be cast by the background directly.
+        * rendering/RenderBoxModelObject.h:
+        * rendering/RenderFieldset.cpp:
+        (WebCore::RenderFieldset::paintBoxDecorations): Changed to not paint normal box shadows if
+        they are going to be cast by the background.
+        * rendering/RenderTable.cpp:
+        (WebCore::RenderTable::paintBoxDecorations): Ditto.
+        * rendering/RenderTableCell.cpp:
+        (WebCore::RenderTableCell::boxShadowShouldBeAppliedToBackground): Added this override that
+        always returns false, because table cells sometimes apply a clip before drawing the background.
+        * rendering/RenderTableCell.h:
+
 2012-02-15  Ojan Vafai  <ojan@chromium.org>
 
         getComputedStyle of flex-item-align:auto should resolve to it's parent's flex-align value
index b7436a52ce0a59197924b55db976b85f593cf48d..0d8e861400f95a89e57e5b80c63bbb08648f4cb7 100644 (file)
@@ -1203,7 +1203,8 @@ void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&
     if ((!parent() && m_firstLine && styleToUse != renderer()->style()) || (parent() && renderer()->hasBoxDecorations())) {
         LayoutRect paintRect = LayoutRect(adjustedPaintoffset, frameRect.size());
         // Shadow comes first and is behind the background and border.
-        paintBoxShadow(paintInfo, styleToUse, Normal, paintRect);
+        if (!boxModelObject()->boxShadowShouldBeAppliedToBackground(BackgroundBleedNone))
+            paintBoxShadow(paintInfo, styleToUse, Normal, paintRect);
 
         Color c = styleToUse->visitedDependentColor(CSSPropertyBackgroundColor);
         paintFillLayers(paintInfo, c, styleToUse->backgroundLayers(), paintRect);
index 96de2b946ea54f26809bcc8eb41eea4824aaac57..a6600256143760b3e0efb3a725986cf8c9853729 100644 (file)
@@ -917,11 +917,12 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai
     // balloon layout is an example of this).
     borderFitAdjust(paintRect);
 
+    BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context);
+
     // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
     // custom shadows of their own.
-    paintBoxShadow(paintInfo, paintRect, style(), Normal);
-
-    BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context);
+    if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance))
+        paintBoxShadow(paintInfo, paintRect, style(), Normal);
 
     GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
     if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
index ee26382aaea44e765cec3bce1b429524ad45c260..b706ab4215076d36dfa7e8dc5b4cd5937b1717f4 100644 (file)
@@ -460,6 +460,8 @@ protected:
 
     void paintMaskImages(const PaintInfo&, const LayoutRect&);
 
+    BackgroundBleedAvoidance determineBackgroundBleedAvoidance(GraphicsContext*) const;
+
 #if PLATFORM(MAC)
     void paintCustomHighlight(const LayoutPoint&, const AtomicString& type, bool behindText);
 #endif
@@ -504,8 +506,6 @@ private:
     // These include tables, positioned objects, floats and flexible boxes.
     virtual void computePreferredLogicalWidths() { setPreferredLogicalWidthsDirty(false); }
 
-    BackgroundBleedAvoidance determineBackgroundBleedAvoidance(GraphicsContext*) const;
-
 private:
     // The width/height of the contents + borders + padding.  The x/y location is relative to our container (which is not always our parent).
     LayoutRect m_frameRect;
index 8329ebd1f45d38e8b29feebae315fa6d2d390b79..63a7386238cb8447a5310bf02a8e56e4fb26879e 100644 (file)
@@ -619,6 +619,19 @@ static LayoutRect backgroundRectAdjustedForBleedAvoidance(GraphicsContext* conte
     return adjustedRect;
 }
 
+static void applyBoxShadowForBackground(GraphicsContext* context, RenderStyle* style)
+{
+    const ShadowData* boxShadow = style->boxShadow();
+    while (boxShadow->style() != Normal)
+        boxShadow = boxShadow->next();
+
+    FloatSize shadowOffset(boxShadow->x(), boxShadow->y());
+    if (!boxShadow->isWebkitBoxShadow())
+        context->setShadow(shadowOffset, boxShadow->blur(), boxShadow->color(), style->colorSpace());
+    else
+        context->setLegacyShadow(shadowOffset, boxShadow->blur(), boxShadow->color(), style->colorSpace());
+}
+
 void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& color, const FillLayer* bgLayer, const LayoutRect& rect,
     BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, CompositeOperator op, RenderObject* backgroundObject)
 {
@@ -669,6 +682,11 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
         if (!colorVisible)
             return;
 
+        bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance);
+        GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
+        if (boxShadowShouldBeAppliedToBackground)
+            applyBoxShadowForBackground(context, style());
+
         if (hasRoundedBorder && bleedAvoidance != BackgroundBleedUseTransparencyLayer) {
             RoundedRect border = getBackgroundRoundedRect(backgroundRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance), box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
             context->fillRoundedRect(border, bgColor, style()->colorSpace());
@@ -778,7 +796,10 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
     // Paint the color first underneath all images.
     if (!bgLayer->next()) {
         IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
-        backgroundRect.intersect(paintInfo.rect);
+        bool boxShadowShouldBeAppliedToBackground = this->boxShadowShouldBeAppliedToBackground(bleedAvoidance);
+        if (!boxShadowShouldBeAppliedToBackground)
+            backgroundRect.intersect(paintInfo.rect);
+
         // If we have an alpha and we are painting the root element, go ahead and blend with the base background color.
         Color baseColor;
         bool shouldClearBackground = false;
@@ -788,6 +809,10 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
                 shouldClearBackground = true;
         }
 
+        GraphicsContextStateSaver shadowStateSaver(*context, boxShadowShouldBeAppliedToBackground);
+        if (boxShadowShouldBeAppliedToBackground)
+            applyBoxShadowForBackground(context, style());
+
         if (baseColor.alpha()) {
             if (bgColor.alpha())
                 baseColor = baseColor.blend(bgColor);
@@ -2556,6 +2581,44 @@ bool RenderBoxModelObject::borderObscuresBackground() const
     return true;
 }
 
+bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance bleedAvoidance) const
+{
+    if (bleedAvoidance != BackgroundBleedNone)
+        return false;
+
+    if (style()->hasAppearance())
+        return false;
+
+    const ShadowData* boxShadow = style()->boxShadow();
+    bool hasOneNormalBoxShadow = false;
+    for (const ShadowData* currentShadow = boxShadow; currentShadow; currentShadow = currentShadow->next()) {
+        if (currentShadow->style() != Normal)
+            continue;
+        if (hasOneNormalBoxShadow)
+            return false;
+        hasOneNormalBoxShadow = true;
+    }
+
+    if (!hasOneNormalBoxShadow)
+        return false;
+
+    Color backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor);
+    if (!backgroundColor.isValid() || backgroundColor.alpha() < 255)
+        return false;
+
+    const FillLayer* lastBackgroundLayer = style()->backgroundLayers();
+    for (const FillLayer* next = lastBackgroundLayer->next(); next; )
+        lastBackgroundLayer = next;
+
+    if (lastBackgroundLayer->clip() != BorderFillBox)
+        return false;
+
+    if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroundAttachment)
+        return false;
+
+    return true;
+}
+
 static inline LayoutRect areaCastingShadowInHole(const LayoutRect& holeRect, int shadowBlur, int shadowSpread, const LayoutSize& shadowOffset)
 {
     LayoutRect bounds(holeRect);
index 027b1fb891fc858ca3673001b6fd3de518fdf1d4..c1d0497224e6c8a8657640fca0c1f2766cb8ecbc 100644 (file)
@@ -127,6 +127,8 @@ public:
     void paintBoxShadow(const PaintInfo&, const LayoutRect&, const RenderStyle*, ShadowStyle, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true);
     void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, const LayoutRect&, BackgroundBleedAvoidance, InlineFlowBox* = 0, const LayoutSize& = LayoutSize(), CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0);
     
+    virtual bool boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance) const;
+
     // Overridden by subclasses to determine line height and baseline position.
     virtual LayoutUnit lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0;
     virtual LayoutUnit baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0;
index 3a70866e259483cbad7ef61149ad1b7602057206..8ecd47121fcdfeb6c330d492547155bce9d11e73 100644 (file)
@@ -140,8 +140,9 @@ void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint
         paintRect.setWidth(paintRect.width() - xOff);
         paintRect.setX(paintRect.x() + xOff);
     }
-    
-    paintBoxShadow(paintInfo, paintRect, style(), Normal);
+
+    if (!boxShadowShouldBeAppliedToBackground(determineBackgroundBleedAvoidance(paintInfo.context)))
+        paintBoxShadow(paintInfo, paintRect, style(), Normal);
     paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect);
     paintBoxShadow(paintInfo, paintRect, style(), Inset);
 
index 5de2ddbdaf8153d913409409d4d30509bea42e2f..83c4dd5a547938710198665d200e32dc4f3e90ba 100644 (file)
@@ -617,7 +617,8 @@ void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& p
     LayoutRect rect(paintOffset, size());
     subtractCaptionRect(rect);
 
-    paintBoxShadow(paintInfo, rect, style(), Normal);
+    if (!boxShadowShouldBeAppliedToBackground(determineBackgroundBleedAvoidance(paintInfo.context)))
+        paintBoxShadow(paintInfo, rect, style(), Normal);
     paintBackground(paintInfo, rect);
     paintBoxShadow(paintInfo, rect, style(), Inset);
 
index 5b690f4c7106f571ffc77f5087a4b394fddb9584..0fef8ad84ea418407a5d259287e09c2ff8c384b2 100644 (file)
@@ -1068,6 +1068,11 @@ void RenderTableCell::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOf
     paintMaskImages(paintInfo, LayoutRect(paintOffset, size()));
 }
 
+bool RenderTableCell::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance) const
+{
+    return false;
+}
+
 void RenderTableCell::scrollbarsChanged(bool horizontalScrollbarChanged, bool verticalScrollbarChanged)
 {
     LayoutUnit scrollbarHeight = scrollbarLogicalHeight();
index 2548b471325c15d0310175b48bcce227431af3cf..6bfbf1ed8cf8cac58a979a963814fbe1c8512dee 100644 (file)
@@ -151,6 +151,8 @@ private:
     virtual void paintBoxDecorations(PaintInfo&, const LayoutPoint&);
     virtual void paintMask(PaintInfo&, const LayoutPoint&);
 
+    virtual bool boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance) const OVERRIDE;
+
     virtual LayoutSize offsetFromContainer(RenderObject*, const LayoutPoint&) const;
     virtual LayoutRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) const;
     virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, LayoutRect&, bool fixed = false) const;