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