1 2/* 3 * Copyright 2006 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#ifndef SkRect_DEFINED 11#define SkRect_DEFINED 12 13#include "SkPoint.h" 14#include "SkSize.h" 15 16/** \struct SkIRect 17 18 SkIRect holds four 32 bit integer coordinates for a rectangle 19*/ 20struct 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 /** 81 * Since the center of an integer rect may fall on a factional value, this 82 * method is defined to return (right + left) >> 1. 83 * 84 * This is a specific "truncation" of the average, which is different than 85 * (right + left) / 2 when the sum is negative. 86 */ 87 int centerX() const { return (fRight + fLeft) >> 1; } 88 89 /** 90 * Since the center of an integer rect may fall on a factional value, this 91 * method is defined to return (bottom + top) >> 1 92 * 93 * This is a specific "truncation" of the average, which is different than 94 * (bottom + top) / 2 when the sum is negative. 95 */ 96 int centerY() const { return (fBottom + fTop) >> 1; } 97 98 /** 99 * Return true if the rectangle's width or height are <= 0 100 */ 101 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 102 103 bool isLargest() const { return SK_MinS32 == fLeft && 104 SK_MinS32 == fTop && 105 SK_MaxS32 == fRight && 106 SK_MaxS32 == fBottom; } 107 108 friend bool operator==(const SkIRect& a, const SkIRect& b) { 109 return !memcmp(&a, &b, sizeof(a)); 110 } 111 112 friend bool operator!=(const SkIRect& a, const SkIRect& b) { 113 return !(a == b); 114 } 115 116 bool is16Bit() const { 117 return SkIsS16(fLeft) && SkIsS16(fTop) && 118 SkIsS16(fRight) && SkIsS16(fBottom); 119 } 120 121 /** Set the rectangle to (0,0,0,0) 122 */ 123 void setEmpty() { memset(this, 0, sizeof(*this)); } 124 125 void set(int32_t left, int32_t top, int32_t right, int32_t bottom) { 126 fLeft = left; 127 fTop = top; 128 fRight = right; 129 fBottom = bottom; 130 } 131 // alias for set(l, t, r, b) 132 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) { 133 this->set(left, top, right, bottom); 134 } 135 136 void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height) { 137 fLeft = x; 138 fTop = y; 139 fRight = x + width; 140 fBottom = y + height; 141 } 142 143 /** 144 * Make the largest representable rectangle 145 */ 146 void setLargest() { 147 fLeft = fTop = SK_MinS32; 148 fRight = fBottom = SK_MaxS32; 149 } 150 151 /** 152 * Make the largest representable rectangle, but inverted (e.g. fLeft will 153 * be max 32bit and right will be min 32bit). 154 */ 155 void setLargestInverted() { 156 fLeft = fTop = SK_MaxS32; 157 fRight = fBottom = SK_MinS32; 158 } 159 160 /** 161 * Return a new IRect, built as an offset of this rect. 162 */ 163 SkIRect makeOffset(int dx, int dy) const { 164 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy); 165 } 166 167 /** 168 * Return a new IRect, built as an inset of this rect. 169 */ 170 SkIRect makeInset(int dx, int dy) const { 171 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy); 172 } 173 174 /** Offset set the rectangle by adding dx to its left and right, 175 and adding dy to its top and bottom. 176 */ 177 void offset(int32_t dx, int32_t dy) { 178 fLeft += dx; 179 fTop += dy; 180 fRight += dx; 181 fBottom += dy; 182 } 183 184 void offset(const SkIPoint& delta) { 185 this->offset(delta.fX, delta.fY); 186 } 187 188 /** 189 * Offset this rect such its new x() and y() will equal newX and newY. 190 */ 191 void offsetTo(int32_t newX, int32_t newY) { 192 fRight += newX - fLeft; 193 fBottom += newY - fTop; 194 fLeft = newX; 195 fTop = newY; 196 } 197 198 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards, 199 making the rectangle narrower. If dx is negative, then the sides are moved outwards, 200 making the rectangle wider. The same holds true for dy and the top and bottom. 201 */ 202 void inset(int32_t dx, int32_t dy) { 203 fLeft += dx; 204 fTop += dy; 205 fRight -= dx; 206 fBottom -= dy; 207 } 208 209 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are 210 moved outwards, making the rectangle wider. If dx is negative, then the 211 sides are moved inwards, making the rectangle narrower. The same holds 212 true for dy and the top and bottom. 213 */ 214 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); } 215 216 bool quickReject(int l, int t, int r, int b) const { 217 return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b; 218 } 219 220 /** Returns true if (x,y) is inside the rectangle and the rectangle is not 221 empty. The left and top are considered to be inside, while the right 222 and bottom are not. Thus for the rectangle (0, 0, 5, 10), the 223 points (0,0) and (0,9) are inside, while (-1,0) and (5,9) are not. 224 */ 225 bool contains(int32_t x, int32_t y) const { 226 return (unsigned)(x - fLeft) < (unsigned)(fRight - fLeft) && 227 (unsigned)(y - fTop) < (unsigned)(fBottom - fTop); 228 } 229 230 /** Returns true if the 4 specified sides of a rectangle are inside or equal to this rectangle. 231 If either rectangle is empty, contains() returns false. 232 */ 233 bool contains(int32_t left, int32_t top, int32_t right, int32_t bottom) const { 234 return left < right && top < bottom && !this->isEmpty() && // check for empties 235 fLeft <= left && fTop <= top && 236 fRight >= right && fBottom >= bottom; 237 } 238 239 /** Returns true if the specified rectangle r is inside or equal to this rectangle. 240 */ 241 bool contains(const SkIRect& r) const { 242 return !r.isEmpty() && !this->isEmpty() && // check for empties 243 fLeft <= r.fLeft && fTop <= r.fTop && 244 fRight >= r.fRight && fBottom >= r.fBottom; 245 } 246 247 /** Return true if this rectangle contains the specified rectangle. 248 For speed, this method does not check if either this or the specified 249 rectangles are empty, and if either is, its return value is undefined. 250 In the debugging build however, we assert that both this and the 251 specified rectangles are non-empty. 252 */ 253 bool containsNoEmptyCheck(int32_t left, int32_t top, 254 int32_t right, int32_t bottom) const { 255 SkASSERT(fLeft < fRight && fTop < fBottom); 256 SkASSERT(left < right && top < bottom); 257 258 return fLeft <= left && fTop <= top && 259 fRight >= right && fBottom >= bottom; 260 } 261 262 bool containsNoEmptyCheck(const SkIRect& r) const { 263 return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom); 264 } 265 266 /** If r intersects this rectangle, return true and set this rectangle to that 267 intersection, otherwise return false and do not change this rectangle. 268 If either rectangle is empty, do nothing and return false. 269 */ 270 bool intersect(const SkIRect& r) { 271 SkASSERT(&r); 272 return this->intersect(r.fLeft, r.fTop, r.fRight, r.fBottom); 273 } 274 275 /** If rectangles a and b intersect, return true and set this rectangle to 276 that intersection, otherwise return false and do not change this 277 rectangle. If either rectangle is empty, do nothing and return false. 278 */ 279 bool intersect(const SkIRect& a, const SkIRect& b) { 280 281 if (!a.isEmpty() && !b.isEmpty() && 282 a.fLeft < b.fRight && b.fLeft < a.fRight && 283 a.fTop < b.fBottom && b.fTop < a.fBottom) { 284 fLeft = SkMax32(a.fLeft, b.fLeft); 285 fTop = SkMax32(a.fTop, b.fTop); 286 fRight = SkMin32(a.fRight, b.fRight); 287 fBottom = SkMin32(a.fBottom, b.fBottom); 288 return true; 289 } 290 return false; 291 } 292 293 /** If rectangles a and b intersect, return true and set this rectangle to 294 that intersection, otherwise return false and do not change this 295 rectangle. For speed, no check to see if a or b are empty is performed. 296 If either is, then the return result is undefined. In the debug build, 297 we assert that both rectangles are non-empty. 298 */ 299 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) { 300 SkASSERT(!a.isEmpty() && !b.isEmpty()); 301 302 if (a.fLeft < b.fRight && b.fLeft < a.fRight && 303 a.fTop < b.fBottom && b.fTop < a.fBottom) { 304 fLeft = SkMax32(a.fLeft, b.fLeft); 305 fTop = SkMax32(a.fTop, b.fTop); 306 fRight = SkMin32(a.fRight, b.fRight); 307 fBottom = SkMin32(a.fBottom, b.fBottom); 308 return true; 309 } 310 return false; 311 } 312 313 /** If the rectangle specified by left,top,right,bottom intersects this rectangle, 314 return true and set this rectangle to that intersection, 315 otherwise return false and do not change this rectangle. 316 If either rectangle is empty, do nothing and return false. 317 */ 318 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) { 319 if (left < right && top < bottom && !this->isEmpty() && 320 fLeft < right && left < fRight && fTop < bottom && top < fBottom) { 321 if (fLeft < left) fLeft = left; 322 if (fTop < top) fTop = top; 323 if (fRight > right) fRight = right; 324 if (fBottom > bottom) fBottom = bottom; 325 return true; 326 } 327 return false; 328 } 329 330 /** Returns true if a and b are not empty, and they intersect 331 */ 332 static bool Intersects(const SkIRect& a, const SkIRect& b) { 333 return !a.isEmpty() && !b.isEmpty() && // check for empties 334 a.fLeft < b.fRight && b.fLeft < a.fRight && 335 a.fTop < b.fBottom && b.fTop < a.fBottom; 336 } 337 338 /** 339 * Returns true if a and b intersect. debug-asserts that neither are empty. 340 */ 341 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) { 342 SkASSERT(!a.isEmpty()); 343 SkASSERT(!b.isEmpty()); 344 return a.fLeft < b.fRight && b.fLeft < a.fRight && 345 a.fTop < b.fBottom && b.fTop < a.fBottom; 346 } 347 348 /** Update this rectangle to enclose itself and the specified rectangle. 349 If this rectangle is empty, just set it to the specified rectangle. If the specified 350 rectangle is empty, do nothing. 351 */ 352 void join(int32_t left, int32_t top, int32_t right, int32_t bottom); 353 354 /** Update this rectangle to enclose itself and the specified rectangle. 355 If this rectangle is empty, just set it to the specified rectangle. If the specified 356 rectangle is empty, do nothing. 357 */ 358 void join(const SkIRect& r) { 359 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 360 } 361 362 /** Swap top/bottom or left/right if there are flipped. 363 This can be called if the edges are computed separately, 364 and may have crossed over each other. 365 When this returns, left <= right && top <= bottom 366 */ 367 void sort(); 368 369 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() { 370 static const SkIRect gEmpty = { 0, 0, 0, 0 }; 371 return gEmpty; 372 } 373}; 374 375/** \struct SkRect 376*/ 377struct SK_API SkRect { 378 SkScalar fLeft, fTop, fRight, fBottom; 379 380 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() { 381 SkRect r; 382 r.setEmpty(); 383 return r; 384 } 385 386 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() { 387 SkRect r; 388 r.setLargest(); 389 return r; 390 } 391 392 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) { 393 SkRect r; 394 r.set(0, 0, w, h); 395 return r; 396 } 397 398 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) { 399 SkRect r; 400 r.set(0, 0, size.width(), size.height()); 401 return r; 402 } 403 404 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { 405 SkRect rect; 406 rect.set(l, t, r, b); 407 return rect; 408 } 409 410 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) { 411 SkRect r; 412 r.set(x, y, x + w, y + h); 413 return r; 414 } 415 416 SK_ATTR_DEPRECATED("use Make()") 417 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) { 418 SkRect r; 419 r.set(SkIntToScalar(irect.fLeft), 420 SkIntToScalar(irect.fTop), 421 SkIntToScalar(irect.fRight), 422 SkIntToScalar(irect.fBottom)); 423 return r; 424 } 425 426 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) { 427 SkRect r; 428 r.set(SkIntToScalar(irect.fLeft), 429 SkIntToScalar(irect.fTop), 430 SkIntToScalar(irect.fRight), 431 SkIntToScalar(irect.fBottom)); 432 return r; 433 } 434 435 /** 436 * Return true if the rectangle's width or height are <= 0 437 */ 438 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 439 440 bool isLargest() const { return SK_ScalarMin == fLeft && 441 SK_ScalarMin == fTop && 442 SK_ScalarMax == fRight && 443 SK_ScalarMax == fBottom; } 444 445 /** 446 * Returns true iff all values in the rect are finite. If any are 447 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this 448 * returns false. 449 */ 450 bool isFinite() const { 451 float accum = 0; 452 accum *= fLeft; 453 accum *= fTop; 454 accum *= fRight; 455 accum *= fBottom; 456 457 // accum is either NaN or it is finite (zero). 458 SkASSERT(0 == accum || !(accum == accum)); 459 460 // value==value will be true iff value is not NaN 461 // TODO: is it faster to say !accum or accum==accum? 462 return accum == accum; 463 } 464 465 SkScalar x() const { return fLeft; } 466 SkScalar y() const { return fTop; } 467 SkScalar left() const { return fLeft; } 468 SkScalar top() const { return fTop; } 469 SkScalar right() const { return fRight; } 470 SkScalar bottom() const { return fBottom; } 471 SkScalar width() const { return fRight - fLeft; } 472 SkScalar height() const { return fBottom - fTop; } 473 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } 474 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); } 475 476 friend bool operator==(const SkRect& a, const SkRect& b) { 477 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); 478 } 479 480 friend bool operator!=(const SkRect& a, const SkRect& b) { 481 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); 482 } 483 484 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right, 485 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW. 486 */ 487 void toQuad(SkPoint quad[4]) const; 488 489 /** Set this rectangle to the empty rectangle (0,0,0,0) 490 */ 491 void setEmpty() { memset(this, 0, sizeof(*this)); } 492 493 void set(const SkIRect& src) { 494 fLeft = SkIntToScalar(src.fLeft); 495 fTop = SkIntToScalar(src.fTop); 496 fRight = SkIntToScalar(src.fRight); 497 fBottom = SkIntToScalar(src.fBottom); 498 } 499 500 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { 501 fLeft = left; 502 fTop = top; 503 fRight = right; 504 fBottom = bottom; 505 } 506 // alias for set(l, t, r, b) 507 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { 508 this->set(left, top, right, bottom); 509 } 510 511 /** Initialize the rect with the 4 specified integers. The routine handles 512 converting them to scalars (by calling SkIntToScalar) 513 */ 514 void iset(int left, int top, int right, int bottom) { 515 fLeft = SkIntToScalar(left); 516 fTop = SkIntToScalar(top); 517 fRight = SkIntToScalar(right); 518 fBottom = SkIntToScalar(bottom); 519 } 520 521 /** 522 * Set this rectangle to be left/top at 0,0, and have the specified width 523 * and height (automatically converted to SkScalar). 524 */ 525 void isetWH(int width, int height) { 526 fLeft = fTop = 0; 527 fRight = SkIntToScalar(width); 528 fBottom = SkIntToScalar(height); 529 } 530 531 /** Set this rectangle to be the bounds of the array of points. 532 If the array is empty (count == 0), then set this rectangle 533 to the empty rectangle (0,0,0,0) 534 */ 535 void set(const SkPoint pts[], int count) { 536 // set() had been checking for non-finite values, so keep that behavior 537 // for now. Now that we have setBoundsCheck(), we may decide to make 538 // set() be simpler/faster, and not check for those. 539 (void)this->setBoundsCheck(pts, count); 540 } 541 542 // alias for set(pts, count) 543 void setBounds(const SkPoint pts[], int count) { 544 (void)this->setBoundsCheck(pts, count); 545 } 546 547 /** 548 * Compute the bounds of the array of points, and set this rect to that 549 * bounds and return true... unless a non-finite value is encountered, 550 * in which case this rect is set to empty and false is returned. 551 */ 552 bool setBoundsCheck(const SkPoint pts[], int count); 553 554 void set(const SkPoint& p0, const SkPoint& p1) { 555 fLeft = SkMinScalar(p0.fX, p1.fX); 556 fRight = SkMaxScalar(p0.fX, p1.fX); 557 fTop = SkMinScalar(p0.fY, p1.fY); 558 fBottom = SkMaxScalar(p0.fY, p1.fY); 559 } 560 561 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) { 562 fLeft = x; 563 fTop = y; 564 fRight = x + width; 565 fBottom = y + height; 566 } 567 568 void setWH(SkScalar width, SkScalar height) { 569 fLeft = 0; 570 fTop = 0; 571 fRight = width; 572 fBottom = height; 573 } 574 575 /** 576 * Make the largest representable rectangle 577 */ 578 void setLargest() { 579 fLeft = fTop = SK_ScalarMin; 580 fRight = fBottom = SK_ScalarMax; 581 } 582 583 /** 584 * Make the largest representable rectangle, but inverted (e.g. fLeft will 585 * be max and right will be min). 586 */ 587 void setLargestInverted() { 588 fLeft = fTop = SK_ScalarMax; 589 fRight = fBottom = SK_ScalarMin; 590 } 591 592 /** 593 * Return a new Rect, built as an offset of this rect. 594 */ 595 SkRect makeOffset(SkScalar dx, SkScalar dy) const { 596 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy); 597 } 598 599 /** 600 * Return a new Rect, built as an inset of this rect. 601 */ 602 SkRect makeInset(SkScalar dx, SkScalar dy) const { 603 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy); 604 } 605 606 /** Offset set the rectangle by adding dx to its left and right, 607 and adding dy to its top and bottom. 608 */ 609 void offset(SkScalar dx, SkScalar dy) { 610 fLeft += dx; 611 fTop += dy; 612 fRight += dx; 613 fBottom += dy; 614 } 615 616 void offset(const SkPoint& delta) { 617 this->offset(delta.fX, delta.fY); 618 } 619 620 /** 621 * Offset this rect such its new x() and y() will equal newX and newY. 622 */ 623 void offsetTo(SkScalar newX, SkScalar newY) { 624 fRight += newX - fLeft; 625 fBottom += newY - fTop; 626 fLeft = newX; 627 fTop = newY; 628 } 629 630 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are 631 moved inwards, making the rectangle narrower. If dx is negative, then 632 the sides are moved outwards, making the rectangle wider. The same holds 633 true for dy and the top and bottom. 634 */ 635 void inset(SkScalar dx, SkScalar dy) { 636 fLeft += dx; 637 fTop += dy; 638 fRight -= dx; 639 fBottom -= dy; 640 } 641 642 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are 643 moved outwards, making the rectangle wider. If dx is negative, then the 644 sides are moved inwards, making the rectangle narrower. The same holds 645 true for dy and the top and bottom. 646 */ 647 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); } 648 649 /** If this rectangle intersects r, return true and set this rectangle to that 650 intersection, otherwise return false and do not change this rectangle. 651 If either rectangle is empty, do nothing and return false. 652 */ 653 bool intersect(const SkRect& r); 654 bool intersect2(const SkRect& r); 655 656 /** If this rectangle intersects the rectangle specified by left, top, right, bottom, 657 return true and set this rectangle to that intersection, otherwise return false 658 and do not change this rectangle. 659 If either rectangle is empty, do nothing and return false. 660 */ 661 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); 662 663 /** 664 * Return true if this rectangle is not empty, and the specified sides of 665 * a rectangle are not empty, and they intersect. 666 */ 667 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const { 668 return // first check that both are not empty 669 left < right && top < bottom && 670 fLeft < fRight && fTop < fBottom && 671 // now check for intersection 672 fLeft < right && left < fRight && 673 fTop < bottom && top < fBottom; 674 } 675 676 /** If rectangles a and b intersect, return true and set this rectangle to 677 * that intersection, otherwise return false and do not change this 678 * rectangle. If either rectangle is empty, do nothing and return false. 679 */ 680 bool intersect(const SkRect& a, const SkRect& b); 681 682 /** 683 * Return true if rectangles a and b are not empty and intersect. 684 */ 685 static bool Intersects(const SkRect& a, const SkRect& b) { 686 return !a.isEmpty() && !b.isEmpty() && 687 a.fLeft < b.fRight && b.fLeft < a.fRight && 688 a.fTop < b.fBottom && b.fTop < a.fBottom; 689 } 690 691 /** 692 * Update this rectangle to enclose itself and the specified rectangle. 693 * If this rectangle is empty, just set it to the specified rectangle. 694 * If the specified rectangle is empty, do nothing. 695 */ 696 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); 697 698 /** Update this rectangle to enclose itself and the specified rectangle. 699 If this rectangle is empty, just set it to the specified rectangle. If the specified 700 rectangle is empty, do nothing. 701 */ 702 void join(const SkRect& r) { 703 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 704 } 705 // alias for join() 706 void growToInclude(const SkRect& r) { this->join(r); } 707 708 /** 709 * Grow the rect to include the specified (x,y). After this call, the 710 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom. 711 * 712 * This is close, but not quite the same contract as contains(), since 713 * contains() treats the left and top different from the right and bottom. 714 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note 715 * that contains(x,y) always returns false if the rect is empty. 716 */ 717 void growToInclude(SkScalar x, SkScalar y) { 718 fLeft = SkMinScalar(x, fLeft); 719 fRight = SkMaxScalar(x, fRight); 720 fTop = SkMinScalar(y, fTop); 721 fBottom = SkMaxScalar(y, fBottom); 722 } 723 724 /** Bulk version of growToInclude */ 725 void growToInclude(const SkPoint pts[], int count) { 726 this->growToInclude(pts, sizeof(SkPoint), count); 727 } 728 729 /** Bulk version of growToInclude with stride. */ 730 void growToInclude(const SkPoint pts[], size_t stride, int count) { 731 SkASSERT(count >= 0); 732 SkASSERT(stride >= sizeof(SkPoint)); 733 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride); 734 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) { 735 this->growToInclude(pts->fX, pts->fY); 736 } 737 } 738 739 /** 740 * Return true if this rectangle contains r, and if both rectangles are 741 * not empty. 742 */ 743 bool contains(const SkRect& r) const { 744 // todo: can we eliminate the this->isEmpty check? 745 return !r.isEmpty() && !this->isEmpty() && 746 fLeft <= r.fLeft && fTop <= r.fTop && 747 fRight >= r.fRight && fBottom >= r.fBottom; 748 } 749 750 /** 751 * Set the dst rectangle by rounding this rectangle's coordinates to their 752 * nearest integer values using SkScalarRoundToInt. 753 */ 754 void round(SkIRect* dst) const { 755 SkASSERT(dst); 756 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop), 757 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom)); 758 } 759 760 /** 761 * Variant of round() that explicitly performs the rounding step (i.e. floor(x + 0.5)) using 762 * double instead of SkScalar (float). It does this by calling SkDScalarRoundToInt(), which 763 * may be slower than calling SkScalarRountToInt(), but gives slightly more accurate results. 764 * 765 * e.g. 766 * SkScalar x = 0.49999997f; 767 * int ix = SkScalarRoundToInt(x); 768 * SkASSERT(0 == ix); // <--- fails 769 * ix = SkDScalarRoundToInt(x); 770 * SkASSERT(0 == ix); // <--- succeeds 771 */ 772 void dround(SkIRect* dst) const { 773 SkASSERT(dst); 774 dst->set(SkDScalarRoundToInt(fLeft), SkDScalarRoundToInt(fTop), 775 SkDScalarRoundToInt(fRight), SkDScalarRoundToInt(fBottom)); 776 } 777 778 /** 779 * Set the dst rectangle by rounding "out" this rectangle, choosing the 780 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom. 781 */ 782 void roundOut(SkIRect* dst) const { 783 SkASSERT(dst); 784 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop), 785 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom)); 786 } 787 788 /** 789 * Expand this rectangle by rounding its coordinates "out", choosing the 790 * floor of top and left, and the ceil of right and bottom. If this rect 791 * is already on integer coordinates, then it will be unchanged. 792 */ 793 void roundOut() { 794 this->set(SkScalarFloorToScalar(fLeft), 795 SkScalarFloorToScalar(fTop), 796 SkScalarCeilToScalar(fRight), 797 SkScalarCeilToScalar(fBottom)); 798 } 799 800 /** 801 * Set the dst rectangle by rounding "in" this rectangle, choosing the 802 * ceil of top and left, and the floor of right and bottom. This does *not* 803 * call sort(), so it is possible that the resulting rect is inverted... 804 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that. 805 */ 806 void roundIn(SkIRect* dst) const { 807 SkASSERT(dst); 808 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop), 809 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom)); 810 } 811 812 /** 813 * Return a new SkIRect which is contains the rounded coordinates of this 814 * rect using SkScalarRoundToInt. 815 */ 816 SkIRect round() const { 817 SkIRect ir; 818 this->round(&ir); 819 return ir; 820 } 821 822 /** 823 * Swap top/bottom or left/right if there are flipped (i.e. if width() 824 * or height() would have returned a negative value.) This should be called 825 * if the edges are computed separately, and may have crossed over each 826 * other. When this returns, left <= right && top <= bottom 827 */ 828 void sort(); 829 830 /** 831 * cast-safe way to treat the rect as an array of (4) SkScalars. 832 */ 833 const SkScalar* asScalars() const { return &fLeft; } 834 835#ifdef SK_DEVELOPER 836 /** 837 * Dumps the rect using SkDebugf. This is intended for Skia development debugging. Don't 838 * rely on the existence of this function or the formatting of its output. 839 */ 840 void dump() const { 841 SkDebugf("{ l: %f, t: %f, r: %f, b: %f }", fLeft, fTop, fRight, fBottom); 842 } 843#endif 844 845}; 846 847#endif 848