1 /*
2 * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Copyright (C) 2013 Xidorn Quan (quanxunzhen@gmail.com)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "config.h"
29 #include "platform/geometry/RoundedRect.h"
30
31 #include "wtf/Assertions.h"
32 #include <algorithm>
33
34 namespace blink {
35
isZero() const36 bool RoundedRect::Radii::isZero() const
37 {
38 return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero();
39 }
40
scale(float factor)41 void RoundedRect::Radii::scale(float factor)
42 {
43 if (factor == 1)
44 return;
45
46 // If either radius on a corner becomes zero, reset both radii on that corner.
47 m_topLeft.scale(factor);
48 if (!m_topLeft.width() || !m_topLeft.height())
49 m_topLeft = IntSize();
50 m_topRight.scale(factor);
51 if (!m_topRight.width() || !m_topRight.height())
52 m_topRight = IntSize();
53 m_bottomLeft.scale(factor);
54 if (!m_bottomLeft.width() || !m_bottomLeft.height())
55 m_bottomLeft = IntSize();
56 m_bottomRight.scale(factor);
57 if (!m_bottomRight.width() || !m_bottomRight.height())
58 m_bottomRight = IntSize();
59
60 }
61
expand(int topWidth,int bottomWidth,int leftWidth,int rightWidth)62 void RoundedRect::Radii::expand(int topWidth, int bottomWidth, int leftWidth, int rightWidth)
63 {
64 if (m_topLeft.width() > 0 && m_topLeft.height() > 0) {
65 m_topLeft.setWidth(std::max<int>(0, m_topLeft.width() + leftWidth));
66 m_topLeft.setHeight(std::max<int>(0, m_topLeft.height() + topWidth));
67 }
68 if (m_topRight.width() > 0 && m_topRight.height() > 0) {
69 m_topRight.setWidth(std::max<int>(0, m_topRight.width() + rightWidth));
70 m_topRight.setHeight(std::max<int>(0, m_topRight.height() + topWidth));
71 }
72 if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) {
73 m_bottomLeft.setWidth(std::max<int>(0, m_bottomLeft.width() + leftWidth));
74 m_bottomLeft.setHeight(std::max<int>(0, m_bottomLeft.height() + bottomWidth));
75 }
76 if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) {
77 m_bottomRight.setWidth(std::max<int>(0, m_bottomRight.width() + rightWidth));
78 m_bottomRight.setHeight(std::max<int>(0, m_bottomRight.height() + bottomWidth));
79 }
80 }
81
inflateWithRadii(int size)82 void RoundedRect::inflateWithRadii(int size)
83 {
84 IntRect old = m_rect;
85
86 m_rect.inflate(size);
87 // Considering the inflation factor of shorter size to scale the radii seems appropriate here
88 float factor;
89 if (m_rect.width() < m_rect.height())
90 factor = old.width() ? (float)m_rect.width() / old.width() : int(0);
91 else
92 factor = old.height() ? (float)m_rect.height() / old.height() : int(0);
93
94 m_radii.scale(factor);
95 }
96
includeLogicalEdges(const RoundedRect::Radii & edges,bool isHorizontal,bool includeLogicalLeftEdge,bool includeLogicalRightEdge)97 void RoundedRect::Radii::includeLogicalEdges(const RoundedRect::Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
98 {
99 if (includeLogicalLeftEdge) {
100 if (isHorizontal)
101 m_bottomLeft = edges.bottomLeft();
102 else
103 m_topRight = edges.topRight();
104 m_topLeft = edges.topLeft();
105 }
106
107 if (includeLogicalRightEdge) {
108 if (isHorizontal)
109 m_topRight = edges.topRight();
110 else
111 m_bottomLeft = edges.bottomLeft();
112 m_bottomRight = edges.bottomRight();
113 }
114 }
115
excludeLogicalEdges(bool isHorizontal,bool excludeLogicalLeftEdge,bool excludeLogicalRightEdge)116 void RoundedRect::Radii::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge)
117 {
118 if (excludeLogicalLeftEdge) {
119 if (isHorizontal)
120 m_bottomLeft = IntSize();
121 else
122 m_topRight = IntSize();
123 m_topLeft = IntSize();
124 }
125
126 if (excludeLogicalRightEdge) {
127 if (isHorizontal)
128 m_topRight = IntSize();
129 else
130 m_bottomLeft = IntSize();
131 m_bottomRight = IntSize();
132 }
133 }
134
RoundedRect(int x,int y,int width,int height)135 RoundedRect::RoundedRect(int x, int y, int width, int height)
136 : m_rect(x, y, width, height)
137 {
138 }
139
RoundedRect(const IntRect & rect,const Radii & radii)140 RoundedRect::RoundedRect(const IntRect& rect, const Radii& radii)
141 : m_rect(rect)
142 , m_radii(radii)
143 {
144 }
145
RoundedRect(const IntRect & rect,const IntSize & topLeft,const IntSize & topRight,const IntSize & bottomLeft,const IntSize & bottomRight)146 RoundedRect::RoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight)
147 : m_rect(rect)
148 , m_radii(topLeft, topRight, bottomLeft, bottomRight)
149 {
150 }
151
radiusCenterRect() const152 IntRect RoundedRect::radiusCenterRect() const
153 {
154 ASSERT(isRenderable());
155 int minX = m_rect.x() + std::max(m_radii.topLeft().width(), m_radii.bottomLeft().width());
156 int minY = m_rect.y() + std::max(m_radii.topLeft().height(), m_radii.topRight().height());
157 int maxX = m_rect.maxX() - std::max(m_radii.topRight().width(), m_radii.bottomRight().width());
158 int maxY = m_rect.maxY() - std::max(m_radii.bottomLeft().height(), m_radii.bottomRight().height());
159 return IntRect(minX, minY, maxX - minX, maxY - minY);
160 }
161
includeLogicalEdges(const Radii & edges,bool isHorizontal,bool includeLogicalLeftEdge,bool includeLogicalRightEdge)162 void RoundedRect::includeLogicalEdges(const Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
163 {
164 m_radii.includeLogicalEdges(edges, isHorizontal, includeLogicalLeftEdge, includeLogicalRightEdge);
165 }
166
excludeLogicalEdges(bool isHorizontal,bool excludeLogicalLeftEdge,bool excludeLogicalRightEdge)167 void RoundedRect::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge)
168 {
169 m_radii.excludeLogicalEdges(isHorizontal, excludeLogicalLeftEdge, excludeLogicalRightEdge);
170 }
171
isRenderable() const172 bool RoundedRect::isRenderable() const
173 {
174 return m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width()
175 && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width()
176 && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height()
177 && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height();
178 }
179
adjustRadii()180 void RoundedRect::adjustRadii()
181 {
182 int maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width());
183 int maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height());
184
185 if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) {
186 m_radii.scale(0.0f);
187 return;
188 }
189 float widthRatio = static_cast<float>(m_rect.width()) / maxRadiusWidth;
190 float heightRatio = static_cast<float>(m_rect.height()) / maxRadiusHeight;
191 m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio);
192 }
193
intersectsQuad(const FloatQuad & quad) const194 bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
195 {
196 FloatRect rect(m_rect);
197 if (!quad.intersectsRect(rect))
198 return false;
199
200 const IntSize& topLeft = m_radii.topLeft();
201 if (!topLeft.isEmpty()) {
202 FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height());
203 if (quad.intersectsRect(rect)) {
204 FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height());
205 FloatSize size(topLeft.width(), topLeft.height());
206 if (!quad.intersectsEllipse(center, size))
207 return false;
208 }
209 }
210
211 const IntSize& topRight = m_radii.topRight();
212 if (!topRight.isEmpty()) {
213 FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height());
214 if (quad.intersectsRect(rect)) {
215 FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height());
216 FloatSize size(topRight.width(), topRight.height());
217 if (!quad.intersectsEllipse(center, size))
218 return false;
219 }
220 }
221
222 const IntSize& bottomLeft = m_radii.bottomLeft();
223 if (!bottomLeft.isEmpty()) {
224 FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height());
225 if (quad.intersectsRect(rect)) {
226 FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height());
227 FloatSize size(bottomLeft.width(), bottomLeft.height());
228 if (!quad.intersectsEllipse(center, size))
229 return false;
230 }
231 }
232
233 const IntSize& bottomRight = m_radii.bottomRight();
234 if (!bottomRight.isEmpty()) {
235 FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height());
236 if (quad.intersectsRect(rect)) {
237 FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height());
238 FloatSize size(bottomRight.width(), bottomRight.height());
239 if (!quad.intersectsEllipse(center, size))
240 return false;
241 }
242 }
243
244 return true;
245 }
246
247 } // namespace blink
248