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