REGRESSION (r169407): Calls to RenderStyle::getRoundedBorderFor() in computeRoundedRe...
[WebKit-https.git] / Source / WebCore / rendering / shapes / BoxShape.cpp
1 /*
2  * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "BoxShape.h"
32
33 #include "RenderBox.h"
34 #include <wtf/MathExtras.h>
35
36 namespace WebCore {
37
38 static inline LayoutUnit adjustRadiusForMarginBoxShape(LayoutUnit radius, LayoutUnit margin)
39 {
40     // This algorithm is defined in the CSS Shapes specifcation
41     if (!margin)
42         return radius;
43
44     LayoutUnit ratio = radius / margin;
45     if (ratio < 1)
46         return radius + (margin * (1 + pow(ratio - 1, 3)));
47
48     return radius + margin;
49 }
50
51 static inline LayoutSize computeMarginBoxShapeRadius(const LayoutSize& radius, const LayoutSize& adjacentMargins)
52 {
53     return LayoutSize(adjustRadiusForMarginBoxShape(radius.width(), adjacentMargins.width()),
54         adjustRadiusForMarginBoxShape(radius.height(), adjacentMargins.height()));
55 }
56
57 static inline RoundedRect::Radii computeMarginBoxShapeRadii(const RoundedRect::Radii& radii, const RenderBox& renderer)
58 {
59     return RoundedRect::Radii(computeMarginBoxShapeRadius(radii.topLeft(), LayoutSize(renderer.marginLeft(), renderer.marginTop())),
60         computeMarginBoxShapeRadius(radii.topRight(), LayoutSize(renderer.marginRight(), renderer.marginTop())),
61         computeMarginBoxShapeRadius(radii.bottomLeft(), LayoutSize(renderer.marginLeft(), renderer.marginBottom())),
62         computeMarginBoxShapeRadius(radii.bottomRight(), LayoutSize(renderer.marginRight(), renderer.marginBottom())));
63 }
64
65 RoundedRect computeRoundedRectForBoxShape(CSSBoxType box, const RenderBox& renderer)
66 {
67     const RenderStyle& style = renderer.style();
68     switch (box) {
69     case MarginBox: {
70         if (!style.hasBorderRadius())
71             return RoundedRect(renderer.marginBoxRect(), RoundedRect::Radii());
72
73         LayoutRect marginBox = renderer.marginBoxRect();
74         RoundedRect::Radii radii = computeMarginBoxShapeRadii(style.getRoundedBorderFor(renderer.borderBoxRect()).radii(), renderer);
75         radii.scale(calcBorderRadiiConstraintScaleFor(marginBox, radii));
76         return RoundedRect(marginBox, radii);
77     }
78     case PaddingBox:
79         return style.getRoundedInnerBorderFor(renderer.borderBoxRect());
80     case ContentBox:
81         return style.getRoundedInnerBorderFor(renderer.borderBoxRect(),
82             renderer.paddingTop() + renderer.borderTop(), renderer.paddingBottom() + renderer.borderBottom(),
83             renderer.paddingLeft() + renderer.borderLeft(), renderer.paddingRight() + renderer.borderRight());
84     // fill, stroke, view-box compute to border-box for HTML elements.
85     case BorderBox:
86     case Fill:
87     case Stroke:
88     case ViewBox:
89     case BoxMissing:
90         return style.getRoundedBorderFor(renderer.borderBoxRect());
91     }
92
93     ASSERT_NOT_REACHED();
94     return style.getRoundedBorderFor(renderer.borderBoxRect());
95 }
96
97 LayoutRect BoxShape::shapeMarginLogicalBoundingBox() const
98 {
99     FloatRect marginBounds(m_bounds.rect());
100     if (shapeMargin() > 0)
101         marginBounds.inflate(shapeMargin());
102     return static_cast<LayoutRect>(marginBounds);
103 }
104
105 FloatRoundedRect BoxShape::shapeMarginBounds() const
106 {
107     FloatRoundedRect marginBounds(m_bounds);
108     if (shapeMargin() > 0) {
109         marginBounds.inflate(shapeMargin());
110         marginBounds.expandRadii(shapeMargin());
111     }
112     return marginBounds;
113 }
114
115 LineSegment BoxShape::getExcludedInterval(LayoutUnit logicalTop, LayoutUnit logicalHeight) const
116 {
117     const FloatRoundedRect& marginBounds = shapeMarginBounds();
118     if (marginBounds.isEmpty() || !lineOverlapsShapeMarginBounds(logicalTop, logicalHeight))
119         return LineSegment();
120
121     float y1 = logicalTop;
122     float y2 = logicalTop + logicalHeight;
123     const FloatRect& rect = marginBounds.rect();
124
125     if (!marginBounds.isRounded())
126         return LineSegment(rect.x(), rect.maxX());
127
128     float topCornerMaxY = std::max<float>(marginBounds.topLeftCorner().maxY(), marginBounds.topRightCorner().maxY());
129     float bottomCornerMinY = std::min<float>(marginBounds.bottomLeftCorner().y(), marginBounds.bottomRightCorner().y());
130
131     if (topCornerMaxY <= bottomCornerMinY && y1 <= topCornerMaxY && y2 >= bottomCornerMinY)
132         return LineSegment(rect.x(), rect.maxX());
133
134     float x1 = rect.maxX();
135     float x2 = rect.x();
136     float minXIntercept;
137     float maxXIntercept;
138
139     if (y1 <= marginBounds.topLeftCorner().maxY() && y2 >= marginBounds.bottomLeftCorner().y())
140         x1 = rect.x();
141
142     if (y1 <= marginBounds.topRightCorner().maxY() && y2 >= marginBounds.bottomRightCorner().y())
143         x2 = rect.maxX();
144
145     if (marginBounds.xInterceptsAtY(y1, minXIntercept, maxXIntercept)) {
146         x1 = std::min<float>(x1, minXIntercept);
147         x2 = std::max<float>(x2, maxXIntercept);
148     }
149
150     if (marginBounds.xInterceptsAtY(y2, minXIntercept, maxXIntercept)) {
151         x1 = std::min<float>(x1, minXIntercept);
152         x2 = std::max<float>(x2, maxXIntercept);
153     }
154
155     ASSERT(x2 >= x1);
156     return LineSegment(x1, x2);
157 }
158
159 void BoxShape::buildDisplayPaths(DisplayPaths& paths) const
160 {
161     paths.shape.addRoundedRect(m_bounds, Path::PreferBezierRoundedRect);
162     if (shapeMargin())
163         paths.marginShape.addRoundedRect(shapeMarginBounds(), Path::PreferBezierRoundedRect);
164 }
165
166 } // namespace WebCore