blob: b2f515129d05bf9a0cca392f880f0d6c75d22e25 [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
robertphillips@google.com5d640682012-05-10 14:50:44 +000029 static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
reed@android.com097a3512010-07-13 18:35:14 +000030 SkIRect r;
31 r.set(0, 0, w, h);
32 return r;
33 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000034
robertphillips@google.com5d640682012-05-10 14:50:44 +000035 static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
reed@android.com097a3512010-07-13 18:35:14 +000036 SkIRect r;
37 r.set(0, 0, size.width(), size.height());
38 return r;
39 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000040
robertphillips@google.com5d640682012-05-10 14:50:44 +000041 static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
reed@android.com097a3512010-07-13 18:35:14 +000042 SkIRect rect;
43 rect.set(l, t, r, b);
44 return rect;
45 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000046
robertphillips@google.com5d640682012-05-10 14:50:44 +000047 static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
reed@android.com097a3512010-07-13 18:35:14 +000048 SkIRect r;
49 r.set(x, y, x + w, y + h);
50 return r;
51 }
reed@google.comc74ab182011-10-03 20:27:14 +000052
53 int left() const { return fLeft; }
54 int top() const { return fTop; }
55 int right() const { return fRight; }
56 int bottom() const { return fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000057
reed@google.comc74ab182011-10-03 20:27:14 +000058 /** return the left edge of the rect */
59 int x() const { return fLeft; }
60 /** return the top edge of the rect */
61 int y() const { return fTop; }
62 /**
63 * Returns the rectangle's width. This does not check for a valid rect
64 * (i.e. left <= right) so the result may be negative.
65 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 int width() const { return fRight - fLeft; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000067
reed@google.comc74ab182011-10-03 20:27:14 +000068 /**
69 * Returns the rectangle's height. This does not check for a valid rect
70 * (i.e. top <= bottom) so the result may be negative.
71 */
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 int height() const { return fBottom - fTop; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000073
reed@google.comc74ab182011-10-03 20:27:14 +000074 /**
reed@google.com6898d522012-11-08 22:36:19 +000075 * Since the center of an integer rect may fall on a factional value, this
76 * method is defined to return (right + left) >> 1.
77 *
78 * This is a specific "truncation" of the average, which is different than
79 * (right + left) / 2 when the sum is negative.
80 */
81 int centerX() const { return (fRight + fLeft) >> 1; }
skia.committer@gmail.comd9f75032012-11-09 02:01:24 +000082
reed@google.com6898d522012-11-08 22:36:19 +000083 /**
84 * Since the center of an integer rect may fall on a factional value, this
85 * method is defined to return (bottom + top) >> 1
86 *
87 * This is a specific "truncation" of the average, which is different than
88 * (bottom + top) / 2 when the sum is negative.
89 */
90 int centerY() const { return (fBottom + fTop) >> 1; }
skia.committer@gmail.comd9f75032012-11-09 02:01:24 +000091
reed@google.com6898d522012-11-08 22:36:19 +000092 /**
reed@google.comc74ab182011-10-03 20:27:14 +000093 * Return true if the rectangle's width or height are <= 0
94 */
95 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000096
reed@google.comb530ef52011-07-20 19:55:42 +000097 friend bool operator==(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 return !memcmp(&a, &b, sizeof(a));
99 }
reed@android.comda6fb322010-02-19 21:41:30 +0000100
reed@google.comb530ef52011-07-20 19:55:42 +0000101 friend bool operator!=(const SkIRect& a, const SkIRect& b) {
102 return !(a == b);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 }
104
reed@android.comd4577752009-11-21 02:48:11 +0000105 bool is16Bit() const {
106 return SkIsS16(fLeft) && SkIsS16(fTop) &&
107 SkIsS16(fRight) && SkIsS16(fBottom);
108 }
109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 /** Set the rectangle to (0,0,0,0)
111 */
112 void setEmpty() { memset(this, 0, sizeof(*this)); }
113
reed@android.comda6fb322010-02-19 21:41:30 +0000114 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 fLeft = left;
116 fTop = top;
117 fRight = right;
118 fBottom = bottom;
119 }
reed@google.com20efde72011-05-09 17:00:02 +0000120 // alias for set(l, t, r, b)
121 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
122 this->set(left, top, right, bottom);
123 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124
reed@google.com1d12b1f2011-03-03 13:23:35 +0000125 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
126 fLeft = x;
127 fTop = y;
128 fRight = x + width;
129 fBottom = y + height;
130 }
reed@google.com20efde72011-05-09 17:00:02 +0000131
132 /**
133 * Make the largest representable rectangle
134 */
135 void setLargest() {
136 fLeft = fTop = SK_MinS32;
137 fRight = fBottom = SK_MaxS32;
138 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000139
reed@google.com20efde72011-05-09 17:00:02 +0000140 /**
141 * Make the largest representable rectangle, but inverted (e.g. fLeft will
142 * be max 32bit and right will be min 32bit).
143 */
144 void setLargestInverted() {
145 fLeft = fTop = SK_MaxS32;
146 fRight = fBottom = SK_MinS32;
147 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149 /** Offset set the rectangle by adding dx to its left and right,
150 and adding dy to its top and bottom.
151 */
reed@android.comda6fb322010-02-19 21:41:30 +0000152 void offset(int32_t dx, int32_t dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 fLeft += dx;
154 fTop += dy;
155 fRight += dx;
156 fBottom += dy;
157 }
158
reed@android.comda6fb322010-02-19 21:41:30 +0000159 void offset(const SkIPoint& delta) {
160 this->offset(delta.fX, delta.fY);
161 }
162
reed@google.com4d303832012-11-06 22:02:51 +0000163 /**
164 * Offset this rect such its new x() and y() will equal newX and newY.
165 */
166 void offsetTo(int32_t newX, int32_t newY) {
167 fRight += newX - fLeft;
168 fBottom += newY - fTop;
169 fLeft = newX;
170 fTop = newY;
171 }
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
174 making the rectangle narrower. If dx is negative, then the sides are moved outwards,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000175 making the rectangle wider. The same holds true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 */
reed@android.comda6fb322010-02-19 21:41:30 +0000177 void inset(int32_t dx, int32_t dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 fLeft += dx;
179 fTop += dy;
180 fRight -= dx;
181 fBottom -= dy;
182 }
reed@android.comda6fb322010-02-19 21:41:30 +0000183
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000184 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
185 moved outwards, making the rectangle wider. If dx is negative, then the
186 sides are moved inwards, making the rectangle narrower. The same holds
187 true for dy and the top and bottom.
188 */
189 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); }
190
reed@google.com20efde72011-05-09 17:00:02 +0000191 bool quickReject(int l, int t, int r, int b) const {
192 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
193 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000194
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 /** Returns true if (x,y) is inside the rectangle and the rectangle is not
196 empty. The left and top are considered to be inside, while the right
197 and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
198 points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
199 */
reed@android.comda6fb322010-02-19 21:41:30 +0000200 bool contains(int32_t x, int32_t y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
202 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
203 }
204
205 /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
206 If either rectangle is empty, contains() returns false.
207 */
reed@android.comda6fb322010-02-19 21:41:30 +0000208 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 return left < right && top < bottom && !this->isEmpty() && // check for empties
210 fLeft <= left && fTop <= top &&
211 fRight >= right && fBottom >= bottom;
212 }
213
214 /** Returns true if the specified rectangle r is inside or equal to this rectangle.
215 */
reed@android.comda6fb322010-02-19 21:41:30 +0000216 bool contains(const SkIRect& r) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 return !r.isEmpty() && !this->isEmpty() && // check for empties
218 fLeft <= r.fLeft && fTop <= r.fTop &&
219 fRight >= r.fRight && fBottom >= r.fBottom;
220 }
221
222 /** Return true if this rectangle contains the specified rectangle.
robertphillips@google.com5d640682012-05-10 14:50:44 +0000223 For speed, this method does not check if either this or the specified
224 rectangles are empty, and if either is, its return value is undefined.
225 In the debugging build however, we assert that both this and the
226 specified rectangles are non-empty.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 */
228 bool containsNoEmptyCheck(int32_t left, int32_t top,
robertphillips@google.com5d640682012-05-10 14:50:44 +0000229 int32_t right, int32_t bottom) const {
230 SkASSERT(fLeft < fRight && fTop < fBottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 SkASSERT(left < right && top < bottom);
232
233 return fLeft <= left && fTop <= top &&
robertphillips@google.com5d640682012-05-10 14:50:44 +0000234 fRight >= right && fBottom >= bottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000236
reed@google.com0d102802012-05-31 18:28:59 +0000237 bool containsNoEmptyCheck(const SkIRect& r) const {
238 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
239 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000240
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 /** If r intersects this rectangle, return true and set this rectangle to that
242 intersection, otherwise return false and do not change this rectangle.
243 If either rectangle is empty, do nothing and return false.
244 */
reed@android.comda6fb322010-02-19 21:41:30 +0000245 bool intersect(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 SkASSERT(&r);
247 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
248 }
249
250 /** If rectangles a and b intersect, return true and set this rectangle to
251 that intersection, otherwise return false and do not change this
252 rectangle. If either rectangle is empty, do nothing and return false.
253 */
reed@android.comda6fb322010-02-19 21:41:30 +0000254 bool intersect(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 SkASSERT(&a && &b);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 if (!a.isEmpty() && !b.isEmpty() &&
reed@android.comda6fb322010-02-19 21:41:30 +0000258 a.fLeft < b.fRight && b.fLeft < a.fRight &&
259 a.fTop < b.fBottom && b.fTop < a.fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 fLeft = SkMax32(a.fLeft, b.fLeft);
261 fTop = SkMax32(a.fTop, b.fTop);
262 fRight = SkMin32(a.fRight, b.fRight);
263 fBottom = SkMin32(a.fBottom, b.fBottom);
264 return true;
265 }
266 return false;
267 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 /** If rectangles a and b intersect, return true and set this rectangle to
270 that intersection, otherwise return false and do not change this
271 rectangle. For speed, no check to see if a or b are empty is performed.
272 If either is, then the return result is undefined. In the debug build,
273 we assert that both rectangles are non-empty.
274 */
reed@android.comda6fb322010-02-19 21:41:30 +0000275 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 SkASSERT(&a && &b);
277 SkASSERT(!a.isEmpty() && !b.isEmpty());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000278
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
reed@android.comda6fb322010-02-19 21:41:30 +0000280 a.fTop < b.fBottom && b.fTop < a.fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fLeft = SkMax32(a.fLeft, b.fLeft);
282 fTop = SkMax32(a.fTop, b.fTop);
283 fRight = SkMin32(a.fRight, b.fRight);
284 fBottom = SkMin32(a.fBottom, b.fBottom);
285 return true;
286 }
287 return false;
288 }
289
290 /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
291 return true and set this rectangle to that intersection,
292 otherwise return false and do not change this rectangle.
293 If either rectangle is empty, do nothing and return false.
294 */
reed@android.comda6fb322010-02-19 21:41:30 +0000295 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 if (left < right && top < bottom && !this->isEmpty() &&
reed@android.comda6fb322010-02-19 21:41:30 +0000297 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 if (fLeft < left) fLeft = left;
299 if (fTop < top) fTop = top;
300 if (fRight > right) fRight = right;
301 if (fBottom > bottom) fBottom = bottom;
302 return true;
303 }
304 return false;
305 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 /** Returns true if a and b are not empty, and they intersect
reed@google.com0d102802012-05-31 18:28:59 +0000308 */
reed@android.comda6fb322010-02-19 21:41:30 +0000309 static bool Intersects(const SkIRect& a, const SkIRect& b) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 return !a.isEmpty() && !b.isEmpty() && // check for empties
reed@google.com0d102802012-05-31 18:28:59 +0000311 a.fLeft < b.fRight && b.fLeft < a.fRight &&
312 a.fTop < b.fBottom && b.fTop < a.fBottom;
313 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000314
reed@google.com0d102802012-05-31 18:28:59 +0000315 /**
316 * Returns true if a and b intersect. debug-asserts that neither are empty.
317 */
318 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
319 SkASSERT(!a.isEmpty());
320 SkASSERT(!b.isEmpty());
321 return a.fLeft < b.fRight && b.fLeft < a.fRight &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 a.fTop < b.fBottom && b.fTop < a.fBottom;
323 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000324
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 /** Update this rectangle to enclose itself and the specified rectangle.
326 If this rectangle is empty, just set it to the specified rectangle. If the specified
327 rectangle is empty, do nothing.
328 */
329 void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
330
331 /** Update this rectangle to enclose itself and the specified rectangle.
332 If this rectangle is empty, just set it to the specified rectangle. If the specified
333 rectangle is empty, do nothing.
334 */
reed@android.comda6fb322010-02-19 21:41:30 +0000335 void join(const SkIRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000336 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
337 }
338
339 /** Swap top/bottom or left/right if there are flipped.
340 This can be called if the edges are computed separately,
341 and may have crossed over each other.
342 When this returns, left <= right && top <= bottom
343 */
344 void sort();
reed@google.com20efde72011-05-09 17:00:02 +0000345
robertphillips@google.com5d640682012-05-10 14:50:44 +0000346 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
reed@google.com4b4fb3a2011-05-16 18:24:19 +0000347 static const SkIRect gEmpty = { 0, 0, 0, 0 };
reed@google.com20efde72011-05-09 17:00:02 +0000348 return gEmpty;
349 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350};
351
352/** \struct SkRect
353*/
ctguil@chromium.org7ffb1b22011-03-15 21:27:08 +0000354struct SK_API SkRect {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 SkScalar fLeft, fTop, fRight, fBottom;
356
robertphillips@google.com5d640682012-05-10 14:50:44 +0000357 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
reed@android.com2687ae02010-04-12 21:21:59 +0000358 SkRect r;
359 r.setEmpty();
360 return r;
361 }
362
robertphillips@google.com5d640682012-05-10 14:50:44 +0000363 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
reed@android.com2687ae02010-04-12 21:21:59 +0000364 SkRect r;
365 r.set(0, 0, w, h);
366 return r;
367 }
368
robertphillips@google.com5d640682012-05-10 14:50:44 +0000369 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
reed@android.com233481e2010-02-24 01:49:13 +0000370 SkRect r;
371 r.set(0, 0, size.width(), size.height());
372 return r;
373 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000374
robertphillips@google.com5d640682012-05-10 14:50:44 +0000375 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
reed@android.com233481e2010-02-24 01:49:13 +0000376 SkRect rect;
377 rect.set(l, t, r, b);
378 return rect;
379 }
380
robertphillips@google.com5d640682012-05-10 14:50:44 +0000381 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
reed@android.com233481e2010-02-24 01:49:13 +0000382 SkRect r;
383 r.set(x, y, x + w, y + h);
384 return r;
385 }
reed@android.com2687ae02010-04-12 21:21:59 +0000386
reed@google.com1d6cff72013-01-23 15:37:56 +0000387 // DEPRECATED: call Make(r)
bsalomon@google.com7b7cdd12012-11-07 16:17:24 +0000388 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
389 SkRect r;
390 r.set(SkIntToScalar(irect.fLeft),
391 SkIntToScalar(irect.fTop),
392 SkIntToScalar(irect.fRight),
393 SkIntToScalar(irect.fBottom));
394 return r;
395 }
skia.committer@gmail.com43a6b6a2013-01-24 07:06:13 +0000396
reed@google.com1d6cff72013-01-23 15:37:56 +0000397 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
398 SkRect r;
399 r.set(SkIntToScalar(irect.fLeft),
400 SkIntToScalar(irect.fTop),
401 SkIntToScalar(irect.fRight),
402 SkIntToScalar(irect.fBottom));
403 return r;
404 }
skia.committer@gmail.com43a6b6a2013-01-24 07:06:13 +0000405
reed@google.com16078632011-12-06 18:56:37 +0000406 /**
407 * Return true if the rectangle's width or height are <= 0
408 */
409 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000410
reed@google.com16078632011-12-06 18:56:37 +0000411 /**
412 * Returns true iff all values in the rect are finite. If any are
413 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
414 * returns false.
415 */
416 bool isFinite() const {
417#ifdef SK_SCALAR_IS_FLOAT
reed@google.com7b463ac2012-05-16 13:35:36 +0000418 float accum = 0;
419 accum *= fLeft;
420 accum *= fTop;
421 accum *= fRight;
422 accum *= fBottom;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000423
reed@google.com7b463ac2012-05-16 13:35:36 +0000424 // accum is either NaN or it is finite (zero).
425 SkASSERT(0 == accum || !(accum == accum));
426
reed@google.com16078632011-12-06 18:56:37 +0000427 // value==value will be true iff value is not NaN
reed@google.com7b463ac2012-05-16 13:35:36 +0000428 // TODO: is it faster to say !accum or accum==accum?
429 return accum == accum;
reed@google.com16078632011-12-06 18:56:37 +0000430#else
431 // use bit-or for speed, since we don't care about short-circuting the
432 // tests, and we expect the common case will be that we need to check all.
433 int isNaN = (SK_FixedNaN == fLeft) | (SK_FixedNaN == fTop) |
434 (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
435 return !isNaN;
436#endif
437 }
438
reed@google.comd6195f92012-04-19 21:01:24 +0000439 SkScalar x() const { return fLeft; }
440 SkScalar y() const { return fTop; }
reed@google.com20efde72011-05-09 17:00:02 +0000441 SkScalar left() const { return fLeft; }
442 SkScalar top() const { return fTop; }
443 SkScalar right() const { return fRight; }
444 SkScalar bottom() const { return fBottom; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 SkScalar width() const { return fRight - fLeft; }
446 SkScalar height() const { return fBottom - fTop; }
447 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
448 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
449
bsalomon@google.come174ea42011-09-01 15:34:14 +0000450 friend bool operator==(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000451 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 }
reed@android.comda6fb322010-02-19 21:41:30 +0000453
bsalomon@google.come174ea42011-09-01 15:34:14 +0000454 friend bool operator!=(const SkRect& a, const SkRect& b) {
reed@google.come1e7d7a2012-10-31 19:59:23 +0000455 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 }
457
458 /** return the 4 points that enclose the rectangle
459 */
460 void toQuad(SkPoint quad[4]) const;
461
462 /** Set this rectangle to the empty rectangle (0,0,0,0)
463 */
464 void setEmpty() { memset(this, 0, sizeof(*this)); }
465
reed@android.comda6fb322010-02-19 21:41:30 +0000466 void set(const SkIRect& src) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 fLeft = SkIntToScalar(src.fLeft);
468 fTop = SkIntToScalar(src.fTop);
469 fRight = SkIntToScalar(src.fRight);
470 fBottom = SkIntToScalar(src.fBottom);
471 }
472
reed@android.comda6fb322010-02-19 21:41:30 +0000473 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 fLeft = left;
475 fTop = top;
476 fRight = right;
477 fBottom = bottom;
478 }
reed@google.com20efde72011-05-09 17:00:02 +0000479 // alias for set(l, t, r, b)
480 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
481 this->set(left, top, right, bottom);
482 }
483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 /** Initialize the rect with the 4 specified integers. The routine handles
485 converting them to scalars (by calling SkIntToScalar)
486 */
487 void iset(int left, int top, int right, int bottom) {
488 fLeft = SkIntToScalar(left);
489 fTop = SkIntToScalar(top);
490 fRight = SkIntToScalar(right);
491 fBottom = SkIntToScalar(bottom);
492 }
493
reed@google.com6f547242013-01-16 15:15:24 +0000494 /**
495 * Set this rectangle to be left/top at 0,0, and have the specified width
496 * and height (automatically converted to SkScalar).
497 */
498 void isetWH(int width, int height) {
499 fLeft = fTop = 0;
500 fRight = SkIntToScalar(width);
501 fBottom = SkIntToScalar(height);
502 }
skia.committer@gmail.com4d28d982013-01-17 07:06:06 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 /** Set this rectangle to be the bounds of the array of points.
505 If the array is empty (count == 0), then set this rectangle
506 to the empty rectangle (0,0,0,0)
507 */
reed@google.com0bb18bb2012-07-26 15:20:36 +0000508 void set(const SkPoint pts[], int count) {
509 // set() had been checking for non-finite values, so keep that behavior
510 // for now. Now that we have setBoundsCheck(), we may decide to make
511 // set() be simpler/faster, and not check for those.
512 (void)this->setBoundsCheck(pts, count);
513 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514
reed@google.com20efde72011-05-09 17:00:02 +0000515 // alias for set(pts, count)
516 void setBounds(const SkPoint pts[], int count) {
reed@google.com0bb18bb2012-07-26 15:20:36 +0000517 (void)this->setBoundsCheck(pts, count);
reed@google.com20efde72011-05-09 17:00:02 +0000518 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000519
reed@google.com0bb18bb2012-07-26 15:20:36 +0000520 /**
521 * Compute the bounds of the array of points, and set this rect to that
522 * bounds and return true... unless a non-finite value is encountered,
523 * in which case this rect is set to empty and false is returned.
524 */
525 bool setBoundsCheck(const SkPoint pts[], int count);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000526
reed@google.com7b463ac2012-05-16 13:35:36 +0000527 void set(const SkPoint& p0, const SkPoint& p1) {
528 fLeft = SkMinScalar(p0.fX, p1.fX);
529 fRight = SkMaxScalar(p0.fX, p1.fX);
530 fTop = SkMinScalar(p0.fY, p1.fY);
531 fBottom = SkMaxScalar(p0.fY, p1.fY);
532 }
533
reed@google.com1d12b1f2011-03-03 13:23:35 +0000534 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
535 fLeft = x;
536 fTop = y;
537 fRight = x + width;
538 fBottom = y + height;
539 }
540
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000541 void setWH(SkScalar width, SkScalar height) {
542 fLeft = 0;
543 fTop = 0;
544 fRight = width;
545 fBottom = height;
546 }
547
reed@google.com20efde72011-05-09 17:00:02 +0000548 /**
549 * Make the largest representable rectangle
550 */
551 void setLargest() {
552 fLeft = fTop = SK_ScalarMin;
553 fRight = fBottom = SK_ScalarMax;
554 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000555
reed@google.com20efde72011-05-09 17:00:02 +0000556 /**
557 * Make the largest representable rectangle, but inverted (e.g. fLeft will
558 * be max and right will be min).
559 */
560 void setLargestInverted() {
561 fLeft = fTop = SK_ScalarMax;
562 fRight = fBottom = SK_ScalarMin;
563 }
564
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 /** Offset set the rectangle by adding dx to its left and right,
566 and adding dy to its top and bottom.
567 */
reed@android.comda6fb322010-02-19 21:41:30 +0000568 void offset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569 fLeft += dx;
570 fTop += dy;
571 fRight += dx;
572 fBottom += dy;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000573 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574
reed@android.comda6fb322010-02-19 21:41:30 +0000575 void offset(const SkPoint& delta) {
576 this->offset(delta.fX, delta.fY);
577 }
578
reed@google.com4d303832012-11-06 22:02:51 +0000579 /**
580 * Offset this rect such its new x() and y() will equal newX and newY.
581 */
582 void offsetTo(SkScalar newX, SkScalar newY) {
583 fRight += newX - fLeft;
584 fBottom += newY - fTop;
585 fLeft = newX;
586 fTop = newY;
587 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000588
bsalomon@google.com647a8042011-08-23 14:39:01 +0000589 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
590 moved inwards, making the rectangle narrower. If dx is negative, then
591 the sides are moved outwards, making the rectangle wider. The same holds
592 true for dy and the top and bottom.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 */
reed@android.comda6fb322010-02-19 21:41:30 +0000594 void inset(SkScalar dx, SkScalar dy) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 fLeft += dx;
596 fTop += dy;
597 fRight -= dx;
598 fBottom -= dy;
599 }
600
bsalomon@google.com647a8042011-08-23 14:39:01 +0000601 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
602 moved outwards, making the rectangle wider. If dx is negative, then the
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000603 sides are moved inwards, making the rectangle narrower. The same holds
bsalomon@google.com647a8042011-08-23 14:39:01 +0000604 true for dy and the top and bottom.
605 */
606 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 /** If this rectangle intersects r, return true and set this rectangle to that
609 intersection, otherwise return false and do not change this rectangle.
610 If either rectangle is empty, do nothing and return false.
611 */
612 bool intersect(const SkRect& r);
613
614 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
615 return true and set this rectangle to that intersection, otherwise return false
616 and do not change this rectangle.
617 If either rectangle is empty, do nothing and return false.
618 */
619 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
620
reed@google.comaf8edcc2011-05-12 22:31:01 +0000621 /**
622 * Return true if this rectangle is not empty, and the specified sides of
623 * a rectangle are not empty, and they intersect.
624 */
reed@android.comda6fb322010-02-19 21:41:30 +0000625 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 return // first check that both are not empty
627 left < right && top < bottom &&
628 fLeft < fRight && fTop < fBottom &&
629 // now check for intersection
630 fLeft < right && left < fRight &&
631 fTop < bottom && top < fBottom;
632 }
reed@google.comf0f617a2011-10-21 14:05:06 +0000633
634 /** If rectangles a and b intersect, return true and set this rectangle to
635 * that intersection, otherwise return false and do not change this
636 * rectangle. If either rectangle is empty, do nothing and return false.
637 */
638 bool intersect(const SkRect& a, const SkRect& b);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000639
reed@google.comaf8edcc2011-05-12 22:31:01 +0000640 /**
641 * Return true if rectangles a and b are not empty and intersect.
642 */
reed@android.comda6fb322010-02-19 21:41:30 +0000643 static bool Intersects(const SkRect& a, const SkRect& b) {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000644 return !a.isEmpty() && !b.isEmpty() &&
645 a.fLeft < b.fRight && b.fLeft < a.fRight &&
646 a.fTop < b.fBottom && b.fTop < a.fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000648
reed@google.comaf8edcc2011-05-12 22:31:01 +0000649 /**
650 * Update this rectangle to enclose itself and the specified rectangle.
651 * If this rectangle is empty, just set it to the specified rectangle.
652 * If the specified rectangle is empty, do nothing.
653 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
655
656 /** Update this rectangle to enclose itself and the specified rectangle.
657 If this rectangle is empty, just set it to the specified rectangle. If the specified
658 rectangle is empty, do nothing.
659 */
reed@android.comda6fb322010-02-19 21:41:30 +0000660 void join(const SkRect& r) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
662 }
reed@google.com20efde72011-05-09 17:00:02 +0000663 // alias for join()
664 void growToInclude(const SkRect& r) { this->join(r); }
665
reed@google.comaf8edcc2011-05-12 22:31:01 +0000666 /**
667 * Grow the rect to include the specified (x,y). After this call, the
668 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
669 *
670 * This is close, but not quite the same contract as contains(), since
671 * contains() treats the left and top different from the right and bottom.
672 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
673 * that contains(x,y) always returns false if the rect is empty.
674 */
reed@google.com20efde72011-05-09 17:00:02 +0000675 void growToInclude(SkScalar x, SkScalar y) {
676 fLeft = SkMinScalar(x, fLeft);
bsalomon@google.comee9aa302011-05-09 22:32:52 +0000677 fRight = SkMaxScalar(x, fRight);
678 fTop = SkMinScalar(y, fTop);
reed@google.com20efde72011-05-09 17:00:02 +0000679 fBottom = SkMaxScalar(y, fBottom);
680 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000681
reed@google.comaf8edcc2011-05-12 22:31:01 +0000682 /**
683 * Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
684 * is not empty.
685 *
686 * Contains treats the left and top differently from the right and bottom.
687 * The left and top coordinates of the rectangle are themselves considered
688 * to be inside, while the right and bottom are not. Thus for the rectangle
689 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
690 */
reed@android.comda6fb322010-02-19 21:41:30 +0000691 bool contains(const SkPoint& p) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000692 return !this->isEmpty() &&
693 fLeft <= p.fX && p.fX < fRight && fTop <= p.fY && p.fY < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 }
695
reed@google.comaf8edcc2011-05-12 22:31:01 +0000696 /**
697 * Returns true if (x,y) is inside the rectangle, and the rectangle
698 * is not empty.
699 *
700 * Contains treats the left and top differently from the right and bottom.
701 * The left and top coordinates of the rectangle are themselves considered
702 * to be inside, while the right and bottom are not. Thus for the rectangle
703 * {0, 0, 5, 10}, (0,0) is contained, but (0,10), (5,0) and (5,10) are not.
704 */
reed@android.comda6fb322010-02-19 21:41:30 +0000705 bool contains(SkScalar x, SkScalar y) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 return !this->isEmpty() &&
reed@google.comaf8edcc2011-05-12 22:31:01 +0000707 fLeft <= x && x < fRight && fTop <= y && y < fBottom;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 }
709
reed@google.comaf8edcc2011-05-12 22:31:01 +0000710 /**
711 * Return true if this rectangle contains r, and if both rectangles are
712 * not empty.
713 */
reed@android.comda6fb322010-02-19 21:41:30 +0000714 bool contains(const SkRect& r) const {
reed@google.comaf8edcc2011-05-12 22:31:01 +0000715 return !r.isEmpty() && !this->isEmpty() &&
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 fLeft <= r.fLeft && fTop <= r.fTop &&
717 fRight >= r.fRight && fBottom >= r.fBottom;
718 }
719
reed@google.comaf8edcc2011-05-12 22:31:01 +0000720 /**
721 * Set the dst rectangle by rounding this rectangle's coordinates to their
722 * nearest integer values using SkScalarRound.
723 */
reed@android.comda6fb322010-02-19 21:41:30 +0000724 void round(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000726 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
727 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 }
729
reed@google.comaf8edcc2011-05-12 22:31:01 +0000730 /**
731 * Set the dst rectangle by rounding "out" this rectangle, choosing the
732 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
733 */
reed@android.comda6fb322010-02-19 21:41:30 +0000734 void roundOut(SkIRect* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 SkASSERT(dst);
reed@google.com4d303832012-11-06 22:02:51 +0000736 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
737 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 }
739
reed@google.comaf8edcc2011-05-12 22:31:01 +0000740 /**
reed@google.com099d22d2011-08-01 20:53:19 +0000741 * Expand this rectangle by rounding its coordinates "out", choosing the
742 * floor of top and left, and the ceil of right and bottom. If this rect
743 * is already on integer coordinates, then it will be unchanged.
744 */
745 void roundOut() {
746 this->set(SkScalarFloorToScalar(fLeft),
747 SkScalarFloorToScalar(fTop),
748 SkScalarCeilToScalar(fRight),
749 SkScalarCeilToScalar(fBottom));
750 }
751
752 /**
reed@google.com4d303832012-11-06 22:02:51 +0000753 * Set the dst rectangle by rounding "in" this rectangle, choosing the
754 * ceil of top and left, and the floor of right and bottom. This does *not*
755 * call sort(), so it is possible that the resulting rect is inverted...
756 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
757 */
758 void roundIn(SkIRect* dst) const {
759 SkASSERT(dst);
760 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
761 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
762 }
skia.committer@gmail.com72b2e6f2012-11-08 02:03:56 +0000763
764
reed@google.com4d303832012-11-06 22:02:51 +0000765 /**
reed@google.comaf8edcc2011-05-12 22:31:01 +0000766 * Swap top/bottom or left/right if there are flipped (i.e. if width()
767 * or height() would have returned a negative value.) This should be called
768 * if the edges are computed separately, and may have crossed over each
769 * other. When this returns, left <= right && top <= bottom
770 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 void sort();
skia.committer@gmail.comeed625d2013-03-09 07:01:15 +0000772
reed@google.comc7d0ea32013-03-08 16:07:54 +0000773 /**
774 * cast-safe way to treat the rect as an array of (4) SkScalars.
775 */
776 const SkScalar* asScalars() const { return &fLeft; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777};
778
779#endif