blob: 0a5439cdfc771d231e49800bc736449c83be68b5 [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 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000028
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000029 static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() {
30 SkIRect r;
31 r.setLargest();
32 return r;
33 }
34
robertphillips@google.com5d640682012-05-10 14:50:44 +000035 static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
reed@android.com097a3512010-07-13 18:35:14 +000036 SkIRect r;
37 r.set(0, 0, w, h);
38 return r;
39 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000040
robertphillips@google.com5d640682012-05-10 14:50:44 +000041 static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
reed@android.com097a3512010-07-13 18:35:14 +000042 SkIRect r;
43 r.set(0, 0, size.width(), size.height());
44 return r;
45 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000046
robertphillips@google.com5d640682012-05-10 14:50:44 +000047 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 +000048 SkIRect rect;
49 rect.set(l, t, r, b);
50 return rect;
51 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000052
robertphillips@google.com5d640682012-05-10 14:50:44 +000053 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 +000054 SkIRect r;
55 r.set(x, y, x + w, y + h);
56 return r;
57 }
reed@google.comc74ab182011-10-03 20:27:14 +000058
59 int left() const { return fLeft; }
60 int top() const { return fTop; }
61 int right() const { return fRight; }
62 int bottom() const { return fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000063
reed@google.comc74ab182011-10-03 20:27:14 +000064 /** return the left edge of the rect */
65 int x() const { return fLeft; }
66 /** return the top edge of the rect */
67 int y() const { return fTop; }
68 /**
69 * Returns the rectangle's width. This does not check for a valid rect
70 * (i.e. left <= right) so the result may be negative.
71 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 int width() const { return fRight - fLeft; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000073
reed@google.comc74ab182011-10-03 20:27:14 +000074 /**
75 * Returns the rectangle's height. This does not check for a valid rect
76 * (i.e. top <= bottom) so the result may be negative.
77 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 int height() const { return fBottom - fTop; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000079
reed@google.comc74ab182011-10-03 20:27:14 +000080 /**
reed@google.com6898d522012-11-08 22:36:19 +000081 * Since the center of an integer rect may fall on a factional value, this
82 * method is defined to return (right + left) >> 1.
83 *
84 * This is a specific "truncation" of the average, which is different than
85 * (right + left) / 2 when the sum is negative.
86 */
87 int centerX() const { return (fRight + fLeft) >> 1; }
skia.committer@gmail.comd9f75032012-11-09 02:01:24 +000088
reed@google.com6898d522012-11-08 22:36:19 +000089 /**
90 * Since the center of an integer rect may fall on a factional value, this
91 * method is defined to return (bottom + top) >> 1
92 *
93 * This is a specific "truncation" of the average, which is different than
94 * (bottom + top) / 2 when the sum is negative.
95 */
96 int centerY() const { return (fBottom + fTop) >> 1; }
skia.committer@gmail.comd9f75032012-11-09 02:01:24 +000097
reed@google.com6898d522012-11-08 22:36:19 +000098 /**
reed@google.comc74ab182011-10-03 20:27:14 +000099 * Return true if the rectangle's width or height are <= 0
100 */
101 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000102
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000103 bool isLargest() const { return SK_MinS32 == fLeft &&
104 SK_MinS32 == fTop &&
105 SK_MaxS32 == fRight &&
106 SK_MaxS32 == fBottom; }
107
reed@google.comb530ef52011-07-20 19:55:42 +0000108 friend bool operator==(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 return !memcmp(&a, &b, sizeof(a));
110 }
reed@android.comda6fb322010-02-19 21:41:30 +0000111
reed@google.comb530ef52011-07-20 19:55:42 +0000112 friend bool operator!=(const SkIRect& a, const SkIRect& b) {
113 return !(a == b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 }
115
reed@android.comd4577752009-11-21 02:48:11 +0000116 bool is16Bit() const {
117 return SkIsS16(fLeft) && SkIsS16(fTop) &&
118 SkIsS16(fRight) && SkIsS16(fBottom);
119 }
120
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 /** Set the rectangle to (0,0,0,0)
122 */
123 void setEmpty() { memset(this, 0, sizeof(*this)); }
124
reed@android.comda6fb322010-02-19 21:41:30 +0000125 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 fLeft = left;
127 fTop = top;
128 fRight = right;
129 fBottom = bottom;
130 }
reed@google.com20efde72011-05-09 17:00:02 +0000131 // alias for set(l, t, r, b)
132 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
133 this->set(left, top, right, bottom);
134 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135
reed@google.com1d12b1f2011-03-03 13:23:35 +0000136 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
137 fLeft = x;
138 fTop = y;
139 fRight = x + width;
140 fBottom = y + height;
141 }
reed@google.com20efde72011-05-09 17:00:02 +0000142
143 /**
144 * Make the largest representable rectangle
145 */
146 void setLargest() {
147 fLeft = fTop = SK_MinS32;
148 fRight = fBottom = SK_MaxS32;
149 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000150
reed@google.com20efde72011-05-09 17:00:02 +0000151 /**
152 * Make the largest representable rectangle, but inverted (e.g. fLeft will
153 * be max 32bit and right will be min 32bit).
154 */
155 void setLargestInverted() {
156 fLeft = fTop = SK_MaxS32;
157 fRight = fBottom = SK_MinS32;
158 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 /** Offset set the rectangle by adding dx to its left and right,
161 and adding dy to its top and bottom.
162 */
reed@android.comda6fb322010-02-19 21:41:30 +0000163 void offset(int32_t dx, int32_t dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164 fLeft += dx;
165 fTop += dy;
166 fRight += dx;
167 fBottom += dy;
168 }
169
reed@android.comda6fb322010-02-19 21:41:30 +0000170 void offset(const SkIPoint& delta) {
171 this->offset(delta.fX, delta.fY);
172 }
173
reed@google.com4d303832012-11-06 22:02:51 +0000174 /**
175 * Offset this rect such its new x() and y() will equal newX and newY.
176 */
177 void offsetTo(int32_t newX, int32_t newY) {
178 fRight += newX - fLeft;
179 fBottom += newY - fTop;
180 fLeft = newX;
181 fTop = newY;
182 }
183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
185 making the rectangle narrower. If dx is negative, then the sides are moved outwards,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000186 making the rectangle wider. The same holds true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 */
reed@android.comda6fb322010-02-19 21:41:30 +0000188 void inset(int32_t dx, int32_t dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 fLeft += dx;
190 fTop += dy;
191 fRight -= dx;
192 fBottom -= dy;
193 }
reed@android.comda6fb322010-02-19 21:41:30 +0000194
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000195 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
196 moved outwards, making the rectangle wider. If dx is negative, then the
197 sides are moved inwards, making the rectangle narrower. The same holds
198 true for dy and the top and bottom.
199 */
200 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); }
201
reed@google.com20efde72011-05-09 17:00:02 +0000202 bool quickReject(int l, int t, int r, int b) const {
203 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
204 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000205
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 /** Returns true if (x,y) is inside the rectangle and the rectangle is not
207 empty. The left and top are considered to be inside, while the right
208 and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
209 points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
210 */
reed@android.comda6fb322010-02-19 21:41:30 +0000211 bool contains(int32_t x, int32_t y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
213 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
214 }
215
216 /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
217 If either rectangle is empty, contains() returns false.
218 */
reed@android.comda6fb322010-02-19 21:41:30 +0000219 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 return left < right && top < bottom && !this->isEmpty() && // check for empties
221 fLeft <= left && fTop <= top &&
222 fRight >= right && fBottom >= bottom;
223 }
224
225 /** Returns true if the specified rectangle r is inside or equal to this rectangle.
226 */
reed@android.comda6fb322010-02-19 21:41:30 +0000227 bool contains(const SkIRect& r) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 return !r.isEmpty() && !this->isEmpty() && // check for empties
229 fLeft <= r.fLeft && fTop <= r.fTop &&
230 fRight >= r.fRight && fBottom >= r.fBottom;
231 }
232
233 /** Return true if this rectangle contains the specified rectangle.
robertphillips@google.com5d640682012-05-10 14:50:44 +0000234 For speed, this method does not check if either this or the specified
235 rectangles are empty, and if either is, its return value is undefined.
236 In the debugging build however, we assert that both this and the
237 specified rectangles are non-empty.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 */
239 bool containsNoEmptyCheck(int32_t left, int32_t top,
robertphillips@google.com5d640682012-05-10 14:50:44 +0000240 int32_t right, int32_t bottom) const {
241 SkASSERT(fLeft < fRight && fTop < fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 SkASSERT(left < right && top < bottom);
243
244 return fLeft <= left && fTop <= top &&
robertphillips@google.com5d640682012-05-10 14:50:44 +0000245 fRight >= right && fBottom >= bottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000247
reed@google.com0d102802012-05-31 18:28:59 +0000248 bool containsNoEmptyCheck(const SkIRect& r) const {
249 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
250 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 /** If r intersects this rectangle, return true and set this rectangle to that
253 intersection, otherwise return false and do not change this rectangle.
254 If either rectangle is empty, do nothing and return false.
255 */
reed@android.comda6fb322010-02-19 21:41:30 +0000256 bool intersect(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkASSERT(&r);
258 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
259 }
260
261 /** If rectangles a and b intersect, return true and set this rectangle to
262 that intersection, otherwise return false and do not change this
263 rectangle. If either rectangle is empty, do nothing and return false.
264 */
reed@android.comda6fb322010-02-19 21:41:30 +0000265 bool intersect(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkASSERT(&a && &b);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000267
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 if (!a.isEmpty() && !b.isEmpty() &&
reed@android.comda6fb322010-02-19 21:41:30 +0000269 a.fLeft < b.fRight && b.fLeft < a.fRight &&
270 a.fTop < b.fBottom && b.fTop < a.fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 fLeft = SkMax32(a.fLeft, b.fLeft);
272 fTop = SkMax32(a.fTop, b.fTop);
273 fRight = SkMin32(a.fRight, b.fRight);
274 fBottom = SkMin32(a.fBottom, b.fBottom);
275 return true;
276 }
277 return false;
278 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000279
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 /** If rectangles a and b intersect, return true and set this rectangle to
281 that intersection, otherwise return false and do not change this
282 rectangle. For speed, no check to see if a or b are empty is performed.
283 If either is, then the return result is undefined. In the debug build,
284 we assert that both rectangles are non-empty.
285 */
reed@android.comda6fb322010-02-19 21:41:30 +0000286 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 SkASSERT(&a && &b);
288 SkASSERT(!a.isEmpty() && !b.isEmpty());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
reed@android.comda6fb322010-02-19 21:41:30 +0000291 a.fTop < b.fBottom && b.fTop < a.fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 fLeft = SkMax32(a.fLeft, b.fLeft);
293 fTop = SkMax32(a.fTop, b.fTop);
294 fRight = SkMin32(a.fRight, b.fRight);
295 fBottom = SkMin32(a.fBottom, b.fBottom);
296 return true;
297 }
298 return false;
299 }
300
301 /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
302 return true and set this rectangle to that intersection,
303 otherwise return false and do not change this rectangle.
304 If either rectangle is empty, do nothing and return false.
305 */
reed@android.comda6fb322010-02-19 21:41:30 +0000306 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 if (left < right && top < bottom && !this->isEmpty() &&
reed@android.comda6fb322010-02-19 21:41:30 +0000308 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 if (fLeft < left) fLeft = left;
310 if (fTop < top) fTop = top;
311 if (fRight > right) fRight = right;
312 if (fBottom > bottom) fBottom = bottom;
313 return true;
314 }
315 return false;
316 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000317
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318 /** Returns true if a and b are not empty, and they intersect
reed@google.com0d102802012-05-31 18:28:59 +0000319 */
reed@android.comda6fb322010-02-19 21:41:30 +0000320 static bool Intersects(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 return !a.isEmpty() && !b.isEmpty() && // check for empties
reed@google.com0d102802012-05-31 18:28:59 +0000322 a.fLeft < b.fRight && b.fLeft < a.fRight &&
323 a.fTop < b.fBottom && b.fTop < a.fBottom;
324 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000325
reed@google.com0d102802012-05-31 18:28:59 +0000326 /**
327 * Returns true if a and b intersect. debug-asserts that neither are empty.
328 */
329 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
330 SkASSERT(!a.isEmpty());
331 SkASSERT(!b.isEmpty());
332 return a.fLeft < b.fRight && b.fLeft < a.fRight &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 a.fTop < b.fBottom && b.fTop < a.fBottom;
334 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000335
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 /** Update this rectangle to enclose itself and the specified rectangle.
337 If this rectangle is empty, just set it to the specified rectangle. If the specified
338 rectangle is empty, do nothing.
339 */
340 void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
341
342 /** Update this rectangle to enclose itself and the specified rectangle.
343 If this rectangle is empty, just set it to the specified rectangle. If the specified
344 rectangle is empty, do nothing.
345 */
reed@android.comda6fb322010-02-19 21:41:30 +0000346 void join(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
348 }
349
350 /** Swap top/bottom or left/right if there are flipped.
351 This can be called if the edges are computed separately,
352 and may have crossed over each other.
353 When this returns, left <= right && top <= bottom
354 */
355 void sort();
reed@google.com20efde72011-05-09 17:00:02 +0000356
robertphillips@google.com5d640682012-05-10 14:50:44 +0000357 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
reed@google.com4b4fb3a2011-05-16 18:24:19 +0000358 static const SkIRect gEmpty = { 0, 0, 0, 0 };
reed@google.com20efde72011-05-09 17:00:02 +0000359 return gEmpty;
360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361};
362
363/** \struct SkRect
364*/
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +0000365struct SK_API SkRect {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 SkScalar fLeft, fTop, fRight, fBottom;
367
robertphillips@google.com5d640682012-05-10 14:50:44 +0000368 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
reed@android.com2687ae02010-04-12 21:21:59 +0000369 SkRect r;
370 r.setEmpty();
371 return r;
372 }
373
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000374 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() {
375 SkRect r;
376 r.setLargest();
377 return r;
378 }
379
robertphillips@google.com5d640682012-05-10 14:50:44 +0000380 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
reed@android.com2687ae02010-04-12 21:21:59 +0000381 SkRect r;
382 r.set(0, 0, w, h);
383 return r;
384 }
385
robertphillips@google.com5d640682012-05-10 14:50:44 +0000386 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
reed@android.com233481e2010-02-24 01:49:13 +0000387 SkRect r;
388 r.set(0, 0, size.width(), size.height());
389 return r;
390 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000391
robertphillips@google.com5d640682012-05-10 14:50:44 +0000392 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
reed@android.com233481e2010-02-24 01:49:13 +0000393 SkRect rect;
394 rect.set(l, t, r, b);
395 return rect;
396 }
397
robertphillips@google.com5d640682012-05-10 14:50:44 +0000398 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
reed@android.com233481e2010-02-24 01:49:13 +0000399 SkRect r;
400 r.set(x, y, x + w, y + h);
401 return r;
402 }
reed@android.com2687ae02010-04-12 21:21:59 +0000403
reed@google.com44699382013-10-31 17:28:30 +0000404 SK_ATTR_DEPRECATED("use Make()")
bsalomon@google.com7b7cdd12012-11-07 16:17:24 +0000405 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
406 SkRect r;
407 r.set(SkIntToScalar(irect.fLeft),
408 SkIntToScalar(irect.fTop),
409 SkIntToScalar(irect.fRight),
410 SkIntToScalar(irect.fBottom));
411 return r;
412 }
skia.committer@gmail.com43a6b6a2013-01-24 07:06:13 +0000413
reed@google.com1d6cff72013-01-23 15:37:56 +0000414 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
415 SkRect r;
416 r.set(SkIntToScalar(irect.fLeft),
417 SkIntToScalar(irect.fTop),
418 SkIntToScalar(irect.fRight),
419 SkIntToScalar(irect.fBottom));
420 return r;
421 }
skia.committer@gmail.com43a6b6a2013-01-24 07:06:13 +0000422
reed@google.com16078632011-12-06 18:56:37 +0000423 /**
424 * Return true if the rectangle's width or height are <= 0
425 */
426 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000427
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +0000428 bool isLargest() const { return SK_ScalarMin == fLeft &&
429 SK_ScalarMin == fTop &&
430 SK_ScalarMax == fRight &&
431 SK_ScalarMax == fBottom; }
432
reed@google.com16078632011-12-06 18:56:37 +0000433 /**
434 * Returns true iff all values in the rect are finite. If any are
435 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
436 * returns false.
437 */
438 bool isFinite() const {
reed@google.com7b463ac2012-05-16 13:35:36 +0000439 float accum = 0;
440 accum *= fLeft;
441 accum *= fTop;
442 accum *= fRight;
443 accum *= fBottom;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000444
reed@google.com7b463ac2012-05-16 13:35:36 +0000445 // accum is either NaN or it is finite (zero).
446 SkASSERT(0 == accum || !(accum == accum));
447
reed@google.com16078632011-12-06 18:56:37 +0000448 // value==value will be true iff value is not NaN
reed@google.com7b463ac2012-05-16 13:35:36 +0000449 // TODO: is it faster to say !accum or accum==accum?
450 return accum == accum;
reed@google.com16078632011-12-06 18:56:37 +0000451 }
452
reed@google.comd6195f92012-04-19 21:01:24 +0000453 SkScalar x() const { return fLeft; }
454 SkScalar y() const { return fTop; }
reed@google.com20efde72011-05-09 17:00:02 +0000455 SkScalar left() const { return fLeft; }
456 SkScalar top() const { return fTop; }
457 SkScalar right() const { return fRight; }
458 SkScalar bottom() const { return fBottom; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 SkScalar width() const { return fRight - fLeft; }
460 SkScalar height() const { return fBottom - fTop; }
461 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
462 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
463
bsalomon@google.come174ea42011-09-01 15:34:14 +0000464 friend bool operator==(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000465 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 }
reed@android.comda6fb322010-02-19 21:41:30 +0000467
bsalomon@google.come174ea42011-09-01 15:34:14 +0000468 friend bool operator!=(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000469 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 }
471
commit-bot@chromium.org24ab3b02013-08-14 21:56:37 +0000472 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
473 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
474 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000475 void toQuad(SkPoint quad[4]) const;
476
477 /** Set this rectangle to the empty rectangle (0,0,0,0)
478 */
479 void setEmpty() { memset(this, 0, sizeof(*this)); }
480
reed@android.comda6fb322010-02-19 21:41:30 +0000481 void set(const SkIRect& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 fLeft = SkIntToScalar(src.fLeft);
483 fTop = SkIntToScalar(src.fTop);
484 fRight = SkIntToScalar(src.fRight);
485 fBottom = SkIntToScalar(src.fBottom);
486 }
487
reed@android.comda6fb322010-02-19 21:41:30 +0000488 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 fLeft = left;
490 fTop = top;
491 fRight = right;
492 fBottom = bottom;
493 }
reed@google.com20efde72011-05-09 17:00:02 +0000494 // alias for set(l, t, r, b)
495 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
496 this->set(left, top, right, bottom);
497 }
498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 /** Initialize the rect with the 4 specified integers. The routine handles
500 converting them to scalars (by calling SkIntToScalar)
501 */
502 void iset(int left, int top, int right, int bottom) {
503 fLeft = SkIntToScalar(left);
504 fTop = SkIntToScalar(top);
505 fRight = SkIntToScalar(right);
506 fBottom = SkIntToScalar(bottom);
507 }
508
reed@google.com6f547242013-01-16 15:15:24 +0000509 /**
510 * Set this rectangle to be left/top at 0,0, and have the specified width
511 * and height (automatically converted to SkScalar).
512 */
513 void isetWH(int width, int height) {
514 fLeft = fTop = 0;
515 fRight = SkIntToScalar(width);
516 fBottom = SkIntToScalar(height);
517 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 /** Set this rectangle to be the bounds of the array of points.
520 If the array is empty (count == 0), then set this rectangle
521 to the empty rectangle (0,0,0,0)
522 */
reed@google.com0bb18bb2012-07-26 15:20:36 +0000523 void set(const SkPoint pts[], int count) {
524 // set() had been checking for non-finite values, so keep that behavior
525 // for now. Now that we have setBoundsCheck(), we may decide to make
526 // set() be simpler/faster, and not check for those.
527 (void)this->setBoundsCheck(pts, count);
528 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529
reed@google.com20efde72011-05-09 17:00:02 +0000530 // alias for set(pts, count)
531 void setBounds(const SkPoint pts[], int count) {
reed@google.com0bb18bb2012-07-26 15:20:36 +0000532 (void)this->setBoundsCheck(pts, count);
reed@google.com20efde72011-05-09 17:00:02 +0000533 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000534
reed@google.com0bb18bb2012-07-26 15:20:36 +0000535 /**
536 * Compute the bounds of the array of points, and set this rect to that
537 * bounds and return true... unless a non-finite value is encountered,
538 * in which case this rect is set to empty and false is returned.
539 */
540 bool setBoundsCheck(const SkPoint pts[], int count);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000541
reed@google.com7b463ac2012-05-16 13:35:36 +0000542 void set(const SkPoint& p0, const SkPoint& p1) {
543 fLeft = SkMinScalar(p0.fX, p1.fX);
544 fRight = SkMaxScalar(p0.fX, p1.fX);
545 fTop = SkMinScalar(p0.fY, p1.fY);
546 fBottom = SkMaxScalar(p0.fY, p1.fY);
547 }
548
reed@google.com1d12b1f2011-03-03 13:23:35 +0000549 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
550 fLeft = x;
551 fTop = y;
552 fRight = x + width;
553 fBottom = y + height;
554 }
555
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000556 void setWH(SkScalar width, SkScalar height) {
557 fLeft = 0;
558 fTop = 0;
559 fRight = width;
560 fBottom = height;
561 }
562
reed@google.com20efde72011-05-09 17:00:02 +0000563 /**
564 * Make the largest representable rectangle
565 */
566 void setLargest() {
567 fLeft = fTop = SK_ScalarMin;
568 fRight = fBottom = SK_ScalarMax;
569 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000570
reed@google.com20efde72011-05-09 17:00:02 +0000571 /**
572 * Make the largest representable rectangle, but inverted (e.g. fLeft will
573 * be max and right will be min).
574 */
575 void setLargestInverted() {
576 fLeft = fTop = SK_ScalarMax;
577 fRight = fBottom = SK_ScalarMin;
578 }
579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 /** Offset set the rectangle by adding dx to its left and right,
581 and adding dy to its top and bottom.
582 */
reed@android.comda6fb322010-02-19 21:41:30 +0000583 void offset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 fLeft += dx;
585 fTop += dy;
586 fRight += dx;
587 fBottom += dy;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000588 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589
reed@android.comda6fb322010-02-19 21:41:30 +0000590 void offset(const SkPoint& delta) {
591 this->offset(delta.fX, delta.fY);
592 }
593
reed@google.com4d303832012-11-06 22:02:51 +0000594 /**
595 * Offset this rect such its new x() and y() will equal newX and newY.
596 */
597 void offsetTo(SkScalar newX, SkScalar newY) {
598 fRight += newX - fLeft;
599 fBottom += newY - fTop;
600 fLeft = newX;
601 fTop = newY;
602 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000603
bsalomon@google.com647a8042011-08-23 14:39:01 +0000604 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
605 moved inwards, making the rectangle narrower. If dx is negative, then
606 the sides are moved outwards, making the rectangle wider. The same holds
607 true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 */
reed@android.comda6fb322010-02-19 21:41:30 +0000609 void inset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 fLeft += dx;
611 fTop += dy;
612 fRight -= dx;
613 fBottom -= dy;
614 }
615
bsalomon@google.com647a8042011-08-23 14:39:01 +0000616 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
617 moved outwards, making the rectangle wider. If dx is negative, then the
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000618 sides are moved inwards, making the rectangle narrower. The same holds
bsalomon@google.com647a8042011-08-23 14:39:01 +0000619 true for dy and the top and bottom.
620 */
621 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 /** If this rectangle intersects r, return true and set this rectangle to that
624 intersection, otherwise return false and do not change this rectangle.
625 If either rectangle is empty, do nothing and return false.
626 */
627 bool intersect(const SkRect& r);
628
629 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
630 return true and set this rectangle to that intersection, otherwise return false
631 and do not change this rectangle.
632 If either rectangle is empty, do nothing and return false.
633 */
634 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
635
reed@google.comaf8edcc2011-05-12 22:31:01 +0000636 /**
637 * Return true if this rectangle is not empty, and the specified sides of
638 * a rectangle are not empty, and they intersect.
639 */
reed@android.comda6fb322010-02-19 21:41:30 +0000640 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 return // first check that both are not empty
642 left < right && top < bottom &&
643 fLeft < fRight && fTop < fBottom &&
644 // now check for intersection
645 fLeft < right && left < fRight &&
646 fTop < bottom && top < fBottom;
647 }
reed@google.comf0f617a2011-10-21 14:05:06 +0000648
649 /** If rectangles a and b intersect, return true and set this rectangle to
650 * that intersection, otherwise return false and do not change this
651 * rectangle. If either rectangle is empty, do nothing and return false.
652 */
653 bool intersect(const SkRect& a, const SkRect& b);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000654
reed@google.comaf8edcc2011-05-12 22:31:01 +0000655 /**
656 * Return true if rectangles a and b are not empty and intersect.
657 */
reed@android.comda6fb322010-02-19 21:41:30 +0000658 static bool Intersects(const SkRect& a, const SkRect& b) {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000659 return !a.isEmpty() && !b.isEmpty() &&
660 a.fLeft < b.fRight && b.fLeft < a.fRight &&
661 a.fTop < b.fBottom && b.fTop < a.fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000663
reed@google.comaf8edcc2011-05-12 22:31:01 +0000664 /**
665 * Update this rectangle to enclose itself and the specified rectangle.
666 * If this rectangle is empty, just set it to the specified rectangle.
667 * If the specified rectangle is empty, do nothing.
668 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
670
671 /** Update this rectangle to enclose itself and the specified rectangle.
672 If this rectangle is empty, just set it to the specified rectangle. If the specified
673 rectangle is empty, do nothing.
674 */
reed@android.comda6fb322010-02-19 21:41:30 +0000675 void join(const SkRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
677 }
reed@google.com20efde72011-05-09 17:00:02 +0000678 // alias for join()
679 void growToInclude(const SkRect& r) { this->join(r); }
680
reed@google.comaf8edcc2011-05-12 22:31:01 +0000681 /**
682 * Grow the rect to include the specified (x,y). After this call, the
683 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
684 *
685 * This is close, but not quite the same contract as contains(), since
686 * contains() treats the left and top different from the right and bottom.
687 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
688 * that contains(x,y) always returns false if the rect is empty.
689 */
reed@google.com20efde72011-05-09 17:00:02 +0000690 void growToInclude(SkScalar x, SkScalar y) {
691 fLeft = SkMinScalar(x, fLeft);
bsalomon@google.comee9aa302011-05-09 22:32:52 +0000692 fRight = SkMaxScalar(x, fRight);
693 fTop = SkMinScalar(y, fTop);
reed@google.com20efde72011-05-09 17:00:02 +0000694 fBottom = SkMaxScalar(y, fBottom);
695 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000696
commit-bot@chromium.orgb8bd6cb2013-09-03 14:56:17 +0000697 /** Bulk version of growToInclude */
698 void growToInclude(const SkPoint pts[], int count) {
699 this->growToInclude(pts, sizeof(SkPoint), count);
700 }
701
702 /** Bulk version of growToInclude with stride. */
703 void growToInclude(const SkPoint pts[], size_t stride, int count) {
704 SkASSERT(count >= 0);
705 SkASSERT(stride >= sizeof(SkPoint));
706 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
707 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
708 this->growToInclude(pts->fX, pts->fY);
709 }
710 }
711
reed@google.comaf8edcc2011-05-12 22:31:01 +0000712 /**
reed@google.comaf8edcc2011-05-12 22:31:01 +0000713 * Return true if this rectangle contains r, and if both rectangles are
714 * not empty.
715 */
reed@android.comda6fb322010-02-19 21:41:30 +0000716 bool contains(const SkRect& r) const {
reed@google.com4db592c2013-10-30 17:39:43 +0000717 // todo: can we eliminate the this->isEmpty check?
reed@google.comaf8edcc2011-05-12 22:31:01 +0000718 return !r.isEmpty() && !this->isEmpty() &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 fLeft <= r.fLeft && fTop <= r.fTop &&
720 fRight >= r.fRight && fBottom >= r.fBottom;
721 }
722
reed@google.comaf8edcc2011-05-12 22:31:01 +0000723 /**
724 * Set the dst rectangle by rounding this rectangle's coordinates to their
reed@google.com126f7f52013-11-07 16:06:53 +0000725 * nearest integer values using SkScalarRoundToInt.
reed@google.comaf8edcc2011-05-12 22:31:01 +0000726 */
reed@android.comda6fb322010-02-19 21:41:30 +0000727 void round(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000729 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
730 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 }
732
reed@google.comaf8edcc2011-05-12 22:31:01 +0000733 /**
734 * Set the dst rectangle by rounding "out" this rectangle, choosing the
735 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
736 */
reed@android.comda6fb322010-02-19 21:41:30 +0000737 void roundOut(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000739 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
740 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 }
742
reed@google.comaf8edcc2011-05-12 22:31:01 +0000743 /**
reed@google.com099d22d2011-08-01 20:53:19 +0000744 * Expand this rectangle by rounding its coordinates "out", choosing the
745 * floor of top and left, and the ceil of right and bottom. If this rect
746 * is already on integer coordinates, then it will be unchanged.
747 */
748 void roundOut() {
749 this->set(SkScalarFloorToScalar(fLeft),
750 SkScalarFloorToScalar(fTop),
751 SkScalarCeilToScalar(fRight),
752 SkScalarCeilToScalar(fBottom));
753 }
754
755 /**
reed@google.com4d303832012-11-06 22:02:51 +0000756 * Set the dst rectangle by rounding "in" this rectangle, choosing the
757 * ceil of top and left, and the floor of right and bottom. This does *not*
758 * call sort(), so it is possible that the resulting rect is inverted...
759 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
760 */
761 void roundIn(SkIRect* dst) const {
762 SkASSERT(dst);
763 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
764 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
765 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000766
reed@google.com126f7f52013-11-07 16:06:53 +0000767 /**
768 * Return a new SkIRect which is contains the rounded coordinates of this
769 * rect using SkScalarRoundToInt.
770 */
771 SkIRect round() const {
772 SkIRect ir;
773 this->round(&ir);
774 return ir;
775 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000776
reed@google.com4d303832012-11-06 22:02:51 +0000777 /**
reed@google.comaf8edcc2011-05-12 22:31:01 +0000778 * Swap top/bottom or left/right if there are flipped (i.e. if width()
779 * or height() would have returned a negative value.) This should be called
780 * if the edges are computed separately, and may have crossed over each
781 * other. When this returns, left <= right && top <= bottom
782 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 void sort();
skia.committer@gmail.comeed625d2013-03-09 07:01:15 +0000784
reed@google.comc7d0ea32013-03-08 16:07:54 +0000785 /**
786 * cast-safe way to treat the rect as an array of (4) SkScalars.
787 */
788 const SkScalar* asScalars() const { return &fLeft; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789};
790
791#endif