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 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 intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
331 if (left < right && top < bottom && !this->isEmpty() &&
332 fLeft < right && left < fRight && fTop < bottom && top < fBottom) {
333 if (fLeft < left) fLeft = left;
334 if (fTop < top) fTop = top;
335 if (fRight > right) fRight = right;
336 if (fBottom > bottom) fBottom = bottom;
337 return true;
338 }
339 return false;
340 }
341
342 /** Returns true if a and b are not empty, and they intersect
343 */
IntersectsSkIRect344 static bool Intersects(const SkIRect& a, const SkIRect& b) {
345 return !a.isEmpty() && !b.isEmpty() && // check for empties
346 a.fLeft < b.fRight && b.fLeft < a.fRight &&
347 a.fTop < b.fBottom && b.fTop < a.fBottom;
348 }
349
350 /**
351 * Returns true if a and b intersect. debug-asserts that neither are empty.
352 */
IntersectsNoEmptyCheckSkIRect353 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
354 SkASSERT(!a.isEmpty());
355 SkASSERT(!b.isEmpty());
356 return a.fLeft < b.fRight && b.fLeft < a.fRight &&
357 a.fTop < b.fBottom && b.fTop < a.fBottom;
358 }
359
360 /** Update this rectangle to enclose itself and the specified rectangle.
361 If this rectangle is empty, just set it to the specified rectangle. If the specified
362 rectangle is empty, do nothing.
363 */
364 void join(int32_t left, int32_t top, int32_t right, int32_t bottom);
365
366 /** Update this rectangle to enclose itself and the specified rectangle.
367 If this rectangle is empty, just set it to the specified rectangle. If the specified
368 rectangle is empty, do nothing.
369 */
joinSkIRect370 void join(const SkIRect& r) {
371 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
372 }
373
374 /** Swap top/bottom or left/right if there are flipped.
375 This can be called if the edges are computed separately,
376 and may have crossed over each other.
377 When this returns, left <= right && top <= bottom
378 */
sortSkIRect379 void sort() {
380 if (fLeft > fRight) {
381 SkTSwap<int32_t>(fLeft, fRight);
382 }
383 if (fTop > fBottom) {
384 SkTSwap<int32_t>(fTop, fBottom);
385 }
386 }
387
388 /**
389 * Return a new Rect that is the sorted version of this rect (left <= right, top <= bottom).
390 */
makeSortedSkIRect391 SkIRect makeSorted() const {
392 return MakeLTRB(SkMin32(fLeft, fRight), SkMin32(fTop, fBottom),
393 SkMax32(fLeft, fRight), SkMax32(fTop, fBottom));
394 }
395
EmptyIRectSkIRect396 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
397 static const SkIRect gEmpty = { 0, 0, 0, 0 };
398 return gEmpty;
399 }
400 };
401
402 /** \struct SkRect
403 */
404 struct SK_API SkRect {
405 SkScalar fLeft, fTop, fRight, fBottom;
406
MakeEmptySkRect407 static constexpr SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
408 return SkRect{0, 0, 0, 0};
409 }
410
MakeLargestSkRect411 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() {
412 SkRect r;
413 r.setLargest();
414 return r;
415 }
416
MakeWHSkRect417 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
418 SkRect r;
419 r.set(0, 0, w, h);
420 return r;
421 }
422
MakeIWHSkRect423 static SkRect SK_WARN_UNUSED_RESULT MakeIWH(int w, int h) {
424 SkRect r;
425 r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
426 return r;
427 }
428
MakeSizeSkRect429 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
430 SkRect r;
431 r.set(0, 0, size.width(), size.height());
432 return r;
433 }
434
MakeLTRBSkRect435 static constexpr SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r,
436 SkScalar b) {
437 return SkRect {l, t, r, b};
438 }
439
MakeXYWHSkRect440 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
441 SkRect r;
442 r.set(x, y, x + w, y + h);
443 return r;
444 }
445
446 SK_ATTR_DEPRECATED("use Make()")
MakeFromIRectSkRect447 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
448 SkRect r;
449 r.set(SkIntToScalar(irect.fLeft),
450 SkIntToScalar(irect.fTop),
451 SkIntToScalar(irect.fRight),
452 SkIntToScalar(irect.fBottom));
453 return r;
454 }
455
MakeSkRect456 static SkRect Make(const SkISize& size) {
457 return MakeIWH(size.width(), size.height());
458 }
459
MakeSkRect460 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
461 SkRect r;
462 r.set(SkIntToScalar(irect.fLeft),
463 SkIntToScalar(irect.fTop),
464 SkIntToScalar(irect.fRight),
465 SkIntToScalar(irect.fBottom));
466 return r;
467 }
468
469 /**
470 * Return true if the rectangle's width or height are <= 0
471 */
isEmptySkRect472 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
473
474 /**
475 * Return true if the rectangle's width and height are >= 0
476 */
isSortedSkRect477 bool isSorted() const { return fLeft <= fRight && fTop <= fBottom; }
478
isLargestSkRect479 bool isLargest() const { return SK_ScalarMin == fLeft &&
480 SK_ScalarMin == fTop &&
481 SK_ScalarMax == fRight &&
482 SK_ScalarMax == fBottom; }
483
484 /**
485 * Returns true iff all values in the rect are finite. If any are
486 * infinite or NaN then this returns false.
487 */
isFiniteSkRect488 bool isFinite() const {
489 float accum = 0;
490 accum *= fLeft;
491 accum *= fTop;
492 accum *= fRight;
493 accum *= fBottom;
494
495 // accum is either NaN or it is finite (zero).
496 SkASSERT(0 == accum || SkScalarIsNaN(accum));
497
498 // value==value will be true iff value is not NaN
499 // TODO: is it faster to say !accum or accum==accum?
500 return !SkScalarIsNaN(accum);
501 }
502
xSkRect503 SkScalar x() const { return fLeft; }
ySkRect504 SkScalar y() const { return fTop; }
leftSkRect505 SkScalar left() const { return fLeft; }
topSkRect506 SkScalar top() const { return fTop; }
rightSkRect507 SkScalar right() const { return fRight; }
bottomSkRect508 SkScalar bottom() const { return fBottom; }
widthSkRect509 SkScalar width() const { return fRight - fLeft; }
heightSkRect510 SkScalar height() const { return fBottom - fTop; }
centerXSkRect511 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
centerYSkRect512 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
513
514 friend bool operator==(const SkRect& a, const SkRect& b) {
515 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
516 }
517
518 friend bool operator!=(const SkRect& a, const SkRect& b) {
519 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
520 }
521
522 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
523 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
524 */
525 void toQuad(SkPoint quad[4]) const;
526
527 /** Set this rectangle to the empty rectangle (0,0,0,0)
528 */
setEmptySkRect529 void setEmpty() { *this = MakeEmpty(); }
530
setSkRect531 void set(const SkIRect& src) {
532 fLeft = SkIntToScalar(src.fLeft);
533 fTop = SkIntToScalar(src.fTop);
534 fRight = SkIntToScalar(src.fRight);
535 fBottom = SkIntToScalar(src.fBottom);
536 }
537
setSkRect538 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
539 fLeft = left;
540 fTop = top;
541 fRight = right;
542 fBottom = bottom;
543 }
544 // alias for set(l, t, r, b)
setLTRBSkRect545 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
546 this->set(left, top, right, bottom);
547 }
548
549 /** Initialize the rect with the 4 specified integers. The routine handles
550 converting them to scalars (by calling SkIntToScalar)
551 */
isetSkRect552 void iset(int left, int top, int right, int bottom) {
553 fLeft = SkIntToScalar(left);
554 fTop = SkIntToScalar(top);
555 fRight = SkIntToScalar(right);
556 fBottom = SkIntToScalar(bottom);
557 }
558
559 /**
560 * Set this rectangle to be left/top at 0,0, and have the specified width
561 * and height (automatically converted to SkScalar).
562 */
isetWHSkRect563 void isetWH(int width, int height) {
564 fLeft = fTop = 0;
565 fRight = SkIntToScalar(width);
566 fBottom = SkIntToScalar(height);
567 }
568
569 /** Set this rectangle to be the bounds of the array of points.
570 If the array is empty (count == 0), then set this rectangle
571 to the empty rectangle (0,0,0,0)
572 */
setSkRect573 void set(const SkPoint pts[], int count) {
574 // set() had been checking for non-finite values, so keep that behavior
575 // for now. Now that we have setBoundsCheck(), we may decide to make
576 // set() be simpler/faster, and not check for those.
577 (void)this->setBoundsCheck(pts, count);
578 }
579
580 // alias for set(pts, count)
setBoundsSkRect581 void setBounds(const SkPoint pts[], int count) {
582 (void)this->setBoundsCheck(pts, count);
583 }
584
585 /**
586 * Compute the bounds of the array of points, and set this rect to that
587 * bounds and return true... unless a non-finite value is encountered,
588 * in which case this rect is set to empty and false is returned.
589 */
590 bool setBoundsCheck(const SkPoint pts[], int count);
591
setSkRect592 void set(const SkPoint& p0, const SkPoint& p1) {
593 fLeft = SkMinScalar(p0.fX, p1.fX);
594 fRight = SkMaxScalar(p0.fX, p1.fX);
595 fTop = SkMinScalar(p0.fY, p1.fY);
596 fBottom = SkMaxScalar(p0.fY, p1.fY);
597 }
598
setXYWHSkRect599 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
600 fLeft = x;
601 fTop = y;
602 fRight = x + width;
603 fBottom = y + height;
604 }
605
setWHSkRect606 void setWH(SkScalar width, SkScalar height) {
607 fLeft = 0;
608 fTop = 0;
609 fRight = width;
610 fBottom = height;
611 }
612
613 /**
614 * Make the largest representable rectangle
615 */
setLargestSkRect616 void setLargest() {
617 fLeft = fTop = SK_ScalarMin;
618 fRight = fBottom = SK_ScalarMax;
619 }
620
621 /**
622 * Make the largest representable rectangle, but inverted (e.g. fLeft will
623 * be max and right will be min).
624 */
setLargestInvertedSkRect625 void setLargestInverted() {
626 fLeft = fTop = SK_ScalarMax;
627 fRight = fBottom = SK_ScalarMin;
628 }
629
630 /**
631 * Return a new Rect, built as an offset of this rect.
632 */
makeOffsetSkRect633 SkRect makeOffset(SkScalar dx, SkScalar dy) const {
634 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
635 }
636
637 /**
638 * Return a new Rect, built as an inset of this rect.
639 */
makeInsetSkRect640 SkRect makeInset(SkScalar dx, SkScalar dy) const {
641 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
642 }
643
644 /**
645 * Return a new Rect, built as an outset of this rect.
646 */
makeOutsetSkRect647 SkRect makeOutset(SkScalar dx, SkScalar dy) const {
648 return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
649 }
650
651 /** Offset set the rectangle by adding dx to its left and right,
652 and adding dy to its top and bottom.
653 */
offsetSkRect654 void offset(SkScalar dx, SkScalar dy) {
655 fLeft += dx;
656 fTop += dy;
657 fRight += dx;
658 fBottom += dy;
659 }
660
offsetSkRect661 void offset(const SkPoint& delta) {
662 this->offset(delta.fX, delta.fY);
663 }
664
665 /**
666 * Offset this rect such its new x() and y() will equal newX and newY.
667 */
offsetToSkRect668 void offsetTo(SkScalar newX, SkScalar newY) {
669 fRight += newX - fLeft;
670 fBottom += newY - fTop;
671 fLeft = newX;
672 fTop = newY;
673 }
674
675 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
676 moved inwards, making the rectangle narrower. If dx is negative, then
677 the sides are moved outwards, making the rectangle wider. The same holds
678 true for dy and the top and bottom.
679 */
insetSkRect680 void inset(SkScalar dx, SkScalar dy) {
681 fLeft += dx;
682 fTop += dy;
683 fRight -= dx;
684 fBottom -= dy;
685 }
686
687 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
688 moved outwards, making the rectangle wider. If dx is negative, then the
689 sides are moved inwards, making the rectangle narrower. The same holds
690 true for dy and the top and bottom.
691 */
outsetSkRect692 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
693
694 /** If this rectangle intersects r, return true and set this rectangle to that
695 intersection, otherwise return false and do not change this rectangle.
696 If either rectangle is empty, do nothing and return false.
697 */
698 bool intersect(const SkRect& r);
699
700 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
701 return true and set this rectangle to that intersection, otherwise return false
702 and do not change this rectangle.
703 If either rectangle is empty, do nothing and return false.
704 */
705 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
706
707 /**
708 * If rectangles a and b intersect, return true and set this rectangle to
709 * that intersection, otherwise return false and do not change this
710 * rectangle. If either rectangle is empty, do nothing and return false.
711 */
712 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& a, const SkRect& b);
713
714
715 private:
IntersectsSkRect716 static bool Intersects(SkScalar al, SkScalar at, SkScalar ar, SkScalar ab,
717 SkScalar bl, SkScalar bt, SkScalar br, SkScalar bb) {
718 SkScalar L = SkMaxScalar(al, bl);
719 SkScalar R = SkMinScalar(ar, br);
720 SkScalar T = SkMaxScalar(at, bt);
721 SkScalar B = SkMinScalar(ab, bb);
722 return L < R && T < B;
723 }
724
725 public:
726 /**
727 * Return true if this rectangle is not empty, and the specified sides of
728 * a rectangle are not empty, and they intersect.
729 */
intersectsSkRect730 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
731 return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom);
732 }
733
intersectsSkRect734 bool intersects(const SkRect& r) const {
735 return Intersects(fLeft, fTop, fRight, fBottom,
736 r.fLeft, r.fTop, r.fRight, r.fBottom);
737 }
738
739 /**
740 * Return true if rectangles a and b are not empty and intersect.
741 */
IntersectsSkRect742 static bool Intersects(const SkRect& a, const SkRect& b) {
743 return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom,
744 b.fLeft, b.fTop, b.fRight, b.fBottom);
745 }
746
747 /**
748 * Update this rectangle to enclose itself and the specified rectangle.
749 * If this rectangle is empty, just set it to the specified rectangle.
750 * If the specified rectangle is empty, do nothing.
751 */
752 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
753
754 /** Update this rectangle to enclose itself and the specified rectangle.
755 If this rectangle is empty, just set it to the specified rectangle. If the specified
756 rectangle is empty, do nothing.
757 */
joinSkRect758 void join(const SkRect& r) {
759 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
760 }
761
joinNonEmptyArgSkRect762 void joinNonEmptyArg(const SkRect& r) {
763 SkASSERT(!r.isEmpty());
764 // if we are empty, just assign
765 if (fLeft >= fRight || fTop >= fBottom) {
766 *this = r;
767 } else {
768 this->joinPossiblyEmptyRect(r);
769 }
770 }
771
772 /**
773 * Joins the rectangle with another without checking if either are empty (may produce unexpected
774 * results if either rect is inverted).
775 */
joinPossiblyEmptyRectSkRect776 void joinPossiblyEmptyRect(const SkRect& r) {
777 fLeft = SkMinScalar(fLeft, r.left());
778 fTop = SkMinScalar(fTop, r.top());
779 fRight = SkMaxScalar(fRight, r.right());
780 fBottom = SkMaxScalar(fBottom, r.bottom());
781 }
782
783 /**
784 * Grow the rect to include the specified (x,y). After this call, the
785 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
786 *
787 * This is close, but not quite the same contract as contains(), since
788 * contains() treats the left and top different from the right and bottom.
789 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
790 * that contains(x,y) always returns false if the rect is empty.
791 */
growToIncludeSkRect792 void growToInclude(SkScalar x, SkScalar y) {
793 fLeft = SkMinScalar(x, fLeft);
794 fRight = SkMaxScalar(x, fRight);
795 fTop = SkMinScalar(y, fTop);
796 fBottom = SkMaxScalar(y, fBottom);
797 }
798
799 /** Bulk version of growToInclude */
growToIncludeSkRect800 void growToInclude(const SkPoint pts[], int count) {
801 this->growToInclude(pts, sizeof(SkPoint), count);
802 }
803
804 /** Bulk version of growToInclude with stride. */
growToIncludeSkRect805 void growToInclude(const SkPoint pts[], size_t stride, int count) {
806 SkASSERT(count >= 0);
807 SkASSERT(stride >= sizeof(SkPoint));
808 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
809 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
810 this->growToInclude(pts->fX, pts->fY);
811 }
812 }
813
814 /**
815 * Return true if this rectangle contains r, and if both rectangles are
816 * not empty.
817 */
containsSkRect818 bool contains(const SkRect& r) const {
819 // todo: can we eliminate the this->isEmpty check?
820 return !r.isEmpty() && !this->isEmpty() &&
821 fLeft <= r.fLeft && fTop <= r.fTop &&
822 fRight >= r.fRight && fBottom >= r.fBottom;
823 }
824
825 /**
826 * Returns true if the specified rectangle r is inside or equal to this rectangle.
827 */
containsSkRect828 bool contains(const SkIRect& r) const {
829 // todo: can we eliminate the this->isEmpty check?
830 return !r.isEmpty() && !this->isEmpty() &&
831 fLeft <= SkIntToScalar(r.fLeft) && fTop <= SkIntToScalar(r.fTop) &&
832 fRight >= SkIntToScalar(r.fRight) && fBottom >= SkIntToScalar(r.fBottom);
833 }
834
835 /**
836 * Set the dst rectangle by rounding this rectangle's coordinates to their
837 * nearest integer values using SkScalarRoundToInt.
838 */
roundSkRect839 void round(SkIRect* dst) const {
840 SkASSERT(dst);
841 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
842 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
843 }
844
845 /**
846 * Set the dst rectangle by rounding "out" this rectangle, choosing the
847 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom.
848 */
roundOutSkRect849 void roundOut(SkIRect* dst) const {
850 SkASSERT(dst);
851 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
852 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
853 }
854
855 /**
856 * Set the dst rectangle by rounding "out" this rectangle, choosing the
857 * SkScalarFloorToScalar of top and left, and the SkScalarCeilToScalar of right and bottom.
858 *
859 * It is safe for this == dst
860 */
roundOutSkRect861 void roundOut(SkRect* dst) const {
862 dst->set(SkScalarFloorToScalar(fLeft),
863 SkScalarFloorToScalar(fTop),
864 SkScalarCeilToScalar(fRight),
865 SkScalarCeilToScalar(fBottom));
866 }
867
868 /**
869 * Set the dst rectangle by rounding "in" this rectangle, choosing the
870 * ceil of top and left, and the floor of right and bottom. This does *not*
871 * call sort(), so it is possible that the resulting rect is inverted...
872 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
873 */
roundInSkRect874 void roundIn(SkIRect* dst) const {
875 SkASSERT(dst);
876 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
877 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
878 }
879
880 //! Returns the result of calling round(&dst)
roundSkRect881 SkIRect round() const {
882 SkIRect ir;
883 this->round(&ir);
884 return ir;
885 }
886
887 //! Returns the result of calling roundOut(&dst)
roundOutSkRect888 SkIRect roundOut() const {
889 SkIRect ir;
890 this->roundOut(&ir);
891 return ir;
892 }
893
894 /**
895 * Swap top/bottom or left/right if there are flipped (i.e. if width()
896 * or height() would have returned a negative value.) This should be called
897 * if the edges are computed separately, and may have crossed over each
898 * other. When this returns, left <= right && top <= bottom
899 */
sortSkRect900 void sort() {
901 if (fLeft > fRight) {
902 SkTSwap<SkScalar>(fLeft, fRight);
903 }
904
905 if (fTop > fBottom) {
906 SkTSwap<SkScalar>(fTop, fBottom);
907 }
908 }
909
910 /**
911 * Return a new Rect that is the sorted version of this rect (left <= right, top <= bottom).
912 */
makeSortedSkRect913 SkRect makeSorted() const {
914 return MakeLTRB(SkMinScalar(fLeft, fRight), SkMinScalar(fTop, fBottom),
915 SkMaxScalar(fLeft, fRight), SkMaxScalar(fTop, fBottom));
916 }
917
918 /**
919 * cast-safe way to treat the rect as an array of (4) SkScalars.
920 */
asScalarsSkRect921 const SkScalar* asScalars() const { return &fLeft; }
922
923 void dump(bool asHex) const;
dumpSkRect924 void dump() const { this->dump(false); }
dumpHexSkRect925 void dumpHex() const { this->dump(true); }
926 };
927
contains(const SkRect & r)928 inline bool SkIRect::contains(const SkRect& r) const {
929 return !r.isEmpty() && !this->isEmpty() && // check for empties
930 (SkScalar)fLeft <= r.fLeft && (SkScalar)fTop <= r.fTop &&
931 (SkScalar)fRight >= r.fRight && (SkScalar)fBottom >= r.fBottom;
932 }
933
934 #endif
935