• 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