blob: c615603d7e85b1d7adb645c20390a402f0c8f908 [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.com1d6cff72013-01-23 15:37:56 +0000404 // DEPRECATED: call Make(r)
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 {
439#ifdef SK_SCALAR_IS_FLOAT
reed@google.com7b463ac2012-05-16 13:35:36 +0000440 float accum = 0;
441 accum *= fLeft;
442 accum *= fTop;
443 accum *= fRight;
444 accum *= fBottom;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000445
reed@google.com7b463ac2012-05-16 13:35:36 +0000446 // accum is either NaN or it is finite (zero).
447 SkASSERT(0 == accum || !(accum == accum));
448
reed@google.com16078632011-12-06 18:56:37 +0000449 // value==value will be true iff value is not NaN
reed@google.com7b463ac2012-05-16 13:35:36 +0000450 // TODO: is it faster to say !accum or accum==accum?
451 return accum == accum;
reed@google.com16078632011-12-06 18:56:37 +0000452#else
453 // use bit-or for speed, since we don't care about short-circuting the
454 // tests, and we expect the common case will be that we need to check all.
455 int isNaN = (SK_FixedNaN == fLeft) | (SK_FixedNaN == fTop) |
456 (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
457 return !isNaN;
458#endif
459 }
460
reed@google.comd6195f92012-04-19 21:01:24 +0000461 SkScalar x() const { return fLeft; }
462 SkScalar y() const { return fTop; }
reed@google.com20efde72011-05-09 17:00:02 +0000463 SkScalar left() const { return fLeft; }
464 SkScalar top() const { return fTop; }
465 SkScalar right() const { return fRight; }
466 SkScalar bottom() const { return fBottom; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 SkScalar width() const { return fRight - fLeft; }
468 SkScalar height() const { return fBottom - fTop; }
469 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
470 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
471
bsalomon@google.come174ea42011-09-01 15:34:14 +0000472 friend bool operator==(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000473 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 }
reed@android.comda6fb322010-02-19 21:41:30 +0000475
bsalomon@google.come174ea42011-09-01 15:34:14 +0000476 friend bool operator!=(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000477 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 }
479
commit-bot@chromium.org24ab3b02013-08-14 21:56:37 +0000480 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
481 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
482 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 void toQuad(SkPoint quad[4]) const;
484
485 /** Set this rectangle to the empty rectangle (0,0,0,0)
486 */
487 void setEmpty() { memset(this, 0, sizeof(*this)); }
488
reed@android.comda6fb322010-02-19 21:41:30 +0000489 void set(const SkIRect& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 fLeft = SkIntToScalar(src.fLeft);
491 fTop = SkIntToScalar(src.fTop);
492 fRight = SkIntToScalar(src.fRight);
493 fBottom = SkIntToScalar(src.fBottom);
494 }
495
reed@android.comda6fb322010-02-19 21:41:30 +0000496 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 fLeft = left;
498 fTop = top;
499 fRight = right;
500 fBottom = bottom;
501 }
reed@google.com20efde72011-05-09 17:00:02 +0000502 // alias for set(l, t, r, b)
503 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
504 this->set(left, top, right, bottom);
505 }
506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 /** Initialize the rect with the 4 specified integers. The routine handles
508 converting them to scalars (by calling SkIntToScalar)
509 */
510 void iset(int left, int top, int right, int bottom) {
511 fLeft = SkIntToScalar(left);
512 fTop = SkIntToScalar(top);
513 fRight = SkIntToScalar(right);
514 fBottom = SkIntToScalar(bottom);
515 }
516
reed@google.com6f547242013-01-16 15:15:24 +0000517 /**
518 * Set this rectangle to be left/top at 0,0, and have the specified width
519 * and height (automatically converted to SkScalar).
520 */
521 void isetWH(int width, int height) {
522 fLeft = fTop = 0;
523 fRight = SkIntToScalar(width);
524 fBottom = SkIntToScalar(height);
525 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000526
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 /** Set this rectangle to be the bounds of the array of points.
528 If the array is empty (count == 0), then set this rectangle
529 to the empty rectangle (0,0,0,0)
530 */
reed@google.com0bb18bb2012-07-26 15:20:36 +0000531 void set(const SkPoint pts[], int count) {
532 // set() had been checking for non-finite values, so keep that behavior
533 // for now. Now that we have setBoundsCheck(), we may decide to make
534 // set() be simpler/faster, and not check for those.
535 (void)this->setBoundsCheck(pts, count);
536 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537
reed@google.com20efde72011-05-09 17:00:02 +0000538 // alias for set(pts, count)
539 void setBounds(const SkPoint pts[], int count) {
reed@google.com0bb18bb2012-07-26 15:20:36 +0000540 (void)this->setBoundsCheck(pts, count);
reed@google.com20efde72011-05-09 17:00:02 +0000541 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000542
reed@google.com0bb18bb2012-07-26 15:20:36 +0000543 /**
544 * Compute the bounds of the array of points, and set this rect to that
545 * bounds and return true... unless a non-finite value is encountered,
546 * in which case this rect is set to empty and false is returned.
547 */
548 bool setBoundsCheck(const SkPoint pts[], int count);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000549
reed@google.com7b463ac2012-05-16 13:35:36 +0000550 void set(const SkPoint& p0, const SkPoint& p1) {
551 fLeft = SkMinScalar(p0.fX, p1.fX);
552 fRight = SkMaxScalar(p0.fX, p1.fX);
553 fTop = SkMinScalar(p0.fY, p1.fY);
554 fBottom = SkMaxScalar(p0.fY, p1.fY);
555 }
556
reed@google.com1d12b1f2011-03-03 13:23:35 +0000557 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
558 fLeft = x;
559 fTop = y;
560 fRight = x + width;
561 fBottom = y + height;
562 }
563
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000564 void setWH(SkScalar width, SkScalar height) {
565 fLeft = 0;
566 fTop = 0;
567 fRight = width;
568 fBottom = height;
569 }
570
reed@google.com20efde72011-05-09 17:00:02 +0000571 /**
572 * Make the largest representable rectangle
573 */
574 void setLargest() {
575 fLeft = fTop = SK_ScalarMin;
576 fRight = fBottom = SK_ScalarMax;
577 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000578
reed@google.com20efde72011-05-09 17:00:02 +0000579 /**
580 * Make the largest representable rectangle, but inverted (e.g. fLeft will
581 * be max and right will be min).
582 */
583 void setLargestInverted() {
584 fLeft = fTop = SK_ScalarMax;
585 fRight = fBottom = SK_ScalarMin;
586 }
587
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 /** Offset set the rectangle by adding dx to its left and right,
589 and adding dy to its top and bottom.
590 */
reed@android.comda6fb322010-02-19 21:41:30 +0000591 void offset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 fLeft += dx;
593 fTop += dy;
594 fRight += dx;
595 fBottom += dy;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000596 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597
reed@android.comda6fb322010-02-19 21:41:30 +0000598 void offset(const SkPoint& delta) {
599 this->offset(delta.fX, delta.fY);
600 }
601
reed@google.com4d303832012-11-06 22:02:51 +0000602 /**
603 * Offset this rect such its new x() and y() will equal newX and newY.
604 */
605 void offsetTo(SkScalar newX, SkScalar newY) {
606 fRight += newX - fLeft;
607 fBottom += newY - fTop;
608 fLeft = newX;
609 fTop = newY;
610 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000611
bsalomon@google.com647a8042011-08-23 14:39:01 +0000612 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
613 moved inwards, making the rectangle narrower. If dx is negative, then
614 the sides are moved outwards, making the rectangle wider. The same holds
615 true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 */
reed@android.comda6fb322010-02-19 21:41:30 +0000617 void inset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 fLeft += dx;
619 fTop += dy;
620 fRight -= dx;
621 fBottom -= dy;
622 }
623
bsalomon@google.com647a8042011-08-23 14:39:01 +0000624 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
625 moved outwards, making the rectangle wider. If dx is negative, then the
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000626 sides are moved inwards, making the rectangle narrower. The same holds
bsalomon@google.com647a8042011-08-23 14:39:01 +0000627 true for dy and the top and bottom.
628 */
629 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
630
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 /** If this rectangle intersects r, return true and set this rectangle to that
632 intersection, otherwise return false and do not change this rectangle.
633 If either rectangle is empty, do nothing and return false.
634 */
635 bool intersect(const SkRect& r);
636
637 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
638 return true and set this rectangle to that intersection, otherwise return false
639 and do not change this rectangle.
640 If either rectangle is empty, do nothing and return false.
641 */
642 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
643
reed@google.comaf8edcc2011-05-12 22:31:01 +0000644 /**
645 * Return true if this rectangle is not empty, and the specified sides of
646 * a rectangle are not empty, and they intersect.
647 */
reed@android.comda6fb322010-02-19 21:41:30 +0000648 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 return // first check that both are not empty
650 left < right && top < bottom &&
651 fLeft < fRight && fTop < fBottom &&
652 // now check for intersection
653 fLeft < right && left < fRight &&
654 fTop < bottom && top < fBottom;
655 }
reed@google.comf0f617a2011-10-21 14:05:06 +0000656
657 /** If rectangles a and b intersect, return true and set this rectangle to
658 * that intersection, otherwise return false and do not change this
659 * rectangle. If either rectangle is empty, do nothing and return false.
660 */
661 bool intersect(const SkRect& a, const SkRect& b);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000662
reed@google.comaf8edcc2011-05-12 22:31:01 +0000663 /**
664 * Return true if rectangles a and b are not empty and intersect.
665 */
reed@android.comda6fb322010-02-19 21:41:30 +0000666 static bool Intersects(const SkRect& a, const SkRect& b) {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000667 return !a.isEmpty() && !b.isEmpty() &&
668 a.fLeft < b.fRight && b.fLeft < a.fRight &&
669 a.fTop < b.fBottom && b.fTop < a.fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000671
reed@google.comaf8edcc2011-05-12 22:31:01 +0000672 /**
673 * Update this rectangle to enclose itself and the specified rectangle.
674 * If this rectangle is empty, just set it to the specified rectangle.
675 * If the specified rectangle is empty, do nothing.
676 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
678
679 /** Update this rectangle to enclose itself and the specified rectangle.
680 If this rectangle is empty, just set it to the specified rectangle. If the specified
681 rectangle is empty, do nothing.
682 */
reed@android.comda6fb322010-02-19 21:41:30 +0000683 void join(const SkRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
685 }
reed@google.com20efde72011-05-09 17:00:02 +0000686 // alias for join()
687 void growToInclude(const SkRect& r) { this->join(r); }
688
reed@google.comaf8edcc2011-05-12 22:31:01 +0000689 /**
690 * Grow the rect to include the specified (x,y). After this call, the
691 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
692 *
693 * This is close, but not quite the same contract as contains(), since
694 * contains() treats the left and top different from the right and bottom.
695 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
696 * that contains(x,y) always returns false if the rect is empty.
697 */
reed@google.com20efde72011-05-09 17:00:02 +0000698 void growToInclude(SkScalar x, SkScalar y) {
699 fLeft = SkMinScalar(x, fLeft);
bsalomon@google.comee9aa302011-05-09 22:32:52 +0000700 fRight = SkMaxScalar(x, fRight);
701 fTop = SkMinScalar(y, fTop);
reed@google.com20efde72011-05-09 17:00:02 +0000702 fBottom = SkMaxScalar(y, fBottom);
703 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000704
commit-bot@chromium.orgb8bd6cb2013-09-03 14:56:17 +0000705 /** Bulk version of growToInclude */
706 void growToInclude(const SkPoint pts[], int count) {
707 this->growToInclude(pts, sizeof(SkPoint), count);
708 }
709
710 /** Bulk version of growToInclude with stride. */
711 void growToInclude(const SkPoint pts[], size_t stride, int count) {
712 SkASSERT(count >= 0);
713 SkASSERT(stride >= sizeof(SkPoint));
714 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
715 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
716 this->growToInclude(pts->fX, pts->fY);
717 }
718 }
719
reed@google.comaf8edcc2011-05-12 22:31:01 +0000720 /**
721 * Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
722 * is not empty.
723 *
724 * Contains treats the left and top differently from the right and bottom.
725 * The left and top coordinates of the rectangle are themselves considered
726 * to be inside, while the right and bottom are not. Thus for the rectangle
727 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
728 */
reed@android.comda6fb322010-02-19 21:41:30 +0000729 bool contains(const SkPoint& p) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000730 return !this->isEmpty() &&
731 fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 }
733
reed@google.comaf8edcc2011-05-12 22:31:01 +0000734 /**
735 * Returns true if (x,y) is inside the rectangle, and the rectangle
736 * is not empty.
737 *
738 * Contains treats the left and top differently from the right and bottom.
739 * The left and top coordinates of the rectangle are themselves considered
740 * to be inside, while the right and bottom are not. Thus for the rectangle
741 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
742 */
reed@android.comda6fb322010-02-19 21:41:30 +0000743 bool contains(SkScalar x, SkScalar y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 return !this->isEmpty() &&
reed@google.comaf8edcc2011-05-12 22:31:01 +0000745 fLeft <= x && x < fRight && fTop <= y && y < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 }
747
reed@google.comaf8edcc2011-05-12 22:31:01 +0000748 /**
749 * Return true if this rectangle contains r, and if both rectangles are
750 * not empty.
751 */
reed@android.comda6fb322010-02-19 21:41:30 +0000752 bool contains(const SkRect& r) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000753 return !r.isEmpty() && !this->isEmpty() &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 fLeft <= r.fLeft && fTop <= r.fTop &&
755 fRight >= r.fRight && fBottom >= r.fBottom;
756 }
757
reed@google.comaf8edcc2011-05-12 22:31:01 +0000758 /**
759 * Set the dst rectangle by rounding this rectangle's coordinates to their
760 * nearest integer values using SkScalarRound.
761 */
reed@android.comda6fb322010-02-19 21:41:30 +0000762 void round(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000763 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000764 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
765 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766 }
767
reed@google.comaf8edcc2011-05-12 22:31:01 +0000768 /**
769 * Set the dst rectangle by rounding "out" this rectangle, choosing the
770 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
771 */
reed@android.comda6fb322010-02-19 21:41:30 +0000772 void roundOut(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000774 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
775 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 }
777
reed@google.comaf8edcc2011-05-12 22:31:01 +0000778 /**
reed@google.com099d22d2011-08-01 20:53:19 +0000779 * Expand this rectangle by rounding its coordinates "out", choosing the
780 * floor of top and left, and the ceil of right and bottom. If this rect
781 * is already on integer coordinates, then it will be unchanged.
782 */
783 void roundOut() {
784 this->set(SkScalarFloorToScalar(fLeft),
785 SkScalarFloorToScalar(fTop),
786 SkScalarCeilToScalar(fRight),
787 SkScalarCeilToScalar(fBottom));
788 }
789
790 /**
reed@google.com4d303832012-11-06 22:02:51 +0000791 * Set the dst rectangle by rounding "in" this rectangle, choosing the
792 * ceil of top and left, and the floor of right and bottom. This does *not*
793 * call sort(), so it is possible that the resulting rect is inverted...
794 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
795 */
796 void roundIn(SkIRect* dst) const {
797 SkASSERT(dst);
798 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
799 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
800 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000801
802
reed@google.com4d303832012-11-06 22:02:51 +0000803 /**
reed@google.comaf8edcc2011-05-12 22:31:01 +0000804 * Swap top/bottom or left/right if there are flipped (i.e. if width()
805 * or height() would have returned a negative value.) This should be called
806 * if the edges are computed separately, and may have crossed over each
807 * other. When this returns, left <= right && top <= bottom
808 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 void sort();
skia.committer@gmail.comeed625d2013-03-09 07:01:15 +0000810
reed@google.comc7d0ea32013-03-08 16:07:54 +0000811 /**
812 * cast-safe way to treat the rect as an array of (4) SkScalars.
813 */
814 const SkScalar* asScalars() const { return &fLeft; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815};
816
817#endif