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 "include/core/SkMatrix.h" 9 #include "include/core/SkString.h" 10 #include "include/private/SkMalloc.h" 11 #include "src/core/SkBuffer.h" 12 #include "src/core/SkRRectPriv.h" 13 #include "src/core/SkRectPriv.h" 14 #include "src/core/SkScaleToSides.h" 15 16 #include <cmath> 17 #include <utility> 18 19 /////////////////////////////////////////////////////////////////////////////// 20 setOval(const SkRect & oval)21 void SkRRect::setOval(const SkRect& oval) { 22 if (!this->initializeRect(oval)) { 23 return; 24 } 25 26 SkScalar xRad = SkRectPriv::HalfWidth(fRect); 27 SkScalar yRad = SkRectPriv::HalfHeight(fRect); 28 29 if (xRad == 0.0f || yRad == 0.0f) { 30 // All the corners will be square 31 memset(fRadii, 0, sizeof(fRadii)); 32 fType = kRect_Type; 33 } else { 34 for (int i = 0; i < 4; ++i) { 35 fRadii[i].set(xRad, yRad); 36 } 37 fType = kOval_Type; 38 } 39 40 SkASSERT(this->isValid()); 41 } 42 setRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)43 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 44 if (!this->initializeRect(rect)) { 45 return; 46 } 47 48 if (!SkScalarsAreFinite(xRad, yRad)) { 49 xRad = yRad = 0; // devolve into a simple rect 50 } 51 52 if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) { 53 // At most one of these two divides will be by zero, and neither numerator is zero. 54 SkScalar scale = std::min(sk_ieee_float_divide(fRect. width(), xRad + xRad), 55 sk_ieee_float_divide(fRect.height(), yRad + yRad)); 56 SkASSERT(scale < SK_Scalar1); 57 xRad *= scale; 58 yRad *= scale; 59 } 60 61 if (xRad <= 0 || yRad <= 0) { 62 // all corners are square in this case 63 this->setRect(rect); 64 return; 65 } 66 67 for (int i = 0; i < 4; ++i) { 68 fRadii[i].set(xRad, yRad); 69 } 70 fType = kSimple_Type; 71 if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) { 72 fType = kOval_Type; 73 // TODO: assert that all the x&y radii are already W/2 & H/2 74 } 75 76 SkASSERT(this->isValid()); 77 } 78 setNinePatch(const SkRect & rect,SkScalar leftRad,SkScalar topRad,SkScalar rightRad,SkScalar bottomRad)79 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, 80 SkScalar rightRad, SkScalar bottomRad) { 81 if (!this->initializeRect(rect)) { 82 return; 83 } 84 85 const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; 86 if (!SkScalarsAreFinite(array, 4)) { 87 this->setRect(rect); // devolve into a simple rect 88 return; 89 } 90 91 leftRad = std::max(leftRad, 0.0f); 92 topRad = std::max(topRad, 0.0f); 93 rightRad = std::max(rightRad, 0.0f); 94 bottomRad = std::max(bottomRad, 0.0f); 95 96 SkScalar scale = SK_Scalar1; 97 if (leftRad + rightRad > fRect.width()) { 98 scale = fRect.width() / (leftRad + rightRad); 99 } 100 if (topRad + bottomRad > fRect.height()) { 101 scale = std::min(scale, fRect.height() / (topRad + bottomRad)); 102 } 103 104 if (scale < SK_Scalar1) { 105 leftRad *= scale; 106 topRad *= scale; 107 rightRad *= scale; 108 bottomRad *= scale; 109 } 110 111 if (leftRad == rightRad && topRad == bottomRad) { 112 if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) { 113 fType = kOval_Type; 114 } else if (0 == leftRad || 0 == topRad) { 115 // If the left and (by equality check above) right radii are zero then it is a rect. 116 // Same goes for top/bottom. 117 fType = kRect_Type; 118 leftRad = 0; 119 topRad = 0; 120 rightRad = 0; 121 bottomRad = 0; 122 } else { 123 fType = kSimple_Type; 124 } 125 } else { 126 fType = kNinePatch_Type; 127 } 128 129 fRadii[kUpperLeft_Corner].set(leftRad, topRad); 130 fRadii[kUpperRight_Corner].set(rightRad, topRad); 131 fRadii[kLowerRight_Corner].set(rightRad, bottomRad); 132 fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); 133 134 SkASSERT(this->isValid()); 135 } 136 137 // These parameters intentionally double. Apropos crbug.com/463920, if one of the 138 // radii is huge while the other is small, single precision math can completely 139 // miss the fact that a scale is required. compute_min_scale(double rad1,double rad2,double limit,double curMin)140 static double compute_min_scale(double rad1, double rad2, double limit, double curMin) { 141 if ((rad1 + rad2) > limit) { 142 return std::min(curMin, limit / (rad1 + rad2)); 143 } 144 return curMin; 145 } 146 clamp_to_zero(SkVector radii[4])147 static bool clamp_to_zero(SkVector radii[4]) { 148 bool allCornersSquare = true; 149 150 // Clamp negative radii to zero 151 for (int i = 0; i < 4; ++i) { 152 if (radii[i].fX <= 0 || radii[i].fY <= 0) { 153 // In this case we are being a little fast & loose. Since one of 154 // the radii is 0 the corner is square. However, the other radii 155 // could still be non-zero and play in the global scale factor 156 // computation. 157 radii[i].fX = 0; 158 radii[i].fY = 0; 159 } else { 160 allCornersSquare = false; 161 } 162 } 163 164 return allCornersSquare; 165 } 166 setRectRadii(const SkRect & rect,const SkVector radii[4])167 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) { 168 if (!this->initializeRect(rect)) { 169 return; 170 } 171 172 if (!SkScalarsAreFinite(&radii[0].fX, 8)) { 173 this->setRect(rect); // devolve into a simple rect 174 return; 175 } 176 177 memcpy(fRadii, radii, sizeof(fRadii)); 178 179 if (clamp_to_zero(fRadii)) { 180 this->setRect(rect); 181 return; 182 } 183 184 this->scaleRadii(); 185 186 if (!this->isValid()) { 187 this->setRect(rect); 188 return; 189 } 190 } 191 initializeRect(const SkRect & rect)192 bool SkRRect::initializeRect(const SkRect& rect) { 193 // Check this before sorting because sorting can hide nans. 194 if (!rect.isFinite()) { 195 *this = SkRRect(); 196 return false; 197 } 198 fRect = rect.makeSorted(); 199 if (fRect.isEmpty()) { 200 memset(fRadii, 0, sizeof(fRadii)); 201 fType = kEmpty_Type; 202 return false; 203 } 204 return true; 205 } 206 207 // If we can't distinguish one of the radii relative to the other, force it to zero so it 208 // doesn't confuse us later. See crbug.com/850350 209 // flush_to_zero(SkScalar & a,SkScalar & b)210 static void flush_to_zero(SkScalar& a, SkScalar& b) { 211 SkASSERT(a >= 0); 212 SkASSERT(b >= 0); 213 if (a + b == a) { 214 b = 0; 215 } else if (a + b == b) { 216 a = 0; 217 } 218 } 219 scaleRadii()220 bool SkRRect::scaleRadii() { 221 // Proportionally scale down all radii to fit. Find the minimum ratio 222 // of a side and the radii on that side (for all four sides) and use 223 // that to scale down _all_ the radii. This algorithm is from the 224 // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping 225 // Curves: 226 // "Let f = min(Li/Si), where i is one of { top, right, bottom, left }, 227 // Si is the sum of the two corresponding radii of the corners on side i, 228 // and Ltop = Lbottom = the width of the box, 229 // and Lleft = Lright = the height of the box. 230 // If f < 1, then all corner radii are reduced by multiplying them by f." 231 double scale = 1.0; 232 233 // The sides of the rectangle may be larger than a float. 234 double width = (double)fRect.fRight - (double)fRect.fLeft; 235 double height = (double)fRect.fBottom - (double)fRect.fTop; 236 scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width, scale); 237 scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale); 238 scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width, scale); 239 scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale); 240 241 flush_to_zero(fRadii[0].fX, fRadii[1].fX); 242 flush_to_zero(fRadii[1].fY, fRadii[2].fY); 243 flush_to_zero(fRadii[2].fX, fRadii[3].fX); 244 flush_to_zero(fRadii[3].fY, fRadii[0].fY); 245 246 if (scale < 1.0) { 247 SkScaleToSides::AdjustRadii(width, scale, &fRadii[0].fX, &fRadii[1].fX); 248 SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY); 249 SkScaleToSides::AdjustRadii(width, scale, &fRadii[2].fX, &fRadii[3].fX); 250 SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY); 251 } 252 253 // adjust radii may set x or y to zero; set companion to zero as well 254 clamp_to_zero(fRadii); 255 256 // May be simple, oval, or complex, or become a rect/empty if the radii adjustment made them 0 257 this->computeType(); 258 259 // TODO: Why can't we assert this here? 260 //SkASSERT(this->isValid()); 261 262 return scale < 1.0; 263 } 264 265 // This method determines if a point known to be inside the RRect's bounds is 266 // inside all the corners. checkCornerContainment(SkScalar x,SkScalar y) const267 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const { 268 SkPoint canonicalPt; // (x,y) translated to one of the quadrants 269 int index; 270 271 if (kOval_Type == this->type()) { 272 canonicalPt.set(x - fRect.centerX(), y - fRect.centerY()); 273 index = kUpperLeft_Corner; // any corner will do in this case 274 } else { 275 if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX && 276 y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) { 277 // UL corner 278 index = kUpperLeft_Corner; 279 canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX), 280 y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY)); 281 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0); 282 } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX && 283 y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) { 284 // LL corner 285 index = kLowerLeft_Corner; 286 canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX), 287 y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY)); 288 SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0); 289 } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX && 290 y < fRect.fTop + fRadii[kUpperRight_Corner].fY) { 291 // UR corner 292 index = kUpperRight_Corner; 293 canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX), 294 y - (fRect.fTop + fRadii[kUpperRight_Corner].fY)); 295 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0); 296 } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX && 297 y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) { 298 // LR corner 299 index = kLowerRight_Corner; 300 canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX), 301 y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY)); 302 SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0); 303 } else { 304 // not in any of the corners 305 return true; 306 } 307 } 308 309 // A point is in an ellipse (in standard position) if: 310 // x^2 y^2 311 // ----- + ----- <= 1 312 // a^2 b^2 313 // or : 314 // b^2*x^2 + a^2*y^2 <= (ab)^2 315 SkScalar dist = SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) + 316 SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX); 317 return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY); 318 } 319 IsNearlySimpleCircular(const SkRRect & rr,SkScalar tolerance)320 bool SkRRectPriv::IsNearlySimpleCircular(const SkRRect& rr, SkScalar tolerance) { 321 SkScalar simpleRadius = rr.fRadii[0].fX; 322 return SkScalarNearlyEqual(simpleRadius, rr.fRadii[0].fY, tolerance) && 323 SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fX, tolerance) && 324 SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fY, tolerance) && 325 SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fX, tolerance) && 326 SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fY, tolerance) && 327 SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fX, tolerance) && 328 SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fY, tolerance); 329 } 330 AllCornersCircular(const SkRRect & rr,SkScalar tolerance)331 bool SkRRectPriv::AllCornersCircular(const SkRRect& rr, SkScalar tolerance) { 332 return SkScalarNearlyEqual(rr.fRadii[0].fX, rr.fRadii[0].fY, tolerance) && 333 SkScalarNearlyEqual(rr.fRadii[1].fX, rr.fRadii[1].fY, tolerance) && 334 SkScalarNearlyEqual(rr.fRadii[2].fX, rr.fRadii[2].fY, tolerance) && 335 SkScalarNearlyEqual(rr.fRadii[3].fX, rr.fRadii[3].fY, tolerance); 336 } 337 contains(const SkRect & rect) const338 bool SkRRect::contains(const SkRect& rect) const { 339 if (!this->getBounds().contains(rect)) { 340 // If 'rect' isn't contained by the RR's bounds then the 341 // RR definitely doesn't contain it 342 return false; 343 } 344 345 if (this->isRect()) { 346 // the prior test was sufficient 347 return true; 348 } 349 350 // At this point we know all four corners of 'rect' are inside the 351 // bounds of of this RR. Check to make sure all the corners are inside 352 // all the curves 353 return this->checkCornerContainment(rect.fLeft, rect.fTop) && 354 this->checkCornerContainment(rect.fRight, rect.fTop) && 355 this->checkCornerContainment(rect.fRight, rect.fBottom) && 356 this->checkCornerContainment(rect.fLeft, rect.fBottom); 357 } 358 radii_are_nine_patch(const SkVector radii[4])359 static bool radii_are_nine_patch(const SkVector radii[4]) { 360 return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX && 361 radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY && 362 radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX && 363 radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY; 364 } 365 366 // There is a simplified version of this method in setRectXY computeType()367 void SkRRect::computeType() { 368 if (fRect.isEmpty()) { 369 SkASSERT(fRect.isSorted()); 370 for (size_t i = 0; i < SK_ARRAY_COUNT(fRadii); ++i) { 371 SkASSERT((fRadii[i] == SkVector{0, 0})); 372 } 373 fType = kEmpty_Type; 374 SkASSERT(this->isValid()); 375 return; 376 } 377 378 bool allRadiiEqual = true; // are all x radii equal and all y radii? 379 bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY; 380 381 for (int i = 1; i < 4; ++i) { 382 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 383 // if either radius is zero the corner is square so both have to 384 // be non-zero to have a rounded corner 385 allCornersSquare = false; 386 } 387 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 388 allRadiiEqual = false; 389 } 390 } 391 392 if (allCornersSquare) { 393 fType = kRect_Type; 394 SkASSERT(this->isValid()); 395 return; 396 } 397 398 if (allRadiiEqual) { 399 if (fRadii[0].fX >= SkScalarHalf(fRect.width()) && 400 fRadii[0].fY >= SkScalarHalf(fRect.height())) { 401 fType = kOval_Type; 402 } else { 403 fType = kSimple_Type; 404 } 405 SkASSERT(this->isValid()); 406 return; 407 } 408 409 if (radii_are_nine_patch(fRadii)) { 410 fType = kNinePatch_Type; 411 } else { 412 fType = kComplex_Type; 413 } 414 415 if (!this->isValid()) { 416 this->setRect(this->rect()); 417 SkASSERT(this->isValid()); 418 } 419 } 420 transform(const SkMatrix & matrix,SkRRect * dst) const421 bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const { 422 if (nullptr == dst) { 423 return false; 424 } 425 426 // Assert that the caller is not trying to do this in place, which 427 // would violate const-ness. Do not return false though, so that 428 // if they know what they're doing and want to violate it they can. 429 SkASSERT(dst != this); 430 431 if (matrix.isIdentity()) { 432 *dst = *this; 433 return true; 434 } 435 436 if (!matrix.preservesAxisAlignment()) { 437 return false; 438 } 439 440 SkRect newRect; 441 if (!matrix.mapRect(&newRect, fRect)) { 442 return false; 443 } 444 445 // The matrix may have scaled us to zero (or due to float madness, we now have collapsed 446 // some dimension of the rect, so we need to check for that. Note that matrix must be 447 // scale and translate and mapRect() produces a sorted rect. So an empty rect indicates 448 // loss of precision. 449 if (!newRect.isFinite() || newRect.isEmpty()) { 450 return false; 451 } 452 453 // At this point, this is guaranteed to succeed, so we can modify dst. 454 dst->fRect = newRect; 455 456 // Since the only transforms that were allowed are axis aligned, the type 457 // remains unchanged. 458 dst->fType = fType; 459 460 if (kRect_Type == fType) { 461 SkASSERT(dst->isValid()); 462 return true; 463 } 464 if (kOval_Type == fType) { 465 for (int i = 0; i < 4; ++i) { 466 dst->fRadii[i].fX = SkScalarHalf(newRect.width()); 467 dst->fRadii[i].fY = SkScalarHalf(newRect.height()); 468 } 469 SkASSERT(dst->isValid()); 470 return true; 471 } 472 473 // Now scale each corner 474 SkScalar xScale = matrix.getScaleX(); 475 SkScalar yScale = matrix.getScaleY(); 476 477 // There is a rotation of 90 (Clockwise 90) or 270 (Counter clockwise 90). 478 // 180 degrees rotations are simply flipX with a flipY and would come under 479 // a scale transform. 480 if (!matrix.isScaleTranslate()) { 481 const bool isClockwise = matrix.getSkewX() < 0; 482 483 // The matrix location for scale changes if there is a rotation. 484 xScale = matrix.getSkewY() * (isClockwise ? 1 : -1); 485 yScale = matrix.getSkewX() * (isClockwise ? -1 : 1); 486 487 const int dir = isClockwise ? 3 : 1; 488 for (int i = 0; i < 4; ++i) { 489 const int src = (i + dir) >= 4 ? (i + dir) % 4 : (i + dir); 490 // Swap X and Y axis for the radii. 491 dst->fRadii[i].fX = fRadii[src].fY; 492 dst->fRadii[i].fY = fRadii[src].fX; 493 } 494 } else { 495 for (int i = 0; i < 4; ++i) { 496 dst->fRadii[i].fX = fRadii[i].fX; 497 dst->fRadii[i].fY = fRadii[i].fY; 498 } 499 } 500 501 const bool flipX = xScale < 0; 502 if (flipX) { 503 xScale = -xScale; 504 } 505 506 const bool flipY = yScale < 0; 507 if (flipY) { 508 yScale = -yScale; 509 } 510 511 // Scale the radii without respecting the flip. 512 for (int i = 0; i < 4; ++i) { 513 dst->fRadii[i].fX *= xScale; 514 dst->fRadii[i].fY *= yScale; 515 } 516 517 // Now swap as necessary. 518 using std::swap; 519 if (flipX) { 520 if (flipY) { 521 // Swap with opposite corners 522 swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]); 523 swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]); 524 } else { 525 // Only swap in x 526 swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]); 527 swap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]); 528 } 529 } else if (flipY) { 530 // Only swap in y 531 swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]); 532 swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]); 533 } 534 535 if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) { 536 return false; 537 } 538 539 dst->scaleRadii(); 540 dst->isValid(); // TODO: is this meant to be SkASSERT(dst->isValid())? 541 542 return true; 543 } 544 545 /////////////////////////////////////////////////////////////////////////////// 546 inset(SkScalar dx,SkScalar dy,SkRRect * dst) const547 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 548 SkRect r = fRect.makeInset(dx, dy); 549 bool degenerate = false; 550 if (r.fRight <= r.fLeft) { 551 degenerate = true; 552 r.fLeft = r.fRight = SkScalarAve(r.fLeft, r.fRight); 553 } 554 if (r.fBottom <= r.fTop) { 555 degenerate = true; 556 r.fTop = r.fBottom = SkScalarAve(r.fTop, r.fBottom); 557 } 558 if (degenerate) { 559 dst->fRect = r; 560 memset(dst->fRadii, 0, sizeof(dst->fRadii)); 561 dst->fType = kEmpty_Type; 562 return; 563 } 564 if (!r.isFinite()) { 565 *dst = SkRRect(); 566 return; 567 } 568 569 SkVector radii[4]; 570 memcpy(radii, fRadii, sizeof(radii)); 571 for (int i = 0; i < 4; ++i) { 572 if (radii[i].fX) { 573 radii[i].fX -= dx; 574 } 575 if (radii[i].fY) { 576 radii[i].fY -= dy; 577 } 578 } 579 dst->setRectRadii(r, radii); 580 } 581 582 /////////////////////////////////////////////////////////////////////////////// 583 writeToMemory(void * buffer) const584 size_t SkRRect::writeToMemory(void* buffer) const { 585 // Serialize only the rect and corners, but not the derived type tag. 586 memcpy(buffer, this, kSizeInMemory); 587 return kSizeInMemory; 588 } 589 WriteToBuffer(const SkRRect & rr,SkWBuffer * buffer)590 void SkRRectPriv::WriteToBuffer(const SkRRect& rr, SkWBuffer* buffer) { 591 // Serialize only the rect and corners, but not the derived type tag. 592 buffer->write(&rr, SkRRect::kSizeInMemory); 593 } 594 readFromMemory(const void * buffer,size_t length)595 size_t SkRRect::readFromMemory(const void* buffer, size_t length) { 596 if (length < kSizeInMemory) { 597 return 0; 598 } 599 600 // The extra (void*) tells GCC not to worry that kSizeInMemory < sizeof(SkRRect). 601 602 SkRRect raw; 603 memcpy((void*)&raw, buffer, kSizeInMemory); 604 this->setRectRadii(raw.fRect, raw.fRadii); 605 return kSizeInMemory; 606 } 607 ReadFromBuffer(SkRBuffer * buffer,SkRRect * rr)608 bool SkRRectPriv::ReadFromBuffer(SkRBuffer* buffer, SkRRect* rr) { 609 if (buffer->available() < SkRRect::kSizeInMemory) { 610 return false; 611 } 612 SkRRect storage; 613 return buffer->read(&storage, SkRRect::kSizeInMemory) && 614 (rr->readFromMemory(&storage, SkRRect::kSizeInMemory) == SkRRect::kSizeInMemory); 615 } 616 617 #include "include/core/SkString.h" 618 #include "src/core/SkStringUtils.h" 619 dumpToString(bool asHex) const620 SkString SkRRect::dumpToString(bool asHex) const { 621 SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 622 623 fRect.dump(asHex); 624 SkString line("const SkPoint corners[] = {\n"); 625 for (int i = 0; i < 4; ++i) { 626 SkString strX, strY; 627 SkAppendScalar(&strX, fRadii[i].x(), asType); 628 SkAppendScalar(&strY, fRadii[i].y(), asType); 629 line.appendf(" { %s, %s },", strX.c_str(), strY.c_str()); 630 if (asHex) { 631 line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y()); 632 } 633 line.append("\n"); 634 } 635 line.append("};"); 636 return line; 637 } 638 dump(bool asHex) const639 void SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); } 640 641 /////////////////////////////////////////////////////////////////////////////// 642 643 /** 644 * We need all combinations of predicates to be true to have a "safe" radius value. 645 */ are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)646 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) { 647 return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) && 648 rad >= 0; 649 } 650 isValid() const651 bool SkRRect::isValid() const { 652 if (!AreRectAndRadiiValid(fRect, fRadii)) { 653 return false; 654 } 655 656 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 657 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 658 bool allRadiiSame = true; 659 660 for (int i = 1; i < 4; ++i) { 661 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 662 allRadiiZero = false; 663 } 664 665 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 666 allRadiiSame = false; 667 } 668 669 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 670 allCornersSquare = false; 671 } 672 } 673 bool patchesOfNine = radii_are_nine_patch(fRadii); 674 675 if (fType < 0 || fType > kLastType) { 676 return false; 677 } 678 679 switch (fType) { 680 case kEmpty_Type: 681 if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 682 return false; 683 } 684 break; 685 case kRect_Type: 686 if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) { 687 return false; 688 } 689 break; 690 case kOval_Type: 691 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 692 return false; 693 } 694 695 for (int i = 0; i < 4; ++i) { 696 if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) || 697 !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) { 698 return false; 699 } 700 } 701 break; 702 case kSimple_Type: 703 if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) { 704 return false; 705 } 706 break; 707 case kNinePatch_Type: 708 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 709 !patchesOfNine) { 710 return false; 711 } 712 break; 713 case kComplex_Type: 714 if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare || 715 patchesOfNine) { 716 return false; 717 } 718 break; 719 } 720 721 return true; 722 } 723 AreRectAndRadiiValid(const SkRect & rect,const SkVector radii[4])724 bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) { 725 if (!rect.isFinite() || !rect.isSorted()) { 726 return false; 727 } 728 for (int i = 0; i < 4; ++i) { 729 if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) || 730 !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) { 731 return false; 732 } 733 } 734 return true; 735 } 736 /////////////////////////////////////////////////////////////////////////////// 737 InnerBounds(const SkRRect & rr)738 SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) { 739 if (rr.isEmpty() || rr.isRect()) { 740 return rr.rect(); 741 } 742 743 // We start with the outer bounds of the round rect and consider three subsets and take the 744 // one with maximum area. The first two are the horizontal and vertical rects inset from the 745 // corners, the third is the rect inscribed at the corner curves' maximal point. This forms 746 // the exact solution when all corners have the same radii (the radii do not have to be 747 // circular). 748 SkRect innerBounds = rr.getBounds(); 749 SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner); 750 SkVector tr = rr.radii(SkRRect::kUpperRight_Corner); 751 SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner); 752 SkVector br = rr.radii(SkRRect::kLowerRight_Corner); 753 754 // Select maximum inset per edge, which may move an adjacent corner of the inscribed 755 // rectangle off of the rounded-rect path, but that is acceptable given that the general 756 // equation for inscribed area is non-trivial to evaluate. 757 SkScalar leftShift = std::max(tl.fX, bl.fX); 758 SkScalar topShift = std::max(tl.fY, tr.fY); 759 SkScalar rightShift = std::max(tr.fX, br.fX); 760 SkScalar bottomShift = std::max(bl.fY, br.fY); 761 762 SkScalar dw = leftShift + rightShift; 763 SkScalar dh = topShift + bottomShift; 764 765 // Area removed by shifting left/right 766 SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height(); 767 // And by shifting top/bottom 768 SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width(); 769 // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has 770 // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the 771 // safe shift per edge (since the shifts already are the max radius for that edge). 772 // - We actually scale by a value slightly increased to make it so that the shifted corners are 773 // safely inside the curves, otherwise numerical stability can cause it to fail contains(). 774 static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f; 775 SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh); 776 777 if (horizArea > vertArea && horizArea > innerArea) { 778 // Cut off corners by insetting left and right 779 innerBounds.fLeft += leftShift; 780 innerBounds.fRight -= rightShift; 781 } else if (vertArea > innerArea) { 782 // Cut off corners by insetting top and bottom 783 innerBounds.fTop += topShift; 784 innerBounds.fBottom -= bottomShift; 785 } else if (innerArea > 0.f) { 786 // Inset on all sides, scaled to touch 787 innerBounds.fLeft += kScale * leftShift; 788 innerBounds.fRight -= kScale * rightShift; 789 innerBounds.fTop += kScale * topShift; 790 innerBounds.fBottom -= kScale * bottomShift; 791 } else { 792 // Inner region would collapse to empty 793 return SkRect::MakeEmpty(); 794 } 795 796 SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty()); 797 return innerBounds; 798 } 799 ConservativeIntersect(const SkRRect & a,const SkRRect & b)800 SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) { 801 // Returns the coordinate of the rect matching the corner enum. 802 auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint { 803 switch(corner) { 804 case SkRRect::kUpperLeft_Corner: return {r.fLeft, r.fTop}; 805 case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop}; 806 case SkRRect::kLowerLeft_Corner: return {r.fLeft, r.fBottom}; 807 case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom}; 808 default: SkUNREACHABLE; 809 } 810 }; 811 // Returns true if shape A's extreme point is contained within shape B's extreme point, relative 812 // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this 813 // is sufficient for A's ellipse arc to be contained by B's ellipse arc. 814 auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) { 815 switch(corner) { 816 case SkRRect::kUpperLeft_Corner: return a.fX >= b.fX && a.fY >= b.fY; 817 case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY; 818 case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY; 819 case SkRRect::kLowerLeft_Corner: return a.fX >= b.fX && a.fY <= b.fY; 820 default: SkUNREACHABLE; 821 } 822 }; 823 824 auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) { 825 SkPoint test = getCorner(r, corner); 826 SkPoint aCorner = getCorner(a.rect(), corner); 827 SkPoint bCorner = getCorner(b.rect(), corner); 828 829 if (test == aCorner && test == bCorner) { 830 // The round rects share a corner anchor, so pick A or B such that its X and Y radii 831 // are both larger than the other rrect's, or return false if neither A or B has the max 832 // corner radii (this is more permissive than the single corner tests below). 833 SkVector aRadii = a.radii(corner); 834 SkVector bRadii = b.radii(corner); 835 if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) { 836 *radii = aRadii; 837 return true; 838 } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) { 839 *radii = bRadii; 840 return true; 841 } else { 842 return false; 843 } 844 } else if (test == aCorner) { 845 // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate 846 // so we resrict it to when the corners have the same radii. If not, we use the more 847 // conservative test that the extreme point of A's bounding box is contained in B. 848 *radii = a.radii(corner); 849 if (*radii == b.radii(corner)) { 850 return insideCorner(corner, aCorner, bCorner); // A inside B 851 } else { 852 return b.checkCornerContainment(aCorner.fX, aCorner.fY); 853 } 854 } else if (test == bCorner) { 855 // Mirror of the above 856 *radii = b.radii(corner); 857 if (*radii == a.radii(corner)) { 858 return insideCorner(corner, bCorner, aCorner); // B inside A 859 } else { 860 return a.checkCornerContainment(bCorner.fX, bCorner.fY); 861 } 862 } else { 863 // This is a corner formed by two straight edges of A and B, so confirm that it is 864 // contained in both (if not, then the intersection can't be a round rect). 865 *radii = {0.f, 0.f}; 866 return a.checkCornerContainment(test.fX, test.fY) && 867 b.checkCornerContainment(test.fX, test.fY); 868 } 869 }; 870 871 // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by 872 // valid existing SkRRects, we know we are finite. 873 SkRRect intersection; 874 if (!intersection.fRect.intersect(a.rect(), b.rect())) { 875 // Definitely no intersection 876 return SkRRect::MakeEmpty(); 877 } 878 879 const SkRRect::Corner corners[] = { 880 SkRRect::kUpperLeft_Corner, 881 SkRRect::kUpperRight_Corner, 882 SkRRect::kLowerRight_Corner, 883 SkRRect::kLowerLeft_Corner 884 }; 885 // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider 886 // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s. 887 // If the bound's corner point matches a's edges and is inside 'b', we use a's radii. 888 // Same for b's radii. If any corner fails these conditions, we reject the intersection as an 889 // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the 890 // intersection shape. 891 for (auto c : corners) { 892 if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) { 893 return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect 894 } 895 } 896 897 // Check for radius overlap along the four edges, since the earlier evaluation was only a 898 // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect. 899 // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit. 900 // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means 901 // the intersection shape is definitively not a round rect. 902 if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) || 903 intersection.scaleRadii()) { 904 return SkRRect::MakeEmpty(); 905 } 906 907 // The intersection is an rrect of the given radii. Potentially all 4 corners could have 908 // been simplified to (0,0) radii, making the intersection a rectangle. 909 intersection.computeType(); 910 return intersection; 911 } 912