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 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 */ 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 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 */ 345 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 */ 354 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 */ 371 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 382 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 393 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() { 394 SkRect r; 395 r.setEmpty(); 396 return r; 397 } 398 399 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() { 400 SkRect r; 401 r.setLargest(); 402 return r; 403 } 404 405 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 411 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 417 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 423 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 429 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()") 436 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 445 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 */ 457 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 458 459 bool isLargest() const { return SK_ScalarMin == fLeft && 460 SK_ScalarMin == fTop && 461 SK_ScalarMax == fRight && 462 SK_ScalarMax == fBottom; } 463 464 /** 465 * Returns true iff all values in the rect are finite. If any are 466 * infinite or NaN then this returns false. 467 */ 468 bool isFinite() const { 469 float accum = 0; 470 accum *= fLeft; 471 accum *= fTop; 472 accum *= fRight; 473 accum *= fBottom; 474 475 // accum is either NaN or it is finite (zero). 476 SkASSERT(0 == accum || SkScalarIsNaN(accum)); 477 478 // value==value will be true iff value is not NaN 479 // TODO: is it faster to say !accum or accum==accum? 480 return !SkScalarIsNaN(accum); 481 } 482 483 SkScalar x() const { return fLeft; } 484 SkScalar y() const { return fTop; } 485 SkScalar left() const { return fLeft; } 486 SkScalar top() const { return fTop; } 487 SkScalar right() const { return fRight; } 488 SkScalar bottom() const { return fBottom; } 489 SkScalar width() const { return fRight - fLeft; } 490 SkScalar height() const { return fBottom - fTop; } 491 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } 492 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); } 493 494 friend bool operator==(const SkRect& a, const SkRect& b) { 495 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); 496 } 497 498 friend bool operator!=(const SkRect& a, const SkRect& b) { 499 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); 500 } 501 502 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right, 503 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW. 504 */ 505 void toQuad(SkPoint quad[4]) const; 506 507 /** Set this rectangle to the empty rectangle (0,0,0,0) 508 */ 509 void setEmpty() { memset(this, 0, sizeof(*this)); } 510 511 void set(const SkIRect& src) { 512 fLeft = SkIntToScalar(src.fLeft); 513 fTop = SkIntToScalar(src.fTop); 514 fRight = SkIntToScalar(src.fRight); 515 fBottom = SkIntToScalar(src.fBottom); 516 } 517 518 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { 519 fLeft = left; 520 fTop = top; 521 fRight = right; 522 fBottom = bottom; 523 } 524 // alias for set(l, t, r, b) 525 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { 526 this->set(left, top, right, bottom); 527 } 528 529 /** Initialize the rect with the 4 specified integers. The routine handles 530 converting them to scalars (by calling SkIntToScalar) 531 */ 532 void iset(int left, int top, int right, int bottom) { 533 fLeft = SkIntToScalar(left); 534 fTop = SkIntToScalar(top); 535 fRight = SkIntToScalar(right); 536 fBottom = SkIntToScalar(bottom); 537 } 538 539 /** 540 * Set this rectangle to be left/top at 0,0, and have the specified width 541 * and height (automatically converted to SkScalar). 542 */ 543 void isetWH(int width, int height) { 544 fLeft = fTop = 0; 545 fRight = SkIntToScalar(width); 546 fBottom = SkIntToScalar(height); 547 } 548 549 /** Set this rectangle to be the bounds of the array of points. 550 If the array is empty (count == 0), then set this rectangle 551 to the empty rectangle (0,0,0,0) 552 */ 553 void set(const SkPoint pts[], int count) { 554 // set() had been checking for non-finite values, so keep that behavior 555 // for now. Now that we have setBoundsCheck(), we may decide to make 556 // set() be simpler/faster, and not check for those. 557 (void)this->setBoundsCheck(pts, count); 558 } 559 560 // alias for set(pts, count) 561 void setBounds(const SkPoint pts[], int count) { 562 (void)this->setBoundsCheck(pts, count); 563 } 564 565 /** 566 * Compute the bounds of the array of points, and set this rect to that 567 * bounds and return true... unless a non-finite value is encountered, 568 * in which case this rect is set to empty and false is returned. 569 */ 570 bool setBoundsCheck(const SkPoint pts[], int count); 571 572 void set(const SkPoint& p0, const SkPoint& p1) { 573 fLeft = SkMinScalar(p0.fX, p1.fX); 574 fRight = SkMaxScalar(p0.fX, p1.fX); 575 fTop = SkMinScalar(p0.fY, p1.fY); 576 fBottom = SkMaxScalar(p0.fY, p1.fY); 577 } 578 579 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) { 580 fLeft = x; 581 fTop = y; 582 fRight = x + width; 583 fBottom = y + height; 584 } 585 586 void setWH(SkScalar width, SkScalar height) { 587 fLeft = 0; 588 fTop = 0; 589 fRight = width; 590 fBottom = height; 591 } 592 593 /** 594 * Make the largest representable rectangle 595 */ 596 void setLargest() { 597 fLeft = fTop = SK_ScalarMin; 598 fRight = fBottom = SK_ScalarMax; 599 } 600 601 /** 602 * Make the largest representable rectangle, but inverted (e.g. fLeft will 603 * be max and right will be min). 604 */ 605 void setLargestInverted() { 606 fLeft = fTop = SK_ScalarMax; 607 fRight = fBottom = SK_ScalarMin; 608 } 609 610 /** 611 * Return a new Rect, built as an offset of this rect. 612 */ 613 SkRect makeOffset(SkScalar dx, SkScalar dy) const { 614 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy); 615 } 616 617 /** 618 * Return a new Rect, built as an inset of this rect. 619 */ 620 SkRect makeInset(SkScalar dx, SkScalar dy) const { 621 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy); 622 } 623 624 /** 625 * Return a new Rect, built as an outset of this rect. 626 */ 627 SkRect makeOutset(SkScalar dx, SkScalar dy) const { 628 return MakeLTRB(fLeft - dx, fTop - dy, fRight + dx, fBottom + dy); 629 } 630 631 /** Offset set the rectangle by adding dx to its left and right, 632 and adding dy to its top and bottom. 633 */ 634 void offset(SkScalar dx, SkScalar dy) { 635 fLeft += dx; 636 fTop += dy; 637 fRight += dx; 638 fBottom += dy; 639 } 640 641 void offset(const SkPoint& delta) { 642 this->offset(delta.fX, delta.fY); 643 } 644 645 /** 646 * Offset this rect such its new x() and y() will equal newX and newY. 647 */ 648 void offsetTo(SkScalar newX, SkScalar newY) { 649 fRight += newX - fLeft; 650 fBottom += newY - fTop; 651 fLeft = newX; 652 fTop = newY; 653 } 654 655 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are 656 moved inwards, making the rectangle narrower. If dx is negative, then 657 the sides are moved outwards, making the rectangle wider. The same holds 658 true for dy and the top and bottom. 659 */ 660 void inset(SkScalar dx, SkScalar dy) { 661 fLeft += dx; 662 fTop += dy; 663 fRight -= dx; 664 fBottom -= dy; 665 } 666 667 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are 668 moved outwards, making the rectangle wider. If dx is negative, then the 669 sides are moved inwards, making the rectangle narrower. The same holds 670 true for dy and the top and bottom. 671 */ 672 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); } 673 674 /** If this rectangle intersects r, return true and set this rectangle to that 675 intersection, otherwise return false and do not change this rectangle. 676 If either rectangle is empty, do nothing and return false. 677 */ 678 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& r); 679 680 /** If this rectangle intersects the rectangle specified by left, top, right, bottom, 681 return true and set this rectangle to that intersection, otherwise return false 682 and do not change this rectangle. 683 If either rectangle is empty, do nothing and return false. 684 */ 685 bool SK_WARN_UNUSED_RESULT intersect(SkScalar left, SkScalar top, 686 SkScalar right, SkScalar bottom); 687 688 /** 689 * If rectangles a and b intersect, return true and set this rectangle to 690 * that intersection, otherwise return false and do not change this 691 * rectangle. If either rectangle is empty, do nothing and return false. 692 */ 693 bool SK_WARN_UNUSED_RESULT intersect(const SkRect& a, const SkRect& b); 694 695 696 private: 697 static bool Intersects(SkScalar al, SkScalar at, SkScalar ar, SkScalar ab, 698 SkScalar bl, SkScalar bt, SkScalar br, SkScalar bb) { 699 SkScalar L = SkMaxScalar(al, bl); 700 SkScalar R = SkMinScalar(ar, br); 701 SkScalar T = SkMaxScalar(at, bt); 702 SkScalar B = SkMinScalar(ab, bb); 703 return L < R && T < B; 704 } 705 706 public: 707 /** 708 * Return true if this rectangle is not empty, and the specified sides of 709 * a rectangle are not empty, and they intersect. 710 */ 711 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const { 712 return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom); 713 } 714 715 bool intersects(const SkRect& r) const { 716 return Intersects(fLeft, fTop, fRight, fBottom, 717 r.fLeft, r.fTop, r.fRight, r.fBottom); 718 } 719 720 /** 721 * Return true if rectangles a and b are not empty and intersect. 722 */ 723 static bool Intersects(const SkRect& a, const SkRect& b) { 724 return Intersects(a.fLeft, a.fTop, a.fRight, a.fBottom, 725 b.fLeft, b.fTop, b.fRight, b.fBottom); 726 } 727 728 /** 729 * Update this rectangle to enclose itself and the specified rectangle. 730 * If this rectangle is empty, just set it to the specified rectangle. 731 * If the specified rectangle is empty, do nothing. 732 */ 733 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); 734 735 /** Update this rectangle to enclose itself and the specified rectangle. 736 If this rectangle is empty, just set it to the specified rectangle. If the specified 737 rectangle is empty, do nothing. 738 */ 739 void join(const SkRect& r) { 740 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 741 } 742 743 void joinNonEmptyArg(const SkRect& r) { 744 SkASSERT(!r.isEmpty()); 745 // if we are empty, just assign 746 if (fLeft >= fRight || fTop >= fBottom) { 747 *this = r; 748 } else { 749 this->joinPossiblyEmptyRect(r); 750 } 751 } 752 753 /** 754 * Joins the rectangle with another without checking if either are empty (may produce unexpected 755 * results if either rect is inverted). 756 */ 757 void joinPossiblyEmptyRect(const SkRect& r) { 758 fLeft = SkMinScalar(fLeft, r.left()); 759 fTop = SkMinScalar(fTop, r.top()); 760 fRight = SkMaxScalar(fRight, r.right()); 761 fBottom = SkMaxScalar(fBottom, r.bottom()); 762 } 763 764 /** 765 * Grow the rect to include the specified (x,y). After this call, the 766 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom. 767 * 768 * This is close, but not quite the same contract as contains(), since 769 * contains() treats the left and top different from the right and bottom. 770 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note 771 * that contains(x,y) always returns false if the rect is empty. 772 */ 773 void growToInclude(SkScalar x, SkScalar y) { 774 fLeft = SkMinScalar(x, fLeft); 775 fRight = SkMaxScalar(x, fRight); 776 fTop = SkMinScalar(y, fTop); 777 fBottom = SkMaxScalar(y, fBottom); 778 } 779 780 /** Bulk version of growToInclude */ 781 void growToInclude(const SkPoint pts[], int count) { 782 this->growToInclude(pts, sizeof(SkPoint), count); 783 } 784 785 /** Bulk version of growToInclude with stride. */ 786 void growToInclude(const SkPoint pts[], size_t stride, int count) { 787 SkASSERT(count >= 0); 788 SkASSERT(stride >= sizeof(SkPoint)); 789 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride); 790 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) { 791 this->growToInclude(pts->fX, pts->fY); 792 } 793 } 794 795 /** 796 * Return true if this rectangle contains r, and if both rectangles are 797 * not empty. 798 */ 799 bool contains(const SkRect& r) const { 800 // todo: can we eliminate the this->isEmpty check? 801 return !r.isEmpty() && !this->isEmpty() && 802 fLeft <= r.fLeft && fTop <= r.fTop && 803 fRight >= r.fRight && fBottom >= r.fBottom; 804 } 805 806 /** 807 * Returns true if the specified rectangle r is inside or equal to this rectangle. 808 */ 809 bool contains(const SkIRect& r) const { 810 // todo: can we eliminate the this->isEmpty check? 811 return !r.isEmpty() && !this->isEmpty() && 812 fLeft <= SkIntToScalar(r.fLeft) && fTop <= SkIntToScalar(r.fTop) && 813 fRight >= SkIntToScalar(r.fRight) && fBottom >= SkIntToScalar(r.fBottom); 814 } 815 816 /** 817 * Set the dst rectangle by rounding this rectangle's coordinates to their 818 * nearest integer values using SkScalarRoundToInt. 819 */ 820 void round(SkIRect* dst) const { 821 SkASSERT(dst); 822 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop), 823 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom)); 824 } 825 826 /** 827 * Set the dst rectangle by rounding "out" this rectangle, choosing the 828 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom. 829 */ 830 void roundOut(SkIRect* dst) const { 831 SkASSERT(dst); 832 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop), 833 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom)); 834 } 835 836 /** 837 * Set the dst rectangle by rounding "out" this rectangle, choosing the 838 * SkScalarFloorToScalar of top and left, and the SkScalarCeilToScalar of right and bottom. 839 * 840 * It is safe for this == dst 841 */ 842 void roundOut(SkRect* dst) const { 843 dst->set(SkScalarFloorToScalar(fLeft), 844 SkScalarFloorToScalar(fTop), 845 SkScalarCeilToScalar(fRight), 846 SkScalarCeilToScalar(fBottom)); 847 } 848 849 /** 850 * Set the dst rectangle by rounding "in" this rectangle, choosing the 851 * ceil of top and left, and the floor of right and bottom. This does *not* 852 * call sort(), so it is possible that the resulting rect is inverted... 853 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that. 854 */ 855 void roundIn(SkIRect* dst) const { 856 SkASSERT(dst); 857 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop), 858 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom)); 859 } 860 861 //! Returns the result of calling round(&dst) 862 SkIRect round() const { 863 SkIRect ir; 864 this->round(&ir); 865 return ir; 866 } 867 868 //! Returns the result of calling roundOut(&dst) 869 SkIRect roundOut() const { 870 SkIRect ir; 871 this->roundOut(&ir); 872 return ir; 873 } 874 875 /** 876 * Swap top/bottom or left/right if there are flipped (i.e. if width() 877 * or height() would have returned a negative value.) This should be called 878 * if the edges are computed separately, and may have crossed over each 879 * other. When this returns, left <= right && top <= bottom 880 */ 881 void sort() { 882 if (fLeft > fRight) { 883 SkTSwap<SkScalar>(fLeft, fRight); 884 } 885 886 if (fTop > fBottom) { 887 SkTSwap<SkScalar>(fTop, fBottom); 888 } 889 } 890 891 /** 892 * cast-safe way to treat the rect as an array of (4) SkScalars. 893 */ 894 const SkScalar* asScalars() const { return &fLeft; } 895 896 void dump(bool asHex) const; 897 void dump() const { this->dump(false); } 898 void dumpHex() const { this->dump(true); } 899 }; 900 901 inline bool SkIRect::contains(const SkRect& r) const { 902 return !r.isEmpty() && !this->isEmpty() && // check for empties 903 (SkScalar)fLeft <= r.fLeft && (SkScalar)fTop <= r.fTop && 904 (SkScalar)fRight >= r.fRight && (SkScalar)fBottom >= r.fBottom; 905 } 906 907 #endif 908