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