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 15 void 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 *= scale; 37 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 SkASSERT(this->isValid()); 50 } 51 52 void 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 *= scale; 83 topRad *= scale; 84 rightRad *= scale; 85 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 SkASSERT(this->isValid()); 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. 117 static 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 124 void 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 164 void 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 SkASSERT(this->isValid()); 197 } 198 199 // This method determines if a point known to be inside the RRect's bounds is 200 // inside all the corners. 201 bool 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 = SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) + 250 SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX); 251 return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY); 252 } 253 254 bool 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 261 bool 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 282 static 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 290 void SkRRect::computeType() { 291 struct Validator { 292 Validator(const SkRRect* r) : fR(r) {} 293 ~Validator() { SkASSERT(fR->isValid()); } 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 338 static 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 344 bool 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 SkASSERT(dst->isValid()); 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 = fRadii[i].fX * xScale; 408 dst->fRadii[i].fY = 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 435 void 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 458 size_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 466 size_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 485 void 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 /** 507 * We need all combinations of predicates to be true to have a "safe" radius value. 508 */ 509 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) { 510 return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min); 511 } 512 513 bool SkRRect::isValid() const { 514 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 515 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 516 bool allRadiiSame = true; 517 518 for (int i = 1; i < 4; ++i) { 519 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 520 allRadiiZero = false; 521 } 522 523 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 524 allRadiiSame = false; 525 } 526 527 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 528 allCornersSquare = false; 529 } 530 } 531 bool patchesOfNine = radii_are_nine_patch(fRadii); 532 533 switch (fType) { 534 case kEmpty_Type: 535 if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 536 return false; 537 } 538 break; 539 case kRect_Type: 540 if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 541 return false; 542 } 543 break; 544 case kOval_Type: 545 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 546 return false; 547 } 548 549 for (int i = 0; i < 4; ++i) { 550 if (!SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())) || 551 !SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))) { 552 return false; 553 } 554 } 555 break; 556 case kSimple_Type: 557 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 558 return false; 559 } 560 break; 561 case kNinePatch_Type: 562 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 563 !patchesOfNine) { 564 return false; 565 } 566 break; 567 case kComplex_Type: 568 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 569 patchesOfNine) { 570 return false; 571 } 572 break; 573 } 574 575 for (int i = 0; i < 4; ++i) { 576 if (!are_radius_check_predicates_valid(fRadii[i].fX, fRect.fLeft, fRect.fRight) || 577 !are_radius_check_predicates_valid(fRadii[i].fY, fRect.fTop, fRect.fBottom)) { 578 return false; 579 } 580 } 581 582 return true; 583 } 584 585 /////////////////////////////////////////////////////////////////////////////// 586