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