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 : T gfx_min(T aVal1, T aVal2)
50 : {
51 : return (aVal1 < aVal2) ? aVal1 : aVal2;
52 : }
53 :
54 : template<typename T>
55 : T gfx_max(T aVal1, T aVal2)
56 : {
57 : 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 : BaseRect() : x(0), y(0), width(0), height(0) {}
89 : BaseRect(const Point& aOrigin, const SizeT &aSize) :
90 : x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
91 : {
92 : }
93 0 : BaseRect(T aX, T aY, T aWidth, T aHeight) :
94 0 : x(aX), y(aY), width(aWidth), height(aHeight)
95 : {
96 0 : }
97 :
98 : // Emptiness. An empty rect is one that has no area, i.e. its height or width
99 : // is <= 0
100 0 : bool IsEmpty() const { return height <= 0 || width <= 0; }
101 : 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 : bool Contains(const Sub& aRect) const
107 : {
108 : return aRect.IsEmpty() ||
109 : (x <= aRect.x && aRect.XMost() <= XMost() &&
110 : y <= aRect.y && aRect.YMost() <= YMost());
111 : }
112 : // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
113 : bool Contains(T aX, T aY) const
114 : {
115 : return x <= aX && aX + 1 <= XMost() &&
116 : y <= aY && aY + 1 <= YMost();
117 : }
118 : // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
119 : 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 : bool Intersects(const Sub& aRect) const
125 : {
126 : return x < aRect.XMost() && aRect.x < XMost() &&
127 : 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 : Sub Intersect(const Sub& aRect) const
134 : {
135 : Sub result;
136 : result.x = gfx_max(x, aRect.x);
137 : result.y = gfx_max(y, aRect.y);
138 : result.width = gfx_min(XMost(), aRect.XMost()) - result.x;
139 : result.height = gfx_min(YMost(), aRect.YMost()) - result.y;
140 : if (result.width < 0 || result.height < 0) {
141 : 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 : bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
152 : {
153 : *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
154 : 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 : Sub Union(const Sub& aRect) const
162 : {
163 : if (IsEmpty()) {
164 : return aRect;
165 : } else if (aRect.IsEmpty()) {
166 : return *static_cast<const Sub*>(this);
167 : } else {
168 : 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 : Sub UnionEdges(const Sub& aRect) const
175 : {
176 : Sub result;
177 : result.x = gfx_min(x, aRect.x);
178 : result.y = gfx_min(y, aRect.y);
179 : result.width = gfx_max(XMost(), aRect.XMost()) - result.x;
180 : 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 : void UnionRect(const Sub& aRect1, const Sub& aRect2)
190 : {
191 : *static_cast<Sub*>(this) = aRect1.Union(aRect2);
192 : }
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 : void SetRect(T aX, T aY, T aWidth, T aHeight)
205 : {
206 : x = aX; y = aY; width = aWidth; height = aHeight;
207 : }
208 : void SetRect(const Point& aPt, const SizeT& aSize)
209 : {
210 : SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
211 : }
212 : void MoveTo(T aX, T aY) { x = aX; y = aY; }
213 : void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
214 : void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
215 : void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
216 : void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
217 : void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
218 :
219 : void Inflate(T aD) { Inflate(aD, aD); }
220 : void Inflate(T aDx, T aDy)
221 : {
222 : x -= aDx;
223 : y -= aDy;
224 : width += 2 * aDx;
225 : height += 2 * aDy;
226 : }
227 : void Inflate(const Margin& aMargin)
228 : {
229 : x -= aMargin.left;
230 : y -= aMargin.top;
231 : width += aMargin.LeftRight();
232 : height += aMargin.TopBottom();
233 : }
234 : void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
235 :
236 : void Deflate(T aD) { Deflate(aD, aD); }
237 : void Deflate(T aDx, T aDy)
238 : {
239 : x += aDx;
240 : y += aDy;
241 : width = gfx_max(T(0), width - 2 * aDx);
242 : height = gfx_max(T(0), height - 2 * aDy);
243 : }
244 : void Deflate(const Margin& aMargin)
245 : {
246 : x += aMargin.left;
247 : y += aMargin.top;
248 : width = gfx_max(T(0), width - aMargin.LeftRight());
249 : height = gfx_max(T(0), height - aMargin.TopBottom());
250 : }
251 : 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 : bool IsEqualEdges(const Sub& aRect) const
258 : {
259 : return x == aRect.x && y == aRect.y &&
260 : 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 : bool IsEqualInterior(const Sub& aRect) const
265 : {
266 : return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
267 : }
268 :
269 : Sub operator+(const Point& aPoint) const
270 : {
271 : return Sub(x + aPoint.x, y + aPoint.y, width, height);
272 : }
273 : Sub operator-(const Point& aPoint) const
274 : {
275 : return Sub(x - aPoint.x, y - aPoint.y, width, height);
276 : }
277 : Sub& operator+=(const Point& aPoint)
278 : {
279 : MoveBy(aPoint);
280 : return *static_cast<Sub*>(this);
281 : }
282 : Sub& operator-=(const Point& aPoint)
283 : {
284 : MoveBy(-aPoint);
285 : return *static_cast<Sub*>(this);
286 : }
287 :
288 : // Find difference as a Margin
289 : Margin operator-(const Sub& aRect) const
290 : {
291 : return Margin(aRect.x - x, aRect.y - y,
292 : XMost() - aRect.XMost(), YMost() - aRect.YMost());
293 : }
294 :
295 : // Helpers for accessing the vertices
296 0 : 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 : Point Center() const { return Point(x, y) + Point(width, height)/2; }
301 : SizeT Size() const { return SizeT(width, height); }
302 :
303 : // Helper methods for computing the extents
304 0 : T X() const { return x; }
305 0 : T Y() const { return y; }
306 0 : T Width() const { return width; }
307 0 : T Height() const { return height; }
308 0 : T XMost() const { return x + width; }
309 0 : 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 : void Round()
323 : {
324 : T x0 = static_cast<T>(floor(T(X()) + 0.5));
325 : T y0 = static_cast<T>(floor(T(Y()) + 0.5));
326 : T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
327 : T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
328 :
329 : x = x0;
330 : y = y0;
331 :
332 : width = x1 - x0;
333 : height = y1 - y0;
334 : }
335 :
336 : // Snap the rectangle edges to integer coordinates, such that the
337 : // original rectangle contains the resulting rectangle.
338 : void RoundIn()
339 : {
340 : T x0 = static_cast<T>(ceil(T(X())));
341 : T y0 = static_cast<T>(ceil(T(Y())));
342 : T x1 = static_cast<T>(floor(T(XMost())));
343 : T y1 = static_cast<T>(floor(T(YMost())));
344 :
345 : x = x0;
346 : y = y0;
347 :
348 : width = x1 - x0;
349 : height = y1 - y0;
350 : }
351 :
352 : // Snap the rectangle edges to integer coordinates, such that the
353 : // resulting rectangle contains the original rectangle.
354 : void RoundOut()
355 : {
356 : T x0 = static_cast<T>(floor(T(X())));
357 : T y0 = static_cast<T>(floor(T(Y())));
358 : T x1 = static_cast<T>(ceil(T(XMost())));
359 : T y1 = static_cast<T>(ceil(T(YMost())));
360 :
361 : x = x0;
362 : y = y0;
363 :
364 : width = x1 - x0;
365 : height = y1 - y0;
366 : }
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 : 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 : void ScaleRoundOut(double aXScale, double aYScale)
377 : {
378 : T right = static_cast<T>(ceil(double(XMost()) * aXScale));
379 : T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
380 : x = static_cast<T>(floor(double(x) * aXScale));
381 : y = static_cast<T>(floor(double(y) * aYScale));
382 : width = right - x;
383 : height = bottom - y;
384 : }
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 : void ScaleRoundIn(double aXScale, double aYScale)
392 : {
393 : T right = static_cast<T>(floor(double(XMost()) * aXScale));
394 : T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
395 : x = static_cast<T>(ceil(double(x) * aXScale));
396 : y = static_cast<T>(ceil(double(y) * aYScale));
397 : width = gfx_max<T>(0, right - x);
398 : height = gfx_max<T>(0, bottom - y);
399 : }
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 : void ScaleInverseRoundOut(double aXScale, double aYScale)
409 : {
410 : T right = static_cast<T>(ceil(double(XMost()) / aXScale));
411 : T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
412 : x = static_cast<T>(floor(double(x) / aXScale));
413 : y = static_cast<T>(floor(double(y) / aYScale));
414 : width = right - x;
415 : height = bottom - y;
416 : }
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_ */
|