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 (or SK_FixedNaN when SkScalar is fixed) then this 467 * returns false. 468 */ 469 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 484 SkScalar x() const { return fLeft; } 485 SkScalar y() const { return fTop; } 486 SkScalar left() const { return fLeft; } 487 SkScalar top() const { return fTop; } 488 SkScalar right() const { return fRight; } 489 SkScalar bottom() const { return fBottom; } 490 SkScalar width() const { return fRight - fLeft; } 491 SkScalar height() const { return fBottom - fTop; } 492 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } 493 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 */ 510 void setEmpty() { memset(this, 0, sizeof(*this)); } 511 512 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 519 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) 526 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 */ 533 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 */ 544 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 */ 554 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) 562 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 573 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 580 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 587 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 */ 597 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 */ 606 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 */ 614 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 */ 621 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 */ 628 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 */ 635 void offset(SkScalar dx, SkScalar dy) { 636 fLeft += dx; 637 fTop += dy; 638 fRight += dx; 639 fBottom += dy; 640 } 641 642 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 */ 649 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 */ 661 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 */ 673 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: 698 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 */ 712 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const { 713 return Intersects(fLeft, fTop, fRight, fBottom, left, top, right, bottom); 714 } 715 716 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 */ 724 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 */ 740 void join(const SkRect& r) { 741 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 742 } 743 744 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 */ 758 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 */ 774 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 */ 782 void growToInclude(const SkPoint pts[], int count) { 783 this->growToInclude(pts, sizeof(SkPoint), count); 784 } 785 786 /** Bulk version of growToInclude with stride. */ 787 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 */ 800 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 */ 810 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 */ 821 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 */ 839 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 */ 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 * cast-safe way to treat the rect as an array of (4) SkScalars. 912 */ 913 const SkScalar* asScalars() const { return &fLeft; } 914 915 void dump(bool asHex) const; 916 void dump() const { this->dump(false); } 917 void dumpHex() const { this->dump(true); } 918 }; 919 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