• 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 
dump(std::string & desc,int depth) const641 void SkRRect::dump(std::string& desc, int depth) const {
642     std::string split(depth, '\t');
643     desc += split + "\n SkRRect:{ \n";
644 
645     fRect.dump(desc, depth + 1);
646     desc += split + "\t const SkPoint corners[] = {\n";
647     for (int i = 0; i < 4; ++i) {
648         fRadii[i].dump(desc, depth + 1);
649     }
650     desc += split + "\t}\n";
651     desc += split + "\t fType:" + std::to_string(fType) + "\n";
652     desc += split + "}\n";
653 }
654 
655 ///////////////////////////////////////////////////////////////////////////////
656 
657 /**
658  *  We need all combinations of predicates to be true to have a "safe" radius value.
659  */
are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)660 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
661     return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
662            rad >= 0;
663 }
664 
isValid() const665 bool SkRRect::isValid() const {
666     if (!AreRectAndRadiiValid(fRect, fRadii)) {
667         return false;
668     }
669 
670     bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
671     bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
672     bool allRadiiSame = true;
673 
674     for (int i = 1; i < 4; ++i) {
675         if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
676             allRadiiZero = false;
677         }
678 
679         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
680             allRadiiSame = false;
681         }
682 
683         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
684             allCornersSquare = false;
685         }
686     }
687     bool patchesOfNine = radii_are_nine_patch(fRadii);
688 
689     if (fType < 0 || fType > kLastType) {
690         return false;
691     }
692 
693     switch (fType) {
694         case kEmpty_Type:
695             if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
696                 return false;
697             }
698             break;
699         case kRect_Type:
700             if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
701                 return false;
702             }
703             break;
704         case kOval_Type:
705             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
706                 return false;
707             }
708 
709             for (int i = 0; i < 4; ++i) {
710                 if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
711                     !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
712                     return false;
713                 }
714             }
715             break;
716         case kSimple_Type:
717             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
718                 return false;
719             }
720             break;
721         case kNinePatch_Type:
722             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
723                 !patchesOfNine) {
724                 return false;
725             }
726             break;
727         case kComplex_Type:
728             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
729                 patchesOfNine) {
730                 return false;
731             }
732             break;
733     }
734 
735     return true;
736 }
737 
AreRectAndRadiiValid(const SkRect & rect,const SkVector radii[4])738 bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
739     if (!rect.isFinite() || !rect.isSorted()) {
740         return false;
741     }
742     for (int i = 0; i < 4; ++i) {
743         if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
744             !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
745             return false;
746         }
747     }
748     return true;
749 }
750 ///////////////////////////////////////////////////////////////////////////////
751 
InnerBounds(const SkRRect & rr)752 SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
753     if (rr.isEmpty() || rr.isRect()) {
754         return rr.rect();
755     }
756 
757     // We start with the outer bounds of the round rect and consider three subsets and take the
758     // one with maximum area. The first two are the horizontal and vertical rects inset from the
759     // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
760     // the exact solution when all corners have the same radii (the radii do not have to be
761     // circular).
762     SkRect innerBounds = rr.getBounds();
763     SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
764     SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
765     SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
766     SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
767 
768     // Select maximum inset per edge, which may move an adjacent corner of the inscribed
769     // rectangle off of the rounded-rect path, but that is acceptable given that the general
770     // equation for inscribed area is non-trivial to evaluate.
771     SkScalar leftShift   = std::max(tl.fX, bl.fX);
772     SkScalar topShift    = std::max(tl.fY, tr.fY);
773     SkScalar rightShift  = std::max(tr.fX, br.fX);
774     SkScalar bottomShift = std::max(bl.fY, br.fY);
775 
776     SkScalar dw = leftShift + rightShift;
777     SkScalar dh = topShift + bottomShift;
778 
779     // Area removed by shifting left/right
780     SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
781     // And by shifting top/bottom
782     SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
783     // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
784     // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
785     // safe shift per edge (since the shifts already are the max radius for that edge).
786     // - We actually scale by a value slightly increased to make it so that the shifted corners are
787     //   safely inside the curves, otherwise numerical stability can cause it to fail contains().
788     static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
789     SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
790 
791     if (horizArea > vertArea && horizArea > innerArea) {
792         // Cut off corners by insetting left and right
793         innerBounds.fLeft += leftShift;
794         innerBounds.fRight -= rightShift;
795     } else if (vertArea > innerArea) {
796         // Cut off corners by insetting top and bottom
797         innerBounds.fTop += topShift;
798         innerBounds.fBottom -= bottomShift;
799     } else if (innerArea > 0.f) {
800         // Inset on all sides, scaled to touch
801         innerBounds.fLeft += kScale * leftShift;
802         innerBounds.fRight -= kScale * rightShift;
803         innerBounds.fTop += kScale * topShift;
804         innerBounds.fBottom -= kScale * bottomShift;
805     } else {
806         // Inner region would collapse to empty
807         return SkRect::MakeEmpty();
808     }
809 
810     SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
811     return innerBounds;
812 }
813 
ConservativeIntersect(const SkRRect & a,const SkRRect & b)814 SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
815     // Returns the coordinate of the rect matching the corner enum.
816     auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
817         switch(corner) {
818             case SkRRect::kUpperLeft_Corner:  return {r.fLeft, r.fTop};
819             case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
820             case SkRRect::kLowerLeft_Corner:  return {r.fLeft, r.fBottom};
821             case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
822             default: SkUNREACHABLE;
823         }
824     };
825     // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
826     // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
827     // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
828     auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
829         switch(corner) {
830             case SkRRect::kUpperLeft_Corner:  return a.fX >= b.fX && a.fY >= b.fY;
831             case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
832             case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
833             case SkRRect::kLowerLeft_Corner:  return a.fX >= b.fX && a.fY <= b.fY;
834             default:  SkUNREACHABLE;
835         }
836     };
837 
838     auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
839         SkPoint test = getCorner(r, corner);
840         SkPoint aCorner = getCorner(a.rect(), corner);
841         SkPoint bCorner = getCorner(b.rect(), corner);
842 
843         if (test == aCorner && test == bCorner) {
844             // The round rects share a corner anchor, so pick A or B such that its X and Y radii
845             // are both larger than the other rrect's, or return false if neither A or B has the max
846             // corner radii (this is more permissive than the single corner tests below).
847             SkVector aRadii = a.radii(corner);
848             SkVector bRadii = b.radii(corner);
849             if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
850                 *radii = aRadii;
851                 return true;
852             } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
853                 *radii = bRadii;
854                 return true;
855             } else {
856                 return false;
857             }
858         } else if (test == aCorner) {
859             // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
860             // so we resrict it to when the corners have the same radii. If not, we use the more
861             // conservative test that the extreme point of A's bounding box is contained in B.
862             *radii = a.radii(corner);
863             if (*radii == b.radii(corner)) {
864                 return insideCorner(corner, aCorner, bCorner); // A inside B
865             } else {
866                 return b.checkCornerContainment(aCorner.fX, aCorner.fY);
867             }
868         } else if (test == bCorner) {
869             // Mirror of the above
870             *radii = b.radii(corner);
871             if (*radii == a.radii(corner)) {
872                 return insideCorner(corner, bCorner, aCorner); // B inside A
873             } else {
874                 return a.checkCornerContainment(bCorner.fX, bCorner.fY);
875             }
876         } else {
877             // This is a corner formed by two straight edges of A and B, so confirm that it is
878             // contained in both (if not, then the intersection can't be a round rect).
879             *radii = {0.f, 0.f};
880             return a.checkCornerContainment(test.fX, test.fY) &&
881                    b.checkCornerContainment(test.fX, test.fY);
882         }
883     };
884 
885     // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
886     // valid existing SkRRects, we know we are finite.
887     SkRRect intersection;
888     if (!intersection.fRect.intersect(a.rect(), b.rect())) {
889         // Definitely no intersection
890         return SkRRect::MakeEmpty();
891     }
892 
893     const SkRRect::Corner corners[] = {
894         SkRRect::kUpperLeft_Corner,
895         SkRRect::kUpperRight_Corner,
896         SkRRect::kLowerRight_Corner,
897         SkRRect::kLowerLeft_Corner
898     };
899     // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
900     // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
901     // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
902     // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
903     // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
904     // intersection shape.
905     for (auto c : corners) {
906         if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
907             return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
908         }
909     }
910 
911     // Check for radius overlap along the four edges, since the earlier evaluation was only a
912     // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
913     // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
914     // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
915     // the intersection shape is definitively not a round rect.
916     if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
917         intersection.scaleRadii()) {
918         return SkRRect::MakeEmpty();
919     }
920 
921     // The intersection is an rrect of the given radii. Potentially all 4 corners could have
922     // been simplified to (0,0) radii, making the intersection a rectangle.
923     intersection.computeType();
924     return intersection;
925 }
926