xref: /aosp_15_r20/external/skia/src/core/SkRRect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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         // If we got here, the matrix preserves axis alignment (earlier return check) but isn't
497         // a regular scale matrix. To confirm that it's a 90/270 rotation, the scale components are
498         // 0s and the skew components are non-zero (+/-1 if there is no other scale factor).
499         SkASSERT(matrix.getScaleX() == 0.f && matrix.getScaleY() == 0.f &&
500                  matrix.getSkewX() != 0.f && matrix.getSkewY() != 0.f);
501         const bool isClockwise = matrix.getSkewX() < 0;
502 
503         // The matrix location for scale changes if there is a rotation.
504         // xScale and yScale represent scales applied to the dst radii, so we store the src x scale
505         // in yScale and vice versa.
506         yScale = matrix.getSkewY() * (isClockwise ? 1 : -1);
507         xScale = matrix.getSkewX() * (isClockwise ? -1 : 1);
508 
509         const int dir = isClockwise ? 3 : 1;
510         for (int i = 0; i < 4; ++i) {
511             const int src = (i + dir) >= 4 ? (i + dir) % 4 : (i + dir);
512             // Swap X and Y axis for the radii.
513             dst->fRadii[i].fX = fRadii[src].fY;
514             dst->fRadii[i].fY = fRadii[src].fX;
515         }
516     } else {
517         for (int i = 0; i < 4; ++i) {
518             dst->fRadii[i].fX = fRadii[i].fX;
519             dst->fRadii[i].fY = fRadii[i].fY;
520         }
521     }
522 
523     const bool flipX = xScale < 0;
524     if (flipX) {
525         xScale = -xScale;
526     }
527 
528     const bool flipY = yScale < 0;
529     if (flipY) {
530         yScale = -yScale;
531     }
532 
533     // Scale the radii without respecting the flip.
534     for (int i = 0; i < 4; ++i) {
535         dst->fRadii[i].fX *= xScale;
536         dst->fRadii[i].fY *= yScale;
537     }
538 
539     // Now swap as necessary.
540     using std::swap;
541     if (flipX) {
542         if (flipY) {
543             // Swap with opposite corners
544             swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
545             swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
546         } else {
547             // Only swap in x
548             swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
549             swap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
550         }
551     } else if (flipY) {
552         // Only swap in y
553         swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
554         swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
555     }
556 
557     dst->scaleRadii();
558 
559     if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) {
560         return false;
561     }
562 
563     SkASSERT(dst->isValid());
564     return true;
565 }
566 
567 ///////////////////////////////////////////////////////////////////////////////
568 
inset(SkScalar dx,SkScalar dy,SkRRect * dst) const569 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
570     SkRect r = fRect.makeInset(dx, dy);
571     bool degenerate = false;
572     if (r.fRight <= r.fLeft) {
573         degenerate = true;
574         r.fLeft = r.fRight = SkScalarAve(r.fLeft, r.fRight);
575     }
576     if (r.fBottom <= r.fTop) {
577         degenerate = true;
578         r.fTop = r.fBottom = SkScalarAve(r.fTop, r.fBottom);
579     }
580     if (degenerate) {
581         dst->fRect = r;
582         memset(dst->fRadii, 0, sizeof(dst->fRadii));
583         dst->fType = kEmpty_Type;
584         return;
585     }
586     if (!r.isFinite()) {
587         *dst = SkRRect();
588         return;
589     }
590 
591     SkVector radii[4];
592     memcpy(radii, fRadii, sizeof(radii));
593     for (int i = 0; i < 4; ++i) {
594         if (radii[i].fX) {
595             radii[i].fX -= dx;
596         }
597         if (radii[i].fY) {
598             radii[i].fY -= dy;
599         }
600     }
601     dst->setRectRadii(r, radii);
602 }
603 
604 ///////////////////////////////////////////////////////////////////////////////
605 
writeToMemory(void * buffer) const606 size_t SkRRect::writeToMemory(void* buffer) const {
607     // Serialize only the rect and corners, but not the derived type tag.
608     memcpy(buffer, this, kSizeInMemory);
609     return kSizeInMemory;
610 }
611 
WriteToBuffer(const SkRRect & rr,SkWBuffer * buffer)612 void SkRRectPriv::WriteToBuffer(const SkRRect& rr, SkWBuffer* buffer) {
613     // Serialize only the rect and corners, but not the derived type tag.
614     buffer->write(&rr, SkRRect::kSizeInMemory);
615 }
616 
readFromMemory(const void * buffer,size_t length)617 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
618     if (length < kSizeInMemory) {
619         return 0;
620     }
621 
622     // The extra (void*) tells GCC not to worry that kSizeInMemory < sizeof(SkRRect).
623 
624     SkRRect raw;
625     memcpy((void*)&raw, buffer, kSizeInMemory);
626     this->setRectRadii(raw.fRect, raw.fRadii);
627     return kSizeInMemory;
628 }
629 
ReadFromBuffer(SkRBuffer * buffer,SkRRect * rr)630 bool SkRRectPriv::ReadFromBuffer(SkRBuffer* buffer, SkRRect* rr) {
631     if (buffer->available() < SkRRect::kSizeInMemory) {
632         return false;
633     }
634     SkRRect storage;
635     return buffer->read(&storage, SkRRect::kSizeInMemory) &&
636            (rr->readFromMemory(&storage, SkRRect::kSizeInMemory) == SkRRect::kSizeInMemory);
637 }
638 
dumpToString(bool asHex) const639 SkString SkRRect::dumpToString(bool asHex) const {
640     SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
641 
642     SkString line = fRect.dumpToString(asHex);
643     line.appendf("\nconst SkPoint corners[] = {\n");
644     for (int i = 0; i < 4; ++i) {
645         SkString strX, strY;
646         SkAppendScalar(&strX, fRadii[i].x(), asType);
647         SkAppendScalar(&strY, fRadii[i].y(), asType);
648         line.appendf("    { %s, %s },", strX.c_str(), strY.c_str());
649         if (asHex) {
650             line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
651         }
652         line.append("\n");
653     }
654     line.append("};");
655     return line;
656 }
657 
dump(bool asHex) const658 void SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); }
659 
660 ///////////////////////////////////////////////////////////////////////////////
661 
662 /**
663  *  We need all combinations of predicates to be true to have a "safe" radius value.
664  */
are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)665 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
666     return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
667            rad >= 0;
668 }
669 
isValid() const670 bool SkRRect::isValid() const {
671     if (!AreRectAndRadiiValid(fRect, fRadii)) {
672         return false;
673     }
674 
675     bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
676     bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
677     bool allRadiiSame = true;
678 
679     for (int i = 1; i < 4; ++i) {
680         if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
681             allRadiiZero = false;
682         }
683 
684         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
685             allRadiiSame = false;
686         }
687 
688         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
689             allCornersSquare = false;
690         }
691     }
692     bool patchesOfNine = radii_are_nine_patch(fRadii);
693 
694     if (fType < 0 || fType > kLastType) {
695         return false;
696     }
697 
698     switch (fType) {
699         case kEmpty_Type:
700             if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
701                 return false;
702             }
703             break;
704         case kRect_Type:
705             if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
706                 return false;
707             }
708             break;
709         case kOval_Type:
710             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
711                 return false;
712             }
713 
714             for (int i = 0; i < 4; ++i) {
715                 if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
716                     !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
717                     return false;
718                 }
719             }
720             break;
721         case kSimple_Type:
722             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
723                 return false;
724             }
725             break;
726         case kNinePatch_Type:
727             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
728                 !patchesOfNine) {
729                 return false;
730             }
731             break;
732         case kComplex_Type:
733             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
734                 patchesOfNine) {
735                 return false;
736             }
737             break;
738     }
739 
740     return true;
741 }
742 
AreRectAndRadiiValid(const SkRect & rect,const SkVector radii[4])743 bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
744     if (!rect.isFinite() || !rect.isSorted()) {
745         return false;
746     }
747     for (int i = 0; i < 4; ++i) {
748         if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
749             !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
750             return false;
751         }
752     }
753     return true;
754 }
755 ///////////////////////////////////////////////////////////////////////////////
756 
InnerBounds(const SkRRect & rr)757 SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
758     if (rr.isEmpty() || rr.isRect()) {
759         return rr.rect();
760     }
761 
762     // We start with the outer bounds of the round rect and consider three subsets and take the
763     // one with maximum area. The first two are the horizontal and vertical rects inset from the
764     // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
765     // the exact solution when all corners have the same radii (the radii do not have to be
766     // circular).
767     SkRect innerBounds = rr.getBounds();
768     SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
769     SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
770     SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
771     SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
772 
773     // Select maximum inset per edge, which may move an adjacent corner of the inscribed
774     // rectangle off of the rounded-rect path, but that is acceptable given that the general
775     // equation for inscribed area is non-trivial to evaluate.
776     SkScalar leftShift   = std::max(tl.fX, bl.fX);
777     SkScalar topShift    = std::max(tl.fY, tr.fY);
778     SkScalar rightShift  = std::max(tr.fX, br.fX);
779     SkScalar bottomShift = std::max(bl.fY, br.fY);
780 
781     SkScalar dw = leftShift + rightShift;
782     SkScalar dh = topShift + bottomShift;
783 
784     // Area removed by shifting left/right
785     SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
786     // And by shifting top/bottom
787     SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
788     // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
789     // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
790     // safe shift per edge (since the shifts already are the max radius for that edge).
791     // - We actually scale by a value slightly increased to make it so that the shifted corners are
792     //   safely inside the curves, otherwise numerical stability can cause it to fail contains().
793     static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
794     SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
795 
796     if (horizArea > vertArea && horizArea > innerArea) {
797         // Cut off corners by insetting left and right
798         innerBounds.fLeft += leftShift;
799         innerBounds.fRight -= rightShift;
800     } else if (vertArea > innerArea) {
801         // Cut off corners by insetting top and bottom
802         innerBounds.fTop += topShift;
803         innerBounds.fBottom -= bottomShift;
804     } else if (innerArea > 0.f) {
805         // Inset on all sides, scaled to touch
806         innerBounds.fLeft += kScale * leftShift;
807         innerBounds.fRight -= kScale * rightShift;
808         innerBounds.fTop += kScale * topShift;
809         innerBounds.fBottom -= kScale * bottomShift;
810     } else {
811         // Inner region would collapse to empty
812         return SkRect::MakeEmpty();
813     }
814 
815     SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
816     return innerBounds;
817 }
818 
ConservativeIntersect(const SkRRect & a,const SkRRect & b)819 SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
820     // Returns the coordinate of the rect matching the corner enum.
821     auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
822         switch(corner) {
823             case SkRRect::kUpperLeft_Corner:  return {r.fLeft, r.fTop};
824             case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
825             case SkRRect::kLowerLeft_Corner:  return {r.fLeft, r.fBottom};
826             case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
827             default: SkUNREACHABLE;
828         }
829     };
830     // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
831     // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
832     // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
833     auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
834         switch(corner) {
835             case SkRRect::kUpperLeft_Corner:  return a.fX >= b.fX && a.fY >= b.fY;
836             case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
837             case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
838             case SkRRect::kLowerLeft_Corner:  return a.fX >= b.fX && a.fY <= b.fY;
839             default:  SkUNREACHABLE;
840         }
841     };
842 
843     auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
844         SkPoint test = getCorner(r, corner);
845         SkPoint aCorner = getCorner(a.rect(), corner);
846         SkPoint bCorner = getCorner(b.rect(), corner);
847 
848         if (test == aCorner && test == bCorner) {
849             // The round rects share a corner anchor, so pick A or B such that its X and Y radii
850             // are both larger than the other rrect's, or return false if neither A or B has the max
851             // corner radii (this is more permissive than the single corner tests below).
852             SkVector aRadii = a.radii(corner);
853             SkVector bRadii = b.radii(corner);
854             if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
855                 *radii = aRadii;
856                 return true;
857             } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
858                 *radii = bRadii;
859                 return true;
860             } else {
861                 return false;
862             }
863         } else if (test == aCorner) {
864             // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
865             // so we resrict it to when the corners have the same radii. If not, we use the more
866             // conservative test that the extreme point of A's bounding box is contained in B.
867             *radii = a.radii(corner);
868             if (*radii == b.radii(corner)) {
869                 return insideCorner(corner, aCorner, bCorner); // A inside B
870             } else {
871                 return b.checkCornerContainment(aCorner.fX, aCorner.fY);
872             }
873         } else if (test == bCorner) {
874             // Mirror of the above
875             *radii = b.radii(corner);
876             if (*radii == a.radii(corner)) {
877                 return insideCorner(corner, bCorner, aCorner); // B inside A
878             } else {
879                 return a.checkCornerContainment(bCorner.fX, bCorner.fY);
880             }
881         } else {
882             // This is a corner formed by two straight edges of A and B, so confirm that it is
883             // contained in both (if not, then the intersection can't be a round rect).
884             *radii = {0.f, 0.f};
885             return a.checkCornerContainment(test.fX, test.fY) &&
886                    b.checkCornerContainment(test.fX, test.fY);
887         }
888     };
889 
890     // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
891     // valid existing SkRRects, we know we are finite.
892     SkRRect intersection;
893     if (!intersection.fRect.intersect(a.rect(), b.rect())) {
894         // Definitely no intersection
895         return SkRRect::MakeEmpty();
896     }
897 
898     const SkRRect::Corner corners[] = {
899         SkRRect::kUpperLeft_Corner,
900         SkRRect::kUpperRight_Corner,
901         SkRRect::kLowerRight_Corner,
902         SkRRect::kLowerLeft_Corner
903     };
904     // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
905     // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
906     // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
907     // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
908     // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
909     // intersection shape.
910     for (auto c : corners) {
911         if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
912             return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
913         }
914     }
915 
916     // Check for radius overlap along the four edges, since the earlier evaluation was only a
917     // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
918     // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
919     // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
920     // the intersection shape is definitively not a round rect.
921     if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
922         intersection.scaleRadii()) {
923         return SkRRect::MakeEmpty();
924     }
925 
926     // The intersection is an rrect of the given radii. Potentially all 4 corners could have
927     // been simplified to (0,0) radii, making the intersection a rectangle.
928     intersection.computeType();
929     return intersection;
930 }
931