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 "SkRRect.h" 9#include "SkMatrix.h" 10 11/////////////////////////////////////////////////////////////////////////////// 12 13void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { 14 if (rect.isEmpty()) { 15 this->setEmpty(); 16 return; 17 } 18 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 // Now scale each corner 346 SkScalar xScale = matrix.getScaleX(); 347 const bool flipX = xScale < 0; 348 if (flipX) { 349 xScale = -xScale; 350 } 351 SkScalar yScale = matrix.getScaleY(); 352 const bool flipY = yScale < 0; 353 if (flipY) { 354 yScale = -yScale; 355 } 356 357 // Scale the radii without respecting the flip. 358 for (int i = 0; i < 4; ++i) { 359 dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale); 360 dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale); 361 } 362 363 // Now swap as necessary. 364 if (flipX) { 365 if (flipY) { 366 // Swap with opposite corners 367 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]); 368 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]); 369 } else { 370 // Only swap in x 371 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]); 372 SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]); 373 } 374 } else if (flipY) { 375 // Only swap in y 376 SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]); 377 SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]); 378 } 379 380 // Since the only transforms that were allowed are scale and translate, the type 381 // remains unchanged. 382 dst->fType = fType; 383 384 SkDEBUGCODE(dst->validate();) 385 386 return true; 387} 388 389/////////////////////////////////////////////////////////////////////////////// 390 391void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const { 392 SkRect r = fRect; 393 394 r.inset(dx, dy); 395 if (r.isEmpty()) { 396 dst->setEmpty(); 397 return; 398 } 399 400 SkVector radii[4]; 401 memcpy(radii, fRadii, sizeof(radii)); 402 for (int i = 0; i < 4; ++i) { 403 if (radii[i].fX) { 404 radii[i].fX -= dx; 405 } 406 if (radii[i].fY) { 407 radii[i].fY -= dy; 408 } 409 } 410 dst->setRectRadii(r, radii); 411} 412 413/////////////////////////////////////////////////////////////////////////////// 414 415size_t SkRRect::writeToMemory(void* buffer) const { 416 SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii)); 417 418 memcpy(buffer, &fRect, sizeof(SkRect)); 419 memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii)); 420 return kSizeInMemory; 421} 422 423size_t SkRRect::readFromMemory(const void* buffer, size_t length) { 424 if (length < kSizeInMemory) { 425 return 0; 426 } 427 428 SkScalar storage[12]; 429 SkASSERT(sizeof(storage) == kSizeInMemory); 430 431 // we make a local copy, to ensure alignment before we cast 432 memcpy(storage, buffer, kSizeInMemory); 433 434 this->setRectRadii(*(const SkRect*)&storage[0], 435 (const SkVector*)&storage[4]); 436 return kSizeInMemory; 437} 438 439#ifdef SK_DEVELOPER 440void SkRRect::dump() const { 441 SkDebugf("Rect: "); 442 fRect.dump(); 443 SkDebugf(" Corners: { TL: (%f, %f), TR: (%f, %f), BR: (%f, %f), BL: (%f, %f) }", 444 fRadii[kUpperLeft_Corner].fX, fRadii[kUpperLeft_Corner].fY, 445 fRadii[kUpperRight_Corner].fX, fRadii[kUpperRight_Corner].fY, 446 fRadii[kLowerRight_Corner].fX, fRadii[kLowerRight_Corner].fY, 447 fRadii[kLowerLeft_Corner].fX, fRadii[kLowerLeft_Corner].fY); 448} 449#endif 450 451/////////////////////////////////////////////////////////////////////////////// 452 453#ifdef SK_DEBUG 454void SkRRect::validate() const { 455 bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY); 456 bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY); 457 bool allRadiiSame = true; 458 459 for (int i = 1; i < 4; ++i) { 460 if (0 != fRadii[i].fX || 0 != fRadii[i].fY) { 461 allRadiiZero = false; 462 } 463 464 if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) { 465 allRadiiSame = false; 466 } 467 468 if (0 != fRadii[i].fX && 0 != fRadii[i].fY) { 469 allCornersSquare = false; 470 } 471 } 472 bool patchesOfNine = radii_are_nine_patch(fRadii); 473 474 switch (fType) { 475 case kEmpty_Type: 476 SkASSERT(fRect.isEmpty()); 477 SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare); 478 break; 479 case kRect_Type: 480 SkASSERT(!fRect.isEmpty()); 481 SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare); 482 break; 483 case kOval_Type: 484 SkASSERT(!fRect.isEmpty()); 485 SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare); 486 487 for (int i = 0; i < 4; ++i) { 488 SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width()))); 489 SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))); 490 } 491 break; 492 case kSimple_Type: 493 SkASSERT(!fRect.isEmpty()); 494 SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare); 495 break; 496 case kNinePatch_Type: 497 SkASSERT(!fRect.isEmpty()); 498 SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare); 499 SkASSERT(patchesOfNine); 500 break; 501 case kComplex_Type: 502 SkASSERT(!fRect.isEmpty()); 503 SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare); 504 SkASSERT(!patchesOfNine); 505 break; 506 case kUnknown_Type: 507 // no limits on this 508 break; 509 } 510} 511#endif // SK_DEBUG 512 513/////////////////////////////////////////////////////////////////////////////// 514