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(SkScalar tolerance) const { 255 return SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY, tolerance) && 256 SkScalarNearlyEqual(fRadii[1].fX, fRadii[1].fY, tolerance) && 257 SkScalarNearlyEqual(fRadii[2].fX, fRadii[2].fY, tolerance) && 258 SkScalarNearlyEqual(fRadii[3].fX, fRadii[3].fY, tolerance); 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 // Serialize only the rect and corners, but not the derived type tag. 460 memcpy(buffer, this, kSizeInMemory); 461 return kSizeInMemory; 462 } 463 464 size_t SkRRect::readFromMemory(const void* buffer, size_t length) { 465 if (length < kSizeInMemory) { 466 return 0; 467 } 468 469 // Deserialize rect and corners, then rederive the type tag. 470 memcpy(this, buffer, kSizeInMemory); 471 this->computeType(); 472 473 return kSizeInMemory; 474 } 475 476 #include "SkString.h" 477 #include "SkStringUtils.h" 478 479 void SkRRect::dump(bool asHex) const { 480 SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 481 482 fRect.dump(asHex); 483 SkString line("const SkPoint corners[] = {\n"); 484 for (int i = 0; i < 4; ++i) { 485 SkString strX, strY; 486 SkAppendScalar(&strX, fRadii[i].x(), asType); 487 SkAppendScalar(&strY, fRadii[i].y(), asType); 488 line.appendf(" { %s, %s },", strX.c_str(), strY.c_str()); 489 if (asHex) { 490 line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y()); 491 } 492 line.append("\n"); 493 } 494 line.append("};"); 495 SkDebugf("%s\n", line.c_str()); 496 } 497 498 /////////////////////////////////////////////////////////////////////////////// 499 500 /** 501 * We need all combinations of predicates to be true to have a "safe" radius value. 502 */ 503 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) { 504 return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min); 505 } 506 507 bool SkRRect::isValid() const { 508 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 509 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 510 bool allRadiiSame = true; 511 512 for (int i = 1; i < 4; ++i) { 513 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 514 allRadiiZero = false; 515 } 516 517 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 518 allRadiiSame = false; 519 } 520 521 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 522 allCornersSquare = false; 523 } 524 } 525 bool patchesOfNine = radii_are_nine_patch(fRadii); 526 527 switch (fType) { 528 case kEmpty_Type: 529 if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 530 return false; 531 } 532 break; 533 case kRect_Type: 534 if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 535 return false; 536 } 537 break; 538 case kOval_Type: 539 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 540 return false; 541 } 542 543 for (int i = 0; i < 4; ++i) { 544 if (!SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())) || 545 !SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))) { 546 return false; 547 } 548 } 549 break; 550 case kSimple_Type: 551 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 552 return false; 553 } 554 break; 555 case kNinePatch_Type: 556 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 557 !patchesOfNine) { 558 return false; 559 } 560 break; 561 case kComplex_Type: 562 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 563 patchesOfNine) { 564 return false; 565 } 566 break; 567 } 568 569 for (int i = 0; i < 4; ++i) { 570 if (!are_radius_check_predicates_valid(fRadii[i].fX, fRect.fLeft, fRect.fRight) || 571 !are_radius_check_predicates_valid(fRadii[i].fY, fRect.fTop, fRect.fBottom)) { 572 return false; 573 } 574 } 575 576 return true; 577 } 578 579 /////////////////////////////////////////////////////////////////////////////// 580