1 | /* |
2 | * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. |
3 | * Copyright (C) 2005 Nokia. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
8 | * 1. Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * 2. Redistributions in binary form must reproduce the above copyright |
11 | * notice, this list of conditions and the following disclaimer in the |
12 | * documentation and/or other materials provided with the distribution. |
13 | * |
14 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
17 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
18 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "FloatRect.h" |
29 | |
30 | #include "FloatConversion.h" |
31 | #include "IntRect.h" |
32 | #include <algorithm> |
33 | #include <math.h> |
34 | #include <wtf/MathExtras.h> |
35 | #include <wtf/text/TextStream.h> |
36 | |
37 | namespace WebCore { |
38 | |
39 | FloatRect::FloatRect(const IntRect& r) |
40 | : m_location(r.location()) |
41 | , m_size(r.size()) |
42 | { |
43 | } |
44 | |
45 | FloatRect FloatRect::narrowPrecision(double x, double y, double width, double height) |
46 | { |
47 | return FloatRect(narrowPrecisionToFloat(x), narrowPrecisionToFloat(y), narrowPrecisionToFloat(width), narrowPrecisionToFloat(height)); |
48 | } |
49 | |
50 | bool FloatRect::isExpressibleAsIntRect() const |
51 | { |
52 | return isWithinIntRange(x()) && isWithinIntRange(y()) |
53 | && isWithinIntRange(width()) && isWithinIntRange(height()) |
54 | && isWithinIntRange(maxX()) && isWithinIntRange(maxY()); |
55 | } |
56 | |
57 | bool FloatRect::intersects(const FloatRect& other) const |
58 | { |
59 | // Checking emptiness handles negative widths as well as zero. |
60 | return !isEmpty() && !other.isEmpty() |
61 | && x() < other.maxX() && other.x() < maxX() |
62 | && y() < other.maxY() && other.y() < maxY(); |
63 | } |
64 | |
65 | bool FloatRect::contains(const FloatRect& other) const |
66 | { |
67 | return x() <= other.x() && maxX() >= other.maxX() |
68 | && y() <= other.y() && maxY() >= other.maxY(); |
69 | } |
70 | |
71 | bool FloatRect::contains(const FloatPoint& point, ContainsMode containsMode) const |
72 | { |
73 | if (containsMode == InsideOrOnStroke) |
74 | return contains(point.x(), point.y()); |
75 | return x() < point.x() && maxX() > point.x() && y() < point.y() && maxY() > point.y(); |
76 | } |
77 | |
78 | void FloatRect::intersect(const FloatRect& other) |
79 | { |
80 | float l = std::max(x(), other.x()); |
81 | float t = std::max(y(), other.y()); |
82 | float r = std::min(maxX(), other.maxX()); |
83 | float b = std::min(maxY(), other.maxY()); |
84 | |
85 | // Return a clean empty rectangle for non-intersecting cases. |
86 | if (l >= r || t >= b) { |
87 | l = 0; |
88 | t = 0; |
89 | r = 0; |
90 | b = 0; |
91 | } |
92 | |
93 | setLocationAndSizeFromEdges(l, t, r, b); |
94 | } |
95 | |
96 | bool FloatRect::edgeInclusiveIntersect(const FloatRect& other) |
97 | { |
98 | FloatPoint newLocation(std::max(x(), other.x()), std::max(y(), other.y())); |
99 | FloatPoint newMaxPoint(std::min(maxX(), other.maxX()), std::min(maxY(), other.maxY())); |
100 | |
101 | bool intersects = true; |
102 | |
103 | // Return a clean empty rectangle for non-intersecting cases. |
104 | if (newLocation.x() > newMaxPoint.x() || newLocation.y() > newMaxPoint.y()) { |
105 | newLocation = { }; |
106 | newMaxPoint = { }; |
107 | intersects = false; |
108 | } |
109 | |
110 | m_location = newLocation; |
111 | m_size = newMaxPoint - newLocation; |
112 | return intersects; |
113 | } |
114 | |
115 | void FloatRect::unite(const FloatRect& other) |
116 | { |
117 | // Handle empty special cases first. |
118 | if (other.isEmpty()) |
119 | return; |
120 | if (isEmpty()) { |
121 | *this = other; |
122 | return; |
123 | } |
124 | |
125 | uniteEvenIfEmpty(other); |
126 | } |
127 | |
128 | void FloatRect::uniteEvenIfEmpty(const FloatRect& other) |
129 | { |
130 | float minX = std::min(x(), other.x()); |
131 | float minY = std::min(y(), other.y()); |
132 | float maxX = std::max(this->maxX(), other.maxX()); |
133 | float maxY = std::max(this->maxY(), other.maxY()); |
134 | |
135 | setLocationAndSizeFromEdges(minX, minY, maxX, maxY); |
136 | } |
137 | |
138 | void FloatRect::uniteIfNonZero(const FloatRect& other) |
139 | { |
140 | // Handle empty special cases first. |
141 | if (other.isZero()) |
142 | return; |
143 | if (isZero()) { |
144 | *this = other; |
145 | return; |
146 | } |
147 | |
148 | uniteEvenIfEmpty(other); |
149 | } |
150 | |
151 | void FloatRect::extend(const FloatPoint& p) |
152 | { |
153 | float minX = std::min(x(), p.x()); |
154 | float minY = std::min(y(), p.y()); |
155 | float maxX = std::max(this->maxX(), p.x()); |
156 | float maxY = std::max(this->maxY(), p.y()); |
157 | |
158 | setLocationAndSizeFromEdges(minX, minY, maxX, maxY); |
159 | } |
160 | |
161 | void FloatRect::scale(float sx, float sy) |
162 | { |
163 | m_location.setX(x() * sx); |
164 | m_location.setY(y() * sy); |
165 | m_size.setWidth(width() * sx); |
166 | m_size.setHeight(height() * sy); |
167 | } |
168 | |
169 | void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1) |
170 | { |
171 | float left = std::min(p0.x(), p1.x()); |
172 | float top = std::min(p0.y(), p1.y()); |
173 | float right = std::max(p0.x(), p1.x()); |
174 | float bottom = std::max(p0.y(), p1.y()); |
175 | |
176 | setLocationAndSizeFromEdges(left, top, right, bottom); |
177 | } |
178 | |
179 | namespace { |
180 | // Helpers for 3- and 4-way max and min. |
181 | |
182 | template <typename T> |
183 | T min3(const T& v1, const T& v2, const T& v3) |
184 | { |
185 | return std::min(std::min(v1, v2), v3); |
186 | } |
187 | |
188 | template <typename T> |
189 | T max3(const T& v1, const T& v2, const T& v3) |
190 | { |
191 | return std::max(std::max(v1, v2), v3); |
192 | } |
193 | |
194 | template <typename T> |
195 | T min4(const T& v1, const T& v2, const T& v3, const T& v4) |
196 | { |
197 | return std::min(std::min(v1, v2), std::min(v3, v4)); |
198 | } |
199 | |
200 | template <typename T> |
201 | T max4(const T& v1, const T& v2, const T& v3, const T& v4) |
202 | { |
203 | return std::max(std::max(v1, v2), std::max(v3, v4)); |
204 | } |
205 | |
206 | } // anonymous namespace |
207 | |
208 | void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2) |
209 | { |
210 | float left = min3(p0.x(), p1.x(), p2.x()); |
211 | float top = min3(p0.y(), p1.y(), p2.y()); |
212 | float right = max3(p0.x(), p1.x(), p2.x()); |
213 | float bottom = max3(p0.y(), p1.y(), p2.y()); |
214 | |
215 | setLocationAndSizeFromEdges(left, top, right, bottom); |
216 | } |
217 | |
218 | void FloatRect::fitToPoints(const FloatPoint& p0, const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& p3) |
219 | { |
220 | float left = min4(p0.x(), p1.x(), p2.x(), p3.x()); |
221 | float top = min4(p0.y(), p1.y(), p2.y(), p3.y()); |
222 | float right = max4(p0.x(), p1.x(), p2.x(), p3.x()); |
223 | float bottom = max4(p0.y(), p1.y(), p2.y(), p3.y()); |
224 | |
225 | setLocationAndSizeFromEdges(left, top, right, bottom); |
226 | } |
227 | |
228 | FloatRect encloseRectToDevicePixels(const FloatRect& rect, float deviceScaleFactor) |
229 | { |
230 | FloatPoint location = floorPointToDevicePixels(rect.minXMinYCorner(), deviceScaleFactor); |
231 | FloatPoint maxPoint = ceilPointToDevicePixels(rect.maxXMaxYCorner(), deviceScaleFactor); |
232 | return FloatRect(location, maxPoint - location); |
233 | } |
234 | |
235 | IntRect enclosingIntRect(const FloatRect& rect) |
236 | { |
237 | FloatPoint location = flooredIntPoint(rect.minXMinYCorner()); |
238 | FloatPoint maxPoint = ceiledIntPoint(rect.maxXMaxYCorner()); |
239 | return IntRect(IntPoint(location), IntSize(maxPoint - location)); |
240 | } |
241 | |
242 | IntRect roundedIntRect(const FloatRect& rect) |
243 | { |
244 | return IntRect(roundedIntPoint(rect.location()), roundedIntSize(rect.size())); |
245 | } |
246 | |
247 | TextStream& operator<<(TextStream& ts, const FloatRect &r) |
248 | { |
249 | if (ts.hasFormattingFlag(TextStream::Formatting::SVGStyleRect)) { |
250 | // FIXME: callers should use the NumberRespectingIntegers flag. |
251 | return ts << "at (" << TextStream::FormatNumberRespectingIntegers(r.x()) << "," << TextStream::FormatNumberRespectingIntegers(r.y()) |
252 | << ") size " << TextStream::FormatNumberRespectingIntegers(r.width()) << "x" << TextStream::FormatNumberRespectingIntegers(r.height()); |
253 | } |
254 | |
255 | return ts << r.location() << " " << r.size(); |
256 | } |
257 | |
258 | } |
259 | |