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 (or SK_FixedNaN when SkScalar is fixed) then this
467 * returns false.
468 */
isFiniteSkRect469 bool isFinite() const {
470 float accum = 0;
471 accum *= fLeft;
472 accum *= fTop;
473 accum *= fRight;
474 accum *= fBottom;
475
476 // accum is either NaN or it is finite (zero).
477 SkASSERT(0 == accum || SkScalarIsNaN(accum));
478
479 // value==value will be true iff value is not NaN
480 // TODO: is it faster to say !accum or accum==accum?
481 return !SkScalarIsNaN(accum);
482 }
483
xSkRect484 SkScalar x() const { return fLeft; }
ySkRect485 SkScalar y() const { return fTop; }
leftSkRect486 SkScalar left() const { return fLeft; }
topSkRect487 SkScalar top() const { return fTop; }
rightSkRect488 SkScalar right() const { return fRight; }
bottomSkRect489 SkScalar bottom() const { return fBottom; }
widthSkRect490 SkScalar width() const { return fRight - fLeft; }
heightSkRect491 SkScalar height() const { return fBottom - fTop; }
centerXSkRect492 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); }
centerYSkRect493 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); }
494
495 friend bool operator==(const SkRect& a, const SkRect& b) {
496 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
497 }
498
499 friend bool operator!=(const SkRect& a, const SkRect& b) {
500 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
501 }
502
503 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right,
504 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW.
505 */
506 void toQuad(SkPoint quad[4]) const;
507
508 /** Set this rectangle to the empty rectangle (0,0,0,0)
509 */
setEmptySkRect510 void setEmpty() { memset(this, 0, sizeof(*this)); }
511
setSkRect512 void set(const SkIRect& src) {
513 fLeft = SkIntToScalar(src.fLeft);
514 fTop = SkIntToScalar(src.fTop);
515 fRight = SkIntToScalar(src.fRight);
516 fBottom = SkIntToScalar(src.fBottom);
517 }
518
setSkRect519 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
520 fLeft = left;
521 fTop = top;
522 fRight = right;
523 fBottom = bottom;
524 }
525 // alias for set(l, t, r, b)
setLTRBSkRect526 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
527 this->set(left, top, right, bottom);
528 }
529
530 /** Initialize the rect with the 4 specified integers. The routine handles
531 converting them to scalars (by calling SkIntToScalar)
532 */
isetSkRect533 void iset(int left, int top, int right, int bottom) {
534 fLeft = SkIntToScalar(left);
535 fTop = SkIntToScalar(top);
536 fRight = SkIntToScalar(right);
537 fBottom = SkIntToScalar(bottom);
538 }
539
540 /**
541 * Set this rectangle to be left/top at 0,0, and have the specified width
542 * and height (automatically converted to SkScalar).
543 */
isetWHSkRect544 void isetWH(int width, int height) {
545 fLeft = fTop = 0;
546 fRight = SkIntToScalar(width);
547 fBottom = SkIntToScalar(height);
548 }
549
550 /** Set this rectangle to be the bounds of the array of points.
551 If the array is empty (count == 0), then set this rectangle
552 to the empty rectangle (0,0,0,0)
553 */
setSkRect554 void set(const SkPoint pts[], int count) {
555 // set() had been checking for non-finite values, so keep that behavior
556 // for now. Now that we have setBoundsCheck(), we may decide to make
557 // set() be simpler/faster, and not check for those.
558 (void)this->setBoundsCheck(pts, count);
559 }
560
561 // alias for set(pts, count)
setBoundsSkRect562 void setBounds(const SkPoint pts[], int count) {
563 (void)this->setBoundsCheck(pts, count);
564 }
565
566 /**
567 * Compute the bounds of the array of points, and set this rect to that
568 * bounds and return true... unless a non-finite value is encountered,
569 * in which case this rect is set to empty and false is returned.
570 */
571 bool setBoundsCheck(const SkPoint pts[], int count);
572
setSkRect573 void set(const SkPoint& p0, const SkPoint& p1) {
574 fLeft = SkMinScalar(p0.fX, p1.fX);
575 fRight = SkMaxScalar(p0.fX, p1.fX);
576 fTop = SkMinScalar(p0.fY, p1.fY);
577 fBottom = SkMaxScalar(p0.fY, p1.fY);
578 }
579
setXYWHSkRect580 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
581 fLeft = x;
582 fTop = y;
583 fRight = x + width;
584 fBottom = y + height;
585 }
586
setWHSkRect587 void setWH(SkScalar width, SkScalar height) {
588 fLeft = 0;
589 fTop = 0;
590 fRight = width;
591 fBottom = height;
592 }
593
594 /**
595 * Make the largest representable rectangle
596 */
setLargestSkRect597 void setLargest() {
598 fLeft = fTop = SK_ScalarMin;
599 fRight = fBottom = SK_ScalarMax;
600 }
601
602 /**
603 * Make the largest representable rectangle, but inverted (e.g. fLeft will
604 * be max and right will be min).
605 */
setLargestInvertedSkRect606 void setLargestInverted() {
607 fLeft = fTop = SK_ScalarMax;
608 fRight = fBottom = SK_ScalarMin;
609 }
610
611 /**
612 * Return a new Rect, built as an offset of this rect.
613 */
makeOffsetSkRect614 SkRect makeOffset(SkScalar dx, SkScalar dy) const {
615 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy);
616 }
617
618 /**
619 * Return a new Rect, built as an inset of this rect.
620 */
makeInsetSkRect621 SkRect makeInset(SkScalar dx, SkScalar dy) const {
622 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy);
623 }
624
625 /**
626 * Return a new Rect, built as an outset of this rect.
627 */
makeOutsetSkRect628 SkRect makeOutset(SkScalar dx, SkScalar dy) const {
629 return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy);
630 }
631
632 /** Offset set the rectangle by adding dx to its left and right,
633 and adding dy to its top and bottom.
634 */
offsetSkRect635 void offset(SkScalar dx, SkScalar dy) {
636 fLeft += dx;
637 fTop += dy;
638 fRight += dx;
639 fBottom += dy;
640 }
641
offsetSkRect642 void offset(const SkPoint& delta) {
643 this->offset(delta.fX, delta.fY);
644 }
645
646 /**
647 * Offset this rect such its new x() and y() will equal newX and newY.
648 */
offsetToSkRect649 void offsetTo(SkScalar newX, SkScalar newY) {
650 fRight += newX - fLeft;
651 fBottom += newY - fTop;
652 fLeft = newX;
653 fTop = newY;
654 }
655
656 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
657 moved inwards, making the rectangle narrower. If dx is negative, then
658 the sides are moved outwards, making the rectangle wider. The same holds
659 true for dy and the top and bottom.
660 */
insetSkRect661 void inset(SkScalar dx, SkScalar dy) {
662 fLeft += dx;
663 fTop += dy;
664 fRight -= dx;
665 fBottom -= dy;
666 }
667
668 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
669 moved outwards, making the rectangle wider. If dx is negative, then the
670 sides are moved inwards, making the rectangle narrower. The same holds
671 true for dy and the top and bottom.
672 */
outsetSkRect673 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); }
674
675 /** If this rectangle intersects r, return true and set this rectangle to that
676 intersection, otherwise return false and do not change this rectangle.
677 If either rectangle is empty, do nothing and return false.
678 */
679 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& r);
680
681 /** If this rectangle intersects the rectangle specified by left, top, right, bottom,
682 return true and set this rectangle to that intersection, otherwise return false
683 and do not change this rectangle.
684 If either rectangle is empty, do nothing and return false.
685 */
686 bool SK_WARN_UNUSED_RESULT intersect(SkScalar left, SkScalar top,
687 SkScalar right, SkScalar bottom);
688
689 /**
690 * If rectangles a and b intersect, return true and set this rectangle to
691 * that intersection, otherwise return false and do not change this
692 * rectangle. If either rectangle is empty, do nothing and return false.
693 */
694 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& a, const SkRect& b);
695
696
697 private:
IntersectsSkRect698 static bool Intersects(SkScalar al, SkScalar at, SkScalar ar, SkScalar ab,
699 SkScalar bl, SkScalar bt, SkScalar br, SkScalar bb) {
700 SkScalar L = SkMaxScalar(al, bl);
701 SkScalar R = SkMinScalar(ar, br);
702 SkScalar T = SkMaxScalar(at, bt);
703 SkScalar B = SkMinScalar(ab, bb);
704 return L < R && T < B;
705 }
706
707 public:
708 /**
709 * Return true if this rectangle is not empty, and the specified sides of
710 * a rectangle are not empty, and they intersect.
711 */
intersectsSkRect712 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const {
713 return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom);
714 }
715
intersectsSkRect716 bool intersects(const SkRect& r) const {
717 return Intersects(fLeft, fTop, fRight, fBottom,
718 r.fLeft, r.fTop, r.fRight, r.fBottom);
719 }
720
721 /**
722 * Return true if rectangles a and b are not empty and intersect.
723 */
IntersectsSkRect724 static bool Intersects(const SkRect& a, const SkRect& b) {
725 return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom,
726 b.fLeft, b.fTop, b.fRight, b.fBottom);
727 }
728
729 /**
730 * Update this rectangle to enclose itself and the specified rectangle.
731 * If this rectangle is empty, just set it to the specified rectangle.
732 * If the specified rectangle is empty, do nothing.
733 */
734 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
735
736 /** Update this rectangle to enclose itself and the specified rectangle.
737 If this rectangle is empty, just set it to the specified rectangle. If the specified
738 rectangle is empty, do nothing.
739 */
joinSkRect740 void join(const SkRect& r) {
741 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom);
742 }
743
joinNonEmptyArgSkRect744 void joinNonEmptyArg(const SkRect& r) {
745 SkASSERT(!r.isEmpty());
746 // if we are empty, just assign
747 if (fLeft >= fRight || fTop >= fBottom) {
748 *this = r;
749 } else {
750 this->joinPossiblyEmptyRect(r);
751 }
752 }
753
754 /**
755 * Joins the rectangle with another without checking if either are empty (may produce unexpected
756 * results if either rect is inverted).
757 */
joinPossiblyEmptyRectSkRect758 void joinPossiblyEmptyRect(const SkRect& r) {
759 fLeft = SkMinScalar(fLeft, r.left());
760 fTop = SkMinScalar(fTop, r.top());
761 fRight = SkMaxScalar(fRight, r.right());
762 fBottom = SkMaxScalar(fBottom, r.bottom());
763 }
764
765 /**
766 * Grow the rect to include the specified (x,y). After this call, the
767 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom.
768 *
769 * This is close, but not quite the same contract as contains(), since
770 * contains() treats the left and top different from the right and bottom.
771 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note
772 * that contains(x,y) always returns false if the rect is empty.
773 */
growToIncludeSkRect774 void growToInclude(SkScalar x, SkScalar y) {
775 fLeft = SkMinScalar(x, fLeft);
776 fRight = SkMaxScalar(x, fRight);
777 fTop = SkMinScalar(y, fTop);
778 fBottom = SkMaxScalar(y, fBottom);
779 }
780
781 /** Bulk version of growToInclude */
growToIncludeSkRect782 void growToInclude(const SkPoint pts[], int count) {
783 this->growToInclude(pts, sizeof(SkPoint), count);
784 }
785
786 /** Bulk version of growToInclude with stride. */
growToIncludeSkRect787 void growToInclude(const SkPoint pts[], size_t stride, int count) {
788 SkASSERT(count >= 0);
789 SkASSERT(stride >= sizeof(SkPoint));
790 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride);
791 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) {
792 this->growToInclude(pts->fX, pts->fY);
793 }
794 }
795
796 /**
797 * Return true if this rectangle contains r, and if both rectangles are
798 * not empty.
799 */
containsSkRect800 bool contains(const SkRect& r) const {
801 // todo: can we eliminate the this->isEmpty check?
802 return !r.isEmpty() && !this->isEmpty() &&
803 fLeft <= r.fLeft && fTop <= r.fTop &&
804 fRight >= r.fRight && fBottom >= r.fBottom;
805 }
806
807 /**
808 * Returns true if the specified rectangle r is inside or equal to this rectangle.
809 */
containsSkRect810 bool contains(const SkIRect& r) const {
811 // todo: can we eliminate the this->isEmpty check?
812 return !r.isEmpty() && !this->isEmpty() &&
813 fLeft <= SkIntToScalar(r.fLeft) && fTop <= SkIntToScalar(r.fTop) &&
814 fRight >= SkIntToScalar(r.fRight) && fBottom >= SkIntToScalar(r.fBottom);
815 }
816
817 /**
818 * Set the dst rectangle by rounding this rectangle's coordinates to their
819 * nearest integer values using SkScalarRoundToInt.
820 */
roundSkRect821 void round(SkIRect* dst) const {
822 SkASSERT(dst);
823 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
824 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
825 }
826
827 /**
828 * Variant of round() that explicitly performs the rounding step (i.e. floor(x + 0.5)) using
829 * double instead of SkScalar (float). It does this by calling SkDScalarRoundToInt(), which
830 * may be slower than calling SkScalarRountToInt(), but gives slightly more accurate results.
831 *
832 * e.g.
833 * SkScalar x = 0.49999997f;
834 * int ix = SkScalarRoundToInt(x);
835 * SkASSERT(0 == ix); // <--- fails
836 * ix = SkDScalarRoundToInt(x);
837 * SkASSERT(0 == ix); // <--- succeeds
838 */
droundSkRect839 void dround(SkIRect* dst) const {
840 SkASSERT(dst);
841 dst->set(SkDScalarRoundToInt(fLeft), SkDScalarRoundToInt(fTop),
842 SkDScalarRoundToInt(fRight), SkDScalarRoundToInt(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 * cast-safe way to treat the rect as an array of (4) SkScalars.
912 */
asScalarsSkRect913 const SkScalar* asScalars() const { return &fLeft; }
914
915 void dump(bool asHex) const;
dumpSkRect916 void dump() const { this->dump(false); }
dumpHexSkRect917 void dumpHex() const { this->dump(true); }
918 };
919
contains(const SkRect & r)920 inline bool SkIRect::contains(const SkRect& r) const {
921 return !r.isEmpty() && !this->isEmpty() && // check for empties
922 (SkScalar)fLeft <= r.fLeft && (SkScalar)fTop <= r.fTop &&
923 (SkScalar)fRight >= r.fRight && (SkScalar)fBottom >= r.fBottom;
924 }
925
926 #endif
927