LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkRRect.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 305 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 20 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13