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 */ 20 struct SK_API SkIRect { 21 int32_t fLeft, fTop, fRight, fBottom; 22 MakeEmptySkIRect23 static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() { 24 SkIRect r; 25 r.setEmpty(); 26 return r; 27 } 28 MakeLargestSkIRect29 static SkIRect SK_WARN_UNUSED_RESULT MakeLargest() { 30 SkIRect r; 31 r.setLargest(); 32 return r; 33 } 34 MakeWHSkIRect35 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 MakeSizeSkIRect41 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 MakeLTRBSkIRect47 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 MakeXYWHSkIRect53 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 leftSkIRect59 int left() const { return fLeft; } topSkIRect60 int top() const { return fTop; } rightSkIRect61 int right() const { return fRight; } bottomSkIRect62 int bottom() const { return fBottom; } 63 64 /** return the left edge of the rect */ xSkIRect65 int x() const { return fLeft; } 66 /** return the top edge of the rect */ ySkIRect67 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 */ widthSkIRect72 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 */ heightSkIRect78 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 */ centerXSkIRect87 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 */ centerYSkIRect96 int centerY() const { return (fBottom + fTop) >> 1; } 97 98 /** 99 * Return true if the rectangle's width or height are <= 0 100 */ isEmptySkIRect101 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 102 isLargestSkIRect103 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 is16BitSkIRect116 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 */ setEmptySkIRect123 void setEmpty() { memset(this, 0, sizeof(*this)); } 124 setSkIRect125 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) setLTRBSkIRect132 void setLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) { 133 this->set(left, top, right, bottom); 134 } 135 setXYWHSkIRect136 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 */ setLargestSkIRect146 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 */ setLargestInvertedSkIRect155 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 */ makeOffsetSkIRect163 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 */ makeInsetSkIRect170 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 */ offsetSkIRect177 void offset(int32_t dx, int32_t dy) { 178 fLeft += dx; 179 fTop += dy; 180 fRight += dx; 181 fBottom += dy; 182 } 183 offsetSkIRect184 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 */ offsetToSkIRect191 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 */ insetSkIRect202 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 */ outsetSkIRect214 void outset(int32_t dx, int32_t dy) { this->inset(-dx, -dy); } 215 quickRejectSkIRect216 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 */ containsSkIRect225 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 */ containsSkIRect233 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 */ containsSkIRect241 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 */ containsNoEmptyCheckSkIRect253 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 containsNoEmptyCheckSkIRect262 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 */ intersectSkIRect270 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 */ intersectSkIRect279 bool intersect(const SkIRect& a, const SkIRect& b) { 280 SkASSERT(&a && &b); 281 282 if (!a.isEmpty() && !b.isEmpty() && 283 a.fLeft < b.fRight && b.fLeft < a.fRight && 284 a.fTop < b.fBottom && b.fTop < a.fBottom) { 285 fLeft = SkMax32(a.fLeft, b.fLeft); 286 fTop = SkMax32(a.fTop, b.fTop); 287 fRight = SkMin32(a.fRight, b.fRight); 288 fBottom = SkMin32(a.fBottom, b.fBottom); 289 return true; 290 } 291 return false; 292 } 293 294 /** If rectangles a and b intersect, return true and set this rectangle to 295 that intersection, otherwise return false and do not change this 296 rectangle. For speed, no check to see if a or b are empty is performed. 297 If either is, then the return result is undefined. In the debug build, 298 we assert that both rectangles are non-empty. 299 */ intersectNoEmptyCheckSkIRect300 bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) { 301 SkASSERT(&a && &b); 302 SkASSERT(!a.isEmpty() && !b.isEmpty()); 303 304 if (a.fLeft < b.fRight && b.fLeft < a.fRight && 305 a.fTop < b.fBottom && b.fTop < a.fBottom) { 306 fLeft = SkMax32(a.fLeft, b.fLeft); 307 fTop = SkMax32(a.fTop, b.fTop); 308 fRight = SkMin32(a.fRight, b.fRight); 309 fBottom = SkMin32(a.fBottom, b.fBottom); 310 return true; 311 } 312 return false; 313 } 314 315 /** If the rectangle specified by left,top,right,bottom intersects this rectangle, 316 return true and set this rectangle to that intersection, 317 otherwise return false and do not change this rectangle. 318 If either rectangle is empty, do nothing and return false. 319 */ intersectSkIRect320 bool intersect(int32_t left, int32_t top, int32_t right, int32_t bottom) { 321 if (left < right && top < bottom && !this->isEmpty() && 322 fLeft < right && left < fRight && fTop < bottom && top < fBottom) { 323 if (fLeft < left) fLeft = left; 324 if (fTop < top) fTop = top; 325 if (fRight > right) fRight = right; 326 if (fBottom > bottom) fBottom = bottom; 327 return true; 328 } 329 return false; 330 } 331 332 /** Returns true if a and b are not empty, and they intersect 333 */ IntersectsSkIRect334 static bool Intersects(const SkIRect& a, const SkIRect& b) { 335 return !a.isEmpty() && !b.isEmpty() && // check for empties 336 a.fLeft < b.fRight && b.fLeft < a.fRight && 337 a.fTop < b.fBottom && b.fTop < a.fBottom; 338 } 339 340 /** 341 * Returns true if a and b intersect. debug-asserts that neither are empty. 342 */ IntersectsNoEmptyCheckSkIRect343 static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) { 344 SkASSERT(!a.isEmpty()); 345 SkASSERT(!b.isEmpty()); 346 return a.fLeft < b.fRight && b.fLeft < a.fRight && 347 a.fTop < b.fBottom && b.fTop < a.fBottom; 348 } 349 350 /** Update this rectangle to enclose itself and the specified rectangle. 351 If this rectangle is empty, just set it to the specified rectangle. If the specified 352 rectangle is empty, do nothing. 353 */ 354 void join(int32_t left, int32_t top, int32_t right, int32_t bottom); 355 356 /** Update this rectangle to enclose itself and the specified rectangle. 357 If this rectangle is empty, just set it to the specified rectangle. If the specified 358 rectangle is empty, do nothing. 359 */ joinSkIRect360 void join(const SkIRect& r) { 361 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 362 } 363 364 /** Swap top/bottom or left/right if there are flipped. 365 This can be called if the edges are computed separately, 366 and may have crossed over each other. 367 When this returns, left <= right && top <= bottom 368 */ 369 void sort(); 370 EmptyIRectSkIRect371 static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() { 372 static const SkIRect gEmpty = { 0, 0, 0, 0 }; 373 return gEmpty; 374 } 375 }; 376 377 /** \struct SkRect 378 */ 379 struct SK_API SkRect { 380 SkScalar fLeft, fTop, fRight, fBottom; 381 MakeEmptySkRect382 static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() { 383 SkRect r; 384 r.setEmpty(); 385 return r; 386 } 387 MakeLargestSkRect388 static SkRect SK_WARN_UNUSED_RESULT MakeLargest() { 389 SkRect r; 390 r.setLargest(); 391 return r; 392 } 393 MakeWHSkRect394 static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) { 395 SkRect r; 396 r.set(0, 0, w, h); 397 return r; 398 } 399 MakeSizeSkRect400 static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) { 401 SkRect r; 402 r.set(0, 0, size.width(), size.height()); 403 return r; 404 } 405 MakeLTRBSkRect406 static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) { 407 SkRect rect; 408 rect.set(l, t, r, b); 409 return rect; 410 } 411 MakeXYWHSkRect412 static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) { 413 SkRect r; 414 r.set(x, y, x + w, y + h); 415 return r; 416 } 417 418 SK_ATTR_DEPRECATED("use Make()") MakeFromIRectSkRect419 static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) { 420 SkRect r; 421 r.set(SkIntToScalar(irect.fLeft), 422 SkIntToScalar(irect.fTop), 423 SkIntToScalar(irect.fRight), 424 SkIntToScalar(irect.fBottom)); 425 return r; 426 } 427 MakeSkRect428 static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) { 429 SkRect r; 430 r.set(SkIntToScalar(irect.fLeft), 431 SkIntToScalar(irect.fTop), 432 SkIntToScalar(irect.fRight), 433 SkIntToScalar(irect.fBottom)); 434 return r; 435 } 436 437 /** 438 * Return true if the rectangle's width or height are <= 0 439 */ isEmptySkRect440 bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; } 441 isLargestSkRect442 bool isLargest() const { return SK_ScalarMin == fLeft && 443 SK_ScalarMin == fTop && 444 SK_ScalarMax == fRight && 445 SK_ScalarMax == fBottom; } 446 447 /** 448 * Returns true iff all values in the rect are finite. If any are 449 * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this 450 * returns false. 451 */ isFiniteSkRect452 bool isFinite() const { 453 float accum = 0; 454 accum *= fLeft; 455 accum *= fTop; 456 accum *= fRight; 457 accum *= fBottom; 458 459 // accum is either NaN or it is finite (zero). 460 SkASSERT(0 == accum || !(accum == accum)); 461 462 // value==value will be true iff value is not NaN 463 // TODO: is it faster to say !accum or accum==accum? 464 return accum == accum; 465 } 466 xSkRect467 SkScalar x() const { return fLeft; } ySkRect468 SkScalar y() const { return fTop; } leftSkRect469 SkScalar left() const { return fLeft; } topSkRect470 SkScalar top() const { return fTop; } rightSkRect471 SkScalar right() const { return fRight; } bottomSkRect472 SkScalar bottom() const { return fBottom; } widthSkRect473 SkScalar width() const { return fRight - fLeft; } heightSkRect474 SkScalar height() const { return fBottom - fTop; } centerXSkRect475 SkScalar centerX() const { return SkScalarHalf(fLeft + fRight); } centerYSkRect476 SkScalar centerY() const { return SkScalarHalf(fTop + fBottom); } 477 478 friend bool operator==(const SkRect& a, const SkRect& b) { 479 return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); 480 } 481 482 friend bool operator!=(const SkRect& a, const SkRect& b) { 483 return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4); 484 } 485 486 /** return the 4 points that enclose the rectangle (top-left, top-right, bottom-right, 487 bottom-left). TODO: Consider adding param to control whether quad is CW or CCW. 488 */ 489 void toQuad(SkPoint quad[4]) const; 490 491 /** Set this rectangle to the empty rectangle (0,0,0,0) 492 */ setEmptySkRect493 void setEmpty() { memset(this, 0, sizeof(*this)); } 494 setSkRect495 void set(const SkIRect& src) { 496 fLeft = SkIntToScalar(src.fLeft); 497 fTop = SkIntToScalar(src.fTop); 498 fRight = SkIntToScalar(src.fRight); 499 fBottom = SkIntToScalar(src.fBottom); 500 } 501 setSkRect502 void set(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { 503 fLeft = left; 504 fTop = top; 505 fRight = right; 506 fBottom = bottom; 507 } 508 // alias for set(l, t, r, b) setLTRBSkRect509 void setLTRB(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) { 510 this->set(left, top, right, bottom); 511 } 512 513 /** Initialize the rect with the 4 specified integers. The routine handles 514 converting them to scalars (by calling SkIntToScalar) 515 */ isetSkRect516 void iset(int left, int top, int right, int bottom) { 517 fLeft = SkIntToScalar(left); 518 fTop = SkIntToScalar(top); 519 fRight = SkIntToScalar(right); 520 fBottom = SkIntToScalar(bottom); 521 } 522 523 /** 524 * Set this rectangle to be left/top at 0,0, and have the specified width 525 * and height (automatically converted to SkScalar). 526 */ isetWHSkRect527 void isetWH(int width, int height) { 528 fLeft = fTop = 0; 529 fRight = SkIntToScalar(width); 530 fBottom = SkIntToScalar(height); 531 } 532 533 /** Set this rectangle to be the bounds of the array of points. 534 If the array is empty (count == 0), then set this rectangle 535 to the empty rectangle (0,0,0,0) 536 */ setSkRect537 void set(const SkPoint pts[], int count) { 538 // set() had been checking for non-finite values, so keep that behavior 539 // for now. Now that we have setBoundsCheck(), we may decide to make 540 // set() be simpler/faster, and not check for those. 541 (void)this->setBoundsCheck(pts, count); 542 } 543 544 // alias for set(pts, count) setBoundsSkRect545 void setBounds(const SkPoint pts[], int count) { 546 (void)this->setBoundsCheck(pts, count); 547 } 548 549 /** 550 * Compute the bounds of the array of points, and set this rect to that 551 * bounds and return true... unless a non-finite value is encountered, 552 * in which case this rect is set to empty and false is returned. 553 */ 554 bool setBoundsCheck(const SkPoint pts[], int count); 555 setSkRect556 void set(const SkPoint& p0, const SkPoint& p1) { 557 fLeft = SkMinScalar(p0.fX, p1.fX); 558 fRight = SkMaxScalar(p0.fX, p1.fX); 559 fTop = SkMinScalar(p0.fY, p1.fY); 560 fBottom = SkMaxScalar(p0.fY, p1.fY); 561 } 562 setXYWHSkRect563 void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) { 564 fLeft = x; 565 fTop = y; 566 fRight = x + width; 567 fBottom = y + height; 568 } 569 setWHSkRect570 void setWH(SkScalar width, SkScalar height) { 571 fLeft = 0; 572 fTop = 0; 573 fRight = width; 574 fBottom = height; 575 } 576 577 /** 578 * Make the largest representable rectangle 579 */ setLargestSkRect580 void setLargest() { 581 fLeft = fTop = SK_ScalarMin; 582 fRight = fBottom = SK_ScalarMax; 583 } 584 585 /** 586 * Make the largest representable rectangle, but inverted (e.g. fLeft will 587 * be max and right will be min). 588 */ setLargestInvertedSkRect589 void setLargestInverted() { 590 fLeft = fTop = SK_ScalarMax; 591 fRight = fBottom = SK_ScalarMin; 592 } 593 594 /** 595 * Return a new Rect, built as an offset of this rect. 596 */ makeOffsetSkRect597 SkRect makeOffset(SkScalar dx, SkScalar dy) const { 598 return MakeLTRB(fLeft + dx, fTop + dy, fRight + dx, fBottom + dy); 599 } 600 601 /** 602 * Return a new Rect, built as an inset of this rect. 603 */ makeInsetSkRect604 SkRect makeInset(SkScalar dx, SkScalar dy) const { 605 return MakeLTRB(fLeft + dx, fTop + dy, fRight - dx, fBottom - dy); 606 } 607 608 /** Offset set the rectangle by adding dx to its left and right, 609 and adding dy to its top and bottom. 610 */ offsetSkRect611 void offset(SkScalar dx, SkScalar dy) { 612 fLeft += dx; 613 fTop += dy; 614 fRight += dx; 615 fBottom += dy; 616 } 617 offsetSkRect618 void offset(const SkPoint& delta) { 619 this->offset(delta.fX, delta.fY); 620 } 621 622 /** 623 * Offset this rect such its new x() and y() will equal newX and newY. 624 */ offsetToSkRect625 void offsetTo(SkScalar newX, SkScalar newY) { 626 fRight += newX - fLeft; 627 fBottom += newY - fTop; 628 fLeft = newX; 629 fTop = newY; 630 } 631 632 /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are 633 moved inwards, making the rectangle narrower. If dx is negative, then 634 the sides are moved outwards, making the rectangle wider. The same holds 635 true for dy and the top and bottom. 636 */ insetSkRect637 void inset(SkScalar dx, SkScalar dy) { 638 fLeft += dx; 639 fTop += dy; 640 fRight -= dx; 641 fBottom -= dy; 642 } 643 644 /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are 645 moved outwards, making the rectangle wider. If dx is negative, then the 646 sides are moved inwards, making the rectangle narrower. The same holds 647 true for dy and the top and bottom. 648 */ outsetSkRect649 void outset(SkScalar dx, SkScalar dy) { this->inset(-dx, -dy); } 650 651 /** If this rectangle intersects r, return true and set this rectangle to that 652 intersection, otherwise return false and do not change this rectangle. 653 If either rectangle is empty, do nothing and return false. 654 */ 655 bool intersect(const SkRect& r); 656 bool intersect2(const SkRect& r); 657 658 /** If this rectangle intersects the rectangle specified by left, top, right, bottom, 659 return true and set this rectangle to that intersection, otherwise return false 660 and do not change this rectangle. 661 If either rectangle is empty, do nothing and return false. 662 */ 663 bool intersect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); 664 665 /** 666 * Return true if this rectangle is not empty, and the specified sides of 667 * a rectangle are not empty, and they intersect. 668 */ intersectsSkRect669 bool intersects(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) const { 670 return // first check that both are not empty 671 left < right && top < bottom && 672 fLeft < fRight && fTop < fBottom && 673 // now check for intersection 674 fLeft < right && left < fRight && 675 fTop < bottom && top < fBottom; 676 } 677 678 /** If rectangles a and b intersect, return true and set this rectangle to 679 * that intersection, otherwise return false and do not change this 680 * rectangle. If either rectangle is empty, do nothing and return false. 681 */ 682 bool intersect(const SkRect& a, const SkRect& b); 683 684 /** 685 * Return true if rectangles a and b are not empty and intersect. 686 */ IntersectsSkRect687 static bool Intersects(const SkRect& a, const SkRect& b) { 688 return !a.isEmpty() && !b.isEmpty() && 689 a.fLeft < b.fRight && b.fLeft < a.fRight && 690 a.fTop < b.fBottom && b.fTop < a.fBottom; 691 } 692 693 /** 694 * Update this rectangle to enclose itself and the specified rectangle. 695 * If this rectangle is empty, just set it to the specified rectangle. 696 * If the specified rectangle is empty, do nothing. 697 */ 698 void join(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom); 699 700 /** Update this rectangle to enclose itself and the specified rectangle. 701 If this rectangle is empty, just set it to the specified rectangle. If the specified 702 rectangle is empty, do nothing. 703 */ joinSkRect704 void join(const SkRect& r) { 705 this->join(r.fLeft, r.fTop, r.fRight, r.fBottom); 706 } 707 // alias for join() growToIncludeSkRect708 void growToInclude(const SkRect& r) { this->join(r); } 709 710 /** 711 * Grow the rect to include the specified (x,y). After this call, the 712 * following will be true: fLeft <= x <= fRight && fTop <= y <= fBottom. 713 * 714 * This is close, but not quite the same contract as contains(), since 715 * contains() treats the left and top different from the right and bottom. 716 * contains(x,y) -> fLeft <= x < fRight && fTop <= y < fBottom. Also note 717 * that contains(x,y) always returns false if the rect is empty. 718 */ growToIncludeSkRect719 void growToInclude(SkScalar x, SkScalar y) { 720 fLeft = SkMinScalar(x, fLeft); 721 fRight = SkMaxScalar(x, fRight); 722 fTop = SkMinScalar(y, fTop); 723 fBottom = SkMaxScalar(y, fBottom); 724 } 725 726 /** Bulk version of growToInclude */ growToIncludeSkRect727 void growToInclude(const SkPoint pts[], int count) { 728 this->growToInclude(pts, sizeof(SkPoint), count); 729 } 730 731 /** Bulk version of growToInclude with stride. */ growToIncludeSkRect732 void growToInclude(const SkPoint pts[], size_t stride, int count) { 733 SkASSERT(count >= 0); 734 SkASSERT(stride >= sizeof(SkPoint)); 735 const SkPoint* end = (const SkPoint*)((intptr_t)pts + count * stride); 736 for (; pts < end; pts = (const SkPoint*)((intptr_t)pts + stride)) { 737 this->growToInclude(pts->fX, pts->fY); 738 } 739 } 740 741 /** 742 * Return true if this rectangle contains r, and if both rectangles are 743 * not empty. 744 */ containsSkRect745 bool contains(const SkRect& r) const { 746 // todo: can we eliminate the this->isEmpty check? 747 return !r.isEmpty() && !this->isEmpty() && 748 fLeft <= r.fLeft && fTop <= r.fTop && 749 fRight >= r.fRight && fBottom >= r.fBottom; 750 } 751 752 /** 753 * Set the dst rectangle by rounding this rectangle's coordinates to their 754 * nearest integer values using SkScalarRoundToInt. 755 */ roundSkRect756 void round(SkIRect* dst) const { 757 SkASSERT(dst); 758 dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop), 759 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom)); 760 } 761 762 /** 763 * Variant of round() that explicitly performs the rounding step (i.e. floor(x + 0.5)) using 764 * double instead of SkScalar (float). It does this by calling SkDScalarRoundToInt(), which 765 * may be slower than calling SkScalarRountToInt(), but gives slightly more accurate results. 766 * 767 * e.g. 768 * SkScalar x = 0.49999997f; 769 * int ix = SkScalarRoundToInt(x); 770 * SkASSERT(0 == ix); // <--- fails 771 * ix = SkDScalarRoundToInt(x); 772 * SkASSERT(0 == ix); // <--- succeeds 773 */ droundSkRect774 void dround(SkIRect* dst) const { 775 SkASSERT(dst); 776 dst->set(SkDScalarRoundToInt(fLeft), SkDScalarRoundToInt(fTop), 777 SkDScalarRoundToInt(fRight), SkDScalarRoundToInt(fBottom)); 778 } 779 780 /** 781 * Set the dst rectangle by rounding "out" this rectangle, choosing the 782 * SkScalarFloor of top and left, and the SkScalarCeil of right and bottom. 783 */ roundOutSkRect784 void roundOut(SkIRect* dst) const { 785 SkASSERT(dst); 786 dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop), 787 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom)); 788 } 789 790 /** 791 * Expand this rectangle by rounding its coordinates "out", choosing the 792 * floor of top and left, and the ceil of right and bottom. If this rect 793 * is already on integer coordinates, then it will be unchanged. 794 */ roundOutSkRect795 void roundOut() { 796 this->set(SkScalarFloorToScalar(fLeft), 797 SkScalarFloorToScalar(fTop), 798 SkScalarCeilToScalar(fRight), 799 SkScalarCeilToScalar(fBottom)); 800 } 801 802 /** 803 * Set the dst rectangle by rounding "in" this rectangle, choosing the 804 * ceil of top and left, and the floor of right and bottom. This does *not* 805 * call sort(), so it is possible that the resulting rect is inverted... 806 * e.g. left >= right or top >= bottom. Call isEmpty() to detect that. 807 */ roundInSkRect808 void roundIn(SkIRect* dst) const { 809 SkASSERT(dst); 810 dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop), 811 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom)); 812 } 813 814 /** 815 * Return a new SkIRect which is contains the rounded coordinates of this 816 * rect using SkScalarRoundToInt. 817 */ roundSkRect818 SkIRect round() const { 819 SkIRect ir; 820 this->round(&ir); 821 return ir; 822 } 823 824 /** 825 * Swap top/bottom or left/right if there are flipped (i.e. if width() 826 * or height() would have returned a negative value.) This should be called 827 * if the edges are computed separately, and may have crossed over each 828 * other. When this returns, left <= right && top <= bottom 829 */ 830 void sort(); 831 832 /** 833 * cast-safe way to treat the rect as an array of (4) SkScalars. 834 */ asScalarsSkRect835 const SkScalar* asScalars() const { return &fLeft; } 836 837 #ifdef SK_DEVELOPER 838 /** 839 * Dumps the rect using SkDebugf. This is intended for Skia development debugging. Don't 840 * rely on the existence of this function or the formatting of its output. 841 */ dumpSkRect842 void dump() const { 843 SkDebugf("{ l: %f, t: %f, r: %f, b: %f }", fLeft, fTop, fRight, fBottom); 844 } 845 #endif 846 847 }; 848 849 #endif 850