blob: 723f5f6e2b0b752e86793fb6700b09fac41aa538 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#ifndef SkRect_DEFINED
11#define SkRect_DEFINED
12
13#include "SkPoint.h"
reed@android.com233481e2010-02-24 01:49:13 +000014#include "SkSize.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015
16/** \struct SkIRect
17
18 SkIRect holds four 32 bit integer coordinates for a rectangle
19*/
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +000020struct SK_API SkIRect {
reed@android.com8a1c16f2008-12-17 15:59:43 +000021 int32_t fLeft, fTop, fRight, fBottom;
22
robertphillips@google.com5d640682012-05-10 14:50:44 +000023 static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() {
reed@android.com097a3512010-07-13 18:35:14 +000024 SkIRect r;
25 r.setEmpty();
26 return r;
27 }
28
robertphillips@google.com5d640682012-05-10 14:50:44 +000029 static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
reed@android.com097a3512010-07-13 18:35:14 +000030 SkIRect r;
31 r.set(0, 0, w, h);
32 return r;
33 }
34
robertphillips@google.com5d640682012-05-10 14:50:44 +000035 static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
reed@android.com097a3512010-07-13 18:35:14 +000036 SkIRect r;
37 r.set(0, 0, size.width(), size.height());
38 return r;
39 }
40
robertphillips@google.com5d640682012-05-10 14:50:44 +000041 static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
reed@android.com097a3512010-07-13 18:35:14 +000042 SkIRect rect;
43 rect.set(l, t, r, b);
44 return rect;
45 }
46
robertphillips@google.com5d640682012-05-10 14:50:44 +000047 static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
reed@android.com097a3512010-07-13 18:35:14 +000048 SkIRect r;
49 r.set(x, y, x + w, y + h);
50 return r;
51 }
reed@google.comc74ab182011-10-03 20:27:14 +000052
53 int left() const { return fLeft; }
54 int top() const { return fTop; }
55 int right() const { return fRight; }
56 int bottom() const { return fBottom; }
reed@android.com097a3512010-07-13 18:35:14 +000057
reed@google.comc74ab182011-10-03 20:27:14 +000058 /** return the left edge of the rect */
59 int x() const { return fLeft; }
60 /** return the top edge of the rect */
61 int y() const { return fTop; }
62 /**
63 * Returns the rectangle's width. This does not check for a valid rect
64 * (i.e. left <= right) so the result may be negative.
65 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 int width() const { return fRight - fLeft; }
reed@google.comc74ab182011-10-03 20:27:14 +000067
68 /**
69 * Returns the rectangle's height. This does not check for a valid rect
70 * (i.e. top <= bottom) so the result may be negative.
71 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 int height() const { return fBottom - fTop; }
reed@google.comc74ab182011-10-03 20:27:14 +000073
74 /**
75 * Return true if the rectangle's width or height are <= 0
76 */
77 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
reed@google.com7b463ac2012-05-16 13:35:36 +000078
reed@google.comb530ef52011-07-20 19:55:42 +000079 friend bool operator==(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 return !memcmp(&a, &b, sizeof(a));
81 }
reed@android.comda6fb322010-02-19 21:41:30 +000082
reed@google.comb530ef52011-07-20 19:55:42 +000083 friend bool operator!=(const SkIRect& a, const SkIRect& b) {
84 return !(a == b);
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 }
86
reed@android.comd4577752009-11-21 02:48:11 +000087 bool is16Bit() const {
88 return SkIsS16(fLeft) && SkIsS16(fTop) &&
89 SkIsS16(fRight) && SkIsS16(fBottom);
90 }
91
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 /** Set the rectangle to (0,0,0,0)
93 */
94 void setEmpty() { memset(this, 0, sizeof(*this)); }
95
reed@android.comda6fb322010-02-19 21:41:30 +000096 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 fLeft = left;
98 fTop = top;
99 fRight = right;
100 fBottom = bottom;
101 }
reed@google.com20efde72011-05-09 17:00:02 +0000102 // alias for set(l, t, r, b)
103 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
104 this->set(left, top, right, bottom);
105 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
reed@google.com1d12b1f2011-03-03 13:23:35 +0000107 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
108 fLeft = x;
109 fTop = y;
110 fRight = x + width;
111 fBottom = y + height;
112 }
reed@google.com20efde72011-05-09 17:00:02 +0000113
114 /**
115 * Make the largest representable rectangle
116 */
117 void setLargest() {
118 fLeft = fTop = SK_MinS32;
119 fRight = fBottom = SK_MaxS32;
120 }
121
122 /**
123 * Make the largest representable rectangle, but inverted (e.g. fLeft will
124 * be max 32bit and right will be min 32bit).
125 */
126 void setLargestInverted() {
127 fLeft = fTop = SK_MaxS32;
128 fRight = fBottom = SK_MinS32;
129 }
reed@google.com1d12b1f2011-03-03 13:23:35 +0000130
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 /** Offset set the rectangle by adding dx to its left and right,
132 and adding dy to its top and bottom.
133 */
reed@android.comda6fb322010-02-19 21:41:30 +0000134 void offset(int32_t dx, int32_t dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135 fLeft += dx;
136 fTop += dy;
137 fRight += dx;
138 fBottom += dy;
139 }
140
reed@android.comda6fb322010-02-19 21:41:30 +0000141 void offset(const SkIPoint& delta) {
142 this->offset(delta.fX, delta.fY);
143 }
144
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
146 making the rectangle narrower. If dx is negative, then the sides are moved outwards,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000147 making the rectangle wider. The same holds true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148 */
reed@android.comda6fb322010-02-19 21:41:30 +0000149 void inset(int32_t dx, int32_t dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 fLeft += dx;
151 fTop += dy;
152 fRight -= dx;
153 fBottom -= dy;
154 }
reed@android.comda6fb322010-02-19 21:41:30 +0000155
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000156 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
157 moved outwards, making the rectangle wider. If dx is negative, then the
158 sides are moved inwards, making the rectangle narrower. The same holds
159 true for dy and the top and bottom.
160 */
161 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); }
162
reed@google.com20efde72011-05-09 17:00:02 +0000163 bool quickReject(int l, int t, int r, int b) const {
164 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
165 }
166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 /** Returns true if (x,y) is inside the rectangle and the rectangle is not
168 empty. The left and top are considered to be inside, while the right
169 and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
170 points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
171 */
reed@android.comda6fb322010-02-19 21:41:30 +0000172 bool contains(int32_t x, int32_t y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
174 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
175 }
176
177 /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
178 If either rectangle is empty, contains() returns false.
179 */
reed@android.comda6fb322010-02-19 21:41:30 +0000180 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 return left < right && top < bottom && !this->isEmpty() && // check for empties
182 fLeft <= left && fTop <= top &&
183 fRight >= right && fBottom >= bottom;
184 }
185
186 /** Returns true if the specified rectangle r is inside or equal to this rectangle.
187 */
reed@android.comda6fb322010-02-19 21:41:30 +0000188 bool contains(const SkIRect& r) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 return !r.isEmpty() && !this->isEmpty() && // check for empties
190 fLeft <= r.fLeft && fTop <= r.fTop &&
191 fRight >= r.fRight && fBottom >= r.fBottom;
192 }
193
194 /** Return true if this rectangle contains the specified rectangle.
robertphillips@google.com5d640682012-05-10 14:50:44 +0000195 For speed, this method does not check if either this or the specified
196 rectangles are empty, and if either is, its return value is undefined.
197 In the debugging build however, we assert that both this and the
198 specified rectangles are non-empty.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 */
200 bool containsNoEmptyCheck(int32_t left, int32_t top,
robertphillips@google.com5d640682012-05-10 14:50:44 +0000201 int32_t right, int32_t bottom) const {
202 SkASSERT(fLeft < fRight && fTop < fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 SkASSERT(left < right && top < bottom);
204
205 return fLeft <= left && fTop <= top &&
robertphillips@google.com5d640682012-05-10 14:50:44 +0000206 fRight >= right && fBottom >= bottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207 }
208
reed@google.com0d102802012-05-31 18:28:59 +0000209 bool containsNoEmptyCheck(const SkIRect& r) const {
210 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
211 }
212
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 /** If r intersects this rectangle, return true and set this rectangle to that
214 intersection, otherwise return false and do not change this rectangle.
215 If either rectangle is empty, do nothing and return false.
216 */
reed@android.comda6fb322010-02-19 21:41:30 +0000217 bool intersect(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 SkASSERT(&r);
219 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
220 }
221
222 /** If rectangles a and b intersect, return true and set this rectangle to
223 that intersection, otherwise return false and do not change this
224 rectangle. If either rectangle is empty, do nothing and return false.
225 */
reed@android.comda6fb322010-02-19 21:41:30 +0000226 bool intersect(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 SkASSERT(&a && &b);
228
229 if (!a.isEmpty() && !b.isEmpty() &&
reed@android.comda6fb322010-02-19 21:41:30 +0000230 a.fLeft < b.fRight && b.fLeft < a.fRight &&
231 a.fTop < b.fBottom && b.fTop < a.fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 fLeft = SkMax32(a.fLeft, b.fLeft);
233 fTop = SkMax32(a.fTop, b.fTop);
234 fRight = SkMin32(a.fRight, b.fRight);
235 fBottom = SkMin32(a.fBottom, b.fBottom);
236 return true;
237 }
238 return false;
239 }
240
241 /** If rectangles a and b intersect, return true and set this rectangle to
242 that intersection, otherwise return false and do not change this
243 rectangle. For speed, no check to see if a or b are empty is performed.
244 If either is, then the return result is undefined. In the debug build,
245 we assert that both rectangles are non-empty.
246 */
reed@android.comda6fb322010-02-19 21:41:30 +0000247 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 SkASSERT(&a && &b);
249 SkASSERT(!a.isEmpty() && !b.isEmpty());
250
251 if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
reed@android.comda6fb322010-02-19 21:41:30 +0000252 a.fTop < b.fBottom && b.fTop < a.fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fLeft = SkMax32(a.fLeft, b.fLeft);
254 fTop = SkMax32(a.fTop, b.fTop);
255 fRight = SkMin32(a.fRight, b.fRight);
256 fBottom = SkMin32(a.fBottom, b.fBottom);
257 return true;
258 }
259 return false;
260 }
261
262 /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
263 return true and set this rectangle to that intersection,
264 otherwise return false and do not change this rectangle.
265 If either rectangle is empty, do nothing and return false.
266 */
reed@android.comda6fb322010-02-19 21:41:30 +0000267 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 if (left < right && top < bottom && !this->isEmpty() &&
reed@android.comda6fb322010-02-19 21:41:30 +0000269 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 if (fLeft < left) fLeft = left;
271 if (fTop < top) fTop = top;
272 if (fRight > right) fRight = right;
273 if (fBottom > bottom) fBottom = bottom;
274 return true;
275 }
276 return false;
277 }
278
279 /** Returns true if a and b are not empty, and they intersect
reed@google.com0d102802012-05-31 18:28:59 +0000280 */
reed@android.comda6fb322010-02-19 21:41:30 +0000281 static bool Intersects(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 return !a.isEmpty() && !b.isEmpty() && // check for empties
reed@google.com0d102802012-05-31 18:28:59 +0000283 a.fLeft < b.fRight && b.fLeft < a.fRight &&
284 a.fTop < b.fBottom && b.fTop < a.fBottom;
285 }
286
287 /**
288 * Returns true if a and b intersect. debug-asserts that neither are empty.
289 */
290 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
291 SkASSERT(!a.isEmpty());
292 SkASSERT(!b.isEmpty());
293 return a.fLeft < b.fRight && b.fLeft < a.fRight &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 a.fTop < b.fBottom && b.fTop < a.fBottom;
295 }
296
297 /** Update this rectangle to enclose itself and the specified rectangle.
298 If this rectangle is empty, just set it to the specified rectangle. If the specified
299 rectangle is empty, do nothing.
300 */
301 void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
302
303 /** Update this rectangle to enclose itself and the specified rectangle.
304 If this rectangle is empty, just set it to the specified rectangle. If the specified
305 rectangle is empty, do nothing.
306 */
reed@android.comda6fb322010-02-19 21:41:30 +0000307 void join(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
309 }
310
311 /** Swap top/bottom or left/right if there are flipped.
312 This can be called if the edges are computed separately,
313 and may have crossed over each other.
314 When this returns, left <= right && top <= bottom
315 */
316 void sort();
reed@google.com20efde72011-05-09 17:00:02 +0000317
robertphillips@google.com5d640682012-05-10 14:50:44 +0000318 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
reed@google.com4b4fb3a2011-05-16 18:24:19 +0000319 static const SkIRect gEmpty = { 0, 0, 0, 0 };
reed@google.com20efde72011-05-09 17:00:02 +0000320 return gEmpty;
321 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322};
323
324/** \struct SkRect
325*/
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +0000326struct SK_API SkRect {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 SkScalar fLeft, fTop, fRight, fBottom;
328
robertphillips@google.com5d640682012-05-10 14:50:44 +0000329 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
reed@android.com2687ae02010-04-12 21:21:59 +0000330 SkRect r;
331 r.setEmpty();
332 return r;
333 }
334
robertphillips@google.com5d640682012-05-10 14:50:44 +0000335 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
reed@android.com2687ae02010-04-12 21:21:59 +0000336 SkRect r;
337 r.set(0, 0, w, h);
338 return r;
339 }
340
robertphillips@google.com5d640682012-05-10 14:50:44 +0000341 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
reed@android.com233481e2010-02-24 01:49:13 +0000342 SkRect r;
343 r.set(0, 0, size.width(), size.height());
344 return r;
345 }
346
robertphillips@google.com5d640682012-05-10 14:50:44 +0000347 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
reed@android.com233481e2010-02-24 01:49:13 +0000348 SkRect rect;
349 rect.set(l, t, r, b);
350 return rect;
351 }
352
robertphillips@google.com5d640682012-05-10 14:50:44 +0000353 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
reed@android.com233481e2010-02-24 01:49:13 +0000354 SkRect r;
355 r.set(x, y, x + w, y + h);
356 return r;
357 }
reed@android.com2687ae02010-04-12 21:21:59 +0000358
reed@google.com16078632011-12-06 18:56:37 +0000359 /**
360 * Return true if the rectangle's width or height are <= 0
361 */
362 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
363
364 /**
365 * Returns true iff all values in the rect are finite. If any are
366 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
367 * returns false.
368 */
369 bool isFinite() const {
370#ifdef SK_SCALAR_IS_FLOAT
reed@google.com7b463ac2012-05-16 13:35:36 +0000371 float accum = 0;
372 accum *= fLeft;
373 accum *= fTop;
374 accum *= fRight;
375 accum *= fBottom;
reed@google.com16078632011-12-06 18:56:37 +0000376
reed@google.com7b463ac2012-05-16 13:35:36 +0000377 // accum is either NaN or it is finite (zero).
378 SkASSERT(0 == accum || !(accum == accum));
379
reed@google.com16078632011-12-06 18:56:37 +0000380 // value==value will be true iff value is not NaN
reed@google.com7b463ac2012-05-16 13:35:36 +0000381 // TODO: is it faster to say !accum or accum==accum?
382 return accum == accum;
reed@google.com16078632011-12-06 18:56:37 +0000383#else
384 // use bit-or for speed, since we don't care about short-circuting the
385 // tests, and we expect the common case will be that we need to check all.
386 int isNaN = (SK_FixedNaN == fLeft) | (SK_FixedNaN == fTop) |
387 (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
388 return !isNaN;
389#endif
390 }
391
reed@google.comd6195f92012-04-19 21:01:24 +0000392 SkScalar x() const { return fLeft; }
393 SkScalar y() const { return fTop; }
reed@google.com20efde72011-05-09 17:00:02 +0000394 SkScalar left() const { return fLeft; }
395 SkScalar top() const { return fTop; }
396 SkScalar right() const { return fRight; }
397 SkScalar bottom() const { return fBottom; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 SkScalar width() const { return fRight - fLeft; }
399 SkScalar height() const { return fBottom - fTop; }
400 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
401 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
402
bsalomon@google.come174ea42011-09-01 15:34:14 +0000403 friend bool operator==(const SkRect& a, const SkRect& b) {
404 return 0 == memcmp(&a, &b, sizeof(a));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405 }
reed@android.comda6fb322010-02-19 21:41:30 +0000406
bsalomon@google.come174ea42011-09-01 15:34:14 +0000407 friend bool operator!=(const SkRect& a, const SkRect& b) {
408 return 0 != memcmp(&a, &b, sizeof(a));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 }
410
411 /** return the 4 points that enclose the rectangle
412 */
413 void toQuad(SkPoint quad[4]) const;
414
415 /** Set this rectangle to the empty rectangle (0,0,0,0)
416 */
417 void setEmpty() { memset(this, 0, sizeof(*this)); }
418
reed@android.comda6fb322010-02-19 21:41:30 +0000419 void set(const SkIRect& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 fLeft = SkIntToScalar(src.fLeft);
421 fTop = SkIntToScalar(src.fTop);
422 fRight = SkIntToScalar(src.fRight);
423 fBottom = SkIntToScalar(src.fBottom);
424 }
425
reed@android.comda6fb322010-02-19 21:41:30 +0000426 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 fLeft = left;
428 fTop = top;
429 fRight = right;
430 fBottom = bottom;
431 }
reed@google.com20efde72011-05-09 17:00:02 +0000432 // alias for set(l, t, r, b)
433 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
434 this->set(left, top, right, bottom);
435 }
436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 /** Initialize the rect with the 4 specified integers. The routine handles
438 converting them to scalars (by calling SkIntToScalar)
439 */
440 void iset(int left, int top, int right, int bottom) {
441 fLeft = SkIntToScalar(left);
442 fTop = SkIntToScalar(top);
443 fRight = SkIntToScalar(right);
444 fBottom = SkIntToScalar(bottom);
445 }
446
447 /** Set this rectangle to be the bounds of the array of points.
448 If the array is empty (count == 0), then set this rectangle
449 to the empty rectangle (0,0,0,0)
450 */
reed@google.com0bb18bb2012-07-26 15:20:36 +0000451 void set(const SkPoint pts[], int count) {
452 // set() had been checking for non-finite values, so keep that behavior
453 // for now. Now that we have setBoundsCheck(), we may decide to make
454 // set() be simpler/faster, and not check for those.
455 (void)this->setBoundsCheck(pts, count);
456 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457
reed@google.com20efde72011-05-09 17:00:02 +0000458 // alias for set(pts, count)
459 void setBounds(const SkPoint pts[], int count) {
reed@google.com0bb18bb2012-07-26 15:20:36 +0000460 (void)this->setBoundsCheck(pts, count);
reed@google.com20efde72011-05-09 17:00:02 +0000461 }
reed@google.com0bb18bb2012-07-26 15:20:36 +0000462
463 /**
464 * Compute the bounds of the array of points, and set this rect to that
465 * bounds and return true... unless a non-finite value is encountered,
466 * in which case this rect is set to empty and false is returned.
467 */
468 bool setBoundsCheck(const SkPoint pts[], int count);
469
reed@google.com7b463ac2012-05-16 13:35:36 +0000470 void set(const SkPoint& p0, const SkPoint& p1) {
471 fLeft = SkMinScalar(p0.fX, p1.fX);
472 fRight = SkMaxScalar(p0.fX, p1.fX);
473 fTop = SkMinScalar(p0.fY, p1.fY);
474 fBottom = SkMaxScalar(p0.fY, p1.fY);
475 }
476
reed@google.com1d12b1f2011-03-03 13:23:35 +0000477 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
478 fLeft = x;
479 fTop = y;
480 fRight = x + width;
481 fBottom = y + height;
482 }
483
reed@google.com20efde72011-05-09 17:00:02 +0000484 /**
485 * Make the largest representable rectangle
486 */
487 void setLargest() {
488 fLeft = fTop = SK_ScalarMin;
489 fRight = fBottom = SK_ScalarMax;
490 }
491
492 /**
493 * Make the largest representable rectangle, but inverted (e.g. fLeft will
494 * be max and right will be min).
495 */
496 void setLargestInverted() {
497 fLeft = fTop = SK_ScalarMax;
498 fRight = fBottom = SK_ScalarMin;
499 }
500
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 /** Offset set the rectangle by adding dx to its left and right,
502 and adding dy to its top and bottom.
503 */
reed@android.comda6fb322010-02-19 21:41:30 +0000504 void offset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 fLeft += dx;
506 fTop += dy;
507 fRight += dx;
508 fBottom += dy;
509 }
510
reed@android.comda6fb322010-02-19 21:41:30 +0000511 void offset(const SkPoint& delta) {
512 this->offset(delta.fX, delta.fY);
513 }
514
bsalomon@google.com647a8042011-08-23 14:39:01 +0000515 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
516 moved inwards, making the rectangle narrower. If dx is negative, then
517 the sides are moved outwards, making the rectangle wider. The same holds
518 true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 */
reed@android.comda6fb322010-02-19 21:41:30 +0000520 void inset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 fLeft += dx;
522 fTop += dy;
523 fRight -= dx;
524 fBottom -= dy;
525 }
526
bsalomon@google.com647a8042011-08-23 14:39:01 +0000527 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
528 moved outwards, making the rectangle wider. If dx is negative, then the
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000529 sides are moved inwards, making the rectangle narrower. The same holds
bsalomon@google.com647a8042011-08-23 14:39:01 +0000530 true for dy and the top and bottom.
531 */
532 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 /** If this rectangle intersects r, return true and set this rectangle to that
535 intersection, otherwise return false and do not change this rectangle.
536 If either rectangle is empty, do nothing and return false.
537 */
538 bool intersect(const SkRect& r);
539
540 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
541 return true and set this rectangle to that intersection, otherwise return false
542 and do not change this rectangle.
543 If either rectangle is empty, do nothing and return false.
544 */
545 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
546
reed@google.comaf8edcc2011-05-12 22:31:01 +0000547 /**
548 * Return true if this rectangle is not empty, and the specified sides of
549 * a rectangle are not empty, and they intersect.
550 */
reed@android.comda6fb322010-02-19 21:41:30 +0000551 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 return // first check that both are not empty
553 left < right && top < bottom &&
554 fLeft < fRight && fTop < fBottom &&
555 // now check for intersection
556 fLeft < right && left < fRight &&
557 fTop < bottom && top < fBottom;
558 }
reed@google.comf0f617a2011-10-21 14:05:06 +0000559
560 /** If rectangles a and b intersect, return true and set this rectangle to
561 * that intersection, otherwise return false and do not change this
562 * rectangle. If either rectangle is empty, do nothing and return false.
563 */
564 bool intersect(const SkRect& a, const SkRect& b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565
reed@google.comaf8edcc2011-05-12 22:31:01 +0000566 /**
567 * Return true if rectangles a and b are not empty and intersect.
568 */
reed@android.comda6fb322010-02-19 21:41:30 +0000569 static bool Intersects(const SkRect& a, const SkRect& b) {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000570 return !a.isEmpty() && !b.isEmpty() &&
571 a.fLeft < b.fRight && b.fLeft < a.fRight &&
572 a.fTop < b.fBottom && b.fTop < a.fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 }
574
reed@google.comaf8edcc2011-05-12 22:31:01 +0000575 /**
576 * Update this rectangle to enclose itself and the specified rectangle.
577 * If this rectangle is empty, just set it to the specified rectangle.
578 * If the specified rectangle is empty, do nothing.
579 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
581
582 /** Update this rectangle to enclose itself and the specified rectangle.
583 If this rectangle is empty, just set it to the specified rectangle. If the specified
584 rectangle is empty, do nothing.
585 */
reed@android.comda6fb322010-02-19 21:41:30 +0000586 void join(const SkRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
588 }
reed@google.com20efde72011-05-09 17:00:02 +0000589 // alias for join()
590 void growToInclude(const SkRect& r) { this->join(r); }
591
reed@google.comaf8edcc2011-05-12 22:31:01 +0000592 /**
593 * Grow the rect to include the specified (x,y). After this call, the
594 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
595 *
596 * This is close, but not quite the same contract as contains(), since
597 * contains() treats the left and top different from the right and bottom.
598 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
599 * that contains(x,y) always returns false if the rect is empty.
600 */
reed@google.com20efde72011-05-09 17:00:02 +0000601 void growToInclude(SkScalar x, SkScalar y) {
602 fLeft = SkMinScalar(x, fLeft);
bsalomon@google.comee9aa302011-05-09 22:32:52 +0000603 fRight = SkMaxScalar(x, fRight);
604 fTop = SkMinScalar(y, fTop);
reed@google.com20efde72011-05-09 17:00:02 +0000605 fBottom = SkMaxScalar(y, fBottom);
606 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607
reed@google.comaf8edcc2011-05-12 22:31:01 +0000608 /**
609 * Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
610 * is not empty.
611 *
612 * Contains treats the left and top differently from the right and bottom.
613 * The left and top coordinates of the rectangle are themselves considered
614 * to be inside, while the right and bottom are not. Thus for the rectangle
615 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
616 */
reed@android.comda6fb322010-02-19 21:41:30 +0000617 bool contains(const SkPoint& p) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000618 return !this->isEmpty() &&
619 fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 }
621
reed@google.comaf8edcc2011-05-12 22:31:01 +0000622 /**
623 * Returns true if (x,y) is inside the rectangle, and the rectangle
624 * is not empty.
625 *
626 * Contains treats the left and top differently from the right and bottom.
627 * The left and top coordinates of the rectangle are themselves considered
628 * to be inside, while the right and bottom are not. Thus for the rectangle
629 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
630 */
reed@android.comda6fb322010-02-19 21:41:30 +0000631 bool contains(SkScalar x, SkScalar y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 return !this->isEmpty() &&
reed@google.comaf8edcc2011-05-12 22:31:01 +0000633 fLeft <= x && x < fRight && fTop <= y && y < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634 }
635
reed@google.comaf8edcc2011-05-12 22:31:01 +0000636 /**
637 * Return true if this rectangle contains r, and if both rectangles are
638 * not empty.
639 */
reed@android.comda6fb322010-02-19 21:41:30 +0000640 bool contains(const SkRect& r) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000641 return !r.isEmpty() && !this->isEmpty() &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 fLeft <= r.fLeft && fTop <= r.fTop &&
643 fRight >= r.fRight && fBottom >= r.fBottom;
644 }
645
reed@google.comaf8edcc2011-05-12 22:31:01 +0000646 /**
647 * Set the dst rectangle by rounding this rectangle's coordinates to their
648 * nearest integer values using SkScalarRound.
649 */
reed@android.comda6fb322010-02-19 21:41:30 +0000650 void round(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 SkASSERT(dst);
reed@google.comaf8edcc2011-05-12 22:31:01 +0000652 dst->set(SkScalarRound(fLeft), SkScalarRound(fTop),
653 SkScalarRound(fRight), SkScalarRound(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 }
655
reed@google.comaf8edcc2011-05-12 22:31:01 +0000656 /**
657 * Set the dst rectangle by rounding "out" this rectangle, choosing the
658 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
659 */
reed@android.comda6fb322010-02-19 21:41:30 +0000660 void roundOut(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 SkASSERT(dst);
reed@google.comaf8edcc2011-05-12 22:31:01 +0000662 dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop),
663 SkScalarCeil(fRight), SkScalarCeil(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 }
665
reed@google.comaf8edcc2011-05-12 22:31:01 +0000666 /**
reed@google.com099d22d2011-08-01 20:53:19 +0000667 * Expand this rectangle by rounding its coordinates "out", choosing the
668 * floor of top and left, and the ceil of right and bottom. If this rect
669 * is already on integer coordinates, then it will be unchanged.
670 */
671 void roundOut() {
672 this->set(SkScalarFloorToScalar(fLeft),
673 SkScalarFloorToScalar(fTop),
674 SkScalarCeilToScalar(fRight),
675 SkScalarCeilToScalar(fBottom));
676 }
677
678 /**
reed@google.comaf8edcc2011-05-12 22:31:01 +0000679 * Swap top/bottom or left/right if there are flipped (i.e. if width()
680 * or height() would have returned a negative value.) This should be called
681 * if the edges are computed separately, and may have crossed over each
682 * other. When this returns, left <= right && top <= bottom
683 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 void sort();
685};
686
687#endif
688