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
36namespace WebCore {
37
38static 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.0)));
47
48 return radius + margin;
49}
50
51static 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
57static 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
65RoundedRect computeRoundedRectForBoxShape(CSSBoxType box, const RenderBox& renderer)
66{
67 const RenderStyle& style = renderer.style();
68 switch (box) {
69 case CSSBoxType::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 CSSBoxType::PaddingBox:
79 return style.getRoundedInnerBorderFor(renderer.borderBoxRect());
80 case CSSBoxType::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 CSSBoxType::BorderBox:
86 case CSSBoxType::FillBox:
87 case CSSBoxType::StrokeBox:
88 case CSSBoxType::ViewBox:
89 case CSSBoxType::BoxMissing:
90 return style.getRoundedBorderFor(renderer.borderBoxRect());
91 }
92
93 ASSERT_NOT_REACHED();
94 return style.getRoundedBorderFor(renderer.borderBoxRect());
95}
96
97LayoutRect 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
105FloatRoundedRect 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
115LineSegment 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
159void 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
167