Home | History | Annotate | Download | only in core
      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 
     13 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
     14     if (rect.isEmpty() || !rect.isFinite()) {
     15         this->setEmpty();
     16         return;
     17     }
     18 
     19     if (!SkScalarsAreFinite(xRad, yRad)) {
     20         xRad = yRad = 0;    // devolve into a simple rect
     21     }
     22     if (xRad <= 0 || yRad <= 0) {
     23         // all corners are square in this case
     24         this->setRect(rect);
     25         return;
     26     }
     27 
     28     if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
     29         SkScalar scale = SkMinScalar(rect.width() / (xRad + xRad), rect.height() / (yRad + yRad));
     30         SkASSERT(scale < SK_Scalar1);
     31         xRad = SkScalarMul(xRad, scale);
     32         yRad = SkScalarMul(yRad, scale);
     33     }
     34 
     35     fRect = rect;
     36     for (int i = 0; i < 4; ++i) {
     37         fRadii[i].set(xRad, yRad);
     38     }
     39     fType = kSimple_Type;
     40     if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
     41         fType = kOval_Type;
     42         // TODO: assert that all the x&y radii are already W/2 & H/2
     43     }
     44 
     45     SkDEBUGCODE(this->validate();)
     46 }
     47 
     48 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
     49                            SkScalar rightRad, SkScalar bottomRad) {
     50     if (rect.isEmpty() || !rect.isFinite()) {
     51         this->setEmpty();
     52         return;
     53     }
     54 
     55     const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad };
     56     if (!SkScalarsAreFinite(array, 4)) {
     57         this->setRect(rect);    // devolve into a simple rect
     58         return;
     59     }
     60 
     61     leftRad = SkMaxScalar(leftRad, 0);
     62     topRad = SkMaxScalar(topRad, 0);
     63     rightRad = SkMaxScalar(rightRad, 0);
     64     bottomRad = SkMaxScalar(bottomRad, 0);
     65 
     66     SkScalar scale = SK_Scalar1;
     67     if (leftRad + rightRad > rect.width()) {
     68         scale = rect.width() / (leftRad + rightRad);
     69     }
     70     if (topRad + bottomRad > rect.height()) {
     71         scale = SkMinScalar(scale, rect.height() / (topRad + bottomRad));
     72     }
     73 
     74     if (scale < SK_Scalar1) {
     75         leftRad = SkScalarMul(leftRad, scale);
     76         topRad = SkScalarMul(topRad, scale);
     77         rightRad = SkScalarMul(rightRad, scale);
     78         bottomRad = SkScalarMul(bottomRad, scale);
     79     }
     80 
     81     if (leftRad == rightRad && topRad == bottomRad) {
     82         if (leftRad >= SkScalarHalf(rect.width()) && topRad >= SkScalarHalf(rect.height())) {
     83             fType = kOval_Type;
     84         } else if (0 == leftRad || 0 == topRad) {
     85             // If the left and (by equality check above) right radii are zero then it is a rect.
     86             // Same goes for top/bottom.
     87             fType = kRect_Type;
     88             leftRad = 0;
     89             topRad = 0;
     90             rightRad = 0;
     91             bottomRad = 0;
     92         } else {
     93             fType = kSimple_Type;
     94         }
     95     } else {
     96         fType = kNinePatch_Type;
     97     }
     98 
     99     fRect = rect;
    100     fRadii[kUpperLeft_Corner].set(leftRad, topRad);
    101     fRadii[kUpperRight_Corner].set(rightRad, topRad);
    102     fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
    103     fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
    104 
    105     SkDEBUGCODE(this->validate();)
    106 }
    107 
    108 /*
    109  *  TODO: clean this guy up and possibly add to SkScalar.h
    110  */
    111 static inline SkScalar SkScalarDecULP(SkScalar value) {
    112 #if SK_SCALAR_IS_FLOAT
    113         return SkBits2Float(SkFloat2Bits(value) - 1);
    114 #else
    115     #error "need impl for doubles"
    116 #endif
    117 }
    118 
    119  /**
    120  *  We need all combinations of predicates to be true to have a "safe" radius value.
    121  */
    122 static SkScalar clamp_radius_check_predicates(SkScalar rad, SkScalar min, SkScalar max) {
    123     SkASSERT(min < max);
    124     if (rad > max - min || min + rad > max || max - rad < min) {
    125         rad = SkScalarDecULP(rad);
    126     }
    127     return rad;
    128 }
    129 
    130 // These parameters intentionally double. Apropos crbug.com/463920, if one of the
    131 // radii is huge while the other is small, single precision math can completely
    132 // miss the fact that a scale is required.
    133 static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
    134     if ((rad1 + rad2) > limit) {
    135         return SkTMin(curMin, limit / (rad1 + rad2));
    136     }
    137     return curMin;
    138 }
    139 
    140 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
    141     if (rect.isEmpty() || !rect.isFinite()) {
    142         this->setEmpty();
    143         return;
    144     }
    145 
    146     if (!SkScalarsAreFinite(&radii[0].fX, 8)) {
    147         this->setRect(rect);    // devolve into a simple rect
    148         return;
    149     }
    150 
    151     fRect = rect;
    152     memcpy(fRadii, radii, sizeof(fRadii));
    153 
    154     bool allCornersSquare = true;
    155 
    156     // Clamp negative radii to zero
    157     for (int i = 0; i < 4; ++i) {
    158         if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
    159             // In this case we are being a little fast & loose. Since one of
    160             // the radii is 0 the corner is square. However, the other radii
    161             // could still be non-zero and play in the global scale factor
    162             // computation.
    163             fRadii[i].fX = 0;
    164             fRadii[i].fY = 0;
    165         } else {
    166             allCornersSquare = false;
    167         }
    168     }
    169 
    170     if (allCornersSquare) {
    171         this->setRect(rect);
    172         return;
    173     }
    174 
    175     // Proportionally scale down all radii to fit. Find the minimum ratio
    176     // of a side and the radii on that side (for all four sides) and use
    177     // that to scale down _all_ the radii. This algorithm is from the
    178     // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
    179     // Curves:
    180     // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
    181     //   Si is the sum of the two corresponding radii of the corners on side i,
    182     //   and Ltop = Lbottom = the width of the box,
    183     //   and Lleft = Lright = the height of the box.
    184     // If f < 1, then all corner radii are reduced by multiplying them by f."
    185     double scale = 1.0;
    186 
    187     scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, rect.width(),  scale);
    188     scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, rect.height(), scale);
    189     scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, rect.width(),  scale);
    190     scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, rect.height(), scale);
    191 
    192     if (scale < 1.0) {
    193         for (int i = 0; i < 4; ++i) {
    194             fRadii[i].fX *= scale;
    195             fRadii[i].fY *= scale;
    196         }
    197     }
    198 
    199     // skbug.com/3239 -- its possible that we can hit the following inconsistency:
    200     //     rad == bounds.bottom - bounds.top
    201     //     bounds.bottom - radius < bounds.top
    202     //     YIKES
    203     // We need to detect and "fix" this now, otherwise we can have the following wackiness:
    204     //     path.addRRect(rrect);
    205     //     rrect.rect() != path.getBounds()
    206     for (int i = 0; i < 4; ++i) {
    207         fRadii[i].fX = clamp_radius_check_predicates(fRadii[i].fX, rect.fLeft, rect.fRight);
    208         fRadii[i].fY = clamp_radius_check_predicates(fRadii[i].fY, rect.fTop, rect.fBottom);
    209     }
    210     // At this point we're either oval, simple, or complex (not empty or rect).
    211     this->computeType();
    212 
    213     SkDEBUGCODE(this->validate();)
    214 }
    215 
    216 // This method determines if a point known to be inside the RRect's bounds is
    217 // inside all the corners.
    218 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
    219     SkPoint canonicalPt; // (x,y) translated to one of the quadrants
    220     int index;
    221 
    222     if (kOval_Type == this->type()) {
    223         canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
    224         index = kUpperLeft_Corner;  // any corner will do in this case
    225     } else {
    226         if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
    227             y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
    228             // UL corner
    229             index = kUpperLeft_Corner;
    230             canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
    231                             y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
    232             SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
    233         } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
    234                    y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
    235             // LL corner
    236             index = kLowerLeft_Corner;
    237             canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
    238                             y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
    239             SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
    240         } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
    241                    y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
    242             // UR corner
    243             index = kUpperRight_Corner;
    244             canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
    245                             y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
    246             SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
    247         } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
    248                    y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
    249             // LR corner
    250             index = kLowerRight_Corner;
    251             canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
    252                             y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
    253             SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
    254         } else {
    255             // not in any of the corners
    256             return true;
    257         }
    258     }
    259 
    260     // A point is in an ellipse (in standard position) if:
    261     //      x^2     y^2
    262     //     ----- + ----- <= 1
    263     //      a^2     b^2
    264     // or :
    265     //     b^2*x^2 + a^2*y^2 <= (ab)^2
    266     SkScalar dist =  SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) +
    267                      SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX));
    268     return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY));
    269 }
    270 
    271 bool SkRRect::allCornersCircular() const {
    272     return fRadii[0].fX == fRadii[0].fY &&
    273         fRadii[1].fX == fRadii[1].fY &&
    274         fRadii[2].fX == fRadii[2].fY &&
    275         fRadii[3].fX == fRadii[3].fY;
    276 }
    277 
    278 bool SkRRect::contains(const SkRect& rect) const {
    279     if (!this->getBounds().contains(rect)) {
    280         // If 'rect' isn't contained by the RR's bounds then the
    281         // RR definitely doesn't contain it
    282         return false;
    283     }
    284 
    285     if (this->isRect()) {
    286         // the prior test was sufficient
    287         return true;
    288     }
    289 
    290     // At this point we know all four corners of 'rect' are inside the
    291     // bounds of of this RR. Check to make sure all the corners are inside
    292     // all the curves
    293     return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
    294            this->checkCornerContainment(rect.fRight, rect.fTop) &&
    295            this->checkCornerContainment(rect.fRight, rect.fBottom) &&
    296            this->checkCornerContainment(rect.fLeft, rect.fBottom);
    297 }
    298 
    299 static bool radii_are_nine_patch(const SkVector radii[4]) {
    300     return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
    301            radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
    302            radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
    303            radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
    304 }
    305 
    306 // There is a simplified version of this method in setRectXY
    307 void SkRRect::computeType() {
    308     struct Validator {
    309         Validator(const SkRRect* r) : fR(r) {}
    310         ~Validator() { SkDEBUGCODE(fR->validate();) }
    311         const SkRRect* fR;
    312     } autoValidate(this);
    313 
    314     if (fRect.isEmpty()) {
    315         fType = kEmpty_Type;
    316         return;
    317     }
    318 
    319     bool allRadiiEqual = true; // are all x radii equal and all y radii?
    320     bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
    321 
    322     for (int i = 1; i < 4; ++i) {
    323         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
    324             // if either radius is zero the corner is square so both have to
    325             // be non-zero to have a rounded corner
    326             allCornersSquare = false;
    327         }
    328         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
    329             allRadiiEqual = false;
    330         }
    331     }
    332 
    333     if (allCornersSquare) {
    334         fType = kRect_Type;
    335         return;
    336     }
    337 
    338     if (allRadiiEqual) {
    339         if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
    340             fRadii[0].fY >= SkScalarHalf(fRect.height())) {
    341             fType = kOval_Type;
    342         } else {
    343             fType = kSimple_Type;
    344         }
    345         return;
    346     }
    347 
    348     if (radii_are_nine_patch(fRadii)) {
    349         fType = kNinePatch_Type;
    350     } else {
    351         fType = kComplex_Type;
    352     }
    353 }
    354 
    355 static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
    356     const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
    357                                     | SkMatrix::kPerspective_Mask);
    358     return (matrix.getType() & m) == 0;
    359 }
    360 
    361 bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
    362     if (NULL == dst) {
    363         return false;
    364     }
    365 
    366     // Assert that the caller is not trying to do this in place, which
    367     // would violate const-ness. Do not return false though, so that
    368     // if they know what they're doing and want to violate it they can.
    369     SkASSERT(dst != this);
    370 
    371     if (matrix.isIdentity()) {
    372         *dst = *this;
    373         return true;
    374     }
    375 
    376     // If transform supported 90 degree rotations (which it could), we could
    377     // use SkMatrix::rectStaysRect() to check for a valid transformation.
    378     if (!matrix_only_scale_and_translate(matrix)) {
    379         return false;
    380     }
    381 
    382     SkRect newRect;
    383     if (!matrix.mapRect(&newRect, fRect)) {
    384         return false;
    385     }
    386 
    387     // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
    388     // some dimension of the rect, so we need to check for that.
    389     if (newRect.isEmpty()) {
    390         dst->setEmpty();
    391         return true;
    392     }
    393 
    394     // At this point, this is guaranteed to succeed, so we can modify dst.
    395     dst->fRect = newRect;
    396 
    397     // Since the only transforms that were allowed are scale and translate, the type
    398     // remains unchanged.
    399     dst->fType = fType;
    400 
    401     if (kOval_Type == fType) {
    402         for (int i = 0; i < 4; ++i) {
    403             dst->fRadii[i].fX = SkScalarHalf(newRect.width());
    404             dst->fRadii[i].fY = SkScalarHalf(newRect.height());
    405         }
    406         SkDEBUGCODE(dst->validate();)
    407         return true;
    408     }
    409 
    410     // Now scale each corner
    411     SkScalar xScale = matrix.getScaleX();
    412     const bool flipX = xScale < 0;
    413     if (flipX) {
    414         xScale = -xScale;
    415     }
    416     SkScalar yScale = matrix.getScaleY();
    417     const bool flipY = yScale < 0;
    418     if (flipY) {
    419         yScale = -yScale;
    420     }
    421 
    422     // Scale the radii without respecting the flip.
    423     for (int i = 0; i < 4; ++i) {
    424         dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale);
    425         dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale);
    426     }
    427 
    428     // Now swap as necessary.
    429     if (flipX) {
    430         if (flipY) {
    431             // Swap with opposite corners
    432             SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
    433             SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
    434         } else {
    435             // Only swap in x
    436             SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
    437             SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
    438         }
    439     } else if (flipY) {
    440         // Only swap in y
    441         SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
    442         SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
    443     }
    444 
    445     SkDEBUGCODE(dst->validate();)
    446 
    447     return true;
    448 }
    449 
    450 ///////////////////////////////////////////////////////////////////////////////
    451 
    452 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
    453     const SkRect r = fRect.makeInset(dx, dy);
    454 
    455     if (r.isEmpty()) {
    456         dst->setEmpty();
    457         return;
    458     }
    459 
    460     SkVector radii[4];
    461     memcpy(radii, fRadii, sizeof(radii));
    462     for (int i = 0; i < 4; ++i) {
    463         if (radii[i].fX) {
    464             radii[i].fX -= dx;
    465         }
    466         if (radii[i].fY) {
    467             radii[i].fY -= dy;
    468         }
    469     }
    470     dst->setRectRadii(r, radii);
    471 }
    472 
    473 ///////////////////////////////////////////////////////////////////////////////
    474 
    475 size_t SkRRect::writeToMemory(void* buffer) const {
    476     SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
    477 
    478     memcpy(buffer, &fRect, sizeof(SkRect));
    479     memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
    480     return kSizeInMemory;
    481 }
    482 
    483 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
    484     if (length < kSizeInMemory) {
    485         return 0;
    486     }
    487 
    488     SkScalar storage[12];
    489     SkASSERT(sizeof(storage) == kSizeInMemory);
    490 
    491     // we make a local copy, to ensure alignment before we cast
    492     memcpy(storage, buffer, kSizeInMemory);
    493 
    494     this->setRectRadii(*(const SkRect*)&storage[0],
    495                        (const SkVector*)&storage[4]);
    496     return kSizeInMemory;
    497 }
    498 
    499 #include "SkString.h"
    500 #include "SkStringUtils.h"
    501 
    502 void SkRRect::dump(bool asHex) const {
    503     SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
    504 
    505     fRect.dump(asHex);
    506     SkString line("const SkPoint corners[] = {\n");
    507     for (int i = 0; i < 4; ++i) {
    508         SkString strX, strY;
    509         SkAppendScalar(&strX, fRadii[i].x(), asType);
    510         SkAppendScalar(&strY, fRadii[i].y(), asType);
    511         line.appendf("    { %s, %s },", strX.c_str(), strY.c_str());
    512         if (asHex) {
    513             line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
    514         }
    515         line.append("\n");
    516     }
    517     line.append("};");
    518     SkDebugf("%s\n", line.c_str());
    519 }
    520 
    521 ///////////////////////////////////////////////////////////////////////////////
    522 
    523 #ifdef SK_DEBUG
    524 /**
    525  *  We need all combinations of predicates to be true to have a "safe" radius value.
    526  */
    527 static void validate_radius_check_predicates(SkScalar rad, SkScalar min, SkScalar max) {
    528     SkASSERT(min <= max);
    529     SkASSERT(rad <= max - min);
    530     SkASSERT(min + rad <= max);
    531     SkASSERT(max - rad >= min);
    532 }
    533 
    534 void SkRRect::validate() const {
    535     bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
    536     bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
    537     bool allRadiiSame = true;
    538 
    539     for (int i = 1; i < 4; ++i) {
    540         if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
    541             allRadiiZero = false;
    542         }
    543 
    544         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
    545             allRadiiSame = false;
    546         }
    547 
    548         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
    549             allCornersSquare = false;
    550         }
    551     }
    552     bool patchesOfNine = radii_are_nine_patch(fRadii);
    553 
    554     switch (fType) {
    555         case kEmpty_Type:
    556             SkASSERT(fRect.isEmpty());
    557             SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
    558             break;
    559         case kRect_Type:
    560             SkASSERT(!fRect.isEmpty());
    561             SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
    562             break;
    563         case kOval_Type:
    564             SkASSERT(!fRect.isEmpty());
    565             SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
    566 
    567             for (int i = 0; i < 4; ++i) {
    568                 SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
    569                 SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
    570             }
    571             break;
    572         case kSimple_Type:
    573             SkASSERT(!fRect.isEmpty());
    574             SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
    575             break;
    576         case kNinePatch_Type:
    577             SkASSERT(!fRect.isEmpty());
    578             SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
    579             SkASSERT(patchesOfNine);
    580             break;
    581         case kComplex_Type:
    582             SkASSERT(!fRect.isEmpty());
    583             SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
    584             SkASSERT(!patchesOfNine);
    585             break;
    586     }
    587 
    588     for (int i = 0; i < 4; ++i) {
    589         validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight);
    590         validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom);
    591     }
    592 }
    593 #endif // SK_DEBUG
    594 
    595 ///////////////////////////////////////////////////////////////////////////////
    596