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()) {
     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 
     46 void 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 
    101 void 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.
    177 bool 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 
    230 bool 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 
    237 bool 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 
    258 static 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
    266 void 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 
    310 static 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 
    316 bool 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 
    391 void 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 
    415 size_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 
    423 size_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
    440 void 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
    454 void 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