Boxes with rounded corners and thin borders are too slow to draw
authorjunov@google.com <junov@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Nov 2012 19:12:02 +0000 (19:12 +0000)
committerjunov@google.com <junov@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Nov 2012 19:12:02 +0000 (19:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=101974

Reviewed by Simon Fraser.

Source/WebCore:

With the current implementation RenderBox::
determineBackgroundBleedAvoidance() uses the slow path
BackgroundBleedUseTransparencyLayer for some very common use cases,
notably for drawing rectangles with rounded corners that have thin
borders. This is because the BackgroundBleedShrinkBackground
strategy requires a border at least two pixels wide on all sides. This
patch introduce drawing strategy BackgroundBleedBackgroundOverBorder.
This approach consists in drawing the border first, with an inset inner
edge (for anti-aliased compositing to work well).  This approach only
works with opaque solid edges and opaque single-layer backgrounds.
By using this approach rather than BackgroundBleedUseTransparencyLayer,
we save two clipPath, one save and one saveLayer on the
GraphicsContext. This patch gets good coverage from existing layout
tests. One additional test was added to exercise mitring, thick edges
and anti-aliasing edge cases under the new painting algorithm.

Test: fast/borders/border-radius-wide-border-05.html

* rendering/RenderBox.cpp:
(WebCore::RenderBox::determineBackgroundBleedAvoidance):
Added selection criteria for BackgroundOverBorder
(WebCore::RenderBox::paintBoxDecorations):
Added a preliminary paintBorder pass for BackgroundOverBorder
(WebCore::RenderBox::paintBackground):
Insetting the background to to innerBorder when bleedAvoidance is
BackgroundOverBorder.  This why BackgroundOverBorder only works for
Opaque solid edges.
(WebCore):
(WebCore::RenderBox::backgroundIsSingleOpaqueLayer):
Utility method use by determineBackgroundBleedAvoidance to test the
background's eligibility for BackgroundOverBorder bleed avoidance
strategy.  The reason the background must be a single layer is to avoid
color bleeding from layer compositing along anti-aliased edges
* rendering/RenderBox.h:
(RenderBox):
* rendering/RenderBoxModelObject.cpp:
(WebCore::RenderBoxModelObject::getBackgroundRoundedRect):
(WebCore::RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance):
Added support for BackgroundOverBorder by applying a one pixel inset.
(WebCore::RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance):
Set the background rect to the inner border for BackgroundOverBorder
(WebCore):
(WebCore::RenderBoxModelObject::paintFillLayerExtended):
Added support for BackgroundOverBorder by using
backgroundRoundedRectAdjustedForBleedAvoidance
(WebCore::RenderBoxModelObject::paintBorderSides):
Added support for BackgroundOverBorder by applying per-side inset
adjustments.
(WebCore::RenderBoxModelObject::paintTranslucentBorderSides):
(WebCore::RenderBoxModelObject::paintBorder):
Added support for BackgroundOverBorder by using an adjusted inner
border, but not if sides are painted individually.
* rendering/RenderBoxModelObject.h:
(RenderBoxModelObject):

LayoutTests:

New test that purposely triggers the BackgroundOverBorder painting path
in WebCore::RenderBox, to test it with thick borders, off-diagonal
mitring, an anti-aliased rounded border wedge, and a border of width 0.

* fast/borders/border-radius-wide-border-05.html: Added.
* fast/borders/border-radius-wide-border-05-expected.txt: Added.
* LayoutTests/platform/chromium-win/fast/borders/border-radius-wide-border-05-expected.png: Added.
* LayoutTests/platform/chromium/TestExpectations: Added Expectations.
Added expectations for tests pixels results that require rebaselining

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

LayoutTests/ChangeLog
LayoutTests/fast/borders/border-radius-wide-border-05-expected.txt [new file with mode: 0644]
LayoutTests/fast/borders/border-radius-wide-border-05.html [new file with mode: 0644]
LayoutTests/platform/chromium-win/fast/borders/border-radius-wide-border-05-expected.png [new file with mode: 0644]
LayoutTests/platform/chromium/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderBox.cpp
Source/WebCore/rendering/RenderBox.h
Source/WebCore/rendering/RenderBoxModelObject.cpp
Source/WebCore/rendering/RenderBoxModelObject.h

index faf13c3..fd9a8d4 100644 (file)
@@ -1,3 +1,20 @@
+2012-11-14  Justin Novosad  <junov@google.com>
+
+        Boxes with rounded corners and thin borders are too slow to draw
+        https://bugs.webkit.org/show_bug.cgi?id=101974
+
+        Reviewed by Simon Fraser.
+
+        New test that purposely triggers the BackgroundOverBorder painting path
+        in WebCore::RenderBox, to test it with thick borders, off-diagonal
+        mitring, an anti-aliased rounded border wedge, and a border of width 0.
+
+        * fast/borders/border-radius-wide-border-05.html: Added.
+        * fast/borders/border-radius-wide-border-05-expected.txt: Added.
+        * LayoutTests/platform/chromium-win/fast/borders/border-radius-wide-border-05-expected.png: Added.
+        * LayoutTests/platform/chromium/TestExpectations: Added Expectations.
+        Added expectations for tests pixels results that require rebaselining
+
 2012-11-14  Sami Kyostila  <skyostil@chromium.org>
 
         Optimize painting of composited scrolling layers
diff --git a/LayoutTests/fast/borders/border-radius-wide-border-05-expected.txt b/LayoutTests/fast/borders/border-radius-wide-border-05-expected.txt
new file mode 100644 (file)
index 0000000..1043394
--- /dev/null
@@ -0,0 +1,6 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,20) size 784x560
+      RenderBlock {DIV} at (20,0) size 200x200 [bgcolor=#008000] [border: (10px solid #FF0000) none (10px solid #FF0000) (36px solid #0000FF)]
diff --git a/LayoutTests/fast/borders/border-radius-wide-border-05.html b/LayoutTests/fast/borders/border-radius-wide-border-05.html
new file mode 100644 (file)
index 0000000..df7a495
--- /dev/null
@@ -0,0 +1,19 @@
+<style>
+    div#t {
+        width: 200px;
+        height: 200px;
+        border: 20px solid rgba(255,0,0,1);
+        border-radius: 100px;
+        border-top-right-radius: 0px;
+        border-bottom-left-radius: 0px;
+        border-left-color: rgba(0,0,255,1);
+        background-color: green;
+        box-sizing: border-box;
+        margin: 20px;
+        border-top-width: 10px;
+        border-bottom-width: 10px;
+        border-left-width: 36px;
+        border-right-width: 0px;
+    }
+</style>
+<div id="t"></div>
diff --git a/LayoutTests/platform/chromium-win/fast/borders/border-radius-wide-border-05-expected.png b/LayoutTests/platform/chromium-win/fast/borders/border-radius-wide-border-05-expected.png
new file mode 100644 (file)
index 0000000..21abd06
Binary files /dev/null and b/LayoutTests/platform/chromium-win/fast/borders/border-radius-wide-border-05-expected.png differ
index 92234d4..291e1fd 100644 (file)
@@ -3086,6 +3086,13 @@ webkit.org/b/76572 http/tests/download [ Failure ]
 
 webkit.org/b/77314 [ Mac Win ] fast/events/touch/send-oncancel-event.html [ Failure Pass ]
 
+# Needs rebaselining after fix for bug 101974
+webkit.org/b/101974 [ Mac Linux ] fast/borders/border-radius-wide-border-05.html [ ImageOnlyFailure Pass ]
+webkit.org/b/101974 fast/forms/validation-message-appearance.html [ ImageOnlyFailure Pass ]
+webkit.org/b/101974 svg/custom/svg-fonts-in-html.html [ ImageOnlyFailure Pass ]
+webkit.org/b/101974 fast/borders/border-radius-wide-border-01.html [ ImageOnlyFailure Pass ]
+webkit.org/b/101974 fast/borders/borderRadiusInset01.html [ ImageOnlyFailure Pass ]
+
 # Skipped until setTouchPointRadius get implemented
 webkit.org/b/91012 touchadjustment/focusout-on-touch.html [ Skip ]
 
index 7fcf571..8423db9 100644 (file)
@@ -1,3 +1,65 @@
+2012-11-14  Justin Novosad  <junov@google.com>
+
+        Boxes with rounded corners and thin borders are too slow to draw
+        https://bugs.webkit.org/show_bug.cgi?id=101974
+
+        Reviewed by Simon Fraser.
+
+        With the current implementation RenderBox::
+        determineBackgroundBleedAvoidance() uses the slow path
+        BackgroundBleedUseTransparencyLayer for some very common use cases,
+        notably for drawing rectangles with rounded corners that have thin
+        borders. This is because the BackgroundBleedShrinkBackground
+        strategy requires a border at least two pixels wide on all sides. This
+        patch introduce drawing strategy BackgroundBleedBackgroundOverBorder.
+        This approach consists in drawing the border first, with an inset inner
+        edge (for anti-aliased compositing to work well).  This approach only
+        works with opaque solid edges and opaque single-layer backgrounds.
+        By using this approach rather than BackgroundBleedUseTransparencyLayer,
+        we save two clipPath, one save and one saveLayer on the
+        GraphicsContext. This patch gets good coverage from existing layout
+        tests. One additional test was added to exercise mitring, thick edges
+        and anti-aliasing edge cases under the new painting algorithm.
+
+        Test: fast/borders/border-radius-wide-border-05.html
+
+        * rendering/RenderBox.cpp:
+        (WebCore::RenderBox::determineBackgroundBleedAvoidance):
+        Added selection criteria for BackgroundOverBorder
+        (WebCore::RenderBox::paintBoxDecorations):
+        Added a preliminary paintBorder pass for BackgroundOverBorder
+        (WebCore::RenderBox::paintBackground):
+        Insetting the background to to innerBorder when bleedAvoidance is
+        BackgroundOverBorder.  This why BackgroundOverBorder only works for
+        Opaque solid edges.
+        (WebCore):
+        (WebCore::RenderBox::backgroundIsSingleOpaqueLayer):
+        Utility method use by determineBackgroundBleedAvoidance to test the
+        background's eligibility for BackgroundOverBorder bleed avoidance
+        strategy.  The reason the background must be a single layer is to avoid
+        color bleeding from layer compositing along anti-aliased edges
+        * rendering/RenderBox.h:
+        (RenderBox):
+        * rendering/RenderBoxModelObject.cpp:
+        (WebCore::RenderBoxModelObject::getBackgroundRoundedRect):
+        (WebCore::RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance):
+        Added support for BackgroundOverBorder by applying a one pixel inset.
+        (WebCore::RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance):
+        Set the background rect to the inner border for BackgroundOverBorder
+        (WebCore):
+        (WebCore::RenderBoxModelObject::paintFillLayerExtended):
+        Added support for BackgroundOverBorder by using 
+        backgroundRoundedRectAdjustedForBleedAvoidance
+        (WebCore::RenderBoxModelObject::paintBorderSides):
+        Added support for BackgroundOverBorder by applying per-side inset
+        adjustments.
+        (WebCore::RenderBoxModelObject::paintTranslucentBorderSides):
+        (WebCore::RenderBoxModelObject::paintBorder):
+        Added support for BackgroundOverBorder by using an adjusted inner
+        border, but not if sides are painted individually.
+        * rendering/RenderBoxModelObject.h:
+        (RenderBoxModelObject):
+
 2012-11-14  Hideki Yoshida  <yoshida-hxa@necst.nec.co.jp>
 
         [WinCairo] Fix cairo_t* memory leak in GraphicsContext::platformInit
index b83923a..046b5ec 100644 (file)
@@ -830,11 +830,8 @@ BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsCo
     FloatSize contextScaling(static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale()));
     if (borderObscuresBackgroundEdge(contextScaling))
         return BackgroundBleedShrinkBackground;
-    
-    // FIXME: there is one more strategy possible, for opaque backgrounds and
-    // translucent borders. In that case we could avoid using a transparency layer,
-    // and paint the border first, and then paint the background clipped to the
-    // inside of the border.
+    if (!style->hasAppearance() && borderObscuresBackground() && backgroundIsSingleOpaqueLayer())
+        return BackgroundBleedBackgroundOverBorder;
 
     return BackgroundBleedUseTransparencyLayer;
 }
@@ -868,12 +865,15 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai
         paintInfo.context->addRoundedRectClip(border);
         paintInfo.context->beginTransparencyLayer(1);
     }
-    
+
     // If we have a native theme appearance, paint that before painting our background.
     // The theme will tell us whether or not we should also paint the CSS background.
     IntRect snappedPaintRect(pixelSnappedIntRect(paintRect));
     bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, snappedPaintRect);
     if (!themePainted) {
+        if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
+            paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
+
         paintBackground(paintInfo, paintRect, bleedAvoidance);
 
         if (style()->hasAppearance())
@@ -882,7 +882,7 @@ void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& pai
     paintBoxShadow(paintInfo, paintRect, style(), Inset);
 
     // The theme will tell us whether or not we should also paint the CSS border.
-    if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, snappedPaintRect))) && style()->hasBorder())
+    if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && (!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, snappedPaintRect))) && style()->hasBorder())
         paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
 
     if (bleedAvoidance == BackgroundBleedUseTransparencyLayer)
@@ -903,6 +903,25 @@ void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& pa
     }
 }
 
+bool RenderBox::backgroundIsSingleOpaqueLayer() const
+{
+    const FillLayer* fillLayer = style()->backgroundLayers();
+    if (!fillLayer || fillLayer->next() || fillLayer->clip() != BorderFillBox || fillLayer->composite() != CompositeSourceOver)
+        return false;
+
+    // Clipped with local scrolling
+    if (hasOverflowClip() && fillLayer->attachment() == LocalBackgroundAttachment)
+        return false;
+
+    Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor);
+    if (bgColor.isValid() && bgColor.alpha() == 255)
+        return true;
+    
+    // FIXME: return true if a background image is present and is opaque
+
+    return false;
+}
+
 void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
     if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
index bec1618..61fcceb 100644 (file)
@@ -570,6 +570,7 @@ protected:
     void paintMaskImages(const PaintInfo&, const LayoutRect&);
 
     BackgroundBleedAvoidance determineBackgroundBleedAvoidance(GraphicsContext*) const;
+    bool backgroundIsSingleOpaqueLayer() const;
 
 #if PLATFORM(MAC)
     void paintCustomHighlight(const LayoutPoint&, const AtomicString& type, bool behindText);
index 1277a7c..ed2bb0f 100644 (file)
@@ -647,7 +647,7 @@ LayoutUnit RenderBoxModelObject::computedCSSPaddingEnd() const
 }
 
 RoundedRect RenderBoxModelObject::getBackgroundRoundedRect(const LayoutRect& borderRect, InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
-    bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
+    bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
 {
     RenderView* renderView = view();
     RoundedRect border = style()->getRoundedBorderFor(borderRect, renderView, includeLogicalLeftEdge, includeLogicalRightEdge);
@@ -691,17 +691,31 @@ void RenderBoxModelObject::clipRoundedInnerRect(GraphicsContext * context, const
     }
 }
 
-static LayoutRect backgroundRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance)
+static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRect& rect)
 {
-    if (bleedAvoidance != BackgroundBleedShrinkBackground)
-        return borderRect;
-
-    // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum.
+    LayoutRect shrunkRect = rect;
     AffineTransform transform = context->getCTM();
-    LayoutRect adjustedRect = borderRect;
-    adjustedRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale())));
-    adjustedRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale())));
-    return adjustedRect;
+    shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale())));
+    shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale())));
+    return shrunkRect;
+}
+
+LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance) const
+{
+    // We shrink the rectangle by one pixel on each side to make it fully overlap the anti-aliased background border
+    return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectByOnePixel(context, rect) : rect;
+}
+
+RoundedRect RenderBoxModelObject::backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext* context, const LayoutRect& borderRect, BackgroundBleedAvoidance bleedAvoidance, InlineFlowBox* box, const LayoutSize& boxSize, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const
+{
+    if (bleedAvoidance == BackgroundBleedShrinkBackground) {
+        // We shrink the rectangle by one pixel on each side because the bleed is one pixel maximum.
+        return getBackgroundRoundedRect(shrinkRectByOnePixel(context, borderRect), box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
+    }
+    if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
+        return style()->getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge);
+
+    return getBackgroundRoundedRect(borderRect, box, boxSize.width(), boxSize.height(), includeLogicalLeftEdge, includeLogicalRightEdge);
 }
 
 static void applyBoxShadowForBackground(GraphicsContext* context, RenderStyle* style)
@@ -773,7 +787,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
             applyBoxShadowForBackground(context, style());
 
         if (hasRoundedBorder && bleedAvoidance != BackgroundBleedUseTransparencyLayer) {
-            RoundedRect border = getBackgroundRoundedRect(backgroundRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance), box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
+            RoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge);
             context->fillRoundedRect(border, bgColor, style()->colorSpace());
         } else
             context->fillRect(pixelSnappedIntRect(rect), bgColor, style()->colorSpace());
@@ -785,8 +799,7 @@ void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, co
     bool clipToBorderRadius = hasRoundedBorder && !(isBorderFill && bleedAvoidance == BackgroundBleedUseTransparencyLayer);
     GraphicsContextStateSaver clipToBorderStateSaver(*context, clipToBorderRadius);
     if (clipToBorderRadius) {
-        LayoutRect adjustedRect = isBorderFill ? backgroundRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance) : rect;
-        RoundedRect border = getBackgroundRoundedRect(adjustedRect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
+        RoundedRect border = isBorderFill ? backgroundRoundedRectAdjustedForBleedAvoidance(context, rect, bleedAvoidance, box, boxSize, includeLeftEdge, includeRightEdge) : getBackgroundRoundedRect(rect, box, boxSize.width(), boxSize.height(), includeLeftEdge, includeRightEdge);
 
         // Clip to the padding or content boxes as necessary.
         if (bgLayer->clip() == ContentFillBox) {
@@ -1729,8 +1742,8 @@ static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdg
 }
 
 void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
-                                            const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance,
-                                            bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
+    const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance,
+    bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor)
 {
     bool renderRadii = outerBorder.isRounded();
 
@@ -1738,9 +1751,14 @@ void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, co
     if (renderRadii)
         roundedPath.addRoundedRect(outerBorder);
     
+    // The inner border adjustment for bleed avoidance mode BackgroundBleedBackgroundOverBorder
+    // is only applied to sideRect, which is okay since BackgroundBleedBackgroundOverBorder
+    // is only to be used for solid borders and the shape of the border painted by drawBoxSideFromPath
+    // only depends on sideRect when painting solid borders.
+
     if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) {
         IntRect sideRect = outerBorder.rect();
-        sideRect.setHeight(edges[BSTop].width);
+        sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y());
 
         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].style) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorder.radii().topRight()));
         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
@@ -1748,7 +1766,7 @@ void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, co
 
     if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) {
         IntRect sideRect = outerBorder.rect();
-        sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width);
+        sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBorderAdjustment.y());
 
         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().bottomRight()));
         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
@@ -1756,7 +1774,7 @@ void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, co
 
     if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) {
         IntRect sideRect = outerBorder.rect();
-        sideRect.setWidth(edges[BSLeft].width);
+        sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x());
 
         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].style) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerBorder.radii().topLeft()));
         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
@@ -1764,14 +1782,14 @@ void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, co
 
     if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) {
         IntRect sideRect = outerBorder.rect();
-        sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width);
+        sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBorderAdjustment.x());
 
         bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].style) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), innerBorder.radii().topRight()));
         paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sideRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor);
     }
 }
 
-void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
+void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphicsContext, const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment,
                                                        const BorderEdge edges[], BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias)
 {
     BorderEdgeFlags edgesToDraw = AllBorderEdges;
@@ -1802,7 +1820,7 @@ void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphics
             commonColor = Color(commonColor.red(), commonColor.green(), commonColor.blue());
         }
 
-        paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor);
+        paintBorderSides(graphicsContext, style, outerBorder, innerBorder, innerBorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, &commonColor);
             
         if (useTransparencyLayer)
             graphicsContext->endTransparencyLayer();
@@ -1825,7 +1843,7 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect&
     BorderEdge edges[4];
     getBorderEdgeInfo(edges, style, includeLogicalLeftEdge, includeLogicalRightEdge);
     RoundedRect outerBorder = style->getRoundedBorderFor(rect, view(), includeLogicalLeftEdge, includeLogicalRightEdge);
-    RoundedRect innerBorder = style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge);
+    RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdjustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLeftEdge, includeLogicalRightEdge);
 
     bool haveAlphaColor = false;
     bool haveAllSolidEdges = true;
@@ -1961,10 +1979,12 @@ void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect&
 
     // If only one edge visible antialiasing doesn't create seams
     bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible == 1;
+    RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge, includeLogicalRightEdge) : innerBorder;
+    IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorder.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y());
     if (haveAlphaColor)
-        paintTranslucentBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
+        paintTranslucentBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
     else
-        paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges, AllBorderEdges, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
+        paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBorder, innerBorderAdjustment, edges, AllBorderEdges, bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, antialias);
 }
 
 void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[],
index 1cfe412..037fc4f 100644 (file)
@@ -38,7 +38,8 @@ typedef unsigned BorderEdgeFlags;
 enum BackgroundBleedAvoidance {
     BackgroundBleedNone,
     BackgroundBleedShrinkBackground,
-    BackgroundBleedUseTransparencyLayer
+    BackgroundBleedUseTransparencyLayer,
+    BackgroundBleedBackgroundOverBorder
 };
 
 enum ContentChangeType {
@@ -151,7 +152,7 @@ public:
     bool paintNinePieceImage(GraphicsContext*, const LayoutRect&, const RenderStyle*, const NinePieceImage&, CompositeOperator = CompositeSourceOver);
     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, InlineFlowBox* = 0) const;
 
     // Overridden by subclasses to determine line height and baseline position.
@@ -233,6 +234,8 @@ protected:
     void getBorderEdgeInfo(class BorderEdge[], const RenderStyle*, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true) const;
     bool borderObscuresBackgroundEdge(const FloatSize& contextScale) const;
     bool borderObscuresBackground() const;
+    RoundedRect backgroundRoundedRectAdjustedForBleedAvoidance(GraphicsContext*, const LayoutRect&, BackgroundBleedAvoidance, InlineFlowBox*, const LayoutSize&, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const;
+    LayoutRect borderInnerRectAdjustedForBleedAvoidance(GraphicsContext*, const LayoutRect&, BackgroundBleedAvoidance) const;
 
     bool shouldPaintAtLowQuality(GraphicsContext*, Image*, const void*, const LayoutSize&);
 
@@ -285,7 +288,7 @@ private:
     IntSize calculateImageIntrinsicDimensions(StyleImage*, const IntSize& scaledPositioningAreaSize, ScaleByEffectiveZoomOrNot) const;
 
     RoundedRect getBackgroundRoundedRect(const LayoutRect&, InlineFlowBox*, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
-        bool includeLogicalLeftEdge, bool includeLogicalRightEdge);
+        bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const;
 
 
     void clipBorderSidePolygon(GraphicsContext*, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
@@ -294,11 +297,11 @@ private:
     void paintOneBorderSide(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
                                 const IntRect& sideRect, BoxSide, BoxSide adjacentSide1, BoxSide adjacentSide2, const class BorderEdge[],
                                 const Path*, BackgroundBleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, const Color* overrideColor = 0);
-    void paintTranslucentBorderSides(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
+    void paintTranslucentBorderSides(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder, const IntPoint& innerBorderAdjustment,
                           const class BorderEdge[], BackgroundBleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias = false);
     void paintBorderSides(GraphicsContext*, const RenderStyle*, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
-                          const class BorderEdge[], BorderEdgeFlags, BackgroundBleedAvoidance,
-                          bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias = false, const Color* overrideColor = 0);
+        const IntPoint& innerBorderAdjustment, const class BorderEdge[], BorderEdgeFlags, BackgroundBleedAvoidance,
+        bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias = false, const Color* overrideColor = 0);
     void drawBoxSideFromPath(GraphicsContext*, const LayoutRect&, const Path&, const class BorderEdge[],
                             float thickness, float drawThickness, BoxSide, const RenderStyle*, 
                             Color, EBorderStyle, BackgroundBleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge);