082646c2a76594c4a33e47fa7b759ef3308634c1
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / core / SkRRect.cpp
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
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
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
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.
177 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
230 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
237 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
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
266 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
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
316 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     // Now scale each corner
346     SkScalar xScale = matrix.getScaleX();
347     const bool flipX = xScale < 0;
348     if (flipX) {
349         xScale = -xScale;
350     }
351     SkScalar yScale = matrix.getScaleY();
352     const bool flipY = yScale < 0;
353     if (flipY) {
354         yScale = -yScale;
355     }
356
357     // Scale the radii without respecting the flip.
358     for (int i = 0; i < 4; ++i) {
359         dst->fRadii[i].fX = SkScalarMul(fRadii[i].fX, xScale);
360         dst->fRadii[i].fY = SkScalarMul(fRadii[i].fY, yScale);
361     }
362
363     // Now swap as necessary.
364     if (flipX) {
365         if (flipY) {
366             // Swap with opposite corners
367             SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
368             SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
369         } else {
370             // Only swap in x
371             SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
372             SkTSwap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
373         }
374     } else if (flipY) {
375         // Only swap in y
376         SkTSwap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
377         SkTSwap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
378     }
379
380     // Since the only transforms that were allowed are scale and translate, the type
381     // remains unchanged.
382     dst->fType = fType;
383
384     SkDEBUGCODE(dst->validate();)
385
386     return true;
387 }
388
389 ///////////////////////////////////////////////////////////////////////////////
390
391 void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
392     SkRect r = fRect;
393
394     r.inset(dx, dy);
395     if (r.isEmpty()) {
396         dst->setEmpty();
397         return;
398     }
399
400     SkVector radii[4];
401     memcpy(radii, fRadii, sizeof(radii));
402     for (int i = 0; i < 4; ++i) {
403         if (radii[i].fX) {
404             radii[i].fX -= dx;
405         }
406         if (radii[i].fY) {
407             radii[i].fY -= dy;
408         }
409     }
410     dst->setRectRadii(r, radii);
411 }
412
413 ///////////////////////////////////////////////////////////////////////////////
414
415 size_t SkRRect::writeToMemory(void* buffer) const {
416     SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
417
418     memcpy(buffer, &fRect, sizeof(SkRect));
419     memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
420     return kSizeInMemory;
421 }
422
423 size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
424     if (length < kSizeInMemory) {
425         return 0;
426     }
427
428     SkScalar storage[12];
429     SkASSERT(sizeof(storage) == kSizeInMemory);
430
431     // we make a local copy, to ensure alignment before we cast
432     memcpy(storage, buffer, kSizeInMemory);
433
434     this->setRectRadii(*(const SkRect*)&storage[0],
435                        (const SkVector*)&storage[4]);
436     return kSizeInMemory;
437 }
438
439 ///////////////////////////////////////////////////////////////////////////////
440
441 #ifdef SK_DEBUG
442 void SkRRect::validate() const {
443     bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
444     bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
445     bool allRadiiSame = true;
446
447     for (int i = 1; i < 4; ++i) {
448         if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
449             allRadiiZero = false;
450         }
451
452         if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
453             allRadiiSame = false;
454         }
455
456         if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
457             allCornersSquare = false;
458         }
459     }
460     bool patchesOfNine = radii_are_nine_patch(fRadii);
461
462     switch (fType) {
463         case kEmpty_Type:
464             SkASSERT(fRect.isEmpty());
465             SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
466
467             SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop &&
468                      0 == fRect.fRight && 0 == fRect.fBottom);
469             break;
470         case kRect_Type:
471             SkASSERT(!fRect.isEmpty());
472             SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
473             break;
474         case kOval_Type:
475             SkASSERT(!fRect.isEmpty());
476             SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
477
478             for (int i = 0; i < 4; ++i) {
479                 SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
480                 SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
481             }
482             break;
483         case kSimple_Type:
484             SkASSERT(!fRect.isEmpty());
485             SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
486             break;
487         case kNinePatch_Type:
488             SkASSERT(!fRect.isEmpty());
489             SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
490             SkASSERT(patchesOfNine);
491             break;
492         case kComplex_Type:
493             SkASSERT(!fRect.isEmpty());
494             SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
495             SkASSERT(!patchesOfNine);
496             break;
497         case kUnknown_Type:
498             // no limits on this
499             break;
500     }
501 }
502 #endif // SK_DEBUG
503
504 ///////////////////////////////////////////////////////////////////////////////