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
15void 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 = SkScalarMul(xRad, scale);
37        yRad = SkScalarMul(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    SkDEBUGCODE(this->validate();)
50}
51
52void 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 = SkScalarMul(leftRad, scale);
83        topRad = SkScalarMul(topRad, scale);
84        rightRad = SkScalarMul(rightRad, scale);
85        bottomRad = SkScalarMul(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    SkDEBUGCODE(this->validate();)
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.
117static 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
124void 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
164void 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    SkDEBUGCODE(this->validate();)
197}
198
199// This method determines if a point known to be inside the RRect's bounds is
200// inside all the corners.
201bool 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 =  SkScalarMul(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fY)) +
250                     SkScalarMul(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fX));
251    return dist <= SkScalarSquare(SkScalarMul(fRadii[index].fX, fRadii[index].fY));
252}
253
254bool SkRRect::allCornersCircular() const {
255    return fRadii[0].fX == fRadii[0].fY &&
256        fRadii[1].fX == fRadii[1].fY &&
257        fRadii[2].fX == fRadii[2].fY &&
258        fRadii[3].fX == fRadii[3].fY;
259}
260
261bool 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
282static 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
290void SkRRect::computeType() {
291    struct Validator {
292        Validator(const SkRRect* r) : fR(r) {}
293        ~Validator() { SkDEBUGCODE(fR->validate();) }
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
338static 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
344bool 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        SkDEBUGCODE(dst->validate();)
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 = SkScalarMul(fRadii[i].fX, xScale);
408        dst->fRadii[i].fY = SkScalarMul(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
435void 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
458size_t SkRRect::writeToMemory(void* buffer) const {
459    SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
460
461    memcpy(buffer, &fRect, sizeof(SkRect));
462    memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
463    return kSizeInMemory;
464}
465
466size_t SkRRect::readFromMemory(const void* buffer, size_t length) {
467    if (length < kSizeInMemory) {
468        return 0;
469    }
470
471    SkScalar storage[12];
472    SkASSERT(sizeof(storage) == kSizeInMemory);
473
474    // we make a local copy, to ensure alignment before we cast
475    memcpy(storage, buffer, kSizeInMemory);
476
477    this->setRectRadii(*(const SkRect*)&storage[0],
478                       (const SkVector*)&storage[4]);
479    return kSizeInMemory;
480}
481
482#include "SkString.h"
483#include "SkStringUtils.h"
484
485void SkRRect::dump(bool asHex) const {
486    SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
487
488    fRect.dump(asHex);
489    SkString line("const SkPoint corners[] = {\n");
490    for (int i = 0; i < 4; ++i) {
491        SkString strX, strY;
492        SkAppendScalar(&strX, fRadii[i].x(), asType);
493        SkAppendScalar(&strY, fRadii[i].y(), asType);
494        line.appendf("    { %s, %s },", strX.c_str(), strY.c_str());
495        if (asHex) {
496            line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
497        }
498        line.append("\n");
499    }
500    line.append("};");
501    SkDebugf("%s\n", line.c_str());
502}
503
504///////////////////////////////////////////////////////////////////////////////
505
506#ifdef SK_DEBUG
507/**
508 *  We need all combinations of predicates to be true to have a "safe" radius value.
509 */
510static void validate_radius_check_predicates(SkScalar rad, SkScalar min, SkScalar max) {
511    SkASSERT(min <= max);
512    SkASSERT(rad <= max - min);
513    SkASSERT(min + rad <= max);
514    SkASSERT(max - rad >= min);
515}
516
517void SkRRect::validate() const {
518    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
519    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
520    bool allRadiiSame = true;
521
522    for (int i = 1; i < 4; ++i) {
523        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
524            allRadiiZero = false;
525        }
526
527        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
528            allRadiiSame = false;
529        }
530
531        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
532            allCornersSquare = false;
533        }
534    }
535    bool patchesOfNine = radii_are_nine_patch(fRadii);
536
537    switch (fType) {
538        case kEmpty_Type:
539            SkASSERT(fRect.isEmpty());
540            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
541            break;
542        case kRect_Type:
543            SkASSERT(!fRect.isEmpty());
544            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
545            break;
546        case kOval_Type:
547            SkASSERT(!fRect.isEmpty());
548            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
549
550            for (int i = 0; i < 4; ++i) {
551                SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
552                SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
553            }
554            break;
555        case kSimple_Type:
556            SkASSERT(!fRect.isEmpty());
557            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
558            break;
559        case kNinePatch_Type:
560            SkASSERT(!fRect.isEmpty());
561            SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
562            SkASSERT(patchesOfNine);
563            break;
564        case kComplex_Type:
565            SkASSERT(!fRect.isEmpty());
566            SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
567            SkASSERT(!patchesOfNine);
568            break;
569    }
570
571    for (int i = 0; i < 4; ++i) {
572        validate_radius_check_predicates(fRadii[i].fX, fRect.fLeft, fRect.fRight);
573        validate_radius_check_predicates(fRadii[i].fY, fRect.fTop, fRect.fBottom);
574    }
575}
576#endif // SK_DEBUG
577
578///////////////////////////////////////////////////////////////////////////////
579