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 23 static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() { 24 SkIRect r; 25 r.setEmpty(); 26 return r; 27 } 28 29 static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() { 30 SkIRect r; 31 r.setLargest(); 32 return r; 33 } 34 35 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 41 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 47 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 53 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 59 int left() const { return fLeft; } 60 int top() const { return fTop; } 61 int right() const { return fRight; } 62 int bottom() const { return fBottom; } 63 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; } 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 */ 72 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 */ 78 int height() const { return fBottom - fTop; } 79 80 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 */ 89 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 */ 98 int centerY() const { return (fBottom + fTop) >> 1; } 99 100 /** 101 * Return true if the rectangle's width or height are <= 0 102 */ 103 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 104 105 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 118 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 */ 125 void setEmpty() { memset(this, 0, sizeof(*this)); } 126 127 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) 134 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) { 135 this->set(left, top, right, bottom); 136 } 137 138 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 */ 148 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 */ 157 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 */ 165 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 */ 172 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 */ 179 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 */ 186 void offset(int32_t dx, int32_t dy) { 187 fLeft += dx; 188 fTop += dy; 189 fRight += dx; 190 fBottom += dy; 191 } 192 193 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 */ 200 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 */ 211 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 */ 223 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); } 224 225 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 */ 234 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 */ 242 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 */ 250 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 */ 266 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 275 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 */ 283 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 */ 291 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 */ 311 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 */ 330 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 */ 344 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 */ 353 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 */ 370 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 */ 379 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 */ 391 SkIRect makeSorted() const { 392 return MakeLTRB(SkMin32(fLeft, fRight), SkMin32(fTop, fBottom), 393 SkMax32(fLeft, fRight), SkMax32(fTop, fBottom)); 394 } 395 396 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 407 static constexpr SkRect SK_WARN_UNUSED_RESULT MakeEmpty() { 408 return SkRect{0, 0, 0, 0}; 409 } 410 411 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() { 412 SkRect r; 413 r.setLargest(); 414 return r; 415 } 416 417 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 423 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 429 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 435 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 440 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()") 447 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 456 static SkRect Make(const SkISize& size) { 457 return MakeIWH(size.width(), size.height()); 458 } 459 460 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 */ 472 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 473 474 /** 475 * Return true if the rectangle's width and height are >= 0 476 */ 477 bool isSorted() const { return fLeft <= fRight && fTop <= fBottom; } 478 479 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 */ 488 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 503 SkScalar x() const { return fLeft; } 504 SkScalar y() const { return fTop; } 505 SkScalar left() const { return fLeft; } 506 SkScalar top() const { return fTop; } 507 SkScalar right() const { return fRight; } 508 SkScalar bottom() const { return fBottom; } 509 SkScalar width() const { return fRight - fLeft; } 510 SkScalar height() const { return fBottom - fTop; } 511 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } 512 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 */ 529 void setEmpty() { *this = MakeEmpty(); } 530 531 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 538 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) 545 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 */ 552 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 */ 563 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 */ 573 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) 581 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 592 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 599 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 606 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 */ 616 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 */ 625 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 */ 633 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 */ 640 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 */ 647 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 */ 654 void offset(SkScalar dx, SkScalar dy) { 655 fLeft += dx; 656 fTop += dy; 657 fRight += dx; 658 fBottom += dy; 659 } 660 661 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 */ 668 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 */ 680 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 */ 692 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: 716 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 */ 730 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const { 731 return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom); 732 } 733 734 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 */ 742 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 */ 758 void join(const SkRect& r) { 759 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 760 } 761 762 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 */ 776 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 */ 792 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 */ 800 void growToInclude(const SkPoint pts[], int count) { 801 this->growToInclude(pts, sizeof(SkPoint), count); 802 } 803 804 /** Bulk version of growToInclude with stride. */ 805 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 */ 818 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 */ 828 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 */ 839 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 */ 849 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 */ 861 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 */ 874 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) 881 SkIRect round() const { 882 SkIRect ir; 883 this->round(&ir); 884 return ir; 885 } 886 887 //! Returns the result of calling roundOut(&dst) 888 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 */ 900 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 */ 913 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 */ 921 const SkScalar* asScalars() const { return &fLeft; } 922 923 void dump(bool asHex) const; 924 void dump() const { this->dump(false); } 925 void dumpHex() const { this->dump(true); } 926 }; 927 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