1/* 2 * Copyright 2012 Google Inc. 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#include <cmath> 9#include "SkRRect.h" 10#include "SkMatrix.h" 11#include "SkScaleToSides.h" 12 13/////////////////////////////////////////////////////////////////////////////// 14 15void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 16 fRect = rect; 17 fRect.sort(); 18 19 if (fRect.isEmpty() || !fRect.isFinite()) { 20 this->setEmpty(); 21 return; 22 } 23 24 if (!SkScalarsAreFinite(xRad, yRad)) { 25 xRad = yRad = 0; // devolve into a simple rect 26 } 27 if (xRad <= 0 || yRad <= 0) { 28 // all corners are square in this case 29 this->setRect(rect); 30 return; 31 } 32 33 if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) { 34 SkScalar scale = SkMinScalar(fRect.width() / (xRad + xRad), fRect.height() / (yRad + yRad)); 35 SkASSERT(scale < SK_Scalar1); 36 xRad = SkScalarMul(xRad, scale); 37 yRad = SkScalarMul(yRad, scale); 38 } 39 40 for (int i = 0; i < 4; ++i) { 41 fRadii[i].set(xRad, yRad); 42 } 43 fType = kSimple_Type; 44 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) { 45 fType = kOval_Type; 46 // TODO: assert that all the x&y radii are already W/2 & H/2 47 } 48 49 SkDEBUGCODE(this->validate();) 50} 51 52void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, 53 SkScalar rightRad, SkScalar bottomRad) { 54 fRect = rect; 55 fRect.sort(); 56 57 if (fRect.isEmpty() || !fRect.isFinite()) { 58 this->setEmpty(); 59 return; 60 } 61 62 const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; 63 if (!SkScalarsAreFinite(array, 4)) { 64 this->setRect(rect); // devolve into a simple rect 65 return; 66 } 67 68 leftRad = SkMaxScalar(leftRad, 0); 69 topRad = SkMaxScalar(topRad, 0); 70 rightRad = SkMaxScalar(rightRad, 0); 71 bottomRad = SkMaxScalar(bottomRad, 0); 72 73 SkScalar scale = SK_Scalar1; 74 if (leftRad + rightRad > fRect.width()) { 75 scale = fRect.width() / (leftRad + rightRad); 76 } 77 if (topRad + bottomRad > fRect.height()) { 78 scale = SkMinScalar(scale, fRect.height() / (topRad + bottomRad)); 79 } 80 81 if (scale < SK_Scalar1) { 82 leftRad = SkScalarMul(leftRad, scale); 83 topRad = SkScalarMul(topRad, scale); 84 rightRad = SkScalarMul(rightRad, scale); 85 bottomRad = SkScalarMul(bottomRad, scale); 86 } 87 88 if (leftRad == rightRad && topRad == bottomRad) { 89 if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) { 90 fType = kOval_Type; 91 } else if (0 == leftRad || 0 == topRad) { 92 // If the left and (by equality check above) right radii are zero then it is a rect. 93 // Same goes for top/bottom. 94 fType = kRect_Type; 95 leftRad = 0; 96 topRad = 0; 97 rightRad = 0; 98 bottomRad = 0; 99 } else { 100 fType = kSimple_Type; 101 } 102 } else { 103 fType = kNinePatch_Type; 104 } 105 106 fRadii[kUpperLeft_Corner].set(leftRad, topRad); 107 fRadii[kUpperRight_Corner].set(rightRad, topRad); 108 fRadii[kLowerRight_Corner].set(rightRad, bottomRad); 109 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); 110 111 SkDEBUGCODE(this->validate();) 112} 113 114// These parameters intentionally double. Apropos crbug.com/463920, if one of the 115// radii is huge while the other is small, single precision math can completely 116// miss the fact that a scale is required. 117static double compute_min_scale(double rad1, double rad2, double limit, double curMin) { 118 if ((rad1 + rad2) > limit) { 119 return SkTMin(curMin, limit / (rad1 + rad2)); 120 } 121 return curMin; 122} 123 124void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { 125 fRect = rect; 126 fRect.sort(); 127 128 if (fRect.isEmpty() || !fRect.isFinite()) { 129 this->setEmpty(); 130 return; 131 } 132 133 if (!SkScalarsAreFinite(&radii[0].fX, 8)) { 134 this->setRect(rect); // devolve into a simple rect 135 return; 136 } 137 138 memcpy(fRadii, radii, sizeof(fRadii)); 139 140 bool allCornersSquare = true; 141 142 // Clamp negative radii to zero 143 for (int i = 0; i < 4; ++i) { 144 if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) { 145 // In this case we are being a little fast & loose. Since one of 146 // the radii is 0 the corner is square. However, the other radii 147 // could still be non-zero and play in the global scale factor 148 // computation. 149 fRadii[i].fX = 0; 150 fRadii[i].fY = 0; 151 } else { 152 allCornersSquare = false; 153 } 154 } 155 156 if (allCornersSquare) { 157 this->setRect(rect); 158 return; 159 } 160 161 this->scaleRadii(); 162} 163 164void SkRRect::scaleRadii() { 165 166 // Proportionally scale down all radii to fit. Find the minimum ratio 167 // of a side and the radii on that side (for all four sides) and use 168 // that to scale down _all_ the radii. This algorithm is from the 169 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping 170 // Curves: 171 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, 172 // Si is the sum of the two corresponding radii of the corners on side i, 173 // and Ltop = Lbottom = the width of the box, 174 // and Lleft = Lright = the height of the box. 175 // If f < 1, then all corner radii are reduced by multiplying them by f." 176 double scale = 1.0; 177 178 // The sides of the rectangle may be larger than a float. 179 double width = (double)fRect.fRight - (double)fRect.fLeft; 180 double height = (double)fRect.fBottom - (double)fRect.fTop; 181 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale); 182 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale); 183 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale); 184 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale); 185 186 if (scale < 1.0) { 187 SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX); 188 SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY); 189 SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX); 190 SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY); 191 } 192 193 // At this point we're either oval, simple, or complex (not empty or rect). 194 this->computeType(); 195 196 SkDEBUGCODE(this->validate();) 197} 198 199// This method determines if a point known to be inside the RRect's bounds is 200// inside all the corners. 201bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { 202 SkPoint canonicalPt; // (x,y) translated to one of the quadrants 203 int index; 204 205 if (kOval_Type == this->type()) { 206 canonicalPt.set(x - fRect.centerX(), y - fRect.centerY()); 207 index = kUpperLeft_Corner; // any corner will do in this case 208 } else { 209 if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX && 210 y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) { 211 // UL corner 212 index = kUpperLeft_Corner; 213 canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX), 214 y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY)); 215 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0); 216 } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX && 217 y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) { 218 // LL corner 219 index = kLowerLeft_Corner; 220 canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX), 221 y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY)); 222 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0); 223 } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX && 224 y < fRect.fTop + fRadii[kUpperRight_Corner].fY) { 225 // UR corner 226 index = kUpperRight_Corner; 227 canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX), 228 y - (fRect.fTop + fRadii[kUpperRight_Corner].fY)); 229 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0); 230 } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX && 231 y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) { 232 // LR corner 233 index = kLowerRight_Corner; 234 canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX), 235 y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY)); 236 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0); 237 } else { 238 // not in any of the corners 239 return true; 240 } 241 } 242 243 // A point is in an ellipse (in standard position) if: 244 // x^2 y^2 245 // ----- + ----- <= 1 246 // a^2 b^2 247 // or : 248 // b^2*x^2 + a^2*y^2 <= (ab)^2 249 SkScalar dist = SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) + 250 SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX)); 251 return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY)); 252} 253 254bool SkRRect::allCornersCircular() const { 255 return fRadii[0].fX == fRadii[0].fY && 256 fRadii[1].fX == fRadii[1].fY && 257 fRadii[2].fX == fRadii[2].fY && 258 fRadii[3].fX == fRadii[3].fY; 259} 260 261bool SkRRect::contains(const SkRect& rect) const { 262 if (!this->getBounds().contains(rect)) { 263 // If 'rect' isn't contained by the RR's bounds then the 264 // RR definitely doesn't contain it 265 return false; 266 } 267 268 if (this->isRect()) { 269 // the prior test was sufficient 270 return true; 271 } 272 273 // At this point we know all four corners of 'rect' are inside the 274 // bounds of of this RR. Check to make sure all the corners are inside 275 // all the curves 276 return this->checkCornerContainment(rect.fLeft, rect.fTop) && 277 this->checkCornerContainment(rect.fRight, rect.fTop) && 278 this->checkCornerContainment(rect.fRight, rect.fBottom) && 279 this->checkCornerContainment(rect.fLeft, rect.fBottom); 280} 281 282static bool radii_are_nine_patch(const SkVector radii[4]) { 283 return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX && 284 radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY && 285 radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX && 286 radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY; 287} 288 289// There is a simplified version of this method in setRectXY 290void SkRRect::computeType() { 291 struct Validator { 292 Validator(const SkRRect* r) : fR(r) {} 293 ~Validator() { SkDEBUGCODE(fR->validate();) } 294 const SkRRect* fR; 295 } autoValidate(this); 296 297 if (fRect.isEmpty()) { 298 fType = kEmpty_Type; 299 return; 300 } 301 302 bool allRadiiEqual = true; // are all x radii equal and all y radii? 303 bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY; 304 305 for (int i = 1; i < 4; ++i) { 306 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 307 // if either radius is zero the corner is square so both have to 308 // be non-zero to have a rounded corner 309 allCornersSquare = false; 310 } 311 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 312 allRadiiEqual = false; 313 } 314 } 315 316 if (allCornersSquare) { 317 fType = kRect_Type; 318 return; 319 } 320 321 if (allRadiiEqual) { 322 if (fRadii[0].fX >= SkScalarHalf(fRect.width()) && 323 fRadii[0].fY >= SkScalarHalf(fRect.height())) { 324 fType = kOval_Type; 325 } else { 326 fType = kSimple_Type; 327 } 328 return; 329 } 330 331 if (radii_are_nine_patch(fRadii)) { 332 fType = kNinePatch_Type; 333 } else { 334 fType = kComplex_Type; 335 } 336} 337 338static bool matrix_only_scale_and_translate(const SkMatrix& matrix) { 339 const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask 340 | SkMatrix::kPerspective_Mask); 341 return (matrix.getType() & m) == 0; 342} 343 344bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { 345 if (nullptr == dst) { 346 return false; 347 } 348 349 // Assert that the caller is not trying to do this in place, which 350 // would violate const-ness. Do not return false though, so that 351 // if they know what they're doing and want to violate it they can. 352 SkASSERT(dst != this); 353 354 if (matrix.isIdentity()) { 355 *dst = *this; 356 return true; 357 } 358 359 // If transform supported 90 degree rotations (which it could), we could 360 // use SkMatrix::rectStaysRect() to check for a valid transformation. 361 if (!matrix_only_scale_and_translate(matrix)) { 362 return false; 363 } 364 365 SkRect newRect; 366 if (!matrix.mapRect(&newRect, fRect)) { 367 return false; 368 } 369 370 // The matrix may have scaled us to zero (or due to float madness, we now have collapsed 371 // some dimension of the rect, so we need to check for that. 372 if (newRect.isEmpty()) { 373 dst->setEmpty(); 374 return true; 375 } 376 377 // At this point, this is guaranteed to succeed, so we can modify dst. 378 dst->fRect = newRect; 379 380 // Since the only transforms that were allowed are scale and translate, the type 381 // remains unchanged. 382 dst->fType = fType; 383 384 if (kOval_Type == fType) { 385 for (int i = 0; i < 4; ++i) { 386 dst->fRadii[i].fX = SkScalarHalf(newRect.width()); 387 dst->fRadii[i].fY = SkScalarHalf(newRect.height()); 388 } 389 SkDEBUGCODE(dst->validate();) 390 return true; 391 } 392 393 // Now scale each corner 394 SkScalar xScale = matrix.getScaleX(); 395 const bool flipX = xScale < 0; 396 if (flipX) { 397 xScale = -xScale; 398 } 399 SkScalar yScale = matrix.getScaleY(); 400 const bool flipY = yScale < 0; 401 if (flipY) { 402 yScale = -yScale; 403 } 404 405 // Scale the radii without respecting the flip. 406 for (int i = 0; i < 4; ++i) { 407 dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale); 408 dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale); 409 } 410 411 // Now swap as necessary. 412 if (flipX) { 413 if (flipY) { 414 // Swap with opposite corners 415 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]); 416 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]); 417 } else { 418 // Only swap in x 419 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]); 420 SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]); 421 } 422 } else if (flipY) { 423 // Only swap in y 424 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]); 425 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]); 426 } 427 428 dst->scaleRadii(); 429 430 return true; 431} 432 433/////////////////////////////////////////////////////////////////////////////// 434 435void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 436 const SkRect r = fRect.makeInset(dx, dy); 437 438 if (r.isEmpty()) { 439 dst->setEmpty(); 440 return; 441 } 442 443 SkVector radii[4]; 444 memcpy(radii, fRadii, sizeof(radii)); 445 for (int i = 0; i < 4; ++i) { 446 if (radii[i].fX) { 447 radii[i].fX -= dx; 448 } 449 if (radii[i].fY) { 450 radii[i].fY -= dy; 451 } 452 } 453 dst->setRectRadii(r, radii); 454} 455 456/////////////////////////////////////////////////////////////////////////////// 457 458size_t SkRRect::writeToMemory(void* buffer) const { 459 SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii)); 460 461 memcpy(buffer, &fRect, sizeof(SkRect)); 462 memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii)); 463 return kSizeInMemory; 464} 465 466size_t SkRRect::readFromMemory(const void* buffer, size_t length) { 467 if (length < kSizeInMemory) { 468 return 0; 469 } 470 471 SkScalar storage[12]; 472 SkASSERT(sizeof(storage) == kSizeInMemory); 473 474 // we make a local copy, to ensure alignment before we cast 475 memcpy(storage, buffer, kSizeInMemory); 476 477 this->setRectRadii(*(const SkRect*)&storage[0], 478 (const SkVector*)&storage[4]); 479 return kSizeInMemory; 480} 481 482#include "SkString.h" 483#include "SkStringUtils.h" 484 485void SkRRect::dump(bool asHex) const { 486 SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 487 488 fRect.dump(asHex); 489 SkString line("const SkPoint corners[] = {\n"); 490 for (int i = 0; i < 4; ++i) { 491 SkString strX, strY; 492 SkAppendScalar(&strX, fRadii[i].x(), asType); 493 SkAppendScalar(&strY, fRadii[i].y(), asType); 494 line.appendf(" { %s, %s },", strX.c_str(), strY.c_str()); 495 if (asHex) { 496 line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y()); 497 } 498 line.append("\n"); 499 } 500 line.append("};"); 501 SkDebugf("%s\n", line.c_str()); 502} 503 504/////////////////////////////////////////////////////////////////////////////// 505 506#ifdef SK_DEBUG 507/** 508 * We need all combinations of predicates to be true to have a "safe" radius value. 509 */ 510static void validate_radius_check_predicates(SkScalar rad, SkScalar min, SkScalar max) { 511 SkASSERT(min <= max); 512 SkASSERT(rad <= max - min); 513 SkASSERT(min + rad <= max); 514 SkASSERT(max - rad >= min); 515} 516 517void SkRRect::validate() const { 518 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 519 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 520 bool allRadiiSame = true; 521 522 for (int i = 1; i < 4; ++i) { 523 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 524 allRadiiZero = false; 525 } 526 527 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 528 allRadiiSame = false; 529 } 530 531 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 532 allCornersSquare = false; 533 } 534 } 535 bool patchesOfNine = radii_are_nine_patch(fRadii); 536 537 switch (fType) { 538 case kEmpty_Type: 539 SkASSERT(fRect.isEmpty()); 540 SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare); 541 break; 542 case kRect_Type: 543 SkASSERT(!fRect.isEmpty()); 544 SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare); 545 break; 546 case kOval_Type: 547 SkASSERT(!fRect.isEmpty()); 548 SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare); 549 550 for (int i = 0; i < 4; ++i) { 551 SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width()))); 552 SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))); 553 } 554 break; 555 case kSimple_Type: 556 SkASSERT(!fRect.isEmpty()); 557 SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare); 558 break; 559 case kNinePatch_Type: 560 SkASSERT(!fRect.isEmpty()); 561 SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare); 562 SkASSERT(patchesOfNine); 563 break; 564 case kComplex_Type: 565 SkASSERT(!fRect.isEmpty()); 566 SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare); 567 SkASSERT(!patchesOfNine); 568 break; 569 } 570 571 for (int i = 0; i < 4; ++i) { 572 validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight); 573 validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom); 574 } 575} 576#endif // SK_DEBUG 577 578/////////////////////////////////////////////////////////////////////////////// 579