xref: /third_party/skia/src/core/SkRRect.cpp (revision cb93a386)
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
21void 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
43void 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
79void 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.
140static 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
147static 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
167void 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
192bool 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//
210static 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
220bool 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.
267bool 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
320bool 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
331bool 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
338bool 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
359static 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
367void 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
421bool 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
547void 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
584size_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
590void 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
595size_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
608bool 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
620SkString 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
639void SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); }
640
641void SkRRect::dump(std::string& desc, int depth) const {
642    std::string split(depth, '\t');
643    desc += split + "\n SkRRect:{ \n";
644
645    fRect.dump(desc, depth + 1);
646    desc += split + "\t const SkPoint corners[] = {\n";
647    for (int i = 0; i < 4; ++i) {
648        fRadii[i].dump(desc, depth + 1);
649    }
650    desc += split + "\t}\n";
651    desc += split + "\t fType:" + std::to_string(fType) + "\n";
652    desc += split + "}\n";
653}
654
655///////////////////////////////////////////////////////////////////////////////
656
657/**
658 *  We need all combinations of predicates to be true to have a "safe" radius value.
659 */
660static bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
661    return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
662           rad >= 0;
663}
664
665bool SkRRect::isValid() const {
666    if (!AreRectAndRadiiValid(fRect, fRadii)) {
667        return false;
668    }
669
670    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
671    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
672    bool allRadiiSame = true;
673
674    for (int i = 1; i < 4; ++i) {
675        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
676            allRadiiZero = false;
677        }
678
679        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
680            allRadiiSame = false;
681        }
682
683        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
684            allCornersSquare = false;
685        }
686    }
687    bool patchesOfNine = radii_are_nine_patch(fRadii);
688
689    if (fType < 0 || fType > kLastType) {
690        return false;
691    }
692
693    switch (fType) {
694        case kEmpty_Type:
695            if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
696                return false;
697            }
698            break;
699        case kRect_Type:
700            if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
701                return false;
702            }
703            break;
704        case kOval_Type:
705            if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
706                return false;
707            }
708
709            for (int i = 0; i < 4; ++i) {
710                if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
711                    !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
712                    return false;
713                }
714            }
715            break;
716        case kSimple_Type:
717            if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
718                return false;
719            }
720            break;
721        case kNinePatch_Type:
722            if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
723                !patchesOfNine) {
724                return false;
725            }
726            break;
727        case kComplex_Type:
728            if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
729                patchesOfNine) {
730                return false;
731            }
732            break;
733    }
734
735    return true;
736}
737
738bool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
739    if (!rect.isFinite() || !rect.isSorted()) {
740        return false;
741    }
742    for (int i = 0; i < 4; ++i) {
743        if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
744            !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
745            return false;
746        }
747    }
748    return true;
749}
750///////////////////////////////////////////////////////////////////////////////
751
752SkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
753    if (rr.isEmpty() || rr.isRect()) {
754        return rr.rect();
755    }
756
757    // We start with the outer bounds of the round rect and consider three subsets and take the
758    // one with maximum area. The first two are the horizontal and vertical rects inset from the
759    // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
760    // the exact solution when all corners have the same radii (the radii do not have to be
761    // circular).
762    SkRect innerBounds = rr.getBounds();
763    SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
764    SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
765    SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
766    SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
767
768    // Select maximum inset per edge, which may move an adjacent corner of the inscribed
769    // rectangle off of the rounded-rect path, but that is acceptable given that the general
770    // equation for inscribed area is non-trivial to evaluate.
771    SkScalar leftShift   = std::max(tl.fX, bl.fX);
772    SkScalar topShift    = std::max(tl.fY, tr.fY);
773    SkScalar rightShift  = std::max(tr.fX, br.fX);
774    SkScalar bottomShift = std::max(bl.fY, br.fY);
775
776    SkScalar dw = leftShift + rightShift;
777    SkScalar dh = topShift + bottomShift;
778
779    // Area removed by shifting left/right
780    SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
781    // And by shifting top/bottom
782    SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
783    // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
784    // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
785    // safe shift per edge (since the shifts already are the max radius for that edge).
786    // - We actually scale by a value slightly increased to make it so that the shifted corners are
787    //   safely inside the curves, otherwise numerical stability can cause it to fail contains().
788    static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
789    SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
790
791    if (horizArea > vertArea && horizArea > innerArea) {
792        // Cut off corners by insetting left and right
793        innerBounds.fLeft += leftShift;
794        innerBounds.fRight -= rightShift;
795    } else if (vertArea > innerArea) {
796        // Cut off corners by insetting top and bottom
797        innerBounds.fTop += topShift;
798        innerBounds.fBottom -= bottomShift;
799    } else if (innerArea > 0.f) {
800        // Inset on all sides, scaled to touch
801        innerBounds.fLeft += kScale * leftShift;
802        innerBounds.fRight -= kScale * rightShift;
803        innerBounds.fTop += kScale * topShift;
804        innerBounds.fBottom -= kScale * bottomShift;
805    } else {
806        // Inner region would collapse to empty
807        return SkRect::MakeEmpty();
808    }
809
810    SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
811    return innerBounds;
812}
813
814SkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
815    // Returns the coordinate of the rect matching the corner enum.
816    auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
817        switch(corner) {
818            case SkRRect::kUpperLeft_Corner:  return {r.fLeft, r.fTop};
819            case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
820            case SkRRect::kLowerLeft_Corner:  return {r.fLeft, r.fBottom};
821            case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
822            default: SkUNREACHABLE;
823        }
824    };
825    // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
826    // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
827    // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
828    auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
829        switch(corner) {
830            case SkRRect::kUpperLeft_Corner:  return a.fX >= b.fX && a.fY >= b.fY;
831            case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
832            case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
833            case SkRRect::kLowerLeft_Corner:  return a.fX >= b.fX && a.fY <= b.fY;
834            default:  SkUNREACHABLE;
835        }
836    };
837
838    auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
839        SkPoint test = getCorner(r, corner);
840        SkPoint aCorner = getCorner(a.rect(), corner);
841        SkPoint bCorner = getCorner(b.rect(), corner);
842
843        if (test == aCorner && test == bCorner) {
844            // The round rects share a corner anchor, so pick A or B such that its X and Y radii
845            // are both larger than the other rrect's, or return false if neither A or B has the max
846            // corner radii (this is more permissive than the single corner tests below).
847            SkVector aRadii = a.radii(corner);
848            SkVector bRadii = b.radii(corner);
849            if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
850                *radii = aRadii;
851                return true;
852            } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
853                *radii = bRadii;
854                return true;
855            } else {
856                return false;
857            }
858        } else if (test == aCorner) {
859            // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
860            // so we resrict it to when the corners have the same radii. If not, we use the more
861            // conservative test that the extreme point of A's bounding box is contained in B.
862            *radii = a.radii(corner);
863            if (*radii == b.radii(corner)) {
864                return insideCorner(corner, aCorner, bCorner); // A inside B
865            } else {
866                return b.checkCornerContainment(aCorner.fX, aCorner.fY);
867            }
868        } else if (test == bCorner) {
869            // Mirror of the above
870            *radii = b.radii(corner);
871            if (*radii == a.radii(corner)) {
872                return insideCorner(corner, bCorner, aCorner); // B inside A
873            } else {
874                return a.checkCornerContainment(bCorner.fX, bCorner.fY);
875            }
876        } else {
877            // This is a corner formed by two straight edges of A and B, so confirm that it is
878            // contained in both (if not, then the intersection can't be a round rect).
879            *radii = {0.f, 0.f};
880            return a.checkCornerContainment(test.fX, test.fY) &&
881                   b.checkCornerContainment(test.fX, test.fY);
882        }
883    };
884
885    // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
886    // valid existing SkRRects, we know we are finite.
887    SkRRect intersection;
888    if (!intersection.fRect.intersect(a.rect(), b.rect())) {
889        // Definitely no intersection
890        return SkRRect::MakeEmpty();
891    }
892
893    const SkRRect::Corner corners[] = {
894        SkRRect::kUpperLeft_Corner,
895        SkRRect::kUpperRight_Corner,
896        SkRRect::kLowerRight_Corner,
897        SkRRect::kLowerLeft_Corner
898    };
899    // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
900    // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
901    // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
902    // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
903    // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
904    // intersection shape.
905    for (auto c : corners) {
906        if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
907            return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
908        }
909    }
910
911    // Check for radius overlap along the four edges, since the earlier evaluation was only a
912    // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
913    // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
914    // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
915    // the intersection shape is definitively not a round rect.
916    if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
917        intersection.scaleRadii()) {
918        return SkRRect::MakeEmpty();
919    }
920
921    // The intersection is an rrect of the given radii. Potentially all 4 corners could have
922    // been simplified to (0,0) radii, making the intersection a rectangle.
923    intersection.computeType();
924    return intersection;
925}
926