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