1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #ifndef SkRect_DEFINED
9 #define SkRect_DEFINED
10
11 #include "SkPoint.h"
12 #include "SkSize.h"
13
14 struct SkRect;
15
16 /** \struct SkIRect
17
18 SkIRect holds four 32 bit integer coordinates for a rectangle
19 */
20 struct SK_API SkIRect {
21 int32_t fLeft, fTop, fRight, fBottom;
22
MakeEmptySkIRect23 static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() {
24 SkIRect r;
25 r.setEmpty();
26 return r;
27 }
28
MakeLargestSkIRect29 static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() {
30 SkIRect r;
31 r.setLargest();
32 return r;
33 }
34
MakeWHSkIRect35 static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
36 SkIRect r;
37 r.set(0, 0, w, h);
38 return r;
39 }
40
MakeSizeSkIRect41 static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
42 SkIRect r;
43 r.set(0, 0, size.width(), size.height());
44 return r;
45 }
46
MakeLTRBSkIRect47 static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
48 SkIRect rect;
49 rect.set(l, t, r, b);
50 return rect;
51 }
52
MakeXYWHSkIRect53 static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
54 SkIRect r;
55 r.set(x, y, x + w, y + h);
56 return r;
57 }
58
leftSkIRect59 int left() const { return fLeft; }
topSkIRect60 int top() const { return fTop; }
rightSkIRect61 int right() const { return fRight; }
bottomSkIRect62 int bottom() const { return fBottom; }
63
64 /** return the left edge of the rect */
xSkIRect65 int x() const { return fLeft; }
66 /** return the top edge of the rect */
ySkIRect67 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 */
widthSkIRect72 int width() const { return fRight - fLeft; }
73
74 /**
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 */
heightSkIRect78 int height() const { return fBottom - fTop; }
79
sizeSkIRect80 SkISize size() const { return SkISize::Make(this->width(), this->height()); }
81
82 /**
83 * Since the center of an integer rect may fall on a factional value, this
84 * method is defined to return (right + left) >> 1.
85 *
86 * This is a specific "truncation" of the average, which is different than
87 * (right + left) / 2 when the sum is negative.
88 */
centerXSkIRect89 int centerX() const { return (fRight + fLeft) >> 1; }
90
91 /**
92 * Since the center of an integer rect may fall on a factional value, this
93 * method is defined to return (bottom + top) >> 1
94 *
95 * This is a specific "truncation" of the average, which is different than
96 * (bottom + top) / 2 when the sum is negative.
97 */
centerYSkIRect98 int centerY() const { return (fBottom + fTop) >> 1; }
99
100 /**
101 * Return true if the rectangle's width or height are <= 0
102 */
isEmptySkIRect103 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
104
isLargestSkIRect105 bool isLargest() const { return SK_MinS32 == fLeft &&
106 SK_MinS32 == fTop &&
107 SK_MaxS32 == fRight &&
108 SK_MaxS32 == fBottom; }
109
110 friend bool operator==(const SkIRect& a, const SkIRect& b) {
111 return !memcmp(&a, &b, sizeof(a));
112 }
113
114 friend bool operator!=(const SkIRect& a, const SkIRect& b) {
115 return !(a == b);
116 }
117
is16BitSkIRect118 bool is16Bit() const {
119 return SkIsS16(fLeft) && SkIsS16(fTop) &&
120 SkIsS16(fRight) && SkIsS16(fBottom);
121 }
122
123 /** Set the rectangle to (0,0,0,0)
124 */
setEmptySkIRect125 void setEmpty() { memset(this, 0, sizeof(*this)); }
126
setSkIRect127 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
128 fLeft = left;
129 fTop = top;
130 fRight = right;
131 fBottom = bottom;
132 }
133 // alias for set(l, t, r, b)
setLTRBSkIRect134 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
135 this->set(left, top, right, bottom);
136 }
137
setXYWHSkIRect138 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
139 fLeft = x;
140 fTop = y;
141 fRight = x + width;
142 fBottom = y + height;
143 }
144
145 /**
146 * Make the largest representable rectangle
147 */
setLargestSkIRect148 void setLargest() {
149 fLeft = fTop = SK_MinS32;
150 fRight = fBottom = SK_MaxS32;
151 }
152
153 /**
154 * Make the largest representable rectangle, but inverted (e.g. fLeft will
155 * be max 32bit and right will be min 32bit).
156 */
setLargestInvertedSkIRect157 void setLargestInverted() {
158 fLeft = fTop = SK_MaxS32;
159 fRight = fBottom = SK_MinS32;
160 }
161
162 /**
163 * Return a new IRect, built as an offset of this rect.
164 */
makeOffsetSkIRect165 SkIRect makeOffset(int32_t dx, int32_t dy) const {
166 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
167 }
168
169 /**
170 * Return a new IRect, built as an inset of this rect.
171 */
makeInsetSkIRect172 SkIRect makeInset(int32_t dx, int32_t dy) const {
173 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
174 }
175
176 /**
177 * Return a new Rect, built as an outset of this rect.
178 */
makeOutsetSkIRect179 SkIRect makeOutset(int32_t dx, int32_t dy) const {
180 return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
181 }
182
183 /** Offset set the rectangle by adding dx to its left and right,
184 and adding dy to its top and bottom.
185 */
offsetSkIRect186 void offset(int32_t dx, int32_t dy) {
187 fLeft += dx;
188 fTop += dy;
189 fRight += dx;
190 fBottom += dy;
191 }
192
offsetSkIRect193 void offset(const SkIPoint& delta) {
194 this->offset(delta.fX, delta.fY);
195 }
196
197 /**
198 * Offset this rect such its new x() and y() will equal newX and newY.
199 */
offsetToSkIRect200 void offsetTo(int32_t newX, int32_t newY) {
201 fRight += newX - fLeft;
202 fBottom += newY - fTop;
203 fLeft = newX;
204 fTop = newY;
205 }
206
207 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
208 making the rectangle narrower. If dx is negative, then the sides are moved outwards,
209 making the rectangle wider. The same holds true for dy and the top and bottom.
210 */
insetSkIRect211 void inset(int32_t dx, int32_t dy) {
212 fLeft += dx;
213 fTop += dy;
214 fRight -= dx;
215 fBottom -= dy;
216 }
217
218 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
219 moved outwards, making the rectangle wider. If dx is negative, then the
220 sides are moved inwards, making the rectangle narrower. The same holds
221 true for dy and the top and bottom.
222 */
outsetSkIRect223 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); }
224
quickRejectSkIRect225 bool quickReject(int l, int t, int r, int b) const {
226 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
227 }
228
229 /** Returns true if (x,y) is inside the rectangle and the rectangle is not
230 empty. The left and top are considered to be inside, while the right
231 and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
232 points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not.
233 */
containsSkIRect234 bool contains(int32_t x, int32_t y) const {
235 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
236 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
237 }
238
239 /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle.
240 If either rectangle is empty, contains() returns false.
241 */
containsSkIRect242 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
243 return left < right && top < bottom && !this->isEmpty() && // check for empties
244 fLeft <= left && fTop <= top &&
245 fRight >= right && fBottom >= bottom;
246 }
247
248 /** Returns true if the specified rectangle r is inside or equal to this rectangle.
249 */
containsSkIRect250 bool contains(const SkIRect& r) const {
251 return !r.isEmpty() && !this->isEmpty() && // check for empties
252 fLeft <= r.fLeft && fTop <= r.fTop &&
253 fRight >= r.fRight && fBottom >= r.fBottom;
254 }
255
256 /** Returns true if the specified rectangle r is inside or equal to this rectangle.
257 */
258 bool contains(const SkRect& r) const;
259
260 /** Return true if this rectangle contains the specified rectangle.
261 For speed, this method does not check if either this or the specified
262 rectangles are empty, and if either is, its return value is undefined.
263 In the debugging build however, we assert that both this and the
264 specified rectangles are non-empty.
265 */
containsNoEmptyCheckSkIRect266 bool containsNoEmptyCheck(int32_t left, int32_t top,
267 int32_t right, int32_t bottom) const {
268 SkASSERT(fLeft < fRight && fTop < fBottom);
269 SkASSERT(left < right && top < bottom);
270
271 return fLeft <= left && fTop <= top &&
272 fRight >= right && fBottom >= bottom;
273 }
274
containsNoEmptyCheckSkIRect275 bool containsNoEmptyCheck(const SkIRect& r) const {
276 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
277 }
278
279 /** If r intersects this rectangle, return true and set this rectangle to that
280 intersection, otherwise return false and do not change this rectangle.
281 If either rectangle is empty, do nothing and return false.
282 */
intersectSkIRect283 bool SK_WARN_UNUSED_RESULT intersect(const SkIRect& r) {
284 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
285 }
286
287 /** If rectangles a and b intersect, return true and set this rectangle to
288 that intersection, otherwise return false and do not change this
289 rectangle. If either rectangle is empty, do nothing and return false.
290 */
intersectSkIRect291 bool SK_WARN_UNUSED_RESULT intersect(const SkIRect& a, const SkIRect& b) {
292
293 if (!a.isEmpty() && !b.isEmpty() &&
294 a.fLeft < b.fRight && b.fLeft < a.fRight &&
295 a.fTop < b.fBottom && b.fTop < a.fBottom) {
296 fLeft = SkMax32(a.fLeft, b.fLeft);
297 fTop = SkMax32(a.fTop, b.fTop);
298 fRight = SkMin32(a.fRight, b.fRight);
299 fBottom = SkMin32(a.fBottom, b.fBottom);
300 return true;
301 }
302 return false;
303 }
304
305 /** If rectangles a and b intersect, return true and set this rectangle to
306 that intersection, otherwise return false and do not change this
307 rectangle. For speed, no check to see if a or b are empty is performed.
308 If either is, then the return result is undefined. In the debug build,
309 we assert that both rectangles are non-empty.
310 */
intersectNoEmptyCheckSkIRect311 bool SK_WARN_UNUSED_RESULT intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
312 SkASSERT(!a.isEmpty() && !b.isEmpty());
313
314 if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
315 a.fTop < b.fBottom && b.fTop < a.fBottom) {
316 fLeft = SkMax32(a.fLeft, b.fLeft);
317 fTop = SkMax32(a.fTop, b.fTop);
318 fRight = SkMin32(a.fRight, b.fRight);
319 fBottom = SkMin32(a.fBottom, b.fBottom);
320 return true;
321 }
322 return false;
323 }
324
325 /** If the rectangle specified by left,top,right,bottom intersects this rectangle,
326 return true and set this rectangle to that intersection,
327 otherwise return false and do not change this rectangle.
328 If either rectangle is empty, do nothing and return false.
329 */
intersectSkIRect330 bool SK_WARN_UNUSED_RESULT intersect(int32_t left, int32_t top,
331 int32_t right, int32_t bottom) {
332 if (left < right && top < bottom && !this->isEmpty() &&
333 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
334 if (fLeft < left) fLeft = left;
335 if (fTop < top) fTop = top;
336 if (fRight > right) fRight = right;
337 if (fBottom > bottom) fBottom = bottom;
338 return true;
339 }
340 return false;
341 }
342
343 /** Returns true if a and b are not empty, and they intersect
344 */
IntersectsSkIRect345 static bool Intersects(const SkIRect& a, const SkIRect& b) {
346 return !a.isEmpty() && !b.isEmpty() && // check for empties
347 a.fLeft < b.fRight && b.fLeft < a.fRight &&
348 a.fTop < b.fBottom && b.fTop < a.fBottom;
349 }
350
351 /**
352 * Returns true if a and b intersect. debug-asserts that neither are empty.
353 */
IntersectsNoEmptyCheckSkIRect354 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
355 SkASSERT(!a.isEmpty());
356 SkASSERT(!b.isEmpty());
357 return a.fLeft < b.fRight && b.fLeft < a.fRight &&
358 a.fTop < b.fBottom && b.fTop < a.fBottom;
359 }
360
361 /** Update this rectangle to enclose itself and the specified rectangle.
362 If this rectangle is empty, just set it to the specified rectangle. If the specified
363 rectangle is empty, do nothing.
364 */
365 void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
366
367 /** Update this rectangle to enclose itself and the specified rectangle.
368 If this rectangle is empty, just set it to the specified rectangle. If the specified
369 rectangle is empty, do nothing.
370 */
joinSkIRect371 void join(const SkIRect& r) {
372 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
373 }
374
375 /** Swap top/bottom or left/right if there are flipped.
376 This can be called if the edges are computed separately,
377 and may have crossed over each other.
378 When this returns, left <= right && top <= bottom
379 */
380 void sort();
381
EmptyIRectSkIRect382 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
383 static const SkIRect gEmpty = { 0, 0, 0, 0 };
384 return gEmpty;
385 }
386 };
387
388 /** \struct SkRect
389 */
390 struct SK_API SkRect {
391 SkScalar fLeft, fTop, fRight, fBottom;
392
MakeEmptySkRect393 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
394 SkRect r;
395 r.setEmpty();
396 return r;
397 }
398
MakeLargestSkRect399 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() {
400 SkRect r;
401 r.setLargest();
402 return r;
403 }
404
MakeWHSkRect405 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
406 SkRect r;
407 r.set(0, 0, w, h);
408 return r;
409 }
410
MakeIWHSkRect411 static SkRect SK_WARN_UNUSED_RESULT MakeIWH(int w, int h) {
412 SkRect r;
413 r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
414 return r;
415 }
416
MakeSizeSkRect417 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
418 SkRect r;
419 r.set(0, 0, size.width(), size.height());
420 return r;
421 }
422
MakeLTRBSkRect423 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
424 SkRect rect;
425 rect.set(l, t, r, b);
426 return rect;
427 }
428
MakeXYWHSkRect429 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
430 SkRect r;
431 r.set(x, y, x + w, y + h);
432 return r;
433 }
434
435 SK_ATTR_DEPRECATED("use Make()")
MakeFromIRectSkRect436 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
437 SkRect r;
438 r.set(SkIntToScalar(irect.fLeft),
439 SkIntToScalar(irect.fTop),
440 SkIntToScalar(irect.fRight),
441 SkIntToScalar(irect.fBottom));
442 return r;
443 }
444
MakeSkRect445 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
446 SkRect r;
447 r.set(SkIntToScalar(irect.fLeft),
448 SkIntToScalar(irect.fTop),
449 SkIntToScalar(irect.fRight),
450 SkIntToScalar(irect.fBottom));
451 return r;
452 }
453
454 /**
455 * Return true if the rectangle's width or height are <= 0
456 */
isEmptySkRect457 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
458
isLargestSkRect459 bool isLargest() const { return SK_ScalarMin == fLeft &&
460 SK_ScalarMin == fTop &&
461 SK_ScalarMax == fRight &&
462 SK_ScalarMax == fBottom; }
463
464 /**
465 * Returns true iff all values in the rect are finite. If any are
466 * infinite or NaN then this returns false.
467 */
isFiniteSkRect468 bool isFinite() const {
469 float accum = 0;
470 accum *= fLeft;
471 accum *= fTop;
472 accum *= fRight;
473 accum *= fBottom;
474
475 // accum is either NaN or it is finite (zero).
476 SkASSERT(0 == accum || SkScalarIsNaN(accum));
477
478 // value==value will be true iff value is not NaN
479 // TODO: is it faster to say !accum or accum==accum?
480 return !SkScalarIsNaN(accum);
481 }
482
xSkRect483 SkScalar x() const { return fLeft; }
ySkRect484 SkScalar y() const { return fTop; }
leftSkRect485 SkScalar left() const { return fLeft; }
topSkRect486 SkScalar top() const { return fTop; }
rightSkRect487 SkScalar right() const { return fRight; }
bottomSkRect488 SkScalar bottom() const { return fBottom; }
widthSkRect489 SkScalar width() const { return fRight - fLeft; }
heightSkRect490 SkScalar height() const { return fBottom - fTop; }
centerXSkRect491 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
centerYSkRect492 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
493
494 friend bool operator==(const SkRect& a, const SkRect& b) {
495 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
496 }
497
498 friend bool operator!=(const SkRect& a, const SkRect& b) {
499 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
500 }
501
502 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
503 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
504 */
505 void toQuad(SkPoint quad[4]) const;
506
507 /** Set this rectangle to the empty rectangle (0,0,0,0)
508 */
setEmptySkRect509 void setEmpty() { memset(this, 0, sizeof(*this)); }
510
setSkRect511 void set(const SkIRect& src) {
512 fLeft = SkIntToScalar(src.fLeft);
513 fTop = SkIntToScalar(src.fTop);
514 fRight = SkIntToScalar(src.fRight);
515 fBottom = SkIntToScalar(src.fBottom);
516 }
517
setSkRect518 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
519 fLeft = left;
520 fTop = top;
521 fRight = right;
522 fBottom = bottom;
523 }
524 // alias for set(l, t, r, b)
setLTRBSkRect525 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
526 this->set(left, top, right, bottom);
527 }
528
529 /** Initialize the rect with the 4 specified integers. The routine handles
530 converting them to scalars (by calling SkIntToScalar)
531 */
isetSkRect532 void iset(int left, int top, int right, int bottom) {
533 fLeft = SkIntToScalar(left);
534 fTop = SkIntToScalar(top);
535 fRight = SkIntToScalar(right);
536 fBottom = SkIntToScalar(bottom);
537 }
538
539 /**
540 * Set this rectangle to be left/top at 0,0, and have the specified width
541 * and height (automatically converted to SkScalar).
542 */
isetWHSkRect543 void isetWH(int width, int height) {
544 fLeft = fTop = 0;
545 fRight = SkIntToScalar(width);
546 fBottom = SkIntToScalar(height);
547 }
548
549 /** Set this rectangle to be the bounds of the array of points.
550 If the array is empty (count == 0), then set this rectangle
551 to the empty rectangle (0,0,0,0)
552 */
setSkRect553 void set(const SkPoint pts[], int count) {
554 // set() had been checking for non-finite values, so keep that behavior
555 // for now. Now that we have setBoundsCheck(), we may decide to make
556 // set() be simpler/faster, and not check for those.
557 (void)this->setBoundsCheck(pts, count);
558 }
559
560 // alias for set(pts, count)
setBoundsSkRect561 void setBounds(const SkPoint pts[], int count) {
562 (void)this->setBoundsCheck(pts, count);
563 }
564
565 /**
566 * Compute the bounds of the array of points, and set this rect to that
567 * bounds and return true... unless a non-finite value is encountered,
568 * in which case this rect is set to empty and false is returned.
569 */
570 bool setBoundsCheck(const SkPoint pts[], int count);
571
setSkRect572 void set(const SkPoint& p0, const SkPoint& p1) {
573 fLeft = SkMinScalar(p0.fX, p1.fX);
574 fRight = SkMaxScalar(p0.fX, p1.fX);
575 fTop = SkMinScalar(p0.fY, p1.fY);
576 fBottom = SkMaxScalar(p0.fY, p1.fY);
577 }
578
setXYWHSkRect579 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
580 fLeft = x;
581 fTop = y;
582 fRight = x + width;
583 fBottom = y + height;
584 }
585
setWHSkRect586 void setWH(SkScalar width, SkScalar height) {
587 fLeft = 0;
588 fTop = 0;
589 fRight = width;
590 fBottom = height;
591 }
592
593 /**
594 * Make the largest representable rectangle
595 */
setLargestSkRect596 void setLargest() {
597 fLeft = fTop = SK_ScalarMin;
598 fRight = fBottom = SK_ScalarMax;
599 }
600
601 /**
602 * Make the largest representable rectangle, but inverted (e.g. fLeft will
603 * be max and right will be min).
604 */
setLargestInvertedSkRect605 void setLargestInverted() {
606 fLeft = fTop = SK_ScalarMax;
607 fRight = fBottom = SK_ScalarMin;
608 }
609
610 /**
611 * Return a new Rect, built as an offset of this rect.
612 */
makeOffsetSkRect613 SkRect makeOffset(SkScalar dx, SkScalar dy) const {
614 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
615 }
616
617 /**
618 * Return a new Rect, built as an inset of this rect.
619 */
makeInsetSkRect620 SkRect makeInset(SkScalar dx, SkScalar dy) const {
621 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
622 }
623
624 /**
625 * Return a new Rect, built as an outset of this rect.
626 */
makeOutsetSkRect627 SkRect makeOutset(SkScalar dx, SkScalar dy) const {
628 return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
629 }
630
631 /** Offset set the rectangle by adding dx to its left and right,
632 and adding dy to its top and bottom.
633 */
offsetSkRect634 void offset(SkScalar dx, SkScalar dy) {
635 fLeft += dx;
636 fTop += dy;
637 fRight += dx;
638 fBottom += dy;
639 }
640
offsetSkRect641 void offset(const SkPoint& delta) {
642 this->offset(delta.fX, delta.fY);
643 }
644
645 /**
646 * Offset this rect such its new x() and y() will equal newX and newY.
647 */
offsetToSkRect648 void offsetTo(SkScalar newX, SkScalar newY) {
649 fRight += newX - fLeft;
650 fBottom += newY - fTop;
651 fLeft = newX;
652 fTop = newY;
653 }
654
655 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
656 moved inwards, making the rectangle narrower. If dx is negative, then
657 the sides are moved outwards, making the rectangle wider. The same holds
658 true for dy and the top and bottom.
659 */
insetSkRect660 void inset(SkScalar dx, SkScalar dy) {
661 fLeft += dx;
662 fTop += dy;
663 fRight -= dx;
664 fBottom -= dy;
665 }
666
667 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
668 moved outwards, making the rectangle wider. If dx is negative, then the
669 sides are moved inwards, making the rectangle narrower. The same holds
670 true for dy and the top and bottom.
671 */
outsetSkRect672 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
673
674 /** If this rectangle intersects r, return true and set this rectangle to that
675 intersection, otherwise return false and do not change this rectangle.
676 If either rectangle is empty, do nothing and return false.
677 */
678 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& r);
679
680 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
681 return true and set this rectangle to that intersection, otherwise return false
682 and do not change this rectangle.
683 If either rectangle is empty, do nothing and return false.
684 */
685 bool SK_WARN_UNUSED_RESULT intersect(SkScalar left, SkScalar top,
686 SkScalar right, SkScalar bottom);
687
688 /**
689 * If rectangles a and b intersect, return true and set this rectangle to
690 * that intersection, otherwise return false and do not change this
691 * rectangle. If either rectangle is empty, do nothing and return false.
692 */
693 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& a, const SkRect& b);
694
695
696 private:
IntersectsSkRect697 static bool Intersects(SkScalar al, SkScalar at, SkScalar ar, SkScalar ab,
698 SkScalar bl, SkScalar bt, SkScalar br, SkScalar bb) {
699 SkScalar L = SkMaxScalar(al, bl);
700 SkScalar R = SkMinScalar(ar, br);
701 SkScalar T = SkMaxScalar(at, bt);
702 SkScalar B = SkMinScalar(ab, bb);
703 return L < R && T < B;
704 }
705
706 public:
707 /**
708 * Return true if this rectangle is not empty, and the specified sides of
709 * a rectangle are not empty, and they intersect.
710 */
intersectsSkRect711 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
712 return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom);
713 }
714
intersectsSkRect715 bool intersects(const SkRect& r) const {
716 return Intersects(fLeft, fTop, fRight, fBottom,
717 r.fLeft, r.fTop, r.fRight, r.fBottom);
718 }
719
720 /**
721 * Return true if rectangles a and b are not empty and intersect.
722 */
IntersectsSkRect723 static bool Intersects(const SkRect& a, const SkRect& b) {
724 return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom,
725 b.fLeft, b.fTop, b.fRight, b.fBottom);
726 }
727
728 /**
729 * Update this rectangle to enclose itself and the specified rectangle.
730 * If this rectangle is empty, just set it to the specified rectangle.
731 * If the specified rectangle is empty, do nothing.
732 */
733 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
734
735 /** Update this rectangle to enclose itself and the specified rectangle.
736 If this rectangle is empty, just set it to the specified rectangle. If the specified
737 rectangle is empty, do nothing.
738 */
joinSkRect739 void join(const SkRect& r) {
740 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
741 }
742
joinNonEmptyArgSkRect743 void joinNonEmptyArg(const SkRect& r) {
744 SkASSERT(!r.isEmpty());
745 // if we are empty, just assign
746 if (fLeft >= fRight || fTop >= fBottom) {
747 *this = r;
748 } else {
749 this->joinPossiblyEmptyRect(r);
750 }
751 }
752
753 /**
754 * Joins the rectangle with another without checking if either are empty (may produce unexpected
755 * results if either rect is inverted).
756 */
joinPossiblyEmptyRectSkRect757 void joinPossiblyEmptyRect(const SkRect& r) {
758 fLeft = SkMinScalar(fLeft, r.left());
759 fTop = SkMinScalar(fTop, r.top());
760 fRight = SkMaxScalar(fRight, r.right());
761 fBottom = SkMaxScalar(fBottom, r.bottom());
762 }
763
764 /**
765 * Grow the rect to include the specified (x,y). After this call, the
766 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
767 *
768 * This is close, but not quite the same contract as contains(), since
769 * contains() treats the left and top different from the right and bottom.
770 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
771 * that contains(x,y) always returns false if the rect is empty.
772 */
growToIncludeSkRect773 void growToInclude(SkScalar x, SkScalar y) {
774 fLeft = SkMinScalar(x, fLeft);
775 fRight = SkMaxScalar(x, fRight);
776 fTop = SkMinScalar(y, fTop);
777 fBottom = SkMaxScalar(y, fBottom);
778 }
779
780 /** Bulk version of growToInclude */
growToIncludeSkRect781 void growToInclude(const SkPoint pts[], int count) {
782 this->growToInclude(pts, sizeof(SkPoint), count);
783 }
784
785 /** Bulk version of growToInclude with stride. */
growToIncludeSkRect786 void growToInclude(const SkPoint pts[], size_t stride, int count) {
787 SkASSERT(count >= 0);
788 SkASSERT(stride >= sizeof(SkPoint));
789 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
790 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
791 this->growToInclude(pts->fX, pts->fY);
792 }
793 }
794
795 /**
796 * Return true if this rectangle contains r, and if both rectangles are
797 * not empty.
798 */
containsSkRect799 bool contains(const SkRect& r) const {
800 // todo: can we eliminate the this->isEmpty check?
801 return !r.isEmpty() && !this->isEmpty() &&
802 fLeft <= r.fLeft && fTop <= r.fTop &&
803 fRight >= r.fRight && fBottom >= r.fBottom;
804 }
805
806 /**
807 * Returns true if the specified rectangle r is inside or equal to this rectangle.
808 */
containsSkRect809 bool contains(const SkIRect& r) const {
810 // todo: can we eliminate the this->isEmpty check?
811 return !r.isEmpty() && !this->isEmpty() &&
812 fLeft <= SkIntToScalar(r.fLeft) && fTop <= SkIntToScalar(r.fTop) &&
813 fRight >= SkIntToScalar(r.fRight) && fBottom >= SkIntToScalar(r.fBottom);
814 }
815
816 /**
817 * Set the dst rectangle by rounding this rectangle's coordinates to their
818 * nearest integer values using SkScalarRoundToInt.
819 */
roundSkRect820 void round(SkIRect* dst) const {
821 SkASSERT(dst);
822 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
823 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
824 }
825
826 /**
827 * Set the dst rectangle by rounding "out" this rectangle, choosing the
828 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
829 */
roundOutSkRect830 void roundOut(SkIRect* dst) const {
831 SkASSERT(dst);
832 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
833 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
834 }
835
836 /**
837 * Set the dst rectangle by rounding "out" this rectangle, choosing the
838 * SkScalarFloorToScalar of top and left, and the SkScalarCeilToScalar of right and bottom.
839 *
840 * It is safe for this == dst
841 */
roundOutSkRect842 void roundOut(SkRect* dst) const {
843 dst->set(SkScalarFloorToScalar(fLeft),
844 SkScalarFloorToScalar(fTop),
845 SkScalarCeilToScalar(fRight),
846 SkScalarCeilToScalar(fBottom));
847 }
848
849 /**
850 * Set the dst rectangle by rounding "in" this rectangle, choosing the
851 * ceil of top and left, and the floor of right and bottom. This does *not*
852 * call sort(), so it is possible that the resulting rect is inverted...
853 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
854 */
roundInSkRect855 void roundIn(SkIRect* dst) const {
856 SkASSERT(dst);
857 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
858 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
859 }
860
861 //! Returns the result of calling round(&dst)
roundSkRect862 SkIRect round() const {
863 SkIRect ir;
864 this->round(&ir);
865 return ir;
866 }
867
868 //! Returns the result of calling roundOut(&dst)
roundOutSkRect869 SkIRect roundOut() const {
870 SkIRect ir;
871 this->roundOut(&ir);
872 return ir;
873 }
874
875 /**
876 * Swap top/bottom or left/right if there are flipped (i.e. if width()
877 * or height() would have returned a negative value.) This should be called
878 * if the edges are computed separately, and may have crossed over each
879 * other. When this returns, left <= right && top <= bottom
880 */
sortSkRect881 void sort() {
882 if (fLeft > fRight) {
883 SkTSwap<SkScalar>(fLeft, fRight);
884 }
885
886 if (fTop > fBottom) {
887 SkTSwap<SkScalar>(fTop, fBottom);
888 }
889 }
890
891 /**
892 * cast-safe way to treat the rect as an array of (4) SkScalars.
893 */
asScalarsSkRect894 const SkScalar* asScalars() const { return &fLeft; }
895
896 void dump(bool asHex) const;
dumpSkRect897 void dump() const { this->dump(false); }
dumpHexSkRect898 void dumpHex() const { this->dump(true); }
899 };
900
contains(const SkRect & r)901 inline bool SkIRect::contains(const SkRect& r) const {
902 return !r.isEmpty() && !this->isEmpty() && // check for empties
903 (SkScalar)fLeft <= r.fLeft && (SkScalar)fTop <= r.fTop &&
904 (SkScalar)fRight >= r.fRight && (SkScalar)fBottom >= r.fBottom;
905 }
906
907 #endif
908