⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 201868 in webkit


Ignore:
Timestamp:
Jun 9, 2016, 9:59:17 AM (10 years ago)
Author:
Simon Fraser
Message:

border-radius with different width and height rendered wrong
​https://bugs.webkit.org/show_bug.cgi?id=158300
<rdar://problem/26672922>

Reviewed by Zalan Bujtas.
Source/WebCore:

Borders with border-radius close to 100% 100% got oddly clipped. This happened because
of the clipping we do to achieve the diagonal corner joins; RenderBoxModelObject::clipBorderSidePolygon()
created trapezoids for each side, but only extended them to the center of the inner border rect.
This clipped out parts of these borders.

Fix by computing the trapezoids by intersecting the corner diagonal line with a line that passes
through the adjacent corners of the inner rect, whose intersection is always going to be inside the
rounded border. Also fix the quads used to do the antialiased/non-antialiased clipping,
by adding a point rather than moving a corner point to ensure we don't mistakenly clip out any
parts of the border.

Finally, improve the rendering of non-renderable cases by actually doing the diagonal
corner joins rather than just giving up.

Tests: fast/borders/border-non-renderable-radius-inner-clip.html

fast/borders/border-radius-inner-clip-vertical.html
fast/borders/border-radius-inner-clip.html

  • rendering/RenderBoxModelObject.cpp:

(WebCore::calculateAdjustedInnerBorder): Moved up.
(WebCore::RenderBoxModelObject::paintOneBorderSide):
(WebCore::RenderBoxModelObject::clipBorderSidePolygon):
(WebCore::findInnerVertex): Deleted.
(WebCore::calculateSideRectIncludingInner): Deleted.
(WebCore::RenderBoxModelObject::clipBorderSideForComplexInnerPath): Deleted.

  • rendering/RenderBoxModelObject.h:

LayoutTests:

  • fast/borders/border-non-renderable-radius-inner-clip-expected.html: Added.
  • fast/borders/border-non-renderable-radius-inner-clip.html: Added.
  • fast/borders/border-radius-inner-clip-expected-mismatch.html: Added.
  • fast/borders/border-radius-inner-clip-vertical-expected-mismatch.html: Added.
  • fast/borders/border-radius-inner-clip-vertical.html: Added.
  • fast/borders/border-radius-inner-clip.html: Added.
  • fast/borders/resources/border-radius-helpers.js: Added.

(createContainer):
(createReferenceContainer):

Location:
trunk
Files:
7 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r201867 r201868  
     12016-06-09  Simon Fraser  <simon.fraser@apple.com>
     2
     3        border-radius with different width and height rendered wrong
     4        https://bugs.webkit.org/show_bug.cgi?id=158300
     5        <rdar://problem/26672922>
     6
     7        Reviewed by Zalan Bujtas.
     8       
     9        * fast/borders/border-non-renderable-radius-inner-clip-expected.html: Added.
     10        * fast/borders/border-non-renderable-radius-inner-clip.html: Added.
     11        * fast/borders/border-radius-inner-clip-expected-mismatch.html: Added.
     12        * fast/borders/border-radius-inner-clip-vertical-expected-mismatch.html: Added.
     13        * fast/borders/border-radius-inner-clip-vertical.html: Added.
     14        * fast/borders/border-radius-inner-clip.html: Added.
     15        * fast/borders/resources/border-radius-helpers.js: Added.
     16        (createContainer):
     17        (createReferenceContainer):
     18
    1192016-06-09  Per Arne Vollan  <pvollan@apple.com>
    220
  • trunk/Source/WebCore/ChangeLog

    r201864 r201868  
     12016-06-09  Simon Fraser  <simon.fraser@apple.com>
     2
     3        border-radius with different width and height rendered wrong
     4        https://bugs.webkit.org/show_bug.cgi?id=158300
     5        <rdar://problem/26672922>
     6
     7        Reviewed by Zalan Bujtas.
     8
     9        Borders with border-radius close to 100% 100% got oddly clipped. This happened because
     10        of the clipping we do to achieve the diagonal corner joins; RenderBoxModelObject::clipBorderSidePolygon()
     11        created trapezoids for each side, but only extended them to the center of the inner border rect.
     12        This clipped out parts of these borders.
     13
     14        Fix by computing the trapezoids by intersecting the corner diagonal line with a line that passes
     15        through the adjacent corners of the inner rect, whose intersection is always going to be inside the
     16        rounded border. Also fix the quads used to do the antialiased/non-antialiased clipping,
     17        by adding a point rather than moving a corner point to ensure we don't mistakenly clip out any
     18        parts of the border.
     19       
     20        Finally, improve the rendering of non-renderable cases by actually doing the diagonal
     21        corner joins rather than just giving up.
     22
     23        Tests: fast/borders/border-non-renderable-radius-inner-clip.html
     24               fast/borders/border-radius-inner-clip-vertical.html
     25               fast/borders/border-radius-inner-clip.html
     26
     27        * rendering/RenderBoxModelObject.cpp:
     28        (WebCore::calculateAdjustedInnerBorder): Moved up.
     29        (WebCore::RenderBoxModelObject::paintOneBorderSide):
     30        (WebCore::RenderBoxModelObject::clipBorderSidePolygon):
     31        (WebCore::findInnerVertex): Deleted.
     32        (WebCore::calculateSideRectIncludingInner): Deleted.
     33        (WebCore::RenderBoxModelObject::clipBorderSideForComplexInnerPath): Deleted.
     34        * rendering/RenderBoxModelObject.h:
     35
    1362016-06-09  Xabier Rodriguez Calvar  <calvaris@igalia.com>
    237
  • trunk/Source/WebCore/rendering/RenderBoxModelObject.cpp

    r201629 r201868  
    14371437}
    14381438
     1439static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, BoxSide side)
     1440{
     1441    // Expand the inner border as necessary to make it a rounded rect (i.e. radii contained within each edge).
     1442    // This function relies on the fact we only get radii not contained within each edge if one of the radii
     1443    // for an edge is zero, so we can shift the arc towards the zero radius corner.
     1444    RoundedRect::Radii newRadii = innerBorder.radii();
     1445    LayoutRect newRect = innerBorder.rect();
     1446
     1447    float overshoot;
     1448    float maxRadii;
     1449
     1450    switch (side) {
     1451    case BSTop:
     1452        overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - newRect.width();
     1453        if (overshoot > 0) {
     1454            ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width()));
     1455            newRect.setWidth(newRect.width() + overshoot);
     1456            if (!newRadii.topLeft().width())
     1457                newRect.move(-overshoot, 0);
     1458        }
     1459        newRadii.setBottomLeft(IntSize(0, 0));
     1460        newRadii.setBottomRight(IntSize(0, 0));
     1461        maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().height());
     1462        if (maxRadii > newRect.height())
     1463            newRect.setHeight(maxRadii);
     1464        break;
     1465
     1466    case BSBottom:
     1467        overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width() - newRect.width();
     1468        if (overshoot > 0) {
     1469            ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().width()));
     1470            newRect.setWidth(newRect.width() + overshoot);
     1471            if (!newRadii.bottomLeft().width())
     1472                newRect.move(-overshoot, 0);
     1473        }
     1474        newRadii.setTopLeft(IntSize(0, 0));
     1475        newRadii.setTopRight(IntSize(0, 0));
     1476        maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight().height());
     1477        if (maxRadii > newRect.height()) {
     1478            newRect.move(0, newRect.height() - maxRadii);
     1479            newRect.setHeight(maxRadii);
     1480        }
     1481        break;
     1482
     1483    case BSLeft:
     1484        overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - newRect.height();
     1485        if (overshoot > 0) {
     1486            ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height()));
     1487            newRect.setHeight(newRect.height() + overshoot);
     1488            if (!newRadii.topLeft().height())
     1489                newRect.move(0, -overshoot);
     1490        }
     1491        newRadii.setTopRight(IntSize(0, 0));
     1492        newRadii.setBottomRight(IntSize(0, 0));
     1493        maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().width());
     1494        if (maxRadii > newRect.width())
     1495            newRect.setWidth(maxRadii);
     1496        break;
     1497
     1498    case BSRight:
     1499        overshoot = newRadii.topRight().height() + newRadii.bottomRight().height() - newRect.height();
     1500        if (overshoot > 0) {
     1501            ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().height()));
     1502            newRect.setHeight(newRect.height() + overshoot);
     1503            if (!newRadii.topRight().height())
     1504                newRect.move(0, -overshoot);
     1505        }
     1506        newRadii.setTopLeft(IntSize(0, 0));
     1507        newRadii.setBottomLeft(IntSize(0, 0));
     1508        maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().width());
     1509        if (maxRadii > newRect.width()) {
     1510            newRect.move(newRect.width() - maxRadii, 0);
     1511            newRect.setWidth(maxRadii);
     1512        }
     1513        break;
     1514    }
     1515
     1516    return RoundedRect(newRect, newRadii);
     1517}
     1518
    14391519void RenderBoxModelObject::paintOneBorderSide(GraphicsContext& graphicsContext, const RenderStyle& style, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
    14401520    const LayoutRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge edges[], const Path* path,
    … …  
    14561536    if (path) {
    14571537        GraphicsContextStateSaver stateSaver(graphicsContext);
    1458         if (innerBorder.isRenderable())
    1459             clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
    1460         else
    1461             clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, innerBorder, side, edges);
     1538
     1539        clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, side, adjacentSide1StylesMatch, adjacentSide2StylesMatch);
     1540
     1541        if (!innerBorder.isRenderable())
     1542            graphicsContext.clipOutRoundedRect(FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side)));
     1543
    14621544        float thickness = std::max(std::max(edgeToRender.widthForPainting(), adjacentEdge1.widthForPainting()), adjacentEdge2.widthForPainting());
    14631545        drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, edgeToRender.widthForPainting(), thickness, side, style,
    … …  
    19081990}
    19091991
    1910 static void findInnerVertex(const FloatPoint& outerCorner, const FloatPoint& innerCorner, const FloatPoint& centerPoint, FloatPoint& result)
    1911 {
    1912     // If the line between outer and inner corner is towards the horizontal, intersect with a vertical line through the center,
    1913     // otherwise with a horizontal line through the center. The points that form this line are arbitrary (we use 0, 100).
    1914     // Note that if findIntersection fails, it will leave result untouched.
    1915     float diffInnerOuterX = fabs(innerCorner.x() - outerCorner.x());
    1916     float diffInnerOuterY = fabs(innerCorner.y() - outerCorner.y());
    1917     float diffCenterOuterX = fabs(centerPoint.x() - outerCorner.x());
    1918     float diffCenterOuterY = fabs(centerPoint.y() - outerCorner.y());
    1919     if (diffInnerOuterY * diffCenterOuterX < diffCenterOuterY * diffInnerOuterX)
    1920         findIntersection(outerCorner, innerCorner, FloatPoint(centerPoint.x(), 0), FloatPoint(centerPoint.x(), 100), result);
    1921     else
    1922         findIntersection(outerCorner, innerCorner, FloatPoint(0, centerPoint.y()), FloatPoint(100, centerPoint.y()), result);
    1923 }
    1924 
    19251992void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext& graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
    19261993                                                 BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches)
    … …  
    19291996    const FloatRect& outerRect = snapRectToDevicePixels(outerBorder.rect(), deviceScaleFactor);
    19301997    const FloatRect& innerRect = snapRectToDevicePixels(innerBorder.rect(), deviceScaleFactor);
    1931 
    1932     FloatPoint centerPoint(innerRect.location().x() + innerRect.width() / 2, innerRect.location().y() + innerRect.height() / 2);
    19331998
    19341999    // For each side, create a quad that encompasses all parts of that side that may draw,
    … …  
    19562021
    19572022        if (!innerBorder.radii().topLeft().isZero())
    1958             findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]);
     2023            findIntersection(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), innerRect.minXMaxYCorner(), innerRect.maxXMinYCorner(), quad[1]);
    19592024
    19602025        if (!innerBorder.radii().topRight().isZero())
    1961             findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[2]);
     2026            findIntersection(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), innerRect.minXMinYCorner(), innerRect.maxXMaxYCorner(), quad[2]);
    19622027        break;
    19632028
    … …  
    19692034
    19702035        if (!innerBorder.radii().topLeft().isZero())
    1971             findInnerVertex(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), centerPoint, quad[1]);
     2036            findIntersection(outerRect.minXMinYCorner(), innerRect.minXMinYCorner(), innerRect.minXMaxYCorner(), innerRect.maxXMinYCorner(), quad[1]);
    19722037
    19732038        if (!innerBorder.radii().bottomLeft().isZero())
    1974             findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[2]);
     2039            findIntersection(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), innerRect.minXMinYCorner(), innerRect.maxXMaxYCorner(), quad[2]);
    19752040        break;
    19762041
    … …  
    19822047
    19832048        if (!innerBorder.radii().bottomLeft().isZero())
    1984             findInnerVertex(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), centerPoint, quad[1]);
     2049            findIntersection(outerRect.minXMaxYCorner(), innerRect.minXMaxYCorner(), innerRect.minXMinYCorner(), innerRect.maxXMaxYCorner(), quad[1]);
    19852050
    19862051        if (!innerBorder.radii().bottomRight().isZero())
    1987             findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]);
     2052            findIntersection(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), innerRect.maxXMinYCorner(), innerRect.minXMaxYCorner(), quad[2]);
    19882053        break;
    19892054
    … …  
    19952060
    19962061        if (!innerBorder.radii().topRight().isZero())
    1997             findInnerVertex(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), centerPoint, quad[1]);
     2062            findIntersection(outerRect.maxXMinYCorner(), innerRect.maxXMinYCorner(), innerRect.minXMinYCorner(), innerRect.maxXMaxYCorner(), quad[1]);
    19982063
    19992064        if (!innerBorder.radii().bottomRight().isZero())
    2000             findInnerVertex(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), centerPoint, quad[2]);
     2065            findIntersection(outerRect.maxXMaxYCorner(), innerRect.maxXMaxYCorner(), innerRect.maxXMinYCorner(), innerRect.minXMaxYCorner(), quad[2]);
    20012066        break;
    20022067    }
    … …  
    20162081        quad[0],
    20172082        quad[1],
     2083        quad[2],
    20182084        side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y()) : FloatPoint(quad[2].x(), quad[3].y()),
    20192085        quad[3]
    … …  
    20262092        quad[0],
    20272093        side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y()) : FloatPoint(quad[1].x(), quad[0].y()),
     2094        quad[1],
    20282095        quad[2],
    20292096        quad[3]
    … …  
    20342101
    20352102    graphicsContext.setShouldAntialias(wasAntialiased);
    2036 }
    2037 
    2038 static LayoutRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, const BorderEdge edges[], BoxSide side)
    2039 {
    2040     LayoutRect sideRect = outerBorder.rect();
    2041     LayoutUnit width;
    2042 
    2043     switch (side) {
    2044     case BSTop:
    2045         width = sideRect.height() - edges[BSBottom].widthForPainting();
    2046         sideRect.setHeight(width);
    2047         break;
    2048     case BSBottom:
    2049         width = sideRect.height() - edges[BSTop].widthForPainting();
    2050         sideRect.shiftYEdgeTo(sideRect.maxY() - width);
    2051         break;
    2052     case BSLeft:
    2053         width = sideRect.width() - edges[BSRight].widthForPainting();
    2054         sideRect.setWidth(width);
    2055         break;
    2056     case BSRight:
    2057         width = sideRect.width() - edges[BSLeft].widthForPainting();
    2058         sideRect.shiftXEdgeTo(sideRect.maxX() - width);
    2059         break;
    2060     }
    2061 
    2062     return sideRect;
    2063 }
    2064 
    2065 static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, BoxSide side)
    2066 {
    2067     // Expand the inner border as necessary to make it a rounded rect (i.e. radii contained within each edge).
    2068     // This function relies on the fact we only get radii not contained within each edge if one of the radii
    2069     // for an edge is zero, so we can shift the arc towards the zero radius corner.
    2070     RoundedRect::Radii newRadii = innerBorder.radii();
    2071     LayoutRect newRect = innerBorder.rect();
    2072 
    2073     float overshoot;
    2074     float maxRadii;
    2075 
    2076     switch (side) {
    2077     case BSTop:
    2078         overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - newRect.width();
    2079         if (overshoot > 0) {
    2080             ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width()));
    2081             newRect.setWidth(newRect.width() + overshoot);
    2082             if (!newRadii.topLeft().width())
    2083                 newRect.move(-overshoot, 0);
    2084         }
    2085         newRadii.setBottomLeft(IntSize(0, 0));
    2086         newRadii.setBottomRight(IntSize(0, 0));
    2087         maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().height());
    2088         if (maxRadii > newRect.height())
    2089             newRect.setHeight(maxRadii);
    2090         break;
    2091 
    2092     case BSBottom:
    2093         overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width() - newRect.width();
    2094         if (overshoot > 0) {
    2095             ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().width()));
    2096             newRect.setWidth(newRect.width() + overshoot);
    2097             if (!newRadii.bottomLeft().width())
    2098                 newRect.move(-overshoot, 0);
    2099         }
    2100         newRadii.setTopLeft(IntSize(0, 0));
    2101         newRadii.setTopRight(IntSize(0, 0));
    2102         maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight().height());
    2103         if (maxRadii > newRect.height()) {
    2104             newRect.move(0, newRect.height() - maxRadii);
    2105             newRect.setHeight(maxRadii);
    2106         }
    2107         break;
    2108 
    2109     case BSLeft:
    2110         overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height() - newRect.height();
    2111         if (overshoot > 0) {
    2112             ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height()));
    2113             newRect.setHeight(newRect.height() + overshoot);
    2114             if (!newRadii.topLeft().height())
    2115                 newRect.move(0, -overshoot);
    2116         }
    2117         newRadii.setTopRight(IntSize(0, 0));
    2118         newRadii.setBottomRight(IntSize(0, 0));
    2119         maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().width());
    2120         if (maxRadii > newRect.width())
    2121             newRect.setWidth(maxRadii);
    2122         break;
    2123 
    2124     case BSRight:
    2125         overshoot = newRadii.topRight().height() + newRadii.bottomRight().height() - newRect.height();
    2126         if (overshoot > 0) {
    2127             ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().height()));
    2128             newRect.setHeight(newRect.height() + overshoot);
    2129             if (!newRadii.topRight().height())
    2130                 newRect.move(0, -overshoot);
    2131         }
    2132         newRadii.setTopLeft(IntSize(0, 0));
    2133         newRadii.setBottomLeft(IntSize(0, 0));
    2134         maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().width());
    2135         if (maxRadii > newRect.width()) {
    2136             newRect.move(newRect.width() - maxRadii, 0);
    2137             newRect.setWidth(maxRadii);
    2138         }
    2139         break;
    2140     }
    2141 
    2142     return RoundedRect(newRect, newRadii);
    2143 }
    2144 
    2145 void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext& graphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
    2146     BoxSide side, const class BorderEdge edges[])
    2147 {
    2148     graphicsContext.clip(calculateSideRectIncludingInner(outerBorder, edges, side));
    2149     graphicsContext.clipOutRoundedRect(FloatRoundedRect(calculateAdjustedInnerBorder(innerBorder, side)));
    21502103}
    21512104
  • trunk/Source/WebCore/rendering/RenderBoxModelObject.h

    r201040 r201868  
    311311    void clipBorderSidePolygon(GraphicsContext&, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
    312312                               BoxSide, bool firstEdgeMatches, bool secondEdgeMatches);
    313     void clipBorderSideForComplexInnerPath(GraphicsContext&, const RoundedRect&, const RoundedRect&, BoxSide, const BorderEdge[]);
     313
    314314    void paintOneBorderSide(GraphicsContext&, const RenderStyle&, const RoundedRect& outerBorder, const RoundedRect& innerBorder,
    315315        const LayoutRect& sideRect, BoxSide, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge[],
Note: See TracChangeset for help on using the changeset viewer.