blob: 9f3b59a38f7a29936de4dc0c9cc4564ae3472278 [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.orgccf225c2013-07-22 20:03:22 +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.orgccf225c2013-07-22 20:03:22 +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
robertphillips@google.com5d640682012-05-10 14:50:44 +0000374 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
reed@android.com2687ae02010-04-12 21:21:59 +0000375 SkRect r;
376 r.set(0, 0, w, h);
377 return r;
378 }
379
robertphillips@google.com5d640682012-05-10 14:50:44 +0000380 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
reed@android.com233481e2010-02-24 01:49:13 +0000381 SkRect r;
382 r.set(0, 0, size.width(), size.height());
383 return r;
384 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000385
robertphillips@google.com5d640682012-05-10 14:50:44 +0000386 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
reed@android.com233481e2010-02-24 01:49:13 +0000387 SkRect rect;
388 rect.set(l, t, r, b);
389 return rect;
390 }
391
robertphillips@google.com5d640682012-05-10 14:50:44 +0000392 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
reed@android.com233481e2010-02-24 01:49:13 +0000393 SkRect r;
394 r.set(x, y, x + w, y + h);
395 return r;
396 }
reed@android.com2687ae02010-04-12 21:21:59 +0000397
reed@google.com1d6cff72013-01-23 15:37:56 +0000398 // DEPRECATED: call Make(r)
bsalomon@google.com7b7cdd12012-11-07 16:17:24 +0000399 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
400 SkRect r;
401 r.set(SkIntToScalar(irect.fLeft),
402 SkIntToScalar(irect.fTop),
403 SkIntToScalar(irect.fRight),
404 SkIntToScalar(irect.fBottom));
405 return r;
406 }
skia.committer@gmail.com43a6b6a2013-01-24 07:06:13 +0000407
reed@google.com1d6cff72013-01-23 15:37:56 +0000408 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
409 SkRect r;
410 r.set(SkIntToScalar(irect.fLeft),
411 SkIntToScalar(irect.fTop),
412 SkIntToScalar(irect.fRight),
413 SkIntToScalar(irect.fBottom));
414 return r;
415 }
skia.committer@gmail.com43a6b6a2013-01-24 07:06:13 +0000416
reed@google.com16078632011-12-06 18:56:37 +0000417 /**
418 * Return true if the rectangle's width or height are <= 0
419 */
420 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000421
reed@google.com16078632011-12-06 18:56:37 +0000422 /**
423 * Returns true iff all values in the rect are finite. If any are
424 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
425 * returns false.
426 */
427 bool isFinite() const {
428#ifdef SK_SCALAR_IS_FLOAT
reed@google.com7b463ac2012-05-16 13:35:36 +0000429 float accum = 0;
430 accum *= fLeft;
431 accum *= fTop;
432 accum *= fRight;
433 accum *= fBottom;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000434
reed@google.com7b463ac2012-05-16 13:35:36 +0000435 // accum is either NaN or it is finite (zero).
436 SkASSERT(0 == accum || !(accum == accum));
437
reed@google.com16078632011-12-06 18:56:37 +0000438 // value==value will be true iff value is not NaN
reed@google.com7b463ac2012-05-16 13:35:36 +0000439 // TODO: is it faster to say !accum or accum==accum?
440 return accum == accum;
reed@google.com16078632011-12-06 18:56:37 +0000441#else
442 // use bit-or for speed, since we don't care about short-circuting the
443 // tests, and we expect the common case will be that we need to check all.
444 int isNaN = (SK_FixedNaN == fLeft) | (SK_FixedNaN == fTop) |
445 (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
446 return !isNaN;
447#endif
448 }
449
reed@google.comd6195f92012-04-19 21:01:24 +0000450 SkScalar x() const { return fLeft; }
451 SkScalar y() const { return fTop; }
reed@google.com20efde72011-05-09 17:00:02 +0000452 SkScalar left() const { return fLeft; }
453 SkScalar top() const { return fTop; }
454 SkScalar right() const { return fRight; }
455 SkScalar bottom() const { return fBottom; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 SkScalar width() const { return fRight - fLeft; }
457 SkScalar height() const { return fBottom - fTop; }
458 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
459 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
460
bsalomon@google.come174ea42011-09-01 15:34:14 +0000461 friend bool operator==(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000462 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 }
reed@android.comda6fb322010-02-19 21:41:30 +0000464
bsalomon@google.come174ea42011-09-01 15:34:14 +0000465 friend bool operator!=(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000466 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 }
468
469 /** return the 4 points that enclose the rectangle
470 */
471 void toQuad(SkPoint quad[4]) const;
472
473 /** Set this rectangle to the empty rectangle (0,0,0,0)
474 */
475 void setEmpty() { memset(this, 0, sizeof(*this)); }
476
reed@android.comda6fb322010-02-19 21:41:30 +0000477 void set(const SkIRect& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 fLeft = SkIntToScalar(src.fLeft);
479 fTop = SkIntToScalar(src.fTop);
480 fRight = SkIntToScalar(src.fRight);
481 fBottom = SkIntToScalar(src.fBottom);
482 }
483
reed@android.comda6fb322010-02-19 21:41:30 +0000484 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 fLeft = left;
486 fTop = top;
487 fRight = right;
488 fBottom = bottom;
489 }
reed@google.com20efde72011-05-09 17:00:02 +0000490 // alias for set(l, t, r, b)
491 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
492 this->set(left, top, right, bottom);
493 }
494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 /** Initialize the rect with the 4 specified integers. The routine handles
496 converting them to scalars (by calling SkIntToScalar)
497 */
498 void iset(int left, int top, int right, int bottom) {
499 fLeft = SkIntToScalar(left);
500 fTop = SkIntToScalar(top);
501 fRight = SkIntToScalar(right);
502 fBottom = SkIntToScalar(bottom);
503 }
504
reed@google.com6f547242013-01-16 15:15:24 +0000505 /**
506 * Set this rectangle to be left/top at 0,0, and have the specified width
507 * and height (automatically converted to SkScalar).
508 */
509 void isetWH(int width, int height) {
510 fLeft = fTop = 0;
511 fRight = SkIntToScalar(width);
512 fBottom = SkIntToScalar(height);
513 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 /** Set this rectangle to be the bounds of the array of points.
516 If the array is empty (count == 0), then set this rectangle
517 to the empty rectangle (0,0,0,0)
518 */
reed@google.com0bb18bb2012-07-26 15:20:36 +0000519 void set(const SkPoint pts[], int count) {
520 // set() had been checking for non-finite values, so keep that behavior
521 // for now. Now that we have setBoundsCheck(), we may decide to make
522 // set() be simpler/faster, and not check for those.
523 (void)this->setBoundsCheck(pts, count);
524 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525
reed@google.com20efde72011-05-09 17:00:02 +0000526 // alias for set(pts, count)
527 void setBounds(const SkPoint pts[], int count) {
reed@google.com0bb18bb2012-07-26 15:20:36 +0000528 (void)this->setBoundsCheck(pts, count);
reed@google.com20efde72011-05-09 17:00:02 +0000529 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000530
reed@google.com0bb18bb2012-07-26 15:20:36 +0000531 /**
532 * Compute the bounds of the array of points, and set this rect to that
533 * bounds and return true... unless a non-finite value is encountered,
534 * in which case this rect is set to empty and false is returned.
535 */
536 bool setBoundsCheck(const SkPoint pts[], int count);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000537
reed@google.com7b463ac2012-05-16 13:35:36 +0000538 void set(const SkPoint& p0, const SkPoint& p1) {
539 fLeft = SkMinScalar(p0.fX, p1.fX);
540 fRight = SkMaxScalar(p0.fX, p1.fX);
541 fTop = SkMinScalar(p0.fY, p1.fY);
542 fBottom = SkMaxScalar(p0.fY, p1.fY);
543 }
544
reed@google.com1d12b1f2011-03-03 13:23:35 +0000545 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
546 fLeft = x;
547 fTop = y;
548 fRight = x + width;
549 fBottom = y + height;
550 }
551
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000552 void setWH(SkScalar width, SkScalar height) {
553 fLeft = 0;
554 fTop = 0;
555 fRight = width;
556 fBottom = height;
557 }
558
reed@google.com20efde72011-05-09 17:00:02 +0000559 /**
560 * Make the largest representable rectangle
561 */
562 void setLargest() {
563 fLeft = fTop = SK_ScalarMin;
564 fRight = fBottom = SK_ScalarMax;
565 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000566
reed@google.com20efde72011-05-09 17:00:02 +0000567 /**
568 * Make the largest representable rectangle, but inverted (e.g. fLeft will
569 * be max and right will be min).
570 */
571 void setLargestInverted() {
572 fLeft = fTop = SK_ScalarMax;
573 fRight = fBottom = SK_ScalarMin;
574 }
575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 /** Offset set the rectangle by adding dx to its left and right,
577 and adding dy to its top and bottom.
578 */
reed@android.comda6fb322010-02-19 21:41:30 +0000579 void offset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 fLeft += dx;
581 fTop += dy;
582 fRight += dx;
583 fBottom += dy;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585
reed@android.comda6fb322010-02-19 21:41:30 +0000586 void offset(const SkPoint& delta) {
587 this->offset(delta.fX, delta.fY);
588 }
589
reed@google.com4d303832012-11-06 22:02:51 +0000590 /**
591 * Offset this rect such its new x() and y() will equal newX and newY.
592 */
593 void offsetTo(SkScalar newX, SkScalar newY) {
594 fRight += newX - fLeft;
595 fBottom += newY - fTop;
596 fLeft = newX;
597 fTop = newY;
598 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000599
bsalomon@google.com647a8042011-08-23 14:39:01 +0000600 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
601 moved inwards, making the rectangle narrower. If dx is negative, then
602 the sides are moved outwards, making the rectangle wider. The same holds
603 true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 */
reed@android.comda6fb322010-02-19 21:41:30 +0000605 void inset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 fLeft += dx;
607 fTop += dy;
608 fRight -= dx;
609 fBottom -= dy;
610 }
611
bsalomon@google.com647a8042011-08-23 14:39:01 +0000612 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
613 moved outwards, making the rectangle wider. If dx is negative, then the
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000614 sides are moved inwards, making the rectangle narrower. The same holds
bsalomon@google.com647a8042011-08-23 14:39:01 +0000615 true for dy and the top and bottom.
616 */
617 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 /** If this rectangle intersects r, return true and set this rectangle to that
620 intersection, otherwise return false and do not change this rectangle.
621 If either rectangle is empty, do nothing and return false.
622 */
623 bool intersect(const SkRect& r);
624
625 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
626 return true and set this rectangle to that intersection, otherwise return false
627 and do not change this rectangle.
628 If either rectangle is empty, do nothing and return false.
629 */
630 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
631
reed@google.comaf8edcc2011-05-12 22:31:01 +0000632 /**
633 * Return true if this rectangle is not empty, and the specified sides of
634 * a rectangle are not empty, and they intersect.
635 */
reed@android.comda6fb322010-02-19 21:41:30 +0000636 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 return // first check that both are not empty
638 left < right && top < bottom &&
639 fLeft < fRight && fTop < fBottom &&
640 // now check for intersection
641 fLeft < right && left < fRight &&
642 fTop < bottom && top < fBottom;
643 }
reed@google.comf0f617a2011-10-21 14:05:06 +0000644
645 /** If rectangles a and b intersect, return true and set this rectangle to
646 * that intersection, otherwise return false and do not change this
647 * rectangle. If either rectangle is empty, do nothing and return false.
648 */
649 bool intersect(const SkRect& a, const SkRect& b);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000650
reed@google.comaf8edcc2011-05-12 22:31:01 +0000651 /**
652 * Return true if rectangles a and b are not empty and intersect.
653 */
reed@android.comda6fb322010-02-19 21:41:30 +0000654 static bool Intersects(const SkRect& a, const SkRect& b) {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000655 return !a.isEmpty() && !b.isEmpty() &&
656 a.fLeft < b.fRight && b.fLeft < a.fRight &&
657 a.fTop < b.fBottom && b.fTop < a.fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000659
reed@google.comaf8edcc2011-05-12 22:31:01 +0000660 /**
661 * Update this rectangle to enclose itself and the specified rectangle.
662 * If this rectangle is empty, just set it to the specified rectangle.
663 * If the specified rectangle is empty, do nothing.
664 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
666
667 /** Update this rectangle to enclose itself and the specified rectangle.
668 If this rectangle is empty, just set it to the specified rectangle. If the specified
669 rectangle is empty, do nothing.
670 */
reed@android.comda6fb322010-02-19 21:41:30 +0000671 void join(const SkRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
673 }
reed@google.com20efde72011-05-09 17:00:02 +0000674 // alias for join()
675 void growToInclude(const SkRect& r) { this->join(r); }
676
reed@google.comaf8edcc2011-05-12 22:31:01 +0000677 /**
678 * Grow the rect to include the specified (x,y). After this call, the
679 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
680 *
681 * This is close, but not quite the same contract as contains(), since
682 * contains() treats the left and top different from the right and bottom.
683 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
684 * that contains(x,y) always returns false if the rect is empty.
685 */
reed@google.com20efde72011-05-09 17:00:02 +0000686 void growToInclude(SkScalar x, SkScalar y) {
687 fLeft = SkMinScalar(x, fLeft);
bsalomon@google.comee9aa302011-05-09 22:32:52 +0000688 fRight = SkMaxScalar(x, fRight);
689 fTop = SkMinScalar(y, fTop);
reed@google.com20efde72011-05-09 17:00:02 +0000690 fBottom = SkMaxScalar(y, fBottom);
691 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000692
reed@google.comaf8edcc2011-05-12 22:31:01 +0000693 /**
694 * Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
695 * is not empty.
696 *
697 * Contains treats the left and top differently from the right and bottom.
698 * The left and top coordinates of the rectangle are themselves considered
699 * to be inside, while the right and bottom are not. Thus for the rectangle
700 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
701 */
reed@android.comda6fb322010-02-19 21:41:30 +0000702 bool contains(const SkPoint& p) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000703 return !this->isEmpty() &&
704 fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 }
706
reed@google.comaf8edcc2011-05-12 22:31:01 +0000707 /**
708 * Returns true if (x,y) is inside the rectangle, and the rectangle
709 * is not empty.
710 *
711 * Contains treats the left and top differently from the right and bottom.
712 * The left and top coordinates of the rectangle are themselves considered
713 * to be inside, while the right and bottom are not. Thus for the rectangle
714 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
715 */
reed@android.comda6fb322010-02-19 21:41:30 +0000716 bool contains(SkScalar x, SkScalar y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 return !this->isEmpty() &&
reed@google.comaf8edcc2011-05-12 22:31:01 +0000718 fLeft <= x && x < fRight && fTop <= y && y < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 }
720
reed@google.comaf8edcc2011-05-12 22:31:01 +0000721 /**
722 * Return true if this rectangle contains r, and if both rectangles are
723 * not empty.
724 */
reed@android.comda6fb322010-02-19 21:41:30 +0000725 bool contains(const SkRect& r) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000726 return !r.isEmpty() && !this->isEmpty() &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 fLeft <= r.fLeft && fTop <= r.fTop &&
728 fRight >= r.fRight && fBottom >= r.fBottom;
729 }
730
reed@google.comaf8edcc2011-05-12 22:31:01 +0000731 /**
732 * Set the dst rectangle by rounding this rectangle's coordinates to their
733 * nearest integer values using SkScalarRound.
734 */
reed@android.comda6fb322010-02-19 21:41:30 +0000735 void round(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000737 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
738 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 }
740
reed@google.comaf8edcc2011-05-12 22:31:01 +0000741 /**
742 * Set the dst rectangle by rounding "out" this rectangle, choosing the
743 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
744 */
reed@android.comda6fb322010-02-19 21:41:30 +0000745 void roundOut(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000747 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
748 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 }
750
reed@google.comaf8edcc2011-05-12 22:31:01 +0000751 /**
reed@google.com099d22d2011-08-01 20:53:19 +0000752 * Expand this rectangle by rounding its coordinates "out", choosing the
753 * floor of top and left, and the ceil of right and bottom. If this rect
754 * is already on integer coordinates, then it will be unchanged.
755 */
756 void roundOut() {
757 this->set(SkScalarFloorToScalar(fLeft),
758 SkScalarFloorToScalar(fTop),
759 SkScalarCeilToScalar(fRight),
760 SkScalarCeilToScalar(fBottom));
761 }
762
763 /**
reed@google.com4d303832012-11-06 22:02:51 +0000764 * Set the dst rectangle by rounding "in" this rectangle, choosing the
765 * ceil of top and left, and the floor of right and bottom. This does *not*
766 * call sort(), so it is possible that the resulting rect is inverted...
767 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
768 */
769 void roundIn(SkIRect* dst) const {
770 SkASSERT(dst);
771 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
772 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
773 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000774
775
reed@google.com4d303832012-11-06 22:02:51 +0000776 /**
reed@google.comaf8edcc2011-05-12 22:31:01 +0000777 * Swap top/bottom or left/right if there are flipped (i.e. if width()
778 * or height() would have returned a negative value.) This should be called
779 * if the edges are computed separately, and may have crossed over each
780 * other. When this returns, left <= right && top <= bottom
781 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 void sort();
skia.committer@gmail.comeed625d2013-03-09 07:01:15 +0000783
reed@google.comc7d0ea32013-03-08 16:07:54 +0000784 /**
785 * cast-safe way to treat the rect as an array of (4) SkScalars.
786 */
787 const SkScalar* asScalars() const { return &fLeft; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788};
789
790#endif