3 * Copyright 2006 The Android Open Source Project
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
10 #ifndef SkRect_DEFINED
11 #define SkRect_DEFINED
18 SkIRect holds four 32 bit integer coordinates for a rectangle
20 struct SK_API SkIRect {
21 int32_t fLeft, fTop, fRight, fBottom;
23 static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() {
29 static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() {
35 static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
41 static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
43 r.set(0, 0, size.width(), size.height());
47 static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
53 static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
55 r.set(x, y, x + w, y + h);
59 int left() const { return fLeft; }
60 int top() const { return fTop; }
61 int right() const { return fRight; }
62 int bottom() const { return fBottom; }
64 /** 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; }
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.
72 int width() const { return fRight - fLeft; }
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.
78 int height() const { return fBottom - fTop; }
81 * Since the center of an integer rect may fall on a factional value, this
82 * method is defined to return (right + left) >> 1.
84 * This is a specific "truncation" of the average, which is different than
85 * (right + left) / 2 when the sum is negative.
87 int centerX() const { return (fRight + fLeft) >> 1; }
90 * Since the center of an integer rect may fall on a factional value, this
91 * method is defined to return (bottom + top) >> 1
93 * This is a specific "truncation" of the average, which is different than
94 * (bottom + top) / 2 when the sum is negative.
96 int centerY() const { return (fBottom + fTop) >> 1; }
99 * Return true if the rectangle's width or height are <= 0
101 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
103 bool isLargest() const { return SK_MinS32 == fLeft &&
105 SK_MaxS32 == fRight &&
106 SK_MaxS32 == fBottom; }
108 friend bool operator==(const SkIRect& a, const SkIRect& b) {
109 return !memcmp(&a, &b, sizeof(a));
112 friend bool operator!=(const SkIRect& a, const SkIRect& b) {
116 bool is16Bit() const {
117 return SkIsS16(fLeft) && SkIsS16(fTop) &&
118 SkIsS16(fRight) && SkIsS16(fBottom);
121 /** Set the rectangle to (0,0,0,0)
123 void setEmpty() { memset(this, 0, sizeof(*this)); }
125 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) {
131 // 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);
136 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) {
140 fBottom = y + height;
144 * Make the largest representable rectangle
147 fLeft = fTop = SK_MinS32;
148 fRight = fBottom = SK_MaxS32;
152 * Make the largest representable rectangle, but inverted (e.g. fLeft will
153 * be max 32bit and right will be min 32bit).
155 void setLargestInverted() {
156 fLeft = fTop = SK_MaxS32;
157 fRight = fBottom = SK_MinS32;
160 /** Offset set the rectangle by adding dx to its left and right,
161 and adding dy to its top and bottom.
163 void offset(int32_t dx, int32_t dy) {
170 void offset(const SkIPoint& delta) {
171 this->offset(delta.fX, delta.fY);
175 * Offset this rect such its new x() and y() will equal newX and newY.
177 void offsetTo(int32_t newX, int32_t newY) {
178 fRight += newX - fLeft;
179 fBottom += newY - fTop;
184 /** 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,
186 making the rectangle wider. The same holds true for dy and the top and bottom.
188 void inset(int32_t dx, int32_t dy) {
195 /** 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.
200 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); }
202 bool quickReject(int l, int t, int r, int b) const {
203 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
206 /** 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.
211 bool contains(int32_t x, int32_t y) const {
212 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) &&
213 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop);
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.
219 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const {
220 return left < right && top < bottom && !this->isEmpty() && // check for empties
221 fLeft <= left && fTop <= top &&
222 fRight >= right && fBottom >= bottom;
225 /** Returns true if the specified rectangle r is inside or equal to this rectangle.
227 bool contains(const SkIRect& r) const {
228 return !r.isEmpty() && !this->isEmpty() && // check for empties
229 fLeft <= r.fLeft && fTop <= r.fTop &&
230 fRight >= r.fRight && fBottom >= r.fBottom;
233 /** Return true if this rectangle contains the specified rectangle.
234 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.
239 bool containsNoEmptyCheck(int32_t left, int32_t top,
240 int32_t right, int32_t bottom) const {
241 SkASSERT(fLeft < fRight && fTop < fBottom);
242 SkASSERT(left < right && top < bottom);
244 return fLeft <= left && fTop <= top &&
245 fRight >= right && fBottom >= bottom;
248 bool containsNoEmptyCheck(const SkIRect& r) const {
249 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
252 /** 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.
256 bool intersect(const SkIRect& r) {
258 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom);
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.
265 bool intersect(const SkIRect& a, const SkIRect& b) {
268 if (!a.isEmpty() && !b.isEmpty() &&
269 a.fLeft < b.fRight && b.fLeft < a.fRight &&
270 a.fTop < b.fBottom && b.fTop < a.fBottom) {
271 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);
280 /** 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.
286 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
288 SkASSERT(!a.isEmpty() && !b.isEmpty());
290 if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
291 a.fTop < b.fBottom && b.fTop < a.fBottom) {
292 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);
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.
306 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
307 if (left < right && top < bottom && !this->isEmpty() &&
308 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
309 if (fLeft < left) fLeft = left;
310 if (fTop < top) fTop = top;
311 if (fRight > right) fRight = right;
312 if (fBottom > bottom) fBottom = bottom;
318 /** Returns true if a and b are not empty, and they intersect
320 static bool Intersects(const SkIRect& a, const SkIRect& b) {
321 return !a.isEmpty() && !b.isEmpty() && // check for empties
322 a.fLeft < b.fRight && b.fLeft < a.fRight &&
323 a.fTop < b.fBottom && b.fTop < a.fBottom;
327 * Returns true if a and b intersect. debug-asserts that neither are empty.
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 &&
333 a.fTop < b.fBottom && b.fTop < a.fBottom;
336 /** 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.
340 void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
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.
346 void join(const SkIRect& r) {
347 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
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
357 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
358 static const SkIRect gEmpty = { 0, 0, 0, 0 };
365 struct SK_API SkRect {
366 SkScalar fLeft, fTop, fRight, fBottom;
368 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
374 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() {
380 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
386 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
388 r.set(0, 0, size.width(), size.height());
392 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
394 rect.set(l, t, r, b);
398 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
400 r.set(x, y, x + w, y + h);
404 SK_ATTR_DEPRECATED("use Make()")
405 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
407 r.set(SkIntToScalar(irect.fLeft),
408 SkIntToScalar(irect.fTop),
409 SkIntToScalar(irect.fRight),
410 SkIntToScalar(irect.fBottom));
414 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
416 r.set(SkIntToScalar(irect.fLeft),
417 SkIntToScalar(irect.fTop),
418 SkIntToScalar(irect.fRight),
419 SkIntToScalar(irect.fBottom));
424 * Return true if the rectangle's width or height are <= 0
426 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
428 bool isLargest() const { return SK_ScalarMin == fLeft &&
429 SK_ScalarMin == fTop &&
430 SK_ScalarMax == fRight &&
431 SK_ScalarMax == fBottom; }
434 * Returns true iff all values in the rect are finite. If any are
435 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
438 bool isFinite() const {
445 // accum is either NaN or it is finite (zero).
446 SkASSERT(0 == accum || !(accum == accum));
448 // value==value will be true iff value is not NaN
449 // TODO: is it faster to say !accum or accum==accum?
450 return accum == accum;
453 SkScalar x() const { return fLeft; }
454 SkScalar y() const { return fTop; }
455 SkScalar left() const { return fLeft; }
456 SkScalar top() const { return fTop; }
457 SkScalar right() const { return fRight; }
458 SkScalar bottom() const { return fBottom; }
459 SkScalar width() const { return fRight - fLeft; }
460 SkScalar height() const { return fBottom - fTop; }
461 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
462 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
464 friend bool operator==(const SkRect& a, const SkRect& b) {
465 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
468 friend bool operator!=(const SkRect& a, const SkRect& b) {
469 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
472 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
473 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
475 void toQuad(SkPoint quad[4]) const;
477 /** Set this rectangle to the empty rectangle (0,0,0,0)
479 void setEmpty() { memset(this, 0, sizeof(*this)); }
481 void set(const SkIRect& src) {
482 fLeft = SkIntToScalar(src.fLeft);
483 fTop = SkIntToScalar(src.fTop);
484 fRight = SkIntToScalar(src.fRight);
485 fBottom = SkIntToScalar(src.fBottom);
488 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
494 // alias for set(l, t, r, b)
495 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
496 this->set(left, top, right, bottom);
499 /** Initialize the rect with the 4 specified integers. The routine handles
500 converting them to scalars (by calling SkIntToScalar)
502 void iset(int left, int top, int right, int bottom) {
503 fLeft = SkIntToScalar(left);
504 fTop = SkIntToScalar(top);
505 fRight = SkIntToScalar(right);
506 fBottom = SkIntToScalar(bottom);
510 * Set this rectangle to be left/top at 0,0, and have the specified width
511 * and height (automatically converted to SkScalar).
513 void isetWH(int width, int height) {
515 fRight = SkIntToScalar(width);
516 fBottom = SkIntToScalar(height);
519 /** Set this rectangle to be the bounds of the array of points.
520 If the array is empty (count == 0), then set this rectangle
521 to the empty rectangle (0,0,0,0)
523 void set(const SkPoint pts[], int count) {
524 // set() had been checking for non-finite values, so keep that behavior
525 // for now. Now that we have setBoundsCheck(), we may decide to make
526 // set() be simpler/faster, and not check for those.
527 (void)this->setBoundsCheck(pts, count);
530 // alias for set(pts, count)
531 void setBounds(const SkPoint pts[], int count) {
532 (void)this->setBoundsCheck(pts, count);
536 * Compute the bounds of the array of points, and set this rect to that
537 * bounds and return true... unless a non-finite value is encountered,
538 * in which case this rect is set to empty and false is returned.
540 bool setBoundsCheck(const SkPoint pts[], int count);
542 void set(const SkPoint& p0, const SkPoint& p1) {
543 fLeft = SkMinScalar(p0.fX, p1.fX);
544 fRight = SkMaxScalar(p0.fX, p1.fX);
545 fTop = SkMinScalar(p0.fY, p1.fY);
546 fBottom = SkMaxScalar(p0.fY, p1.fY);
549 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
553 fBottom = y + height;
556 void setWH(SkScalar width, SkScalar height) {
564 * Make the largest representable rectangle
567 fLeft = fTop = SK_ScalarMin;
568 fRight = fBottom = SK_ScalarMax;
572 * Make the largest representable rectangle, but inverted (e.g. fLeft will
573 * be max and right will be min).
575 void setLargestInverted() {
576 fLeft = fTop = SK_ScalarMax;
577 fRight = fBottom = SK_ScalarMin;
580 /** Offset set the rectangle by adding dx to its left and right,
581 and adding dy to its top and bottom.
583 void offset(SkScalar dx, SkScalar dy) {
590 void offset(const SkPoint& delta) {
591 this->offset(delta.fX, delta.fY);
595 * Offset this rect such its new x() and y() will equal newX and newY.
597 void offsetTo(SkScalar newX, SkScalar newY) {
598 fRight += newX - fLeft;
599 fBottom += newY - fTop;
604 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
605 moved inwards, making the rectangle narrower. If dx is negative, then
606 the sides are moved outwards, making the rectangle wider. The same holds
607 true for dy and the top and bottom.
609 void inset(SkScalar dx, SkScalar dy) {
616 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
617 moved outwards, making the rectangle wider. If dx is negative, then the
618 sides are moved inwards, making the rectangle narrower. The same holds
619 true for dy and the top and bottom.
621 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
623 /** If this rectangle intersects r, return true and set this rectangle to that
624 intersection, otherwise return false and do not change this rectangle.
625 If either rectangle is empty, do nothing and return false.
627 bool intersect(const SkRect& r);
628 bool intersect2(const SkRect& r);
630 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
631 return true and set this rectangle to that intersection, otherwise return false
632 and do not change this rectangle.
633 If either rectangle is empty, do nothing and return false.
635 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
638 * Return true if this rectangle is not empty, and the specified sides of
639 * a rectangle are not empty, and they intersect.
641 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
642 return // first check that both are not empty
643 left < right && top < bottom &&
644 fLeft < fRight && fTop < fBottom &&
645 // now check for intersection
646 fLeft < right && left < fRight &&
647 fTop < bottom && top < fBottom;
650 /** If rectangles a and b intersect, return true and set this rectangle to
651 * that intersection, otherwise return false and do not change this
652 * rectangle. If either rectangle is empty, do nothing and return false.
654 bool intersect(const SkRect& a, const SkRect& b);
657 * Return true if rectangles a and b are not empty and intersect.
659 static bool Intersects(const SkRect& a, const SkRect& b) {
660 return !a.isEmpty() && !b.isEmpty() &&
661 a.fLeft < b.fRight && b.fLeft < a.fRight &&
662 a.fTop < b.fBottom && b.fTop < a.fBottom;
666 * Update this rectangle to enclose itself and the specified rectangle.
667 * If this rectangle is empty, just set it to the specified rectangle.
668 * If the specified rectangle is empty, do nothing.
670 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
672 /** Update this rectangle to enclose itself and the specified rectangle.
673 If this rectangle is empty, just set it to the specified rectangle. If the specified
674 rectangle is empty, do nothing.
676 void join(const SkRect& r) {
677 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
680 void growToInclude(const SkRect& r) { this->join(r); }
683 * Grow the rect to include the specified (x,y). After this call, the
684 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
686 * This is close, but not quite the same contract as contains(), since
687 * contains() treats the left and top different from the right and bottom.
688 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
689 * that contains(x,y) always returns false if the rect is empty.
691 void growToInclude(SkScalar x, SkScalar y) {
692 fLeft = SkMinScalar(x, fLeft);
693 fRight = SkMaxScalar(x, fRight);
694 fTop = SkMinScalar(y, fTop);
695 fBottom = SkMaxScalar(y, fBottom);
698 /** Bulk version of growToInclude */
699 void growToInclude(const SkPoint pts[], int count) {
700 this->growToInclude(pts, sizeof(SkPoint), count);
703 /** Bulk version of growToInclude with stride. */
704 void growToInclude(const SkPoint pts[], size_t stride, int count) {
705 SkASSERT(count >= 0);
706 SkASSERT(stride >= sizeof(SkPoint));
707 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
708 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
709 this->growToInclude(pts->fX, pts->fY);
714 * Return true if this rectangle contains r, and if both rectangles are
717 bool contains(const SkRect& r) const {
718 // todo: can we eliminate the this->isEmpty check?
719 return !r.isEmpty() && !this->isEmpty() &&
720 fLeft <= r.fLeft && fTop <= r.fTop &&
721 fRight >= r.fRight && fBottom >= r.fBottom;
725 * Set the dst rectangle by rounding this rectangle's coordinates to their
726 * nearest integer values using SkScalarRoundToInt.
728 void round(SkIRect* dst) const {
730 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
731 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
735 * Set the dst rectangle by rounding "out" this rectangle, choosing the
736 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
738 void roundOut(SkIRect* dst) const {
740 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
741 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
745 * Expand this rectangle by rounding its coordinates "out", choosing the
746 * floor of top and left, and the ceil of right and bottom. If this rect
747 * is already on integer coordinates, then it will be unchanged.
750 this->set(SkScalarFloorToScalar(fLeft),
751 SkScalarFloorToScalar(fTop),
752 SkScalarCeilToScalar(fRight),
753 SkScalarCeilToScalar(fBottom));
757 * Set the dst rectangle by rounding "in" this rectangle, choosing the
758 * ceil of top and left, and the floor of right and bottom. This does *not*
759 * call sort(), so it is possible that the resulting rect is inverted...
760 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
762 void roundIn(SkIRect* dst) const {
764 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
765 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
769 * Return a new SkIRect which is contains the rounded coordinates of this
770 * rect using SkScalarRoundToInt.
772 SkIRect round() const {
779 * Swap top/bottom or left/right if there are flipped (i.e. if width()
780 * or height() would have returned a negative value.) This should be called
781 * if the edges are computed separately, and may have crossed over each
782 * other. When this returns, left <= right && top <= bottom
787 * cast-safe way to treat the rect as an array of (4) SkScalars.
789 const SkScalar* asScalars() const { return &fLeft; }