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