1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Robert O'Callahan <robert@ocallahan.org>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #ifndef MOZILLA_GFX_BASERECT_H_
39 : #define MOZILLA_GFX_BASERECT_H_
40 :
41 : #include <cmath>
42 :
43 : namespace mozilla {
44 : namespace gfx {
45 :
46 : // XXX - <algorithm> conflicts with exceptions on 10.6. Define our own gfx_min/gfx_max
47 : // functions here. Avoid min/max to avoid conflicts with existing #defines on windows.
48 : template<typename T>
49 468 : T gfx_min(T aVal1, T aVal2)
50 : {
51 468 : return (aVal1 < aVal2) ? aVal1 : aVal2;
52 : }
53 :
54 : template<typename T>
55 468 : T gfx_max(T aVal1, T aVal2)
56 : {
57 468 : return (aVal1 > aVal2) ? aVal1 : aVal2;
58 : }
59 :
60 : /**
61 : * Rectangles have two interpretations: a set of (zero-size) points,
62 : * and a rectangular area of the plane. Most rectangle operations behave
63 : * the same no matter what interpretation is being used, but some operations
64 : * differ:
65 : * -- Equality tests behave differently. When a rectangle represents an area,
66 : * all zero-width and zero-height rectangles are equal to each other since they
67 : * represent the empty area. But when a rectangle represents a set of
68 : * mathematical points, zero-width and zero-height rectangles can be unequal.
69 : * -- The union operation can behave differently. When rectangles represent
70 : * areas, taking the union of a zero-width or zero-height rectangle with
71 : * another rectangle can just ignore the empty rectangle. But when rectangles
72 : * represent sets of mathematical points, we may need to extend the latter
73 : * rectangle to include the points of a zero-width or zero-height rectangle.
74 : *
75 : * To ensure that these interpretations are explicitly disambiguated, we
76 : * deny access to the == and != operators and require use of IsEqualEdges and
77 : * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
78 : * methods.
79 : *
80 : * Do not use this class directly. Subclass it, pass that subclass as the
81 : * Sub parameter, and only use that subclass.
82 : */
83 : template <class T, class Sub, class Point, class SizeT, class Margin>
84 : struct BaseRect {
85 : T x, y, width, height;
86 :
87 : // Constructors
88 1740 : BaseRect() : x(0), y(0), width(0), height(0) {}
89 60 : BaseRect(const Point& aOrigin, const SizeT &aSize) :
90 60 : x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
91 : {
92 60 : }
93 1727 : BaseRect(T aX, T aY, T aWidth, T aHeight) :
94 1727 : x(aX), y(aY), width(aWidth), height(aHeight)
95 : {
96 1727 : }
97 :
98 : // Emptiness. An empty rect is one that has no area, i.e. its height or width
99 : // is <= 0
100 574 : bool IsEmpty() const { return height <= 0 || width <= 0; }
101 32 : void SetEmpty() { width = height = 0; }
102 :
103 : // Returns true if this rectangle contains the interior of aRect. Always
104 : // returns true if aRect is empty, and always returns false is aRect is
105 : // nonempty but this rect is empty.
106 0 : bool Contains(const Sub& aRect) const
107 : {
108 : return aRect.IsEmpty() ||
109 : (x <= aRect.x && aRect.XMost() <= XMost() &&
110 0 : y <= aRect.y && aRect.YMost() <= YMost());
111 : }
112 : // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
113 0 : bool Contains(T aX, T aY) const
114 : {
115 : return x <= aX && aX + 1 <= XMost() &&
116 0 : y <= aY && aY + 1 <= YMost();
117 : }
118 : // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
119 0 : bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
120 :
121 : // Intersection. Returns TRUE if the receiver's area has non-empty
122 : // intersection with aRect's area, and FALSE otherwise.
123 : // Always returns false if aRect is empty or 'this' is empty.
124 0 : bool Intersects(const Sub& aRect) const
125 : {
126 : return x < aRect.XMost() && aRect.x < XMost() &&
127 0 : y < aRect.YMost() && aRect.y < YMost();
128 : }
129 : // Returns the rectangle containing the intersection of the points
130 : // (including edges) of *this and aRect. If there are no points in that
131 : // intersection, returns an empty rectangle with x/y set to the gfx_max of the x/y
132 : // of *this and aRect.
133 36 : Sub Intersect(const Sub& aRect) const
134 : {
135 36 : Sub result;
136 36 : result.x = gfx_max(x, aRect.x);
137 36 : result.y = gfx_max(y, aRect.y);
138 36 : result.width = gfx_min(XMost(), aRect.XMost()) - result.x;
139 36 : result.height = gfx_min(YMost(), aRect.YMost()) - result.y;
140 36 : if (result.width < 0 || result.height < 0) {
141 0 : result.SizeTo(0, 0);
142 : }
143 : return result;
144 : }
145 : // Sets *this to be the rectangle containing the intersection of the points
146 : // (including edges) of *this and aRect. If there are no points in that
147 : // intersection, sets *this to be an empty rectangle with x/y set to the gfx_max
148 : // of the x/y of *this and aRect.
149 : //
150 : // 'this' can be the same object as either aRect1 or aRect2
151 36 : bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
152 : {
153 36 : *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
154 36 : return !IsEmpty();
155 : }
156 :
157 : // Returns the smallest rectangle that contains both the area of both
158 : // this and aRect2.
159 : // Thus, empty input rectangles are ignored.
160 : // If both rectangles are empty, returns this.
161 247 : Sub Union(const Sub& aRect) const
162 : {
163 247 : if (IsEmpty()) {
164 49 : return aRect;
165 198 : } else if (aRect.IsEmpty()) {
166 0 : return *static_cast<const Sub*>(this);
167 : } else {
168 198 : return UnionEdges(aRect);
169 : }
170 : }
171 : // Returns the smallest rectangle that contains both the points (including
172 : // edges) of both aRect1 and aRect2.
173 : // Thus, empty input rectangles are allowed to affect the result.
174 198 : Sub UnionEdges(const Sub& aRect) const
175 : {
176 198 : Sub result;
177 198 : result.x = gfx_min(x, aRect.x);
178 198 : result.y = gfx_min(y, aRect.y);
179 198 : result.width = gfx_max(XMost(), aRect.XMost()) - result.x;
180 198 : result.height = gfx_max(YMost(), aRect.YMost()) - result.y;
181 : return result;
182 : }
183 : // Computes the smallest rectangle that contains both the area of both
184 : // aRect1 and aRect2, and fills 'this' with the result.
185 : // Thus, empty input rectangles are ignored.
186 : // If both rectangles are empty, sets 'this' to aRect2.
187 : //
188 : // 'this' can be the same object as either aRect1 or aRect2
189 247 : void UnionRect(const Sub& aRect1, const Sub& aRect2)
190 : {
191 247 : *static_cast<Sub*>(this) = aRect1.Union(aRect2);
192 247 : }
193 :
194 : // Computes the smallest rectangle that contains both the points (including
195 : // edges) of both aRect1 and aRect2.
196 : // Thus, empty input rectangles are allowed to affect the result.
197 : //
198 : // 'this' can be the same object as either aRect1 or aRect2
199 : void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
200 : {
201 : *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
202 : }
203 :
204 1 : void SetRect(T aX, T aY, T aWidth, T aHeight)
205 : {
206 1 : x = aX; y = aY; width = aWidth; height = aHeight;
207 1 : }
208 0 : void SetRect(const Point& aPt, const SizeT& aSize)
209 : {
210 0 : SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
211 0 : }
212 0 : void MoveTo(T aX, T aY) { x = aX; y = aY; }
213 0 : void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
214 0 : void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
215 0 : void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
216 0 : void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
217 0 : void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
218 :
219 0 : void Inflate(T aD) { Inflate(aD, aD); }
220 0 : void Inflate(T aDx, T aDy)
221 : {
222 0 : x -= aDx;
223 0 : y -= aDy;
224 0 : width += 2 * aDx;
225 0 : height += 2 * aDy;
226 0 : }
227 0 : void Inflate(const Margin& aMargin)
228 : {
229 0 : x -= aMargin.left;
230 0 : y -= aMargin.top;
231 0 : width += aMargin.LeftRight();
232 0 : height += aMargin.TopBottom();
233 0 : }
234 0 : void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
235 :
236 0 : void Deflate(T aD) { Deflate(aD, aD); }
237 0 : void Deflate(T aDx, T aDy)
238 : {
239 0 : x += aDx;
240 0 : y += aDy;
241 0 : width = gfx_max(T(0), width - 2 * aDx);
242 0 : height = gfx_max(T(0), height - 2 * aDy);
243 0 : }
244 0 : void Deflate(const Margin& aMargin)
245 : {
246 0 : x += aMargin.left;
247 0 : y += aMargin.top;
248 0 : width = gfx_max(T(0), width - aMargin.LeftRight());
249 0 : height = gfx_max(T(0), height - aMargin.TopBottom());
250 0 : }
251 0 : void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
252 :
253 : // Return true if the rectangles contain the same set of points, including
254 : // points on the edges.
255 : // Use when we care about the exact x/y/width/height values being
256 : // equal (i.e. we care about differences in empty rectangles).
257 0 : bool IsEqualEdges(const Sub& aRect) const
258 : {
259 : return x == aRect.x && y == aRect.y &&
260 0 : width == aRect.width && height == aRect.height;
261 : }
262 : // Return true if the rectangles contain the same area of the plane.
263 : // Use when we do not care about differences in empty rectangles.
264 0 : bool IsEqualInterior(const Sub& aRect) const
265 : {
266 0 : return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
267 : }
268 :
269 0 : Sub operator+(const Point& aPoint) const
270 : {
271 0 : return Sub(x + aPoint.x, y + aPoint.y, width, height);
272 : }
273 0 : Sub operator-(const Point& aPoint) const
274 : {
275 0 : return Sub(x - aPoint.x, y - aPoint.y, width, height);
276 : }
277 0 : Sub& operator+=(const Point& aPoint)
278 : {
279 0 : MoveBy(aPoint);
280 0 : return *static_cast<Sub*>(this);
281 : }
282 0 : Sub& operator-=(const Point& aPoint)
283 : {
284 0 : MoveBy(-aPoint);
285 0 : return *static_cast<Sub*>(this);
286 : }
287 :
288 : // Find difference as a Margin
289 0 : Margin operator-(const Sub& aRect) const
290 : {
291 : return Margin(aRect.x - x, aRect.y - y,
292 0 : XMost() - aRect.XMost(), YMost() - aRect.YMost());
293 : }
294 :
295 : // Helpers for accessing the vertices
296 19 : Point TopLeft() const { return Point(x, y); }
297 0 : Point TopRight() const { return Point(XMost(), y); }
298 0 : Point BottomLeft() const { return Point(x, YMost()); }
299 0 : Point BottomRight() const { return Point(XMost(), YMost()); }
300 0 : Point Center() const { return Point(x, y) + Point(width, height)/2; }
301 0 : SizeT Size() const { return SizeT(width, height); }
302 :
303 : // Helper methods for computing the extents
304 21 : T X() const { return x; }
305 21 : T Y() const { return y; }
306 21 : T Width() const { return width; }
307 21 : T Height() const { return height; }
308 471 : T XMost() const { return x + width; }
309 470 : T YMost() const { return y + height; }
310 :
311 : // Round the rectangle edges to integer coordinates, such that the rounded
312 : // rectangle has the same set of pixel centers as the original rectangle.
313 : // Edges at offset 0.5 round up.
314 : // Suitable for most places where integral device coordinates
315 : // are needed, but note that any translation should be applied first to
316 : // avoid pixel rounding errors.
317 : // Note that this is *not* rounding to nearest integer if the values are negative.
318 : // They are always rounding as floor(n + 0.5).
319 : // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
320 : // If you need similar method which is using NS_round(), you should create
321 : // new |RoundAwayFromZero()| method.
322 0 : void Round()
323 : {
324 0 : T x0 = static_cast<T>(floor(T(X()) + 0.5));
325 0 : T y0 = static_cast<T>(floor(T(Y()) + 0.5));
326 0 : T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
327 0 : T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
328 :
329 0 : x = x0;
330 0 : y = y0;
331 :
332 0 : width = x1 - x0;
333 0 : height = y1 - y0;
334 0 : }
335 :
336 : // Snap the rectangle edges to integer coordinates, such that the
337 : // original rectangle contains the resulting rectangle.
338 0 : void RoundIn()
339 : {
340 0 : T x0 = static_cast<T>(ceil(T(X())));
341 0 : T y0 = static_cast<T>(ceil(T(Y())));
342 0 : T x1 = static_cast<T>(floor(T(XMost())));
343 0 : T y1 = static_cast<T>(floor(T(YMost())));
344 :
345 0 : x = x0;
346 0 : y = y0;
347 :
348 0 : width = x1 - x0;
349 0 : height = y1 - y0;
350 0 : }
351 :
352 : // Snap the rectangle edges to integer coordinates, such that the
353 : // resulting rectangle contains the original rectangle.
354 0 : void RoundOut()
355 : {
356 0 : T x0 = static_cast<T>(floor(T(X())));
357 0 : T y0 = static_cast<T>(floor(T(Y())));
358 0 : T x1 = static_cast<T>(ceil(T(XMost())));
359 0 : T y1 = static_cast<T>(ceil(T(YMost())));
360 :
361 0 : x = x0;
362 0 : y = y0;
363 :
364 0 : width = x1 - x0;
365 0 : height = y1 - y0;
366 0 : }
367 :
368 : // Scale 'this' by aScale, converting coordinates to integers so that the result is
369 : // the smallest integer-coordinate rectangle containing the unrounded result.
370 : // Note: this can turn an empty rectangle into a non-empty rectangle
371 0 : void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
372 : // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
373 : // that the result is the smallest integer-coordinate rectangle containing the
374 : // unrounded result.
375 : // Note: this can turn an empty rectangle into a non-empty rectangle
376 0 : void ScaleRoundOut(double aXScale, double aYScale)
377 : {
378 0 : T right = static_cast<T>(ceil(double(XMost()) * aXScale));
379 0 : T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
380 0 : x = static_cast<T>(floor(double(x) * aXScale));
381 0 : y = static_cast<T>(floor(double(y) * aYScale));
382 0 : width = right - x;
383 0 : height = bottom - y;
384 0 : }
385 : // Scale 'this' by aScale, converting coordinates to integers so that the result is
386 : // the largest integer-coordinate rectangle contained by the unrounded result.
387 : void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
388 : // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
389 : // that the result is the largest integer-coordinate rectangle contained by the
390 : // unrounded result.
391 0 : void ScaleRoundIn(double aXScale, double aYScale)
392 : {
393 0 : T right = static_cast<T>(floor(double(XMost()) * aXScale));
394 0 : T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
395 0 : x = static_cast<T>(ceil(double(x) * aXScale));
396 0 : y = static_cast<T>(ceil(double(y) * aYScale));
397 0 : width = gfx_max<T>(0, right - x);
398 0 : height = gfx_max<T>(0, bottom - y);
399 0 : }
400 : // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
401 : // the smallest integer-coordinate rectangle containing the unrounded result.
402 : // Note: this can turn an empty rectangle into a non-empty rectangle
403 : void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
404 : // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
405 : // that the result is the smallest integer-coordinate rectangle containing the
406 : // unrounded result.
407 : // Note: this can turn an empty rectangle into a non-empty rectangle
408 0 : void ScaleInverseRoundOut(double aXScale, double aYScale)
409 : {
410 0 : T right = static_cast<T>(ceil(double(XMost()) / aXScale));
411 0 : T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
412 0 : x = static_cast<T>(floor(double(x) / aXScale));
413 0 : y = static_cast<T>(floor(double(y) / aYScale));
414 0 : width = right - x;
415 0 : height = bottom - y;
416 0 : }
417 :
418 : private:
419 : // Do not use the default operator== or operator!= !
420 : // Use IsEqualEdges or IsEqualInterior explicitly.
421 : bool operator==(const Sub& aRect) const { return false; }
422 : bool operator!=(const Sub& aRect) const { return false; }
423 : };
424 :
425 : }
426 : }
427 :
428 : #endif /* MOZILLA_GFX_BASERECT_H_ */
|