1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h"
9cb93a386Sopenharmony_ci#include "include/core/SkString.h"
10cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h"
11cb93a386Sopenharmony_ci#include "src/core/SkBuffer.h"
12cb93a386Sopenharmony_ci#include "src/core/SkRRectPriv.h"
13cb93a386Sopenharmony_ci#include "src/core/SkRectPriv.h"
14cb93a386Sopenharmony_ci#include "src/core/SkScaleToSides.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci#include <cmath>
17cb93a386Sopenharmony_ci#include <utility>
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_civoid SkRRect::setOval(const SkRect& oval) {
22cb93a386Sopenharmony_ci    if (!this->initializeRect(oval)) {
23cb93a386Sopenharmony_ci        return;
24cb93a386Sopenharmony_ci    }
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci    SkScalar xRad = SkRectPriv::HalfWidth(fRect);
27cb93a386Sopenharmony_ci    SkScalar yRad = SkRectPriv::HalfHeight(fRect);
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci    if (xRad == 0.0f || yRad == 0.0f) {
30cb93a386Sopenharmony_ci        // All the corners will be square
31cb93a386Sopenharmony_ci        memset(fRadii, 0, sizeof(fRadii));
32cb93a386Sopenharmony_ci        fType = kRect_Type;
33cb93a386Sopenharmony_ci    } else {
34cb93a386Sopenharmony_ci        for (int i = 0; i < 4; ++i) {
35cb93a386Sopenharmony_ci            fRadii[i].set(xRad, yRad);
36cb93a386Sopenharmony_ci        }
37cb93a386Sopenharmony_ci        fType = kOval_Type;
38cb93a386Sopenharmony_ci    }
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_ci    SkASSERT(this->isValid());
41cb93a386Sopenharmony_ci}
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_civoid SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
44cb93a386Sopenharmony_ci    if (!this->initializeRect(rect)) {
45cb93a386Sopenharmony_ci        return;
46cb93a386Sopenharmony_ci    }
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci    if (!SkScalarsAreFinite(xRad, yRad)) {
49cb93a386Sopenharmony_ci        xRad = yRad = 0;    // devolve into a simple rect
50cb93a386Sopenharmony_ci    }
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci    if (fRect.width() < xRad+xRad || fRect.height() < yRad+yRad) {
53cb93a386Sopenharmony_ci        // At most one of these two divides will be by zero, and neither numerator is zero.
54cb93a386Sopenharmony_ci        SkScalar scale = std::min(sk_ieee_float_divide(fRect. width(), xRad + xRad),
55cb93a386Sopenharmony_ci                                     sk_ieee_float_divide(fRect.height(), yRad + yRad));
56cb93a386Sopenharmony_ci        SkASSERT(scale < SK_Scalar1);
57cb93a386Sopenharmony_ci        xRad *= scale;
58cb93a386Sopenharmony_ci        yRad *= scale;
59cb93a386Sopenharmony_ci    }
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    if (xRad <= 0 || yRad <= 0) {
62cb93a386Sopenharmony_ci        // all corners are square in this case
63cb93a386Sopenharmony_ci        this->setRect(rect);
64cb93a386Sopenharmony_ci        return;
65cb93a386Sopenharmony_ci    }
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
68cb93a386Sopenharmony_ci        fRadii[i].set(xRad, yRad);
69cb93a386Sopenharmony_ci    }
70cb93a386Sopenharmony_ci    fType = kSimple_Type;
71cb93a386Sopenharmony_ci    if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
72cb93a386Sopenharmony_ci        fType = kOval_Type;
73cb93a386Sopenharmony_ci        // TODO: assert that all the x&y radii are already W/2 & H/2
74cb93a386Sopenharmony_ci    }
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci    SkASSERT(this->isValid());
77cb93a386Sopenharmony_ci}
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_civoid SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad,
80cb93a386Sopenharmony_ci                           SkScalar rightRad, SkScalar bottomRad) {
81cb93a386Sopenharmony_ci    if (!this->initializeRect(rect)) {
82cb93a386Sopenharmony_ci        return;
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_ci    const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad };
86cb93a386Sopenharmony_ci    if (!SkScalarsAreFinite(array, 4)) {
87cb93a386Sopenharmony_ci        this->setRect(rect);    // devolve into a simple rect
88cb93a386Sopenharmony_ci        return;
89cb93a386Sopenharmony_ci    }
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_ci    leftRad = std::max(leftRad, 0.0f);
92cb93a386Sopenharmony_ci    topRad = std::max(topRad, 0.0f);
93cb93a386Sopenharmony_ci    rightRad = std::max(rightRad, 0.0f);
94cb93a386Sopenharmony_ci    bottomRad = std::max(bottomRad, 0.0f);
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci    SkScalar scale = SK_Scalar1;
97cb93a386Sopenharmony_ci    if (leftRad + rightRad > fRect.width()) {
98cb93a386Sopenharmony_ci        scale = fRect.width() / (leftRad + rightRad);
99cb93a386Sopenharmony_ci    }
100cb93a386Sopenharmony_ci    if (topRad + bottomRad > fRect.height()) {
101cb93a386Sopenharmony_ci        scale = std::min(scale, fRect.height() / (topRad + bottomRad));
102cb93a386Sopenharmony_ci    }
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ci    if (scale < SK_Scalar1) {
105cb93a386Sopenharmony_ci        leftRad *= scale;
106cb93a386Sopenharmony_ci        topRad *= scale;
107cb93a386Sopenharmony_ci        rightRad *= scale;
108cb93a386Sopenharmony_ci        bottomRad *= scale;
109cb93a386Sopenharmony_ci    }
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci    if (leftRad == rightRad && topRad == bottomRad) {
112cb93a386Sopenharmony_ci        if (leftRad >= SkScalarHalf(fRect.width()) && topRad >= SkScalarHalf(fRect.height())) {
113cb93a386Sopenharmony_ci            fType = kOval_Type;
114cb93a386Sopenharmony_ci        } else if (0 == leftRad || 0 == topRad) {
115cb93a386Sopenharmony_ci            // If the left and (by equality check above) right radii are zero then it is a rect.
116cb93a386Sopenharmony_ci            // Same goes for top/bottom.
117cb93a386Sopenharmony_ci            fType = kRect_Type;
118cb93a386Sopenharmony_ci            leftRad = 0;
119cb93a386Sopenharmony_ci            topRad = 0;
120cb93a386Sopenharmony_ci            rightRad = 0;
121cb93a386Sopenharmony_ci            bottomRad = 0;
122cb93a386Sopenharmony_ci        } else {
123cb93a386Sopenharmony_ci            fType = kSimple_Type;
124cb93a386Sopenharmony_ci        }
125cb93a386Sopenharmony_ci    } else {
126cb93a386Sopenharmony_ci        fType = kNinePatch_Type;
127cb93a386Sopenharmony_ci    }
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci    fRadii[kUpperLeft_Corner].set(leftRad, topRad);
130cb93a386Sopenharmony_ci    fRadii[kUpperRight_Corner].set(rightRad, topRad);
131cb93a386Sopenharmony_ci    fRadii[kLowerRight_Corner].set(rightRad, bottomRad);
132cb93a386Sopenharmony_ci    fRadii[kLowerLeft_Corner].set(leftRad, bottomRad);
133cb93a386Sopenharmony_ci
134cb93a386Sopenharmony_ci    SkASSERT(this->isValid());
135cb93a386Sopenharmony_ci}
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci// These parameters intentionally double. Apropos crbug.com/463920, if one of the
138cb93a386Sopenharmony_ci// radii is huge while the other is small, single precision math can completely
139cb93a386Sopenharmony_ci// miss the fact that a scale is required.
140cb93a386Sopenharmony_cistatic double compute_min_scale(double rad1, double rad2, double limit, double curMin) {
141cb93a386Sopenharmony_ci    if ((rad1 + rad2) > limit) {
142cb93a386Sopenharmony_ci        return std::min(curMin, limit / (rad1 + rad2));
143cb93a386Sopenharmony_ci    }
144cb93a386Sopenharmony_ci    return curMin;
145cb93a386Sopenharmony_ci}
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_cistatic bool clamp_to_zero(SkVector radii[4]) {
148cb93a386Sopenharmony_ci    bool allCornersSquare = true;
149cb93a386Sopenharmony_ci
150cb93a386Sopenharmony_ci    // Clamp negative radii to zero
151cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
152cb93a386Sopenharmony_ci        if (radii[i].fX <= 0 || radii[i].fY <= 0) {
153cb93a386Sopenharmony_ci            // In this case we are being a little fast & loose. Since one of
154cb93a386Sopenharmony_ci            // the radii is 0 the corner is square. However, the other radii
155cb93a386Sopenharmony_ci            // could still be non-zero and play in the global scale factor
156cb93a386Sopenharmony_ci            // computation.
157cb93a386Sopenharmony_ci            radii[i].fX = 0;
158cb93a386Sopenharmony_ci            radii[i].fY = 0;
159cb93a386Sopenharmony_ci        } else {
160cb93a386Sopenharmony_ci            allCornersSquare = false;
161cb93a386Sopenharmony_ci        }
162cb93a386Sopenharmony_ci    }
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci    return allCornersSquare;
165cb93a386Sopenharmony_ci}
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_civoid SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
168cb93a386Sopenharmony_ci    if (!this->initializeRect(rect)) {
169cb93a386Sopenharmony_ci        return;
170cb93a386Sopenharmony_ci    }
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci    if (!SkScalarsAreFinite(&radii[0].fX, 8)) {
173cb93a386Sopenharmony_ci        this->setRect(rect);    // devolve into a simple rect
174cb93a386Sopenharmony_ci        return;
175cb93a386Sopenharmony_ci    }
176cb93a386Sopenharmony_ci
177cb93a386Sopenharmony_ci    memcpy(fRadii, radii, sizeof(fRadii));
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci    if (clamp_to_zero(fRadii)) {
180cb93a386Sopenharmony_ci        this->setRect(rect);
181cb93a386Sopenharmony_ci        return;
182cb93a386Sopenharmony_ci    }
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci    this->scaleRadii();
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_ci    if (!this->isValid()) {
187cb93a386Sopenharmony_ci        this->setRect(rect);
188cb93a386Sopenharmony_ci        return;
189cb93a386Sopenharmony_ci    }
190cb93a386Sopenharmony_ci}
191cb93a386Sopenharmony_ci
192cb93a386Sopenharmony_cibool SkRRect::initializeRect(const SkRect& rect) {
193cb93a386Sopenharmony_ci    // Check this before sorting because sorting can hide nans.
194cb93a386Sopenharmony_ci    if (!rect.isFinite()) {
195cb93a386Sopenharmony_ci        *this = SkRRect();
196cb93a386Sopenharmony_ci        return false;
197cb93a386Sopenharmony_ci    }
198cb93a386Sopenharmony_ci    fRect = rect.makeSorted();
199cb93a386Sopenharmony_ci    if (fRect.isEmpty()) {
200cb93a386Sopenharmony_ci        memset(fRadii, 0, sizeof(fRadii));
201cb93a386Sopenharmony_ci        fType = kEmpty_Type;
202cb93a386Sopenharmony_ci        return false;
203cb93a386Sopenharmony_ci    }
204cb93a386Sopenharmony_ci    return true;
205cb93a386Sopenharmony_ci}
206cb93a386Sopenharmony_ci
207cb93a386Sopenharmony_ci// If we can't distinguish one of the radii relative to the other, force it to zero so it
208cb93a386Sopenharmony_ci// doesn't confuse us later. See crbug.com/850350
209cb93a386Sopenharmony_ci//
210cb93a386Sopenharmony_cistatic void flush_to_zero(SkScalar& a, SkScalar& b) {
211cb93a386Sopenharmony_ci    SkASSERT(a >= 0);
212cb93a386Sopenharmony_ci    SkASSERT(b >= 0);
213cb93a386Sopenharmony_ci    if (a + b == a) {
214cb93a386Sopenharmony_ci        b = 0;
215cb93a386Sopenharmony_ci    } else if (a + b == b) {
216cb93a386Sopenharmony_ci        a = 0;
217cb93a386Sopenharmony_ci    }
218cb93a386Sopenharmony_ci}
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_cibool SkRRect::scaleRadii() {
221cb93a386Sopenharmony_ci    // Proportionally scale down all radii to fit. Find the minimum ratio
222cb93a386Sopenharmony_ci    // of a side and the radii on that side (for all four sides) and use
223cb93a386Sopenharmony_ci    // that to scale down _all_ the radii. This algorithm is from the
224cb93a386Sopenharmony_ci    // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
225cb93a386Sopenharmony_ci    // Curves:
226cb93a386Sopenharmony_ci    // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
227cb93a386Sopenharmony_ci    //   Si is the sum of the two corresponding radii of the corners on side i,
228cb93a386Sopenharmony_ci    //   and Ltop = Lbottom = the width of the box,
229cb93a386Sopenharmony_ci    //   and Lleft = Lright = the height of the box.
230cb93a386Sopenharmony_ci    // If f < 1, then all corner radii are reduced by multiplying them by f."
231cb93a386Sopenharmony_ci    double scale = 1.0;
232cb93a386Sopenharmony_ci
233cb93a386Sopenharmony_ci    // The sides of the rectangle may be larger than a float.
234cb93a386Sopenharmony_ci    double width = (double)fRect.fRight - (double)fRect.fLeft;
235cb93a386Sopenharmony_ci    double height = (double)fRect.fBottom - (double)fRect.fTop;
236cb93a386Sopenharmony_ci    scale = compute_min_scale(fRadii[0].fX, fRadii[1].fX, width,  scale);
237cb93a386Sopenharmony_ci    scale = compute_min_scale(fRadii[1].fY, fRadii[2].fY, height, scale);
238cb93a386Sopenharmony_ci    scale = compute_min_scale(fRadii[2].fX, fRadii[3].fX, width,  scale);
239cb93a386Sopenharmony_ci    scale = compute_min_scale(fRadii[3].fY, fRadii[0].fY, height, scale);
240cb93a386Sopenharmony_ci
241cb93a386Sopenharmony_ci    flush_to_zero(fRadii[0].fX, fRadii[1].fX);
242cb93a386Sopenharmony_ci    flush_to_zero(fRadii[1].fY, fRadii[2].fY);
243cb93a386Sopenharmony_ci    flush_to_zero(fRadii[2].fX, fRadii[3].fX);
244cb93a386Sopenharmony_ci    flush_to_zero(fRadii[3].fY, fRadii[0].fY);
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci    if (scale < 1.0) {
247cb93a386Sopenharmony_ci        SkScaleToSides::AdjustRadii(width,  scale, &fRadii[0].fX, &fRadii[1].fX);
248cb93a386Sopenharmony_ci        SkScaleToSides::AdjustRadii(height, scale, &fRadii[1].fY, &fRadii[2].fY);
249cb93a386Sopenharmony_ci        SkScaleToSides::AdjustRadii(width,  scale, &fRadii[2].fX, &fRadii[3].fX);
250cb93a386Sopenharmony_ci        SkScaleToSides::AdjustRadii(height, scale, &fRadii[3].fY, &fRadii[0].fY);
251cb93a386Sopenharmony_ci    }
252cb93a386Sopenharmony_ci
253cb93a386Sopenharmony_ci    // adjust radii may set x or y to zero; set companion to zero as well
254cb93a386Sopenharmony_ci    clamp_to_zero(fRadii);
255cb93a386Sopenharmony_ci
256cb93a386Sopenharmony_ci    // May be simple, oval, or complex, or become a rect/empty if the radii adjustment made them 0
257cb93a386Sopenharmony_ci    this->computeType();
258cb93a386Sopenharmony_ci
259cb93a386Sopenharmony_ci    // TODO:  Why can't we assert this here?
260cb93a386Sopenharmony_ci    //SkASSERT(this->isValid());
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_ci    return scale < 1.0;
263cb93a386Sopenharmony_ci}
264cb93a386Sopenharmony_ci
265cb93a386Sopenharmony_ci// This method determines if a point known to be inside the RRect's bounds is
266cb93a386Sopenharmony_ci// inside all the corners.
267cb93a386Sopenharmony_cibool SkRRect::checkCornerContainment(SkScalar x, SkScalar y) const {
268cb93a386Sopenharmony_ci    SkPoint canonicalPt; // (x,y) translated to one of the quadrants
269cb93a386Sopenharmony_ci    int index;
270cb93a386Sopenharmony_ci
271cb93a386Sopenharmony_ci    if (kOval_Type == this->type()) {
272cb93a386Sopenharmony_ci        canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
273cb93a386Sopenharmony_ci        index = kUpperLeft_Corner;  // any corner will do in this case
274cb93a386Sopenharmony_ci    } else {
275cb93a386Sopenharmony_ci        if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
276cb93a386Sopenharmony_ci            y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
277cb93a386Sopenharmony_ci            // UL corner
278cb93a386Sopenharmony_ci            index = kUpperLeft_Corner;
279cb93a386Sopenharmony_ci            canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
280cb93a386Sopenharmony_ci                            y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
281cb93a386Sopenharmony_ci            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
282cb93a386Sopenharmony_ci        } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
283cb93a386Sopenharmony_ci                   y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
284cb93a386Sopenharmony_ci            // LL corner
285cb93a386Sopenharmony_ci            index = kLowerLeft_Corner;
286cb93a386Sopenharmony_ci            canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
287cb93a386Sopenharmony_ci                            y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
288cb93a386Sopenharmony_ci            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
289cb93a386Sopenharmony_ci        } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
290cb93a386Sopenharmony_ci                   y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
291cb93a386Sopenharmony_ci            // UR corner
292cb93a386Sopenharmony_ci            index = kUpperRight_Corner;
293cb93a386Sopenharmony_ci            canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
294cb93a386Sopenharmony_ci                            y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
295cb93a386Sopenharmony_ci            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
296cb93a386Sopenharmony_ci        } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
297cb93a386Sopenharmony_ci                   y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
298cb93a386Sopenharmony_ci            // LR corner
299cb93a386Sopenharmony_ci            index = kLowerRight_Corner;
300cb93a386Sopenharmony_ci            canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
301cb93a386Sopenharmony_ci                            y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
302cb93a386Sopenharmony_ci            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
303cb93a386Sopenharmony_ci        } else {
304cb93a386Sopenharmony_ci            // not in any of the corners
305cb93a386Sopenharmony_ci            return true;
306cb93a386Sopenharmony_ci        }
307cb93a386Sopenharmony_ci    }
308cb93a386Sopenharmony_ci
309cb93a386Sopenharmony_ci    // A point is in an ellipse (in standard position) if:
310cb93a386Sopenharmony_ci    //      x^2     y^2
311cb93a386Sopenharmony_ci    //     ----- + ----- <= 1
312cb93a386Sopenharmony_ci    //      a^2     b^2
313cb93a386Sopenharmony_ci    // or :
314cb93a386Sopenharmony_ci    //     b^2*x^2 + a^2*y^2 <= (ab)^2
315cb93a386Sopenharmony_ci    SkScalar dist =  SkScalarSquare(canonicalPt.fX) * SkScalarSquare(fRadii[index].fY) +
316cb93a386Sopenharmony_ci                     SkScalarSquare(canonicalPt.fY) * SkScalarSquare(fRadii[index].fX);
317cb93a386Sopenharmony_ci    return dist <= SkScalarSquare(fRadii[index].fX * fRadii[index].fY);
318cb93a386Sopenharmony_ci}
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_cibool SkRRectPriv::IsNearlySimpleCircular(const SkRRect& rr, SkScalar tolerance) {
321cb93a386Sopenharmony_ci    SkScalar simpleRadius = rr.fRadii[0].fX;
322cb93a386Sopenharmony_ci    return SkScalarNearlyEqual(simpleRadius, rr.fRadii[0].fY, tolerance) &&
323cb93a386Sopenharmony_ci           SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fX, tolerance) &&
324cb93a386Sopenharmony_ci           SkScalarNearlyEqual(simpleRadius, rr.fRadii[1].fY, tolerance) &&
325cb93a386Sopenharmony_ci           SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fX, tolerance) &&
326cb93a386Sopenharmony_ci           SkScalarNearlyEqual(simpleRadius, rr.fRadii[2].fY, tolerance) &&
327cb93a386Sopenharmony_ci           SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fX, tolerance) &&
328cb93a386Sopenharmony_ci           SkScalarNearlyEqual(simpleRadius, rr.fRadii[3].fY, tolerance);
329cb93a386Sopenharmony_ci}
330cb93a386Sopenharmony_ci
331cb93a386Sopenharmony_cibool SkRRectPriv::AllCornersCircular(const SkRRect& rr, SkScalar tolerance) {
332cb93a386Sopenharmony_ci    return SkScalarNearlyEqual(rr.fRadii[0].fX, rr.fRadii[0].fY, tolerance) &&
333cb93a386Sopenharmony_ci           SkScalarNearlyEqual(rr.fRadii[1].fX, rr.fRadii[1].fY, tolerance) &&
334cb93a386Sopenharmony_ci           SkScalarNearlyEqual(rr.fRadii[2].fX, rr.fRadii[2].fY, tolerance) &&
335cb93a386Sopenharmony_ci           SkScalarNearlyEqual(rr.fRadii[3].fX, rr.fRadii[3].fY, tolerance);
336cb93a386Sopenharmony_ci}
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_cibool SkRRect::contains(const SkRect& rect) const {
339cb93a386Sopenharmony_ci    if (!this->getBounds().contains(rect)) {
340cb93a386Sopenharmony_ci        // If 'rect' isn't contained by the RR's bounds then the
341cb93a386Sopenharmony_ci        // RR definitely doesn't contain it
342cb93a386Sopenharmony_ci        return false;
343cb93a386Sopenharmony_ci    }
344cb93a386Sopenharmony_ci
345cb93a386Sopenharmony_ci    if (this->isRect()) {
346cb93a386Sopenharmony_ci        // the prior test was sufficient
347cb93a386Sopenharmony_ci        return true;
348cb93a386Sopenharmony_ci    }
349cb93a386Sopenharmony_ci
350cb93a386Sopenharmony_ci    // At this point we know all four corners of 'rect' are inside the
351cb93a386Sopenharmony_ci    // bounds of of this RR. Check to make sure all the corners are inside
352cb93a386Sopenharmony_ci    // all the curves
353cb93a386Sopenharmony_ci    return this->checkCornerContainment(rect.fLeft, rect.fTop) &&
354cb93a386Sopenharmony_ci           this->checkCornerContainment(rect.fRight, rect.fTop) &&
355cb93a386Sopenharmony_ci           this->checkCornerContainment(rect.fRight, rect.fBottom) &&
356cb93a386Sopenharmony_ci           this->checkCornerContainment(rect.fLeft, rect.fBottom);
357cb93a386Sopenharmony_ci}
358cb93a386Sopenharmony_ci
359cb93a386Sopenharmony_cistatic bool radii_are_nine_patch(const SkVector radii[4]) {
360cb93a386Sopenharmony_ci    return radii[SkRRect::kUpperLeft_Corner].fX == radii[SkRRect::kLowerLeft_Corner].fX &&
361cb93a386Sopenharmony_ci           radii[SkRRect::kUpperLeft_Corner].fY == radii[SkRRect::kUpperRight_Corner].fY &&
362cb93a386Sopenharmony_ci           radii[SkRRect::kUpperRight_Corner].fX == radii[SkRRect::kLowerRight_Corner].fX &&
363cb93a386Sopenharmony_ci           radii[SkRRect::kLowerLeft_Corner].fY == radii[SkRRect::kLowerRight_Corner].fY;
364cb93a386Sopenharmony_ci}
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_ci// There is a simplified version of this method in setRectXY
367cb93a386Sopenharmony_civoid SkRRect::computeType() {
368cb93a386Sopenharmony_ci    if (fRect.isEmpty()) {
369cb93a386Sopenharmony_ci        SkASSERT(fRect.isSorted());
370cb93a386Sopenharmony_ci        for (size_t i = 0; i < SK_ARRAY_COUNT(fRadii); ++i) {
371cb93a386Sopenharmony_ci            SkASSERT((fRadii[i] == SkVector{0, 0}));
372cb93a386Sopenharmony_ci        }
373cb93a386Sopenharmony_ci        fType = kEmpty_Type;
374cb93a386Sopenharmony_ci        SkASSERT(this->isValid());
375cb93a386Sopenharmony_ci        return;
376cb93a386Sopenharmony_ci    }
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_ci    bool allRadiiEqual = true; // are all x radii equal and all y radii?
379cb93a386Sopenharmony_ci    bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
380cb93a386Sopenharmony_ci
381cb93a386Sopenharmony_ci    for (int i = 1; i < 4; ++i) {
382cb93a386Sopenharmony_ci        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
383cb93a386Sopenharmony_ci            // if either radius is zero the corner is square so both have to
384cb93a386Sopenharmony_ci            // be non-zero to have a rounded corner
385cb93a386Sopenharmony_ci            allCornersSquare = false;
386cb93a386Sopenharmony_ci        }
387cb93a386Sopenharmony_ci        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
388cb93a386Sopenharmony_ci            allRadiiEqual = false;
389cb93a386Sopenharmony_ci        }
390cb93a386Sopenharmony_ci    }
391cb93a386Sopenharmony_ci
392cb93a386Sopenharmony_ci    if (allCornersSquare) {
393cb93a386Sopenharmony_ci        fType = kRect_Type;
394cb93a386Sopenharmony_ci        SkASSERT(this->isValid());
395cb93a386Sopenharmony_ci        return;
396cb93a386Sopenharmony_ci    }
397cb93a386Sopenharmony_ci
398cb93a386Sopenharmony_ci    if (allRadiiEqual) {
399cb93a386Sopenharmony_ci        if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
400cb93a386Sopenharmony_ci            fRadii[0].fY >= SkScalarHalf(fRect.height())) {
401cb93a386Sopenharmony_ci            fType = kOval_Type;
402cb93a386Sopenharmony_ci        } else {
403cb93a386Sopenharmony_ci            fType = kSimple_Type;
404cb93a386Sopenharmony_ci        }
405cb93a386Sopenharmony_ci        SkASSERT(this->isValid());
406cb93a386Sopenharmony_ci        return;
407cb93a386Sopenharmony_ci    }
408cb93a386Sopenharmony_ci
409cb93a386Sopenharmony_ci    if (radii_are_nine_patch(fRadii)) {
410cb93a386Sopenharmony_ci        fType = kNinePatch_Type;
411cb93a386Sopenharmony_ci    } else {
412cb93a386Sopenharmony_ci        fType = kComplex_Type;
413cb93a386Sopenharmony_ci    }
414cb93a386Sopenharmony_ci
415cb93a386Sopenharmony_ci    if (!this->isValid()) {
416cb93a386Sopenharmony_ci        this->setRect(this->rect());
417cb93a386Sopenharmony_ci        SkASSERT(this->isValid());
418cb93a386Sopenharmony_ci    }
419cb93a386Sopenharmony_ci}
420cb93a386Sopenharmony_ci
421cb93a386Sopenharmony_cibool SkRRect::transform(const SkMatrix& matrix, SkRRect* dst) const {
422cb93a386Sopenharmony_ci    if (nullptr == dst) {
423cb93a386Sopenharmony_ci        return false;
424cb93a386Sopenharmony_ci    }
425cb93a386Sopenharmony_ci
426cb93a386Sopenharmony_ci    // Assert that the caller is not trying to do this in place, which
427cb93a386Sopenharmony_ci    // would violate const-ness. Do not return false though, so that
428cb93a386Sopenharmony_ci    // if they know what they're doing and want to violate it they can.
429cb93a386Sopenharmony_ci    SkASSERT(dst != this);
430cb93a386Sopenharmony_ci
431cb93a386Sopenharmony_ci    if (matrix.isIdentity()) {
432cb93a386Sopenharmony_ci        *dst = *this;
433cb93a386Sopenharmony_ci        return true;
434cb93a386Sopenharmony_ci    }
435cb93a386Sopenharmony_ci
436cb93a386Sopenharmony_ci    if (!matrix.preservesAxisAlignment()) {
437cb93a386Sopenharmony_ci        return false;
438cb93a386Sopenharmony_ci    }
439cb93a386Sopenharmony_ci
440cb93a386Sopenharmony_ci    SkRect newRect;
441cb93a386Sopenharmony_ci    if (!matrix.mapRect(&newRect, fRect)) {
442cb93a386Sopenharmony_ci        return false;
443cb93a386Sopenharmony_ci    }
444cb93a386Sopenharmony_ci
445cb93a386Sopenharmony_ci    // The matrix may have scaled us to zero (or due to float madness, we now have collapsed
446cb93a386Sopenharmony_ci    // some dimension of the rect, so we need to check for that. Note that matrix must be
447cb93a386Sopenharmony_ci    // scale and translate and mapRect() produces a sorted rect. So an empty rect indicates
448cb93a386Sopenharmony_ci    // loss of precision.
449cb93a386Sopenharmony_ci    if (!newRect.isFinite() || newRect.isEmpty()) {
450cb93a386Sopenharmony_ci        return false;
451cb93a386Sopenharmony_ci    }
452cb93a386Sopenharmony_ci
453cb93a386Sopenharmony_ci    // At this point, this is guaranteed to succeed, so we can modify dst.
454cb93a386Sopenharmony_ci    dst->fRect = newRect;
455cb93a386Sopenharmony_ci
456cb93a386Sopenharmony_ci    // Since the only transforms that were allowed are axis aligned, the type
457cb93a386Sopenharmony_ci    // remains unchanged.
458cb93a386Sopenharmony_ci    dst->fType = fType;
459cb93a386Sopenharmony_ci
460cb93a386Sopenharmony_ci    if (kRect_Type == fType) {
461cb93a386Sopenharmony_ci        SkASSERT(dst->isValid());
462cb93a386Sopenharmony_ci        return true;
463cb93a386Sopenharmony_ci    }
464cb93a386Sopenharmony_ci    if (kOval_Type == fType) {
465cb93a386Sopenharmony_ci        for (int i = 0; i < 4; ++i) {
466cb93a386Sopenharmony_ci            dst->fRadii[i].fX = SkScalarHalf(newRect.width());
467cb93a386Sopenharmony_ci            dst->fRadii[i].fY = SkScalarHalf(newRect.height());
468cb93a386Sopenharmony_ci        }
469cb93a386Sopenharmony_ci        SkASSERT(dst->isValid());
470cb93a386Sopenharmony_ci        return true;
471cb93a386Sopenharmony_ci    }
472cb93a386Sopenharmony_ci
473cb93a386Sopenharmony_ci    // Now scale each corner
474cb93a386Sopenharmony_ci    SkScalar xScale = matrix.getScaleX();
475cb93a386Sopenharmony_ci    SkScalar yScale = matrix.getScaleY();
476cb93a386Sopenharmony_ci
477cb93a386Sopenharmony_ci    // There is a rotation of 90 (Clockwise 90) or 270 (Counter clockwise 90).
478cb93a386Sopenharmony_ci    // 180 degrees rotations are simply flipX with a flipY and would come under
479cb93a386Sopenharmony_ci    // a scale transform.
480cb93a386Sopenharmony_ci    if (!matrix.isScaleTranslate()) {
481cb93a386Sopenharmony_ci        const bool isClockwise = matrix.getSkewX() < 0;
482cb93a386Sopenharmony_ci
483cb93a386Sopenharmony_ci        // The matrix location for scale changes if there is a rotation.
484cb93a386Sopenharmony_ci        xScale = matrix.getSkewY() * (isClockwise ? 1 : -1);
485cb93a386Sopenharmony_ci        yScale = matrix.getSkewX() * (isClockwise ? -1 : 1);
486cb93a386Sopenharmony_ci
487cb93a386Sopenharmony_ci        const int dir = isClockwise ? 3 : 1;
488cb93a386Sopenharmony_ci        for (int i = 0; i < 4; ++i) {
489cb93a386Sopenharmony_ci            const int src = (i + dir) >= 4 ? (i + dir) % 4 : (i + dir);
490cb93a386Sopenharmony_ci            // Swap X and Y axis for the radii.
491cb93a386Sopenharmony_ci            dst->fRadii[i].fX = fRadii[src].fY;
492cb93a386Sopenharmony_ci            dst->fRadii[i].fY = fRadii[src].fX;
493cb93a386Sopenharmony_ci        }
494cb93a386Sopenharmony_ci    } else {
495cb93a386Sopenharmony_ci        for (int i = 0; i < 4; ++i) {
496cb93a386Sopenharmony_ci            dst->fRadii[i].fX = fRadii[i].fX;
497cb93a386Sopenharmony_ci            dst->fRadii[i].fY = fRadii[i].fY;
498cb93a386Sopenharmony_ci        }
499cb93a386Sopenharmony_ci    }
500cb93a386Sopenharmony_ci
501cb93a386Sopenharmony_ci    const bool flipX = xScale < 0;
502cb93a386Sopenharmony_ci    if (flipX) {
503cb93a386Sopenharmony_ci        xScale = -xScale;
504cb93a386Sopenharmony_ci    }
505cb93a386Sopenharmony_ci
506cb93a386Sopenharmony_ci    const bool flipY = yScale < 0;
507cb93a386Sopenharmony_ci    if (flipY) {
508cb93a386Sopenharmony_ci        yScale = -yScale;
509cb93a386Sopenharmony_ci    }
510cb93a386Sopenharmony_ci
511cb93a386Sopenharmony_ci    // Scale the radii without respecting the flip.
512cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
513cb93a386Sopenharmony_ci        dst->fRadii[i].fX *= xScale;
514cb93a386Sopenharmony_ci        dst->fRadii[i].fY *= yScale;
515cb93a386Sopenharmony_ci    }
516cb93a386Sopenharmony_ci
517cb93a386Sopenharmony_ci    // Now swap as necessary.
518cb93a386Sopenharmony_ci    using std::swap;
519cb93a386Sopenharmony_ci    if (flipX) {
520cb93a386Sopenharmony_ci        if (flipY) {
521cb93a386Sopenharmony_ci            // Swap with opposite corners
522cb93a386Sopenharmony_ci            swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerRight_Corner]);
523cb93a386Sopenharmony_ci            swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerLeft_Corner]);
524cb93a386Sopenharmony_ci        } else {
525cb93a386Sopenharmony_ci            // Only swap in x
526cb93a386Sopenharmony_ci            swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kUpperLeft_Corner]);
527cb93a386Sopenharmony_ci            swap(dst->fRadii[kLowerRight_Corner], dst->fRadii[kLowerLeft_Corner]);
528cb93a386Sopenharmony_ci        }
529cb93a386Sopenharmony_ci    } else if (flipY) {
530cb93a386Sopenharmony_ci        // Only swap in y
531cb93a386Sopenharmony_ci        swap(dst->fRadii[kUpperLeft_Corner], dst->fRadii[kLowerLeft_Corner]);
532cb93a386Sopenharmony_ci        swap(dst->fRadii[kUpperRight_Corner], dst->fRadii[kLowerRight_Corner]);
533cb93a386Sopenharmony_ci    }
534cb93a386Sopenharmony_ci
535cb93a386Sopenharmony_ci    if (!AreRectAndRadiiValid(dst->fRect, dst->fRadii)) {
536cb93a386Sopenharmony_ci        return false;
537cb93a386Sopenharmony_ci    }
538cb93a386Sopenharmony_ci
539cb93a386Sopenharmony_ci    dst->scaleRadii();
540cb93a386Sopenharmony_ci    dst->isValid();  // TODO: is this meant to be SkASSERT(dst->isValid())?
541cb93a386Sopenharmony_ci
542cb93a386Sopenharmony_ci    return true;
543cb93a386Sopenharmony_ci}
544cb93a386Sopenharmony_ci
545cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
546cb93a386Sopenharmony_ci
547cb93a386Sopenharmony_civoid SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
548cb93a386Sopenharmony_ci    SkRect r = fRect.makeInset(dx, dy);
549cb93a386Sopenharmony_ci    bool degenerate = false;
550cb93a386Sopenharmony_ci    if (r.fRight <= r.fLeft) {
551cb93a386Sopenharmony_ci        degenerate = true;
552cb93a386Sopenharmony_ci        r.fLeft = r.fRight = SkScalarAve(r.fLeft, r.fRight);
553cb93a386Sopenharmony_ci    }
554cb93a386Sopenharmony_ci    if (r.fBottom <= r.fTop) {
555cb93a386Sopenharmony_ci        degenerate = true;
556cb93a386Sopenharmony_ci        r.fTop = r.fBottom = SkScalarAve(r.fTop, r.fBottom);
557cb93a386Sopenharmony_ci    }
558cb93a386Sopenharmony_ci    if (degenerate) {
559cb93a386Sopenharmony_ci        dst->fRect = r;
560cb93a386Sopenharmony_ci        memset(dst->fRadii, 0, sizeof(dst->fRadii));
561cb93a386Sopenharmony_ci        dst->fType = kEmpty_Type;
562cb93a386Sopenharmony_ci        return;
563cb93a386Sopenharmony_ci    }
564cb93a386Sopenharmony_ci    if (!r.isFinite()) {
565cb93a386Sopenharmony_ci        *dst = SkRRect();
566cb93a386Sopenharmony_ci        return;
567cb93a386Sopenharmony_ci    }
568cb93a386Sopenharmony_ci
569cb93a386Sopenharmony_ci    SkVector radii[4];
570cb93a386Sopenharmony_ci    memcpy(radii, fRadii, sizeof(radii));
571cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
572cb93a386Sopenharmony_ci        if (radii[i].fX) {
573cb93a386Sopenharmony_ci            radii[i].fX -= dx;
574cb93a386Sopenharmony_ci        }
575cb93a386Sopenharmony_ci        if (radii[i].fY) {
576cb93a386Sopenharmony_ci            radii[i].fY -= dy;
577cb93a386Sopenharmony_ci        }
578cb93a386Sopenharmony_ci    }
579cb93a386Sopenharmony_ci    dst->setRectRadii(r, radii);
580cb93a386Sopenharmony_ci}
581cb93a386Sopenharmony_ci
582cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
583cb93a386Sopenharmony_ci
584cb93a386Sopenharmony_cisize_t SkRRect::writeToMemory(void* buffer) const {
585cb93a386Sopenharmony_ci    // Serialize only the rect and corners, but not the derived type tag.
586cb93a386Sopenharmony_ci    memcpy(buffer, this, kSizeInMemory);
587cb93a386Sopenharmony_ci    return kSizeInMemory;
588cb93a386Sopenharmony_ci}
589cb93a386Sopenharmony_ci
590cb93a386Sopenharmony_civoid SkRRectPriv::WriteToBuffer(const SkRRect& rr, SkWBuffer* buffer) {
591cb93a386Sopenharmony_ci    // Serialize only the rect and corners, but not the derived type tag.
592cb93a386Sopenharmony_ci    buffer->write(&rr, SkRRect::kSizeInMemory);
593cb93a386Sopenharmony_ci}
594cb93a386Sopenharmony_ci
595cb93a386Sopenharmony_cisize_t SkRRect::readFromMemory(const void* buffer, size_t length) {
596cb93a386Sopenharmony_ci    if (length < kSizeInMemory) {
597cb93a386Sopenharmony_ci        return 0;
598cb93a386Sopenharmony_ci    }
599cb93a386Sopenharmony_ci
600cb93a386Sopenharmony_ci    // The extra (void*) tells GCC not to worry that kSizeInMemory < sizeof(SkRRect).
601cb93a386Sopenharmony_ci
602cb93a386Sopenharmony_ci    SkRRect raw;
603cb93a386Sopenharmony_ci    memcpy((void*)&raw, buffer, kSizeInMemory);
604cb93a386Sopenharmony_ci    this->setRectRadii(raw.fRect, raw.fRadii);
605cb93a386Sopenharmony_ci    return kSizeInMemory;
606cb93a386Sopenharmony_ci}
607cb93a386Sopenharmony_ci
608cb93a386Sopenharmony_cibool SkRRectPriv::ReadFromBuffer(SkRBuffer* buffer, SkRRect* rr) {
609cb93a386Sopenharmony_ci    if (buffer->available() < SkRRect::kSizeInMemory) {
610cb93a386Sopenharmony_ci        return false;
611cb93a386Sopenharmony_ci    }
612cb93a386Sopenharmony_ci    SkRRect storage;
613cb93a386Sopenharmony_ci    return buffer->read(&storage, SkRRect::kSizeInMemory) &&
614cb93a386Sopenharmony_ci           (rr->readFromMemory(&storage, SkRRect::kSizeInMemory) == SkRRect::kSizeInMemory);
615cb93a386Sopenharmony_ci}
616cb93a386Sopenharmony_ci
617cb93a386Sopenharmony_ci#include "include/core/SkString.h"
618cb93a386Sopenharmony_ci#include "src/core/SkStringUtils.h"
619cb93a386Sopenharmony_ci
620cb93a386Sopenharmony_ciSkString SkRRect::dumpToString(bool asHex) const {
621cb93a386Sopenharmony_ci    SkScalarAsStringType asType = asHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
622cb93a386Sopenharmony_ci
623cb93a386Sopenharmony_ci    fRect.dump(asHex);
624cb93a386Sopenharmony_ci    SkString line("const SkPoint corners[] = {\n");
625cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
626cb93a386Sopenharmony_ci        SkString strX, strY;
627cb93a386Sopenharmony_ci        SkAppendScalar(&strX, fRadii[i].x(), asType);
628cb93a386Sopenharmony_ci        SkAppendScalar(&strY, fRadii[i].y(), asType);
629cb93a386Sopenharmony_ci        line.appendf("    { %s, %s },", strX.c_str(), strY.c_str());
630cb93a386Sopenharmony_ci        if (asHex) {
631cb93a386Sopenharmony_ci            line.appendf(" /* %f %f */", fRadii[i].x(), fRadii[i].y());
632cb93a386Sopenharmony_ci        }
633cb93a386Sopenharmony_ci        line.append("\n");
634cb93a386Sopenharmony_ci    }
635cb93a386Sopenharmony_ci    line.append("};");
636cb93a386Sopenharmony_ci    return line;
637cb93a386Sopenharmony_ci}
638cb93a386Sopenharmony_ci
639cb93a386Sopenharmony_civoid SkRRect::dump(bool asHex) const { SkDebugf("%s\n", this->dumpToString(asHex).c_str()); }
640cb93a386Sopenharmony_ci
641cb93a386Sopenharmony_civoid SkRRect::dump(std::string& desc, int depth) const {
642cb93a386Sopenharmony_ci    std::string split(depth, '\t');
643cb93a386Sopenharmony_ci    desc += split + "\n SkRRect:{ \n";
644cb93a386Sopenharmony_ci
645cb93a386Sopenharmony_ci    fRect.dump(desc, depth + 1);
646cb93a386Sopenharmony_ci    desc += split + "\t const SkPoint corners[] = {\n";
647cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
648cb93a386Sopenharmony_ci        fRadii[i].dump(desc, depth + 1);
649cb93a386Sopenharmony_ci    }
650cb93a386Sopenharmony_ci    desc += split + "\t}\n";
651cb93a386Sopenharmony_ci    desc += split + "\t fType:" + std::to_string(fType) + "\n";
652cb93a386Sopenharmony_ci    desc += split + "}\n";
653cb93a386Sopenharmony_ci}
654cb93a386Sopenharmony_ci
655cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
656cb93a386Sopenharmony_ci
657cb93a386Sopenharmony_ci/**
658cb93a386Sopenharmony_ci *  We need all combinations of predicates to be true to have a "safe" radius value.
659cb93a386Sopenharmony_ci */
660cb93a386Sopenharmony_cistatic bool are_radius_check_predicates_valid(SkScalar rad, SkScalar min, SkScalar max) {
661cb93a386Sopenharmony_ci    return (min <= max) && (rad <= max - min) && (min + rad <= max) && (max - rad >= min) &&
662cb93a386Sopenharmony_ci           rad >= 0;
663cb93a386Sopenharmony_ci}
664cb93a386Sopenharmony_ci
665cb93a386Sopenharmony_cibool SkRRect::isValid() const {
666cb93a386Sopenharmony_ci    if (!AreRectAndRadiiValid(fRect, fRadii)) {
667cb93a386Sopenharmony_ci        return false;
668cb93a386Sopenharmony_ci    }
669cb93a386Sopenharmony_ci
670cb93a386Sopenharmony_ci    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
671cb93a386Sopenharmony_ci    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
672cb93a386Sopenharmony_ci    bool allRadiiSame = true;
673cb93a386Sopenharmony_ci
674cb93a386Sopenharmony_ci    for (int i = 1; i < 4; ++i) {
675cb93a386Sopenharmony_ci        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
676cb93a386Sopenharmony_ci            allRadiiZero = false;
677cb93a386Sopenharmony_ci        }
678cb93a386Sopenharmony_ci
679cb93a386Sopenharmony_ci        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
680cb93a386Sopenharmony_ci            allRadiiSame = false;
681cb93a386Sopenharmony_ci        }
682cb93a386Sopenharmony_ci
683cb93a386Sopenharmony_ci        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
684cb93a386Sopenharmony_ci            allCornersSquare = false;
685cb93a386Sopenharmony_ci        }
686cb93a386Sopenharmony_ci    }
687cb93a386Sopenharmony_ci    bool patchesOfNine = radii_are_nine_patch(fRadii);
688cb93a386Sopenharmony_ci
689cb93a386Sopenharmony_ci    if (fType < 0 || fType > kLastType) {
690cb93a386Sopenharmony_ci        return false;
691cb93a386Sopenharmony_ci    }
692cb93a386Sopenharmony_ci
693cb93a386Sopenharmony_ci    switch (fType) {
694cb93a386Sopenharmony_ci        case kEmpty_Type:
695cb93a386Sopenharmony_ci            if (!fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
696cb93a386Sopenharmony_ci                return false;
697cb93a386Sopenharmony_ci            }
698cb93a386Sopenharmony_ci            break;
699cb93a386Sopenharmony_ci        case kRect_Type:
700cb93a386Sopenharmony_ci            if (fRect.isEmpty() || !allRadiiZero || !allRadiiSame || !allCornersSquare) {
701cb93a386Sopenharmony_ci                return false;
702cb93a386Sopenharmony_ci            }
703cb93a386Sopenharmony_ci            break;
704cb93a386Sopenharmony_ci        case kOval_Type:
705cb93a386Sopenharmony_ci            if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
706cb93a386Sopenharmony_ci                return false;
707cb93a386Sopenharmony_ci            }
708cb93a386Sopenharmony_ci
709cb93a386Sopenharmony_ci            for (int i = 0; i < 4; ++i) {
710cb93a386Sopenharmony_ci                if (!SkScalarNearlyEqual(fRadii[i].fX, SkRectPriv::HalfWidth(fRect)) ||
711cb93a386Sopenharmony_ci                    !SkScalarNearlyEqual(fRadii[i].fY, SkRectPriv::HalfHeight(fRect))) {
712cb93a386Sopenharmony_ci                    return false;
713cb93a386Sopenharmony_ci                }
714cb93a386Sopenharmony_ci            }
715cb93a386Sopenharmony_ci            break;
716cb93a386Sopenharmony_ci        case kSimple_Type:
717cb93a386Sopenharmony_ci            if (fRect.isEmpty() || allRadiiZero || !allRadiiSame || allCornersSquare) {
718cb93a386Sopenharmony_ci                return false;
719cb93a386Sopenharmony_ci            }
720cb93a386Sopenharmony_ci            break;
721cb93a386Sopenharmony_ci        case kNinePatch_Type:
722cb93a386Sopenharmony_ci            if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
723cb93a386Sopenharmony_ci                !patchesOfNine) {
724cb93a386Sopenharmony_ci                return false;
725cb93a386Sopenharmony_ci            }
726cb93a386Sopenharmony_ci            break;
727cb93a386Sopenharmony_ci        case kComplex_Type:
728cb93a386Sopenharmony_ci            if (fRect.isEmpty() || allRadiiZero || allRadiiSame || allCornersSquare ||
729cb93a386Sopenharmony_ci                patchesOfNine) {
730cb93a386Sopenharmony_ci                return false;
731cb93a386Sopenharmony_ci            }
732cb93a386Sopenharmony_ci            break;
733cb93a386Sopenharmony_ci    }
734cb93a386Sopenharmony_ci
735cb93a386Sopenharmony_ci    return true;
736cb93a386Sopenharmony_ci}
737cb93a386Sopenharmony_ci
738cb93a386Sopenharmony_cibool SkRRect::AreRectAndRadiiValid(const SkRect& rect, const SkVector radii[4]) {
739cb93a386Sopenharmony_ci    if (!rect.isFinite() || !rect.isSorted()) {
740cb93a386Sopenharmony_ci        return false;
741cb93a386Sopenharmony_ci    }
742cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
743cb93a386Sopenharmony_ci        if (!are_radius_check_predicates_valid(radii[i].fX, rect.fLeft, rect.fRight) ||
744cb93a386Sopenharmony_ci            !are_radius_check_predicates_valid(radii[i].fY, rect.fTop, rect.fBottom)) {
745cb93a386Sopenharmony_ci            return false;
746cb93a386Sopenharmony_ci        }
747cb93a386Sopenharmony_ci    }
748cb93a386Sopenharmony_ci    return true;
749cb93a386Sopenharmony_ci}
750cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
751cb93a386Sopenharmony_ci
752cb93a386Sopenharmony_ciSkRect SkRRectPriv::InnerBounds(const SkRRect& rr) {
753cb93a386Sopenharmony_ci    if (rr.isEmpty() || rr.isRect()) {
754cb93a386Sopenharmony_ci        return rr.rect();
755cb93a386Sopenharmony_ci    }
756cb93a386Sopenharmony_ci
757cb93a386Sopenharmony_ci    // We start with the outer bounds of the round rect and consider three subsets and take the
758cb93a386Sopenharmony_ci    // one with maximum area. The first two are the horizontal and vertical rects inset from the
759cb93a386Sopenharmony_ci    // corners, the third is the rect inscribed at the corner curves' maximal point. This forms
760cb93a386Sopenharmony_ci    // the exact solution when all corners have the same radii (the radii do not have to be
761cb93a386Sopenharmony_ci    // circular).
762cb93a386Sopenharmony_ci    SkRect innerBounds = rr.getBounds();
763cb93a386Sopenharmony_ci    SkVector tl = rr.radii(SkRRect::kUpperLeft_Corner);
764cb93a386Sopenharmony_ci    SkVector tr = rr.radii(SkRRect::kUpperRight_Corner);
765cb93a386Sopenharmony_ci    SkVector bl = rr.radii(SkRRect::kLowerLeft_Corner);
766cb93a386Sopenharmony_ci    SkVector br = rr.radii(SkRRect::kLowerRight_Corner);
767cb93a386Sopenharmony_ci
768cb93a386Sopenharmony_ci    // Select maximum inset per edge, which may move an adjacent corner of the inscribed
769cb93a386Sopenharmony_ci    // rectangle off of the rounded-rect path, but that is acceptable given that the general
770cb93a386Sopenharmony_ci    // equation for inscribed area is non-trivial to evaluate.
771cb93a386Sopenharmony_ci    SkScalar leftShift   = std::max(tl.fX, bl.fX);
772cb93a386Sopenharmony_ci    SkScalar topShift    = std::max(tl.fY, tr.fY);
773cb93a386Sopenharmony_ci    SkScalar rightShift  = std::max(tr.fX, br.fX);
774cb93a386Sopenharmony_ci    SkScalar bottomShift = std::max(bl.fY, br.fY);
775cb93a386Sopenharmony_ci
776cb93a386Sopenharmony_ci    SkScalar dw = leftShift + rightShift;
777cb93a386Sopenharmony_ci    SkScalar dh = topShift + bottomShift;
778cb93a386Sopenharmony_ci
779cb93a386Sopenharmony_ci    // Area removed by shifting left/right
780cb93a386Sopenharmony_ci    SkScalar horizArea = (innerBounds.width() - dw) * innerBounds.height();
781cb93a386Sopenharmony_ci    // And by shifting top/bottom
782cb93a386Sopenharmony_ci    SkScalar vertArea = (innerBounds.height() - dh) * innerBounds.width();
783cb93a386Sopenharmony_ci    // And by shifting all edges: just considering a corner ellipse, the maximum inscribed rect has
784cb93a386Sopenharmony_ci    // a corner at sqrt(2)/2 * (rX, rY), so scale all corner shifts by (1 - sqrt(2)/2) to get the
785cb93a386Sopenharmony_ci    // safe shift per edge (since the shifts already are the max radius for that edge).
786cb93a386Sopenharmony_ci    // - We actually scale by a value slightly increased to make it so that the shifted corners are
787cb93a386Sopenharmony_ci    //   safely inside the curves, otherwise numerical stability can cause it to fail contains().
788cb93a386Sopenharmony_ci    static constexpr SkScalar kScale = (1.f - SK_ScalarRoot2Over2) + 1e-5f;
789cb93a386Sopenharmony_ci    SkScalar innerArea = (innerBounds.width() - kScale * dw) * (innerBounds.height() - kScale * dh);
790cb93a386Sopenharmony_ci
791cb93a386Sopenharmony_ci    if (horizArea > vertArea && horizArea > innerArea) {
792cb93a386Sopenharmony_ci        // Cut off corners by insetting left and right
793cb93a386Sopenharmony_ci        innerBounds.fLeft += leftShift;
794cb93a386Sopenharmony_ci        innerBounds.fRight -= rightShift;
795cb93a386Sopenharmony_ci    } else if (vertArea > innerArea) {
796cb93a386Sopenharmony_ci        // Cut off corners by insetting top and bottom
797cb93a386Sopenharmony_ci        innerBounds.fTop += topShift;
798cb93a386Sopenharmony_ci        innerBounds.fBottom -= bottomShift;
799cb93a386Sopenharmony_ci    } else if (innerArea > 0.f) {
800cb93a386Sopenharmony_ci        // Inset on all sides, scaled to touch
801cb93a386Sopenharmony_ci        innerBounds.fLeft += kScale * leftShift;
802cb93a386Sopenharmony_ci        innerBounds.fRight -= kScale * rightShift;
803cb93a386Sopenharmony_ci        innerBounds.fTop += kScale * topShift;
804cb93a386Sopenharmony_ci        innerBounds.fBottom -= kScale * bottomShift;
805cb93a386Sopenharmony_ci    } else {
806cb93a386Sopenharmony_ci        // Inner region would collapse to empty
807cb93a386Sopenharmony_ci        return SkRect::MakeEmpty();
808cb93a386Sopenharmony_ci    }
809cb93a386Sopenharmony_ci
810cb93a386Sopenharmony_ci    SkASSERT(innerBounds.isSorted() && !innerBounds.isEmpty());
811cb93a386Sopenharmony_ci    return innerBounds;
812cb93a386Sopenharmony_ci}
813cb93a386Sopenharmony_ci
814cb93a386Sopenharmony_ciSkRRect SkRRectPriv::ConservativeIntersect(const SkRRect& a, const SkRRect& b) {
815cb93a386Sopenharmony_ci    // Returns the coordinate of the rect matching the corner enum.
816cb93a386Sopenharmony_ci    auto getCorner = [](const SkRect& r, SkRRect::Corner corner) -> SkPoint {
817cb93a386Sopenharmony_ci        switch(corner) {
818cb93a386Sopenharmony_ci            case SkRRect::kUpperLeft_Corner:  return {r.fLeft, r.fTop};
819cb93a386Sopenharmony_ci            case SkRRect::kUpperRight_Corner: return {r.fRight, r.fTop};
820cb93a386Sopenharmony_ci            case SkRRect::kLowerLeft_Corner:  return {r.fLeft, r.fBottom};
821cb93a386Sopenharmony_ci            case SkRRect::kLowerRight_Corner: return {r.fRight, r.fBottom};
822cb93a386Sopenharmony_ci            default: SkUNREACHABLE;
823cb93a386Sopenharmony_ci        }
824cb93a386Sopenharmony_ci    };
825cb93a386Sopenharmony_ci    // Returns true if shape A's extreme point is contained within shape B's extreme point, relative
826cb93a386Sopenharmony_ci    // to the 'corner' location. If the two shapes' corners have the same ellipse radii, this
827cb93a386Sopenharmony_ci    // is sufficient for A's ellipse arc to be contained by B's ellipse arc.
828cb93a386Sopenharmony_ci    auto insideCorner = [](SkRRect::Corner corner, const SkPoint& a, const SkPoint& b) {
829cb93a386Sopenharmony_ci        switch(corner) {
830cb93a386Sopenharmony_ci            case SkRRect::kUpperLeft_Corner:  return a.fX >= b.fX && a.fY >= b.fY;
831cb93a386Sopenharmony_ci            case SkRRect::kUpperRight_Corner: return a.fX <= b.fX && a.fY >= b.fY;
832cb93a386Sopenharmony_ci            case SkRRect::kLowerRight_Corner: return a.fX <= b.fX && a.fY <= b.fY;
833cb93a386Sopenharmony_ci            case SkRRect::kLowerLeft_Corner:  return a.fX >= b.fX && a.fY <= b.fY;
834cb93a386Sopenharmony_ci            default:  SkUNREACHABLE;
835cb93a386Sopenharmony_ci        }
836cb93a386Sopenharmony_ci    };
837cb93a386Sopenharmony_ci
838cb93a386Sopenharmony_ci    auto getIntersectionRadii = [&](const SkRect& r, SkRRect::Corner corner, SkVector* radii) {
839cb93a386Sopenharmony_ci        SkPoint test = getCorner(r, corner);
840cb93a386Sopenharmony_ci        SkPoint aCorner = getCorner(a.rect(), corner);
841cb93a386Sopenharmony_ci        SkPoint bCorner = getCorner(b.rect(), corner);
842cb93a386Sopenharmony_ci
843cb93a386Sopenharmony_ci        if (test == aCorner && test == bCorner) {
844cb93a386Sopenharmony_ci            // The round rects share a corner anchor, so pick A or B such that its X and Y radii
845cb93a386Sopenharmony_ci            // are both larger than the other rrect's, or return false if neither A or B has the max
846cb93a386Sopenharmony_ci            // corner radii (this is more permissive than the single corner tests below).
847cb93a386Sopenharmony_ci            SkVector aRadii = a.radii(corner);
848cb93a386Sopenharmony_ci            SkVector bRadii = b.radii(corner);
849cb93a386Sopenharmony_ci            if (aRadii.fX >= bRadii.fX && aRadii.fY >= bRadii.fY) {
850cb93a386Sopenharmony_ci                *radii = aRadii;
851cb93a386Sopenharmony_ci                return true;
852cb93a386Sopenharmony_ci            } else if (bRadii.fX >= aRadii.fX && bRadii.fY >= aRadii.fY) {
853cb93a386Sopenharmony_ci                *radii = bRadii;
854cb93a386Sopenharmony_ci                return true;
855cb93a386Sopenharmony_ci            } else {
856cb93a386Sopenharmony_ci                return false;
857cb93a386Sopenharmony_ci            }
858cb93a386Sopenharmony_ci        } else if (test == aCorner) {
859cb93a386Sopenharmony_ci            // Test that A's ellipse is contained by B. This is a non-trivial function to evaluate
860cb93a386Sopenharmony_ci            // so we resrict it to when the corners have the same radii. If not, we use the more
861cb93a386Sopenharmony_ci            // conservative test that the extreme point of A's bounding box is contained in B.
862cb93a386Sopenharmony_ci            *radii = a.radii(corner);
863cb93a386Sopenharmony_ci            if (*radii == b.radii(corner)) {
864cb93a386Sopenharmony_ci                return insideCorner(corner, aCorner, bCorner); // A inside B
865cb93a386Sopenharmony_ci            } else {
866cb93a386Sopenharmony_ci                return b.checkCornerContainment(aCorner.fX, aCorner.fY);
867cb93a386Sopenharmony_ci            }
868cb93a386Sopenharmony_ci        } else if (test == bCorner) {
869cb93a386Sopenharmony_ci            // Mirror of the above
870cb93a386Sopenharmony_ci            *radii = b.radii(corner);
871cb93a386Sopenharmony_ci            if (*radii == a.radii(corner)) {
872cb93a386Sopenharmony_ci                return insideCorner(corner, bCorner, aCorner); // B inside A
873cb93a386Sopenharmony_ci            } else {
874cb93a386Sopenharmony_ci                return a.checkCornerContainment(bCorner.fX, bCorner.fY);
875cb93a386Sopenharmony_ci            }
876cb93a386Sopenharmony_ci        } else {
877cb93a386Sopenharmony_ci            // This is a corner formed by two straight edges of A and B, so confirm that it is
878cb93a386Sopenharmony_ci            // contained in both (if not, then the intersection can't be a round rect).
879cb93a386Sopenharmony_ci            *radii = {0.f, 0.f};
880cb93a386Sopenharmony_ci            return a.checkCornerContainment(test.fX, test.fY) &&
881cb93a386Sopenharmony_ci                   b.checkCornerContainment(test.fX, test.fY);
882cb93a386Sopenharmony_ci        }
883cb93a386Sopenharmony_ci    };
884cb93a386Sopenharmony_ci
885cb93a386Sopenharmony_ci    // We fill in the SkRRect directly. Since the rect and radii are either 0s or determined by
886cb93a386Sopenharmony_ci    // valid existing SkRRects, we know we are finite.
887cb93a386Sopenharmony_ci    SkRRect intersection;
888cb93a386Sopenharmony_ci    if (!intersection.fRect.intersect(a.rect(), b.rect())) {
889cb93a386Sopenharmony_ci        // Definitely no intersection
890cb93a386Sopenharmony_ci        return SkRRect::MakeEmpty();
891cb93a386Sopenharmony_ci    }
892cb93a386Sopenharmony_ci
893cb93a386Sopenharmony_ci    const SkRRect::Corner corners[] = {
894cb93a386Sopenharmony_ci        SkRRect::kUpperLeft_Corner,
895cb93a386Sopenharmony_ci        SkRRect::kUpperRight_Corner,
896cb93a386Sopenharmony_ci        SkRRect::kLowerRight_Corner,
897cb93a386Sopenharmony_ci        SkRRect::kLowerLeft_Corner
898cb93a386Sopenharmony_ci    };
899cb93a386Sopenharmony_ci    // By definition, edges is contained in the bounds of 'a' and 'b', but now we need to consider
900cb93a386Sopenharmony_ci    // the corners. If the bound's corner point is in both rrects, the corner radii will be 0s.
901cb93a386Sopenharmony_ci    // If the bound's corner point matches a's edges and is inside 'b', we use a's radii.
902cb93a386Sopenharmony_ci    // Same for b's radii. If any corner fails these conditions, we reject the intersection as an
903cb93a386Sopenharmony_ci    // rrect. If after determining radii for all 4 corners, they would overlap, we also reject the
904cb93a386Sopenharmony_ci    // intersection shape.
905cb93a386Sopenharmony_ci    for (auto c : corners) {
906cb93a386Sopenharmony_ci        if (!getIntersectionRadii(intersection.fRect, c, &intersection.fRadii[c])) {
907cb93a386Sopenharmony_ci            return SkRRect::MakeEmpty(); // Resulting intersection is not a rrect
908cb93a386Sopenharmony_ci        }
909cb93a386Sopenharmony_ci    }
910cb93a386Sopenharmony_ci
911cb93a386Sopenharmony_ci    // Check for radius overlap along the four edges, since the earlier evaluation was only a
912cb93a386Sopenharmony_ci    // one-sided corner check. If they aren't valid, a corner's radii doesn't fit within the rect.
913cb93a386Sopenharmony_ci    // If the radii are scaled, the combination of radii from two adjacent corners doesn't fit.
914cb93a386Sopenharmony_ci    // Normally for a regularly constructed SkRRect, we want this scaling, but in this case it means
915cb93a386Sopenharmony_ci    // the intersection shape is definitively not a round rect.
916cb93a386Sopenharmony_ci    if (!SkRRect::AreRectAndRadiiValid(intersection.fRect, intersection.fRadii) ||
917cb93a386Sopenharmony_ci        intersection.scaleRadii()) {
918cb93a386Sopenharmony_ci        return SkRRect::MakeEmpty();
919cb93a386Sopenharmony_ci    }
920cb93a386Sopenharmony_ci
921cb93a386Sopenharmony_ci    // The intersection is an rrect of the given radii. Potentially all 4 corners could have
922cb93a386Sopenharmony_ci    // been simplified to (0,0) radii, making the intersection a rectangle.
923cb93a386Sopenharmony_ci    intersection.computeType();
924cb93a386Sopenharmony_ci    return intersection;
925cb93a386Sopenharmony_ci}
926