Move locale information into FontDescription
[WebKit-https.git] / Source / WebCore / platform / graphics / RoundedRect.cpp
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 "RoundedRect.h"
30
31 #include "FloatRoundedRect.h"
32 #include "LayoutRect.h"
33 #include "LayoutUnit.h"
34
35 #include <algorithm>
36
37 namespace WebCore {
38
39 bool RoundedRect::Radii::isZero() const
40 {
41     return m_topLeft.isZero() && m_topRight.isZero() && m_bottomLeft.isZero() && m_bottomRight.isZero();
42 }
43
44 void RoundedRect::Radii::scale(float factor)
45 {
46     if (factor == 1)
47         return;
48
49     // If either radius on a corner becomes zero, reset both radii on that corner.
50     m_topLeft.scale(factor);
51     if (!m_topLeft.width() || !m_topLeft.height())
52         m_topLeft = LayoutSize();
53     m_topRight.scale(factor);
54     if (!m_topRight.width() || !m_topRight.height())
55         m_topRight = LayoutSize();
56     m_bottomLeft.scale(factor);
57     if (!m_bottomLeft.width() || !m_bottomLeft.height())
58         m_bottomLeft = LayoutSize();
59     m_bottomRight.scale(factor);
60     if (!m_bottomRight.width() || !m_bottomRight.height())
61         m_bottomRight = LayoutSize();
62 }
63
64 void RoundedRect::Radii::expand(const LayoutUnit& topWidth, const LayoutUnit& bottomWidth, const LayoutUnit& leftWidth, const LayoutUnit& rightWidth)
65 {
66     if (m_topLeft.width() > 0 && m_topLeft.height() > 0) {
67         m_topLeft.setWidth(std::max<LayoutUnit>(0, m_topLeft.width() + leftWidth));
68         m_topLeft.setHeight(std::max<LayoutUnit>(0, m_topLeft.height() + topWidth));
69     }
70     if (m_topRight.width() > 0 && m_topRight.height() > 0) {
71         m_topRight.setWidth(std::max<LayoutUnit>(0, m_topRight.width() + rightWidth));
72         m_topRight.setHeight(std::max<LayoutUnit>(0, m_topRight.height() + topWidth));
73     }
74     if (m_bottomLeft.width() > 0 && m_bottomLeft.height() > 0) {
75         m_bottomLeft.setWidth(std::max<LayoutUnit>(0, m_bottomLeft.width() + leftWidth));
76         m_bottomLeft.setHeight(std::max<LayoutUnit>(0, m_bottomLeft.height() + bottomWidth));
77     }
78     if (m_bottomRight.width() > 0 && m_bottomRight.height() > 0) {
79         m_bottomRight.setWidth(std::max<LayoutUnit>(0, m_bottomRight.width() + rightWidth));
80         m_bottomRight.setHeight(std::max<LayoutUnit>(0, m_bottomRight.height() + bottomWidth));
81     }
82 }
83
84 void RoundedRect::inflateWithRadii(const LayoutUnit& size)
85 {
86     LayoutRect old = m_rect;
87
88     m_rect.inflate(size);
89     // Considering the inflation factor of shorter size to scale the radii seems appropriate here
90     float factor;
91     if (m_rect.width() < m_rect.height())
92         factor = old.width() ? (float)m_rect.width() / old.width() : int(0);
93     else
94         factor = old.height() ? (float)m_rect.height() / old.height() : int(0);
95
96     m_radii.scale(factor);
97 }
98
99 void RoundedRect::Radii::includeLogicalEdges(const RoundedRect::Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
100 {
101     if (includeLogicalLeftEdge) {
102         if (isHorizontal)
103             m_bottomLeft = edges.bottomLeft();
104         else
105             m_topRight = edges.topRight();
106         m_topLeft = edges.topLeft();
107     }
108
109     if (includeLogicalRightEdge) {
110         if (isHorizontal)
111             m_topRight = edges.topRight();
112         else
113             m_bottomLeft = edges.bottomLeft();
114         m_bottomRight = edges.bottomRight();
115     }
116 }
117
118 void RoundedRect::Radii::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge)
119 {
120     if (excludeLogicalLeftEdge) {
121         if (isHorizontal)
122             m_bottomLeft = IntSize();
123         else
124             m_topRight = IntSize();
125         m_topLeft = IntSize();
126     }
127         
128     if (excludeLogicalRightEdge) {
129         if (isHorizontal)
130             m_topRight = IntSize();
131         else
132             m_bottomLeft = IntSize();
133         m_bottomRight = IntSize();
134     }
135 }
136
137 RoundedRect::RoundedRect(const LayoutUnit& x, const LayoutUnit& y, const LayoutUnit& width, const LayoutUnit& height)
138     : m_rect(x, y, width, height)
139 {
140 }
141
142 RoundedRect::RoundedRect(const LayoutRect& rect, const Radii& radii)
143     : m_rect(rect)
144     , m_radii(radii)
145 {
146 }
147
148 RoundedRect::RoundedRect(const LayoutRect& rect, const LayoutSize& topLeft, const LayoutSize& topRight, const LayoutSize& bottomLeft, const LayoutSize& bottomRight)
149     : m_rect(rect)
150     , m_radii(topLeft, topRight, bottomLeft, bottomRight)
151 {
152 }
153
154 void RoundedRect::includeLogicalEdges(const Radii& edges, bool isHorizontal, bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
155 {
156     m_radii.includeLogicalEdges(edges, isHorizontal, includeLogicalLeftEdge, includeLogicalRightEdge);
157 }
158
159 void RoundedRect::excludeLogicalEdges(bool isHorizontal, bool excludeLogicalLeftEdge, bool excludeLogicalRightEdge)
160 {
161     m_radii.excludeLogicalEdges(isHorizontal, excludeLogicalLeftEdge, excludeLogicalRightEdge);
162 }
163
164 bool RoundedRect::isRenderable() const
165 {
166     return m_radii.topLeft().width() + m_radii.topRight().width() <= m_rect.width()
167         && m_radii.bottomLeft().width() + m_radii.bottomRight().width() <= m_rect.width()
168         && m_radii.topLeft().height() + m_radii.bottomLeft().height() <= m_rect.height()
169         && m_radii.topRight().height() + m_radii.bottomRight().height() <= m_rect.height();
170 }
171
172 void RoundedRect::adjustRadii()
173 {
174     int maxRadiusWidth = std::max(m_radii.topLeft().width() + m_radii.topRight().width(), m_radii.bottomLeft().width() + m_radii.bottomRight().width());
175     int maxRadiusHeight = std::max(m_radii.topLeft().height() + m_radii.bottomLeft().height(), m_radii.topRight().height() + m_radii.bottomRight().height());
176
177     if (maxRadiusWidth <= 0 || maxRadiusHeight <= 0) {
178         m_radii.scale(0.0f);
179         return;
180     }
181     float widthRatio = static_cast<float>(m_rect.width()) / maxRadiusWidth;
182     float heightRatio = static_cast<float>(m_rect.height()) / maxRadiusHeight;
183     m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio);
184 }
185
186 bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
187 {
188     FloatRect rect(m_rect);
189     if (!quad.intersectsRect(rect))
190         return false;
191
192     const LayoutSize& topLeft = m_radii.topLeft();
193     if (!topLeft.isEmpty()) {
194         FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height());
195         if (quad.intersectsRect(rect)) {
196             FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height());
197             FloatSize size(topLeft.width(), topLeft.height());
198             if (!quad.intersectsEllipse(center, size))
199                 return false;
200         }
201     }
202
203     const LayoutSize& topRight = m_radii.topRight();
204     if (!topRight.isEmpty()) {
205         FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height());
206         if (quad.intersectsRect(rect)) {
207             FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height());
208             FloatSize size(topRight.width(), topRight.height());
209             if (!quad.intersectsEllipse(center, size))
210                 return false;
211         }
212     }
213
214     const LayoutSize& bottomLeft = m_radii.bottomLeft();
215     if (!bottomLeft.isEmpty()) {
216         FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height());
217         if (quad.intersectsRect(rect)) {
218             FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height());
219             FloatSize size(bottomLeft.width(), bottomLeft.height());
220             if (!quad.intersectsEllipse(center, size))
221                 return false;
222         }
223     }
224
225     const LayoutSize& bottomRight = m_radii.bottomRight();
226     if (!bottomRight.isEmpty()) {
227         FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height());
228         if (quad.intersectsRect(rect)) {
229             FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height());
230             FloatSize size(bottomRight.width(), bottomRight.height());
231             if (!quad.intersectsEllipse(center, size))
232                 return false;
233         }
234     }
235
236     return true;
237 }
238
239 FloatRoundedRect RoundedRect::pixelSnappedRoundedRectForPainting(float deviceScaleFactor) const
240 {
241     LayoutRect originalRect = rect();
242     if (originalRect.isEmpty())
243         return FloatRoundedRect(originalRect, radii());
244
245     FloatRect pixelSnappedRect = snapRectToDevicePixels(originalRect, deviceScaleFactor);
246
247     if (!isRenderable())
248         return FloatRoundedRect(pixelSnappedRect, radii());
249
250     // Snapping usually does not alter size, but when it does, we need to make sure that the final rect is still renderable by distributing the size delta proportionally.
251     FloatRoundedRect::Radii adjustedRadii = radii();
252     adjustedRadii.scale(pixelSnappedRect.width() / originalRect.width().toFloat(), pixelSnappedRect.height() / originalRect.height().toFloat());
253     FloatRoundedRect snappedRoundedRect = FloatRoundedRect(pixelSnappedRect, adjustedRadii);
254     if (!snappedRoundedRect.isRenderable()) {
255         // Floating point mantissa overflow can produce a non-renderable rounded rect.
256         adjustedRadii.shrink(1 / deviceScaleFactor);
257         snappedRoundedRect.setRadii(adjustedRadii);
258     }
259     ASSERT(snappedRoundedRect.isRenderable());
260     return snappedRoundedRect;
261 }
262
263 } // namespace WebCore