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