• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkRRect.h"
9 
10 #include "include/core/SkMatrix.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkString.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkDebug.h"
17 #include "include/private/base/SkFloatingPoint.h"
18 #include "src/base/SkBuffer.h"
19 #include "src/core/SkRRectPriv.h"
20 #include "src/core/SkRectPriv.h"
21 #include "src/core/SkScaleToSides.h"
22 #include "src/core/SkStringUtils.h"
23 
24 #include <algorithm>
25 #include <cstring>
26 #include <iterator>
27 
28 ///////////////////////////////////////////////////////////////////////////////
29 
setOval(const SkRect & oval)30 void SkRRect::setOval(const SkRect& oval) {
31     if (!this->initializeRect(oval)) {
32         return;
33     }
34 
35     SkScalar xRad = SkRectPriv::HalfWidth(fRect);
36     SkScalar yRad = SkRectPriv::HalfHeight(fRect);
37 
38     if (xRad == 0.0f || yRad == 0.0f) {
39         // All the corners will be square
40         memset(fRadii, 0, sizeof(fRadii));
41         fType = kRect_Type;
42     } else {
43         for (int i = 0; i < 4; ++i) {
44             fRadii[i].set(xRad, yRad);
45         }
46         fType = kOval_Type;
47     }
48 
49     SkASSERT(this->isValid());
50 }
51 
setRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)52 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
53     if (!this->initializeRect(rect)) {
54         return;
55     }
56 
57     if (!SkIsFinite(xRad, yRad)) {
58         xRad = yRad = 0;    // devolve into a simple rect
59     }
60 
61     if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) {
62         // At most one of these two divides will be by zero, and neither numerator is zero.
63         SkScalar scale = std::min(sk_ieee_float_divide(fRect. width(), xRad + xRad),
64                                      sk_ieee_float_divide(fRect.height(), yRad + yRad));
65         SkASSERT(scale < SK_Scalar1);
66         xRad *= scale;
67         yRad *= scale;
68     }
69 
70     if (xRad <= 0 || yRad <= 0) {
71         // all corners are square in this case
72         this->setRect(rect);
73         return;
74     }
75 
76     for (int i = 0; i < 4; ++i) {
77         fRadii[i].set(xRad, yRad);
78     }
79     fType = kSimple_Type;
80     if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
81         fType = kOval_Type;
82         // TODO: assert that all the x&y radii are already W/2 & H/2
83     }
84 
85     SkASSERT(this->isValid());
86 }
87 
clamp_to_zero(SkVector radii[4])88 static bool clamp_to_zero(SkVector radii[4]) {
89     bool allCornersSquare = true;
90 
91     // Clamp negative radii to zero
92     for (int i = 0; i < 4; ++i) {
93         if (radii[i].fX <= 0 || radii[i].fY <= 0) {
94             // In this case we are being a little fast & loose. Since one of
95             // the radii is 0 the corner is square. However, the other radii
96             // could still be non-zero and play in the global scale factor
97             // computation.
98             radii[i].fX = 0;
99             radii[i].fY = 0;
100         } else {
101             allCornersSquare = false;
102         }
103     }
104 
105     return allCornersSquare;
106 }
107 
radii_are_nine_patch(const SkVector radii[4])108 static bool radii_are_nine_patch(const SkVector radii[4]) {
109     return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
110            radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
111            radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
112            radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
113 }
114 
setNinePatch(const SkRect & rect,SkScalar leftRad,SkScalar topRad,SkScalar rightRad,SkScalar bottomRad)115 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
116                            SkScalar rightRad, SkScalar bottomRad) {
117     if (!this->initializeRect(rect)) {
118         return;
119     }
120 
121     if (!SkIsFinite(leftRad, topRad, rightRad, bottomRad)) {
122         this->setRect(rect);    // devolve into a simple rect
123         return;
124     }
125 
126     leftRad = std::max(leftRad, 0.0f);
127     topRad = std::max(topRad, 0.0f);
128     rightRad = std::max(rightRad, 0.0f);
129     bottomRad = std::max(bottomRad, 0.0f);
130 
131     SkScalar scale = SK_Scalar1;
132     if (leftRad + rightRad > fRect.width()) {
133         scale = fRect.width() / (leftRad + rightRad);
134     }
135     if (topRad + bottomRad > fRect.height()) {
136         scale = std::min(scale, fRect.height() / (topRad + bottomRad));
137     }
138 
139     if (scale < SK_Scalar1) {
140         leftRad *= scale;
141         topRad *= scale;
142         rightRad *= scale;
143         bottomRad *= scale;
144     }
145 
146     if (leftRad == rightRad && topRad == bottomRad) {
147         if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) {
148             fType = kOval_Type;
149         } else if (0 == leftRad || 0 == topRad) {
150             // If the left and (by equality check above) right radii are zero then it is a rect.
151             // Same goes for top/bottom.
152             fType = kRect_Type;
153             leftRad = 0;
154             topRad = 0;
155             rightRad = 0;
156             bottomRad = 0;
157         } else {
158             fType = kSimple_Type;
159         }
160     } else {
161         fType = kNinePatch_Type;
162     }
163 
164     fRadii[kUpperLeft_Corner].set(leftRad, topRad);
165     fRadii[kUpperRight_Corner].set(rightRad, topRad);
166     fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
167     fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
168     if (clamp_to_zero(fRadii)) {
169         this->setRect(rect);    // devolve into a simple rect
170         return;
171     }
172     if (fType == kNinePatch_Type && !radii_are_nine_patch(fRadii)) {
173         fType = kComplex_Type;
174     }
175 
176     SkASSERT(this->isValid());
177 }
178 
179 // These parameters intentionally double. Apropos crbug.com/463920, if one of the
180 // radii is huge while the other is small, single precision math can completely
181 // miss the fact that a scale is required.
compute_min_scale(double rad1,double rad2,double limit,double curMin)182 static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
183     if ((rad1 + rad2) > limit) {
184         return std::min(curMin, limit / (rad1 + rad2));
185     }
186     return curMin;
187 }
188 
setRectRadii(const SkRect & rect,const SkVector radii[4])189 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
190     if (!this->initializeRect(rect)) {
191         return;
192     }
193 
194     if (!SkIsFinite(&radii[0].fX, 8)) {
195         this->setRect(rect);    // devolve into a simple rect
196         return;
197     }
198 
199     memcpy(fRadii, radii, sizeof(fRadii));
200 
201     if (clamp_to_zero(fRadii)) {
202         this->setRect(rect);
203         return;
204     }
205 
206     this->scaleRadii();
207 
208     if (!this->isValid()) {
209         this->setRect(rect);
210         return;
211     }
212 }
213 
initializeRect(const SkRect & rect)214 bool SkRRect::initializeRect(const SkRect& rect) {
215     // Check this before sorting because sorting can hide nans.
216     if (!rect.isFinite()) {
217         *this = SkRRect();
218         return false;
219     }
220     fRect = rect.makeSorted();
221     if (fRect.isEmpty()) {
222         memset(fRadii, 0, sizeof(fRadii));
223         fType = kEmpty_Type;
224         return false;
225     }
226     return true;
227 }
228 
229 // If we can't distinguish one of the radii relative to the other, force it to zero so it
230 // doesn't confuse us later. See crbug.com/850350
231 //
flush_to_zero(SkScalar & a,SkScalar & b)232 static void flush_to_zero(SkScalar& a, SkScalar& b) {
233     SkASSERT(a >= 0);
234     SkASSERT(b >= 0);
235     if (a + b == a) {
236         b = 0;
237     } else if (a + b == b) {
238         a = 0;
239     }
240 }
241 
scaleRadii()242 bool SkRRect::scaleRadii() {
243     // Proportionally scale down all radii to fit. Find the minimum ratio
244     // of a side and the radii on that side (for all four sides) and use
245     // that to scale down _all_ the radii. This algorithm is from the
246     // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
247     // Curves:
248     // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
249     //   Si is the sum of the two corresponding radii of the corners on side i,
250     //   and Ltop = Lbottom = the width of the box,
251     //   and Lleft = Lright = the height of the box.
252     // If f < 1, then all corner radii are reduced by multiplying them by f."
253     double scale = 1.0;
254 
255     // The sides of the rectangle may be larger than a float.
256     double width = (double)fRect.fRight - (double)fRect.fLeft;
257     double height = (double)fRect.fBottom - (double)fRect.fTop;
258     scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width,  scale);
259     scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
260     scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width,  scale);
261     scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
262 
263     flush_to_zero(fRadii[0].fX, fRadii[1].fX);
264     flush_to_zero(fRadii[1].fY, fRadii[2].fY);
265     flush_to_zero(fRadii[2].fX, fRadii[3].fX);
266     flush_to_zero(fRadii[3].fY, fRadii[0].fY);
267 
268     if (scale < 1.0) {
269         SkScaleToSides::AdjustRadii(width,  scale, &fRadii[0].fX, &fRadii[1].fX);
270         SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
271         SkScaleToSides::AdjustRadii(width,  scale, &fRadii[2].fX, &fRadii[3].fX);
272         SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
273     }
274 
275     // adjust radii may set x or y to zero; set companion to zero as well
276     clamp_to_zero(fRadii);
277 
278     // May be simple, oval, or complex, or become a rect/empty if the radii adjustment made them 0
279     this->computeType();
280 
281     // TODO:  Why can't we assert this here?
282     //SkASSERT(this->isValid());
283 
284     return scale < 1.0;
285 }
286 
287 // This method determines if a point known to be inside the RRect's bounds is
288 // inside all the corners.
checkCornerContainment(SkScalar x,SkScalar y) const289 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
290     SkPoint canonicalPt; // (x,y) translated to one of the quadrants
291     int index;
292 
293     if (kOval_Type == this->type()) {
294         canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
295         index = kUpperLeft_Corner;  // any corner will do in this case
296     } else {
297         if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
298             y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
299             // UL corner
300             index = kUpperLeft_Corner;
301             canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
302                             y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
303             SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
304         } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
305                    y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
306             // LL corner
307             index = kLowerLeft_Corner;
308             canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
309                             y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
310             SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
311         } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
312                    y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
313             // UR corner
314             index = kUpperRight_Corner;
315             canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
316                             y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
317             SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
318         } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
319                    y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
320             // LR corner
321             index = kLowerRight_Corner;
322             canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
323                             y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
324             SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
325         } else {
326             // not in any of the corners
327             return true;
328         }
329     }
330 
331     // A point is in an ellipse (in standard position) if:
332     //      x^2     y^2
333     //     ----- + ----- <= 1
334     //      a^2     b^2
335     // or :
336     //     b^2*x^2 + a^2*y^2 <= (ab)^2
337     SkScalar dist =  SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) +
338                      SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX);
339     return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY);
340 }
341 
IsNearlySimpleCircular(const SkRRect & rr,SkScalar tolerance)342 bool SkRRectPriv::IsNearlySimpleCircular(const SkRRect& rr, SkScalar tolerance) {
343     SkScalar simpleRadius = rr.fRadii[0].fX;
344     return SkScalarNearlyEqual(simpleRadius, rr.fRadii[0].fY, tolerance) &&
345            SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fX, tolerance) &&
346            SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fY, tolerance) &&
347            SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fX, tolerance) &&
348            SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fY, tolerance) &&
349            SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fX, tolerance) &&
350            SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fY, tolerance);
351 }
352 
AllCornersCircular(const SkRRect & rr,SkScalar tolerance)353 bool SkRRectPriv::AllCornersCircular(const SkRRect& rr, SkScalar tolerance) {
354     return SkScalarNearlyEqual(rr.fRadii[0].fX, rr.fRadii[0].fY, tolerance) &&
355            SkScalarNearlyEqual(rr.fRadii[1].fX, rr.fRadii[1].fY, tolerance) &&
356            SkScalarNearlyEqual(rr.fRadii[2].fX, rr.fRadii[2].fY, tolerance) &&
357            SkScalarNearlyEqual(rr.fRadii[3].fX, rr.fRadii[3].fY, tolerance);
358 }
359 
contains(const SkRect & rect) const360 bool SkRRect::contains(const SkRect& rect) const {
361     if (!this->getBounds().contains(rect)) {
362         // If 'rect' isn't contained by the RR's bounds then the
363         // RR definitely doesn't contain it
364         return false;
365     }
366 
367     if (this->isRect()) {
368         // the prior test was sufficient
369         return true;
370     }
371 
372     // At this point we know all four corners of 'rect' are inside the
373     // bounds of of this RR. Check to make sure all the corners are inside
374     // all the curves
375     return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
376            this->checkCornerContainment(rect.fRight, rect.fTop) &&
377            this->checkCornerContainment(rect.fRight, rect.fBottom) &&
378            this->checkCornerContainment(rect.fLeft, rect.fBottom);
379 }
380 
381 // There is a simplified version of this method in setRectXY
computeType()382 void SkRRect::computeType() {
383     if (fRect.isEmpty()) {
384         SkASSERT(fRect.isSorted());
385         for (size_t i = 0; i < std::size(fRadii); ++i) {
386             SkASSERT((fRadii[i] == SkVector{0, 0}));
387         }
388         fType = kEmpty_Type;
389         SkASSERT(this->isValid());
390         return;
391     }
392 
393     bool allRadiiEqual = true; // are all x radii equal and all y radii?
394     bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
395 
396     for (int i = 1; i < 4; ++i) {
397         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
398             // if either radius is zero the corner is square so both have to
399             // be non-zero to have a rounded corner
400             allCornersSquare = false;
401         }
402         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
403             allRadiiEqual = false;
404         }
405     }
406 
407     if (allCornersSquare) {
408         fType = kRect_Type;
409         SkASSERT(this->isValid());
410         return;
411     }
412 
413     if (allRadiiEqual) {
414         if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
415             fRadii[0].fY >= SkScalarHalf(fRect.height())) {
416             fType = kOval_Type;
417         } else {
418             fType = kSimple_Type;
419         }
420         SkASSERT(this->isValid());
421         return;
422     }
423 
424     if (radii_are_nine_patch(fRadii)) {
425         fType = kNinePatch_Type;
426     } else {
427         fType = kComplex_Type;
428     }
429 
430     if (!this->isValid()) {
431         this->setRect(this->rect());
432         SkASSERT(this->isValid());
433     }
434 }
435 
transform(const SkMatrix & matrix,SkRRect * dst) const436 bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
437     if (nullptr == dst) {
438         return false;
439     }
440 
441     // Assert that the caller is not trying to do this in place, which
442     // would violate const-ness. Do not return false though, so that
443     // if they know what they're doing and want to violate it they can.
444     SkASSERT(dst != this);
445 
446     if (matrix.isIdentity()) {
447         *dst = *this;
448         return true;
449     }
450 
451     if (!matrix.preservesAxisAlignment()) {
452         return false;
453     }
454 
455     SkRect newRect;
456     if (!matrix.mapRect(&newRect, fRect)) {
457         return false;
458     }
459 
460     // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
461     // some dimension of the rect, so we need to check for that. Note that matrix must be
462     // scale and translate and mapRect() produces a sorted rect. So an empty rect indicates
463     // loss of precision.
464     if (!newRect.isFinite() || newRect.isEmpty()) {
465         return false;
466     }
467 
468     // At this point, this is guaranteed to succeed, so we can modify dst.
469     dst->fRect = newRect;
470 
471     // Since the only transforms that were allowed are axis aligned, the type
472     // remains unchanged.
473     dst->fType = fType;
474 
475     if (kRect_Type == fType) {
476         SkASSERT(dst->isValid());
477         return true;
478     }
479     if (kOval_Type == fType) {
480         for (int i = 0; i < 4; ++i) {
481             dst->fRadii[i].fX = SkScalarHalf(newRect.width());
482             dst->fRadii[i].fY = SkScalarHalf(newRect.height());
483         }
484         SkASSERT(dst->isValid());
485         return true;
486     }
487 
488     // Now scale each corner
489     SkScalar xScale = matrix.getScaleX();
490     SkScalar yScale = matrix.getScaleY();
491 
492     // There is a rotation of 90 (Clockwise 90) or 270 (Counter clockwise 90).
493     // 180 degrees rotations are simply flipX with a flipY and would come under
494     // a scale transform.
495     if (!matrix.isScaleTranslate()) {
496         const bool isClockwise = matrix.getSkewX() < 0;
497 
498         // The matrix location for scale changes if there is a rotation.
499         xScale = matrix.getSkewY() * (isClockwise ? 1 : -1);
500         yScale = matrix.getSkewX() * (isClockwise ? -1 : 1);
501 
502         const int dir = isClockwise ? 3 : 1;
503         for (int i = 0; i < 4; ++i) {
504             const int src = (i + dir) >= 4 ? (i + dir) % 4 : (i + dir);
505             // Swap X and Y axis for the radii.
506             dst->fRadii[i].fX = fRadii[src].fY;
507             dst->fRadii[i].fY = fRadii[src].fX;
508         }
509     } else {
510         for (int i = 0; i < 4; ++i) {
511             dst->fRadii[i].fX = fRadii[i].fX;
512             dst->fRadii[i].fY = fRadii[i].fY;
513         }
514     }
515 
516     const bool flipX = xScale < 0;
517     if (flipX) {
518         xScale = -xScale;
519     }
520 
521     const bool flipY = yScale < 0;
522     if (flipY) {
523         yScale = -yScale;
524     }
525 
526     // Scale the radii without respecting the flip.
527     for (int i = 0; i < 4; ++i) {
528         dst->fRadii[i].fX *= xScale;
529         dst->fRadii[i].fY *= yScale;
530     }
531 
532     // Now swap as necessary.
533     using std::swap;
534     if (flipX) {
535         if (flipY) {
536             // Swap with opposite corners
537             swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
538             swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
539         } else {
540             // Only swap in x
541             swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
542             swap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
543         }
544     } else if (flipY) {
545         // Only swap in y
546         swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
547         swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
548     }
549 
550     if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) {
551         return false;
552     }
553 
554     dst->scaleRadii();
555     dst->isValid();  // TODO: is this meant to be SkASSERT(dst->isValid())?
556 
557     return true;
558 }
559 
560 ///////////////////////////////////////////////////////////////////////////////
561 
inset(SkScalar dx,SkScalar dy,SkRRect * dst) const562 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
563     SkRect r = fRect.makeInset(dx, dy);
564     bool degenerate = false;
565     if (r.fRight <= r.fLeft) {
566         degenerate = true;
567         r.fLeft = r.fRight = SkScalarAve(r.fLeft, r.fRight);
568     }
569     if (r.fBottom <= r.fTop) {
570         degenerate = true;
571         r.fTop = r.fBottom = SkScalarAve(r.fTop, r.fBottom);
572     }
573     if (degenerate) {
574         dst->fRect = r;
575         memset(dst->fRadii, 0, sizeof(dst->fRadii));
576         dst->fType = kEmpty_Type;
577         return;
578     }
579     if (!r.isFinite()) {
580         *dst = SkRRect();
581         return;
582     }
583 
584     SkVector radii[4];
585     memcpy(radii, fRadii, sizeof(radii));
586     for (int i = 0; i < 4; ++i) {
587         if (radii[i].fX) {
588             radii[i].fX -= dx;
589         }
590         if (radii[i].fY) {
591             radii[i].fY -= dy;
592         }
593     }
594     dst->setRectRadii(r, radii);
595 }
596 
597 ///////////////////////////////////////////////////////////////////////////////
598 
writeToMemory(void * buffer) const599 size_t SkRRect::writeToMemory(void* buffer) const {
600     // Serialize only the rect and corners, but not the derived type tag.
601     memcpy(buffer, this, kSizeInMemory);
602     return kSizeInMemory;
603 }
604 
WriteToBuffer(const SkRRect & rr,SkWBuffer * buffer)605 void SkRRectPriv::WriteToBuffer(const SkRRect& rr, SkWBuffer* buffer) {
606     // Serialize only the rect and corners, but not the derived type tag.
607     buffer->write(&rr, SkRRect::kSizeInMemory);
608 }
609 
readFromMemory(const void * buffer,size_t length)610 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
611     if (length < kSizeInMemory) {
612         return 0;
613     }
614 
615     // The extra (void*) tells GCC not to worry that kSizeInMemory < sizeof(SkRRect).
616 
617     SkRRect raw;
618     memcpy((void*)&raw, buffer, kSizeInMemory);
619     this->setRectRadii(raw.fRect, raw.fRadii);
620     return kSizeInMemory;
621 }
622 
ReadFromBuffer(SkRBuffer * buffer,SkRRect * rr)623 bool SkRRectPriv::ReadFromBuffer(SkRBuffer* buffer, SkRRect* rr) {
624     if (buffer->available() < SkRRect::kSizeInMemory) {
625         return false;
626     }
627     SkRRect storage;
628     return buffer->read(&storage, SkRRect::kSizeInMemory) &&
629            (rr->readFromMemory(&storage, SkRRect::kSizeInMemory) == SkRRect::kSizeInMemory);
630 }
631 
dumpToString(bool asHex) const632 SkString SkRRect::dumpToString(bool asHex) const {
633     SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
634 
635     fRect.dump(asHex);
636     SkString line("const SkPoint corners[] = {\n");
637     for (int i = 0; i < 4; ++i) {
638         SkString strX, strY;
639         SkAppendScalar(&strX, fRadii[i].x(), asType);
640         SkAppendScalar(&strY, fRadii[i].y(), asType);
641         line.appendf("    { %s, %s },", strX.c_str(), strY.c_str());
642         if (asHex) {
643             line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
644         }
645         line.append("\n");
646     }
647     line.append("};");
648     return line;
649 }
650 
dump(bool asHex) const651 void SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); }
652 
653 ///////////////////////////////////////////////////////////////////////////////
654 
655 /**
656  *  We need all combinations of predicates to be true to have a "safe" radius value.
657  */
are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)658 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
659     return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
660            rad >= 0;
661 }
662 
isValid() const663 bool SkRRect::isValid() const {
664     if (!AreRectAndRadiiValid(fRect, fRadii)) {
665         return false;
666     }
667 
668     bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
669     bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
670     bool allRadiiSame = true;
671 
672     for (int i = 1; i < 4; ++i) {
673         if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
674             allRadiiZero = false;
675         }
676 
677         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
678             allRadiiSame = false;
679         }
680 
681         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
682             allCornersSquare = false;
683         }
684     }
685     bool patchesOfNine = radii_are_nine_patch(fRadii);
686 
687     if (fType < 0 || fType > kLastType) {
688         return false;
689     }
690 
691     switch (fType) {
692         case kEmpty_Type:
693             if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
694                 return false;
695             }
696             break;
697         case kRect_Type:
698             if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
699                 return false;
700             }
701             break;
702         case kOval_Type:
703             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
704                 return false;
705             }
706 
707             for (int i = 0; i < 4; ++i) {
708                 if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
709                     !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
710                     return false;
711                 }
712             }
713             break;
714         case kSimple_Type:
715             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
716                 return false;
717             }
718             break;
719         case kNinePatch_Type:
720             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
721                 !patchesOfNine) {
722                 return false;
723             }
724             break;
725         case kComplex_Type:
726             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
727                 patchesOfNine) {
728                 return false;
729             }
730             break;
731     }
732 
733     return true;
734 }
735 
AreRectAndRadiiValid(const SkRect & rect,const SkVector radii[4])736 bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
737     if (!rect.isFinite() || !rect.isSorted()) {
738         return false;
739     }
740     for (int i = 0; i < 4; ++i) {
741         if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
742             !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
743             return false;
744         }
745     }
746     return true;
747 }
748 ///////////////////////////////////////////////////////////////////////////////
749 
InnerBounds(const SkRRect & rr)750 SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
751     if (rr.isEmpty() || rr.isRect()) {
752         return rr.rect();
753     }
754 
755     // We start with the outer bounds of the round rect and consider three subsets and take the
756     // one with maximum area. The first two are the horizontal and vertical rects inset from the
757     // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
758     // the exact solution when all corners have the same radii (the radii do not have to be
759     // circular).
760     SkRect innerBounds = rr.getBounds();
761     SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
762     SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
763     SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
764     SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
765 
766     // Select maximum inset per edge, which may move an adjacent corner of the inscribed
767     // rectangle off of the rounded-rect path, but that is acceptable given that the general
768     // equation for inscribed area is non-trivial to evaluate.
769     SkScalar leftShift   = std::max(tl.fX, bl.fX);
770     SkScalar topShift    = std::max(tl.fY, tr.fY);
771     SkScalar rightShift  = std::max(tr.fX, br.fX);
772     SkScalar bottomShift = std::max(bl.fY, br.fY);
773 
774     SkScalar dw = leftShift + rightShift;
775     SkScalar dh = topShift + bottomShift;
776 
777     // Area removed by shifting left/right
778     SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
779     // And by shifting top/bottom
780     SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
781     // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
782     // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
783     // safe shift per edge (since the shifts already are the max radius for that edge).
784     // - We actually scale by a value slightly increased to make it so that the shifted corners are
785     //   safely inside the curves, otherwise numerical stability can cause it to fail contains().
786     static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
787     SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
788 
789     if (horizArea > vertArea && horizArea > innerArea) {
790         // Cut off corners by insetting left and right
791         innerBounds.fLeft += leftShift;
792         innerBounds.fRight -= rightShift;
793     } else if (vertArea > innerArea) {
794         // Cut off corners by insetting top and bottom
795         innerBounds.fTop += topShift;
796         innerBounds.fBottom -= bottomShift;
797     } else if (innerArea > 0.f) {
798         // Inset on all sides, scaled to touch
799         innerBounds.fLeft += kScale * leftShift;
800         innerBounds.fRight -= kScale * rightShift;
801         innerBounds.fTop += kScale * topShift;
802         innerBounds.fBottom -= kScale * bottomShift;
803     } else {
804         // Inner region would collapse to empty
805         return SkRect::MakeEmpty();
806     }
807 
808     SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
809     return innerBounds;
810 }
811 
ConservativeIntersect(const SkRRect & a,const SkRRect & b)812 SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
813     // Returns the coordinate of the rect matching the corner enum.
814     auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
815         switch(corner) {
816             case SkRRect::kUpperLeft_Corner:  return {r.fLeft, r.fTop};
817             case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
818             case SkRRect::kLowerLeft_Corner:  return {r.fLeft, r.fBottom};
819             case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
820             default: SkUNREACHABLE;
821         }
822     };
823     // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
824     // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
825     // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
826     auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
827         switch(corner) {
828             case SkRRect::kUpperLeft_Corner:  return a.fX >= b.fX && a.fY >= b.fY;
829             case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
830             case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
831             case SkRRect::kLowerLeft_Corner:  return a.fX >= b.fX && a.fY <= b.fY;
832             default:  SkUNREACHABLE;
833         }
834     };
835 
836     auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
837         SkPoint test = getCorner(r, corner);
838         SkPoint aCorner = getCorner(a.rect(), corner);
839         SkPoint bCorner = getCorner(b.rect(), corner);
840 
841         if (test == aCorner && test == bCorner) {
842             // The round rects share a corner anchor, so pick A or B such that its X and Y radii
843             // are both larger than the other rrect's, or return false if neither A or B has the max
844             // corner radii (this is more permissive than the single corner tests below).
845             SkVector aRadii = a.radii(corner);
846             SkVector bRadii = b.radii(corner);
847             if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
848                 *radii = aRadii;
849                 return true;
850             } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
851                 *radii = bRadii;
852                 return true;
853             } else {
854                 return false;
855             }
856         } else if (test == aCorner) {
857             // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
858             // so we resrict it to when the corners have the same radii. If not, we use the more
859             // conservative test that the extreme point of A's bounding box is contained in B.
860             *radii = a.radii(corner);
861             if (*radii == b.radii(corner)) {
862                 return insideCorner(corner, aCorner, bCorner); // A inside B
863             } else {
864                 return b.checkCornerContainment(aCorner.fX, aCorner.fY);
865             }
866         } else if (test == bCorner) {
867             // Mirror of the above
868             *radii = b.radii(corner);
869             if (*radii == a.radii(corner)) {
870                 return insideCorner(corner, bCorner, aCorner); // B inside A
871             } else {
872                 return a.checkCornerContainment(bCorner.fX, bCorner.fY);
873             }
874         } else {
875             // This is a corner formed by two straight edges of A and B, so confirm that it is
876             // contained in both (if not, then the intersection can't be a round rect).
877             *radii = {0.f, 0.f};
878             return a.checkCornerContainment(test.fX, test.fY) &&
879                    b.checkCornerContainment(test.fX, test.fY);
880         }
881     };
882 
883     // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
884     // valid existing SkRRects, we know we are finite.
885     SkRRect intersection;
886     if (!intersection.fRect.intersect(a.rect(), b.rect())) {
887         // Definitely no intersection
888         return SkRRect::MakeEmpty();
889     }
890 
891     const SkRRect::Corner corners[] = {
892         SkRRect::kUpperLeft_Corner,
893         SkRRect::kUpperRight_Corner,
894         SkRRect::kLowerRight_Corner,
895         SkRRect::kLowerLeft_Corner
896     };
897     // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
898     // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
899     // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
900     // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
901     // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
902     // intersection shape.
903     for (auto c : corners) {
904         if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
905             return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
906         }
907     }
908 
909     // Check for radius overlap along the four edges, since the earlier evaluation was only a
910     // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
911     // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
912     // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
913     // the intersection shape is definitively not a round rect.
914     if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
915         intersection.scaleRadii()) {
916         return SkRRect::MakeEmpty();
917     }
918 
919     // The intersection is an rrect of the given radii. Potentially all 4 corners could have
920     // been simplified to (0,0) radii, making the intersection a rectangle.
921     intersection.computeType();
922     return intersection;
923 }
924