• 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 <cmath>
9 #include "SkRRect.h"
10 #include "SkMatrix.h"
11 #include "SkScaleToSides.h"
12 
13 ///////////////////////////////////////////////////////////////////////////////
14 
setRectXY(const SkRect & rect,SkScalar xRad,SkScalar yRad)15 void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
16     fRect = rect;
17     fRect.sort();
18 
19     if (fRect.isEmpty() || !fRect.isFinite()) {
20         this->setEmpty();
21         return;
22     }
23 
24     if (!SkScalarsAreFinite(xRad, yRad)) {
25         xRad = yRad = 0;    // devolve into a simple rect
26     }
27     if (xRad <= 0 || yRad <= 0) {
28         // all corners are square in this case
29         this->setRect(rect);
30         return;
31     }
32 
33     if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) {
34         SkScalar scale = SkMinScalar(fRect.width() / (xRad + xRad), fRect.height() / (yRad + yRad));
35         SkASSERT(scale < SK_Scalar1);
36         xRad *= scale;
37         yRad *= scale;
38     }
39 
40     for (int i = 0; i < 4; ++i) {
41         fRadii[i].set(xRad, yRad);
42     }
43     fType = kSimple_Type;
44     if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
45         fType = kOval_Type;
46         // TODO: assert that all the x&y radii are already W/2 & H/2
47     }
48 
49     SkASSERT(this->isValid());
50 }
51 
setNinePatch(const SkRect & rect,SkScalar leftRad,SkScalar topRad,SkScalar rightRad,SkScalar bottomRad)52 void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
53                            SkScalar rightRad, SkScalar bottomRad) {
54     fRect = rect;
55     fRect.sort();
56 
57     if (fRect.isEmpty() || !fRect.isFinite()) {
58         this->setEmpty();
59         return;
60     }
61 
62     const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad };
63     if (!SkScalarsAreFinite(array, 4)) {
64         this->setRect(rect);    // devolve into a simple rect
65         return;
66     }
67 
68     leftRad = SkMaxScalar(leftRad, 0);
69     topRad = SkMaxScalar(topRad, 0);
70     rightRad = SkMaxScalar(rightRad, 0);
71     bottomRad = SkMaxScalar(bottomRad, 0);
72 
73     SkScalar scale = SK_Scalar1;
74     if (leftRad + rightRad > fRect.width()) {
75         scale = fRect.width() / (leftRad + rightRad);
76     }
77     if (topRad + bottomRad > fRect.height()) {
78         scale = SkMinScalar(scale, fRect.height() / (topRad + bottomRad));
79     }
80 
81     if (scale < SK_Scalar1) {
82         leftRad *= scale;
83         topRad *= scale;
84         rightRad *= scale;
85         bottomRad *= scale;
86     }
87 
88     if (leftRad == rightRad && topRad == bottomRad) {
89         if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) {
90             fType = kOval_Type;
91         } else if (0 == leftRad || 0 == topRad) {
92             // If the left and (by equality check above) right radii are zero then it is a rect.
93             // Same goes for top/bottom.
94             fType = kRect_Type;
95             leftRad = 0;
96             topRad = 0;
97             rightRad = 0;
98             bottomRad = 0;
99         } else {
100             fType = kSimple_Type;
101         }
102     } else {
103         fType = kNinePatch_Type;
104     }
105 
106     fRadii[kUpperLeft_Corner].set(leftRad, topRad);
107     fRadii[kUpperRight_Corner].set(rightRad, topRad);
108     fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
109     fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
110 
111     SkASSERT(this->isValid());
112 }
113 
114 // These parameters intentionally double. Apropos crbug.com/463920, if one of the
115 // radii is huge while the other is small, single precision math can completely
116 // miss the fact that a scale is required.
compute_min_scale(double rad1,double rad2,double limit,double curMin)117 static double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
118     if ((rad1 + rad2) > limit) {
119         return SkTMin(curMin, limit / (rad1 + rad2));
120     }
121     return curMin;
122 }
123 
setRectRadii(const SkRect & rect,const SkVector radii[4])124 void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
125     fRect = rect;
126     fRect.sort();
127 
128     if (fRect.isEmpty() || !fRect.isFinite()) {
129         this->setEmpty();
130         return;
131     }
132 
133     if (!SkScalarsAreFinite(&radii[0].fX, 8)) {
134         this->setRect(rect);    // devolve into a simple rect
135         return;
136     }
137 
138     memcpy(fRadii, radii, sizeof(fRadii));
139 
140     bool allCornersSquare = true;
141 
142     // Clamp negative radii to zero
143     for (int i = 0; i < 4; ++i) {
144         if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
145             // In this case we are being a little fast & loose. Since one of
146             // the radii is 0 the corner is square. However, the other radii
147             // could still be non-zero and play in the global scale factor
148             // computation.
149             fRadii[i].fX = 0;
150             fRadii[i].fY = 0;
151         } else {
152             allCornersSquare = false;
153         }
154     }
155 
156     if (allCornersSquare) {
157         this->setRect(rect);
158         return;
159     }
160 
161     this->scaleRadii();
162 }
163 
scaleRadii()164 void SkRRect::scaleRadii() {
165 
166     // Proportionally scale down all radii to fit. Find the minimum ratio
167     // of a side and the radii on that side (for all four sides) and use
168     // that to scale down _all_ the radii. This algorithm is from the
169     // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
170     // Curves:
171     // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
172     //   Si is the sum of the two corresponding radii of the corners on side i,
173     //   and Ltop = Lbottom = the width of the box,
174     //   and Lleft = Lright = the height of the box.
175     // If f < 1, then all corner radii are reduced by multiplying them by f."
176     double scale = 1.0;
177 
178     // The sides of the rectangle may be larger than a float.
179     double width = (double)fRect.fRight - (double)fRect.fLeft;
180     double height = (double)fRect.fBottom - (double)fRect.fTop;
181     scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width,  scale);
182     scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
183     scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width,  scale);
184     scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
185 
186     if (scale < 1.0) {
187         SkScaleToSides::AdjustRadii(width,  scale, &fRadii[0].fX, &fRadii[1].fX);
188         SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
189         SkScaleToSides::AdjustRadii(width,  scale, &fRadii[2].fX, &fRadii[3].fX);
190         SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
191     }
192 
193     // At this point we're either oval, simple, or complex (not empty or rect).
194     this->computeType();
195 
196     SkASSERT(this->isValid());
197 }
198 
199 // This method determines if a point known to be inside the RRect's bounds is
200 // inside all the corners.
checkCornerContainment(SkScalar x,SkScalar y) const201 bool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
202     SkPoint canonicalPt; // (x,y) translated to one of the quadrants
203     int index;
204 
205     if (kOval_Type == this->type()) {
206         canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
207         index = kUpperLeft_Corner;  // any corner will do in this case
208     } else {
209         if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
210             y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
211             // UL corner
212             index = kUpperLeft_Corner;
213             canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
214                             y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
215             SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
216         } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
217                    y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
218             // LL corner
219             index = kLowerLeft_Corner;
220             canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
221                             y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
222             SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
223         } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
224                    y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
225             // UR corner
226             index = kUpperRight_Corner;
227             canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
228                             y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
229             SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
230         } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
231                    y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
232             // LR corner
233             index = kLowerRight_Corner;
234             canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
235                             y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
236             SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
237         } else {
238             // not in any of the corners
239             return true;
240         }
241     }
242 
243     // A point is in an ellipse (in standard position) if:
244     //      x^2     y^2
245     //     ----- + ----- <= 1
246     //      a^2     b^2
247     // or :
248     //     b^2*x^2 + a^2*y^2 <= (ab)^2
249     SkScalar dist =  SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) +
250                      SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX);
251     return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY);
252 }
253 
allCornersCircular(SkScalar tolerance) const254 bool SkRRect::allCornersCircular(SkScalar tolerance) const {
255     return SkScalarNearlyEqual(fRadii[0].fX, fRadii[0].fY, tolerance) &&
256            SkScalarNearlyEqual(fRadii[1].fX, fRadii[1].fY, tolerance) &&
257            SkScalarNearlyEqual(fRadii[2].fX, fRadii[2].fY, tolerance) &&
258            SkScalarNearlyEqual(fRadii[3].fX, fRadii[3].fY, tolerance);
259 }
260 
contains(const SkRect & rect) const261 bool SkRRect::contains(const SkRect& rect) const {
262     if (!this->getBounds().contains(rect)) {
263         // If 'rect' isn't contained by the RR's bounds then the
264         // RR definitely doesn't contain it
265         return false;
266     }
267 
268     if (this->isRect()) {
269         // the prior test was sufficient
270         return true;
271     }
272 
273     // At this point we know all four corners of 'rect' are inside the
274     // bounds of of this RR. Check to make sure all the corners are inside
275     // all the curves
276     return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
277            this->checkCornerContainment(rect.fRight, rect.fTop) &&
278            this->checkCornerContainment(rect.fRight, rect.fBottom) &&
279            this->checkCornerContainment(rect.fLeft, rect.fBottom);
280 }
281 
radii_are_nine_patch(const SkVector radii[4])282 static bool radii_are_nine_patch(const SkVector radii[4]) {
283     return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
284            radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
285            radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
286            radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
287 }
288 
289 // There is a simplified version of this method in setRectXY
computeType()290 void SkRRect::computeType() {
291     struct Validator {
292         Validator(const SkRRect* r) : fR(r) {}
293         ~Validator() { SkASSERT(fR->isValid()); }
294         const SkRRect* fR;
295     } autoValidate(this);
296 
297     if (fRect.isEmpty()) {
298         fType = kEmpty_Type;
299         return;
300     }
301 
302     bool allRadiiEqual = true; // are all x radii equal and all y radii?
303     bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
304 
305     for (int i = 1; i < 4; ++i) {
306         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
307             // if either radius is zero the corner is square so both have to
308             // be non-zero to have a rounded corner
309             allCornersSquare = false;
310         }
311         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
312             allRadiiEqual = false;
313         }
314     }
315 
316     if (allCornersSquare) {
317         fType = kRect_Type;
318         return;
319     }
320 
321     if (allRadiiEqual) {
322         if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
323             fRadii[0].fY >= SkScalarHalf(fRect.height())) {
324             fType = kOval_Type;
325         } else {
326             fType = kSimple_Type;
327         }
328         return;
329     }
330 
331     if (radii_are_nine_patch(fRadii)) {
332         fType = kNinePatch_Type;
333     } else {
334         fType = kComplex_Type;
335     }
336 }
337 
matrix_only_scale_and_translate(const SkMatrix & matrix)338 static bool matrix_only_scale_and_translate(const SkMatrix& matrix) {
339     const SkMatrix::TypeMask m = (SkMatrix::TypeMask) (SkMatrix::kAffine_Mask
340                                     | SkMatrix::kPerspective_Mask);
341     return (matrix.getType() & m) == 0;
342 }
343 
transform(const SkMatrix & matrix,SkRRect * dst) const344 bool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
345     if (nullptr == dst) {
346         return false;
347     }
348 
349     // Assert that the caller is not trying to do this in place, which
350     // would violate const-ness. Do not return false though, so that
351     // if they know what they're doing and want to violate it they can.
352     SkASSERT(dst != this);
353 
354     if (matrix.isIdentity()) {
355         *dst = *this;
356         return true;
357     }
358 
359     // If transform supported 90 degree rotations (which it could), we could
360     // use SkMatrix::rectStaysRect() to check for a valid transformation.
361     if (!matrix_only_scale_and_translate(matrix)) {
362         return false;
363     }
364 
365     SkRect newRect;
366     if (!matrix.mapRect(&newRect, fRect)) {
367         return false;
368     }
369 
370     // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
371     // some dimension of the rect, so we need to check for that.
372     if (newRect.isEmpty()) {
373         dst->setEmpty();
374         return true;
375     }
376 
377     // At this point, this is guaranteed to succeed, so we can modify dst.
378     dst->fRect = newRect;
379 
380     // Since the only transforms that were allowed are scale and translate, the type
381     // remains unchanged.
382     dst->fType = fType;
383 
384     if (kOval_Type == fType) {
385         for (int i = 0; i < 4; ++i) {
386             dst->fRadii[i].fX = SkScalarHalf(newRect.width());
387             dst->fRadii[i].fY = SkScalarHalf(newRect.height());
388         }
389         SkASSERT(dst->isValid());
390         return true;
391     }
392 
393     // Now scale each corner
394     SkScalar xScale = matrix.getScaleX();
395     const bool flipX = xScale < 0;
396     if (flipX) {
397         xScale = -xScale;
398     }
399     SkScalar yScale = matrix.getScaleY();
400     const bool flipY = yScale < 0;
401     if (flipY) {
402         yScale = -yScale;
403     }
404 
405     // Scale the radii without respecting the flip.
406     for (int i = 0; i < 4; ++i) {
407         dst->fRadii[i].fX = fRadii[i].fX * xScale;
408         dst->fRadii[i].fY = fRadii[i].fY * yScale;
409     }
410 
411     // Now swap as necessary.
412     if (flipX) {
413         if (flipY) {
414             // Swap with opposite corners
415             SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
416             SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
417         } else {
418             // Only swap in x
419             SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
420             SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
421         }
422     } else if (flipY) {
423         // Only swap in y
424         SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
425         SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
426     }
427 
428     dst->scaleRadii();
429 
430     return true;
431 }
432 
433 ///////////////////////////////////////////////////////////////////////////////
434 
inset(SkScalar dx,SkScalar dy,SkRRect * dst) const435 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
436     const SkRect r = fRect.makeInset(dx, dy);
437 
438     if (r.isEmpty()) {
439         dst->setEmpty();
440         return;
441     }
442 
443     SkVector radii[4];
444     memcpy(radii, fRadii, sizeof(radii));
445     for (int i = 0; i < 4; ++i) {
446         if (radii[i].fX) {
447             radii[i].fX -= dx;
448         }
449         if (radii[i].fY) {
450             radii[i].fY -= dy;
451         }
452     }
453     dst->setRectRadii(r, radii);
454 }
455 
456 ///////////////////////////////////////////////////////////////////////////////
457 
writeToMemory(void * buffer) const458 size_t SkRRect::writeToMemory(void* buffer) const {
459     // Serialize only the rect and corners, but not the derived type tag.
460     memcpy(buffer, this, kSizeInMemory);
461     return kSizeInMemory;
462 }
463 
readFromMemory(const void * buffer,size_t length)464 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
465     if (length < kSizeInMemory) {
466         return 0;
467     }
468 
469     // Deserialize rect and corners, then rederive the type tag.
470     memcpy(this, buffer, kSizeInMemory);
471     this->computeType();
472 
473     return kSizeInMemory;
474 }
475 
476 #include "SkString.h"
477 #include "SkStringUtils.h"
478 
dump(bool asHex) const479 void SkRRect::dump(bool asHex) const {
480     SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
481 
482     fRect.dump(asHex);
483     SkString line("const SkPoint corners[] = {\n");
484     for (int i = 0; i < 4; ++i) {
485         SkString strX, strY;
486         SkAppendScalar(&strX, fRadii[i].x(), asType);
487         SkAppendScalar(&strY, fRadii[i].y(), asType);
488         line.appendf("    { %s, %s },", strX.c_str(), strY.c_str());
489         if (asHex) {
490             line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
491         }
492         line.append("\n");
493     }
494     line.append("};");
495     SkDebugf("%s\n", line.c_str());
496 }
497 
498 ///////////////////////////////////////////////////////////////////////////////
499 
500 /**
501  *  We need all combinations of predicates to be true to have a "safe" radius value.
502  */
are_radius_check_predicates_valid(SkScalar rad,SkScalar min,SkScalar max)503 static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
504     return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min);
505 }
506 
isValid() const507 bool SkRRect::isValid() const {
508     bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
509     bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
510     bool allRadiiSame = true;
511 
512     for (int i = 1; i < 4; ++i) {
513         if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
514             allRadiiZero = false;
515         }
516 
517         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
518             allRadiiSame = false;
519         }
520 
521         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
522             allCornersSquare = false;
523         }
524     }
525     bool patchesOfNine = radii_are_nine_patch(fRadii);
526 
527     switch (fType) {
528         case kEmpty_Type:
529             if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
530                 return false;
531             }
532             break;
533         case kRect_Type:
534             if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
535                 return false;
536             }
537             break;
538         case kOval_Type:
539             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
540                 return false;
541             }
542 
543             for (int i = 0; i < 4; ++i) {
544                 if (!SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())) ||
545                     !SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height()))) {
546                     return false;
547                 }
548             }
549             break;
550         case kSimple_Type:
551             if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
552                 return false;
553             }
554             break;
555         case kNinePatch_Type:
556             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
557                 !patchesOfNine) {
558                 return false;
559             }
560             break;
561         case kComplex_Type:
562             if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
563                 patchesOfNine) {
564                 return false;
565             }
566             break;
567     }
568 
569     for (int i = 0; i < 4; ++i) {
570         if (!are_radius_check_predicates_valid(fRadii[i].fX, fRect.fLeft, fRect.fRight) ||
571             !are_radius_check_predicates_valid(fRadii[i].fY, fRect.fTop, fRect.fBottom)) {
572             return false;
573         }
574     }
575 
576     return true;
577 }
578 
579 ///////////////////////////////////////////////////////////////////////////////
580