Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/gpu/ganesh/geometry/GrPathUtils.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2011 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 "src/gpu/ganesh/geometry/GrPathUtils.h"
9
10
#include "include/core/SkMatrix.h"
11
#include "include/core/SkRect.h"
12
#include "include/private/base/SkAssert.h"
13
#include "include/private/base/SkFloatingPoint.h"
14
#include "src/core/SkGeometry.h"
15
#include "src/core/SkPathEnums.h"
16
#include "src/core/SkPointPriv.h"
17
#include "src/gpu/tessellate/WangsFormula.h"
18
19
#include <algorithm>
20
21
using namespace skia_private;
22
23
static const SkScalar kMinCurveTol = 0.0001f;
24
25
626k
static float tolerance_to_wangs_precision(float srcTol) {
26
    // You should have called scaleToleranceToSrc, which guarantees this
27
626k
    SkASSERT(srcTol >= kMinCurveTol);
28
29
    // The GrPathUtil API defines tolerance as the max distance the linear segment can be from
30
    // the real curve. Wang's formula guarantees the linear segments will be within 1/precision
31
    // of the true curve, so precision = 1/srcTol
32
626k
    return 1.f / srcTol;
33
626k
}
34
35
626k
uint32_t max_bezier_vertices(uint32_t chopCount) {
36
626k
    static constexpr uint32_t kMaxChopsPerCurve = 10;
37
626k
    static_assert((1 << kMaxChopsPerCurve) == GrPathUtils::kMaxPointsPerCurve);
38
626k
    return 1 << std::min(chopCount, kMaxChopsPerCurve);
39
626k
}
40
41
SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
42
                                          const SkMatrix& viewM,
43
11.7k
                                          const SkRect& pathBounds) {
44
    // In order to tesselate the path we get a bound on how much the matrix can
45
    // scale when mapping to screen coordinates.
46
11.7k
    SkScalar stretch = viewM.getMaxScale();
47
48
11.7k
    if (stretch < 0) {
49
        // take worst case mapRadius amoung four corners.
50
        // (less than perfect)
51
17.1k
        for (int i = 0; i < 4; ++i) {
52
13.7k
            SkMatrix mat;
53
13.7k
            mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight,
54
13.7k
                             (i < 2) ? pathBounds.fTop : pathBounds.fBottom);
55
13.7k
            mat.postConcat(viewM);
56
13.7k
            stretch = std::max(stretch, mat.mapRadius(SK_Scalar1));
57
13.7k
        }
58
3.42k
    }
59
11.7k
    SkScalar srcTol = 0;
60
11.7k
    if (stretch <= 0) {
61
        // We have degenerate bounds or some degenerate matrix. Thus we set the tolerance to be the
62
        // max of the path pathBounds width and height.
63
536
        srcTol = std::max(pathBounds.width(), pathBounds.height());
64
11.2k
    } else {
65
11.2k
        srcTol = devTol / stretch;
66
11.2k
    }
67
11.7k
    if (srcTol < kMinCurveTol) {
68
2.92k
        srcTol = kMinCurveTol;
69
2.92k
    }
70
11.7k
    return srcTol;
71
11.7k
}
72
73
601k
uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) {
74
601k
    return max_bezier_vertices(skgpu::wangs_formula::quadratic_log2(
75
601k
            tolerance_to_wangs_precision(tol), points));
76
601k
}
77
78
uint32_t GrPathUtils::generateQuadraticPoints(const SkPoint& p0,
79
                                              const SkPoint& p1,
80
                                              const SkPoint& p2,
81
                                              SkScalar tolSqd,
82
                                              SkPoint** points,
83
1.07G
                                              uint32_t pointsLeft) {
84
1.07G
    if (pointsLeft < 2 ||
85
1.07G
        (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p2)) < tolSqd) {
86
540M
        (*points)[0] = p2;
87
540M
        *points += 1;
88
540M
        return 1;
89
540M
    }
90
91
539M
    SkPoint q[] = {
92
539M
        { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
93
539M
        { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
94
539M
    };
95
539M
    SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) };
96
97
539M
    pointsLeft >>= 1;
98
539M
    uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
99
539M
    uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
100
539M
    return a + b;
101
1.07G
}
102
103
25.4k
uint32_t GrPathUtils::cubicPointCount(const SkPoint points[], SkScalar tol) {
104
25.4k
    return max_bezier_vertices(skgpu::wangs_formula::cubic_log2(
105
25.4k
            tolerance_to_wangs_precision(tol), points));
106
25.4k
}
107
108
uint32_t GrPathUtils::generateCubicPoints(const SkPoint& p0,
109
                                          const SkPoint& p1,
110
                                          const SkPoint& p2,
111
                                          const SkPoint& p3,
112
                                          SkScalar tolSqd,
113
                                          SkPoint** points,
114
3.58M
                                          uint32_t pointsLeft) {
115
3.58M
    if (pointsLeft < 2 ||
116
3.58M
        (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p3) < tolSqd &&
117
1.79M
         SkPointPriv::DistanceToLineSegmentBetweenSqd(p2, p0, p3) < tolSqd)) {
118
1.79M
        (*points)[0] = p3;
119
1.79M
        *points += 1;
120
1.79M
        return 1;
121
1.79M
    }
122
1.78M
    SkPoint q[] = {
123
1.78M
        { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
124
1.78M
        { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
125
1.78M
        { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) }
126
1.78M
    };
127
1.78M
    SkPoint r[] = {
128
1.78M
        { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) },
129
1.78M
        { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) }
130
1.78M
    };
131
1.78M
    SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) };
132
1.78M
    pointsLeft >>= 1;
133
1.78M
    uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
134
1.78M
    uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
135
1.78M
    return a + b;
136
3.58M
}
137
138
533k
void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) {
139
    // We want M such that M * xy_pt = uv_pt
140
    // We know M * control_pts = [0  1/2 1]
141
    //                           [0  0   1]
142
    //                           [1  1   1]
143
    // And control_pts = [x0 x1 x2]
144
    //                   [y0 y1 y2]
145
    //                   [1  1  1 ]
146
    // We invert the control pt matrix and post concat to both sides to get M.
147
    // Using the known form of the control point matrix and the result, we can
148
    // optimize and improve precision.
149
150
533k
    double x0 = qPts[0].fX;
151
533k
    double y0 = qPts[0].fY;
152
533k
    double x1 = qPts[1].fX;
153
533k
    double y1 = qPts[1].fY;
154
533k
    double x2 = qPts[2].fX;
155
533k
    double y2 = qPts[2].fY;
156
157
    // pre-calculate some adjugate matrix factors for determinant
158
533k
    double a2 = x1*y2-x2*y1;
159
533k
    double a5 = x2*y0-x0*y2;
160
533k
    double a8 = x0*y1-x1*y0;
161
533k
    double det = a2 + a5 + a8;
162
163
533k
    if (!SkIsFinite(det)
164
533k
        || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
165
        // The quad is degenerate. Hopefully this is rare. Find the pts that are
166
        // farthest apart to compute a line (unless it is really a pt).
167
650
        SkScalar maxD = SkPointPriv::DistanceToSqd(qPts[0], qPts[1]);
168
650
        int maxEdge = 0;
169
650
        SkScalar d = SkPointPriv::DistanceToSqd(qPts[1], qPts[2]);
170
650
        if (d > maxD) {
171
186
            maxD = d;
172
186
            maxEdge = 1;
173
186
        }
174
650
        d = SkPointPriv::DistanceToSqd(qPts[2], qPts[0]);
175
650
        if (d > maxD) {
176
499
            maxD = d;
177
499
            maxEdge = 2;
178
499
        }
179
        // We could have a tolerance here, not sure if it would improve anything
180
650
        if (maxD > 0) {
181
            // Set the matrix to give (u = 0, v = distance_to_line)
182
647
            SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
183
            // when looking from the point 0 down the line we want positive
184
            // distances to be to the left. This matches the non-degenerate
185
            // case.
186
647
            lineVec = SkPointPriv::MakeOrthog(lineVec, SkPointPriv::kLeft_Side);
187
            // first row
188
647
            fM[0] = 0;
189
647
            fM[1] = 0;
190
647
            fM[2] = 0;
191
            // second row
192
647
            fM[3] = lineVec.fX;
193
647
            fM[4] = lineVec.fY;
194
647
            fM[5] = -lineVec.dot(qPts[maxEdge]);
195
647
        } else {
196
            // It's a point. It should cover zero area. Just set the matrix such
197
            // that (u, v) will always be far away from the quad.
198
3
            fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
199
3
            fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
200
3
        }
201
533k
    } else {
202
533k
        double scale = 1.0/det;
203
204
        // compute adjugate matrix
205
533k
        double a3, a4, a6, a7;
206
533k
        a3 = y2-y0;
207
533k
        a4 = x0-x2;
208
209
533k
        a6 = y0-y1;
210
533k
        a7 = x1-x0;
211
212
        // this performs the uv_pts*adjugate(control_pts) multiply,
213
        // then does the scale by 1/det afterwards to improve precision
214
533k
        fM[0] = (float)((0.5*a3 + a6)*scale);
215
533k
        fM[1] = (float)((0.5*a4 + a7)*scale);
216
533k
        fM[2] = (float)((0.5*a5 + a8)*scale);
217
533k
        fM[3] = (float)(a6*scale);
218
533k
        fM[4] = (float)(a7*scale);
219
533k
        fM[5] = (float)(a8*scale);
220
533k
    }
221
533k
}
222
223
////////////////////////////////////////////////////////////////////////////////
224
225
// k = (y2 - y0, x0 - x2, x2*y0 - x0*y2)
226
// l = (y1 - y0, x0 - x1, x1*y0 - x0*y1) * 2*w
227
// m = (y2 - y1, x1 - x2, x2*y1 - x1*y2) * 2*w
228
109
void GrPathUtils::getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* out) {
229
109
    SkMatrix& klm = *out;
230
109
    const SkScalar w2 = 2.f * weight;
231
109
    klm[0] = p[2].fY - p[0].fY;
232
109
    klm[1] = p[0].fX - p[2].fX;
233
109
    klm[2] = p[2].fX * p[0].fY - p[0].fX * p[2].fY;
234
235
109
    klm[3] = w2 * (p[1].fY - p[0].fY);
236
109
    klm[4] = w2 * (p[0].fX - p[1].fX);
237
109
    klm[5] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY);
238
239
109
    klm[6] = w2 * (p[2].fY - p[1].fY);
240
109
    klm[7] = w2 * (p[1].fX - p[2].fX);
241
109
    klm[8] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY);
242
243
    // scale the max absolute value of coeffs to 10
244
109
    SkScalar scale = 0.f;
245
1.09k
    for (int i = 0; i < 9; ++i) {
246
981
       scale = std::max(scale, SkScalarAbs(klm[i]));
247
981
    }
248
109
    SkASSERT(scale > 0.f);
249
109
    scale = 10.f / scale;
250
1.09k
    for (int i = 0; i < 9; ++i) {
251
981
        klm[i] *= scale;
252
981
    }
253
109
}
254
255
////////////////////////////////////////////////////////////////////////////////
256
257
namespace {
258
259
// a is the first control point of the cubic.
260
// ab is the vector from a to the second control point.
261
// dc is the vector from the fourth to the third control point.
262
// d is the fourth control point.
263
// p is the candidate quadratic control point.
264
// this assumes that the cubic doesn't inflect and is simple
265
bool is_point_within_cubic_tangents(const SkPoint& a,
266
                                    const SkVector& ab,
267
                                    const SkVector& dc,
268
                                    const SkPoint& d,
269
                                    SkPathFirstDirection dir,
270
10.1k
                                    const SkPoint p) {
271
10.1k
    SkVector ap = p - a;
272
10.1k
    SkScalar apXab = ap.cross(ab);
273
10.1k
    if (SkPathFirstDirection::kCW == dir) {
274
7.88k
        if (apXab > 0) {
275
3.70k
            return false;
276
3.70k
        }
277
7.88k
    } else {
278
2.22k
        SkASSERT(SkPathFirstDirection::kCCW == dir);
279
2.22k
        if (apXab < 0) {
280
1.49k
            return false;
281
1.49k
        }
282
2.22k
    }
283
284
4.91k
    SkVector dp = p - d;
285
4.91k
    SkScalar dpXdc = dp.cross(dc);
286
4.91k
    if (SkPathFirstDirection::kCW == dir) {
287
4.18k
        if (dpXdc < 0) {
288
1.10k
            return false;
289
1.10k
        }
290
4.18k
    } else {
291
729
        SkASSERT(SkPathFirstDirection::kCCW == dir);
292
729
        if (dpXdc > 0) {
293
206
            return false;
294
206
        }
295
729
    }
296
3.60k
    return true;
297
4.91k
}
298
299
void convert_noninflect_cubic_to_quads(const SkPoint p[4],
300
                                       SkScalar toleranceSqd,
301
                                       TArray<SkPoint, true>* quads,
302
                                       int sublevel = 0,
303
                                       bool preserveFirstTangent = true,
304
1.97M
                                       bool preserveLastTangent = true) {
305
    // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
306
    // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1].
307
1.97M
    SkVector ab = p[1] - p[0];
308
1.97M
    SkVector dc = p[2] - p[3];
309
310
1.97M
    if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
311
8.07k
        if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
312
507
            SkPoint* degQuad = quads->push_back_n(3);
313
507
            degQuad[0] = p[0];
314
507
            degQuad[1] = p[0];
315
507
            degQuad[2] = p[3];
316
507
            return;
317
507
        }
318
7.56k
        ab = p[2] - p[0];
319
7.56k
    }
320
1.96M
    if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
321
1.19k
        dc = p[1] - p[3];
322
1.19k
    }
323
324
1.96M
    static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
325
1.96M
    static const int kMaxSubdivs = 10;
326
327
1.96M
    ab.scale(kLengthScale);
328
1.96M
    dc.scale(kLengthScale);
329
330
    // c0 and c1 are extrapolations along vectors ab and dc.
331
1.96M
    SkPoint c0 = p[0] + ab;
332
1.96M
    SkPoint c1 = p[3] + dc;
333
334
1.96M
    SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
335
1.96M
    if (dSqd < toleranceSqd) {
336
985k
        SkPoint newC;
337
985k
        if (preserveFirstTangent == preserveLastTangent) {
338
            // We used to force a split when both tangents need to be preserved and c0 != c1.
339
            // This introduced a large performance regression for tiny paths for no noticeable
340
            // quality improvement. However, we aren't quite fulfilling our contract of guaranteeing
341
            // the two tangent vectors and this could introduce a missed pixel in
342
            // AAHairlinePathRenderer.
343
983k
            newC = (c0 + c1) * 0.5f;
344
983k
        } else if (preserveFirstTangent) {
345
1.06k
            newC = c0;
346
1.06k
        } else {
347
1.06k
            newC = c1;
348
1.06k
        }
349
350
985k
        SkPoint* pts = quads->push_back_n(3);
351
985k
        pts[0] = p[0];
352
985k
        pts[1] = newC;
353
985k
        pts[2] = p[3];
354
985k
        return;
355
985k
    }
356
984k
    SkPoint choppedPts[7];
357
984k
    SkChopCubicAtHalf(p, choppedPts);
358
984k
    convert_noninflect_cubic_to_quads(
359
984k
            choppedPts + 0, toleranceSqd, quads, sublevel + 1, preserveFirstTangent, false);
360
984k
    convert_noninflect_cubic_to_quads(
361
984k
            choppedPts + 3, toleranceSqd, quads, sublevel + 1, false, preserveLastTangent);
362
984k
}
363
364
void convert_noninflect_cubic_to_quads_with_constraint(const SkPoint p[4],
365
                                                       SkScalar toleranceSqd,
366
                                                       SkPathFirstDirection dir,
367
                                                       TArray<SkPoint, true>* quads,
368
25.2k
                                                       int sublevel = 0) {
369
    // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
370
    // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1].
371
372
25.2k
    SkVector ab = p[1] - p[0];
373
25.2k
    SkVector dc = p[2] - p[3];
374
375
25.2k
    if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
376
229
        if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
377
0
            SkPoint* degQuad = quads->push_back_n(3);
378
0
            degQuad[0] = p[0];
379
0
            degQuad[1] = p[0];
380
0
            degQuad[2] = p[3];
381
0
            return;
382
0
        }
383
229
        ab = p[2] - p[0];
384
229
    }
385
25.2k
    if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
386
2
        dc = p[1] - p[3];
387
2
    }
388
389
    // When the ab and cd tangents are degenerate or nearly parallel with vector from d to a the
390
    // constraint that the quad point falls between the tangents becomes hard to enforce and we are
391
    // likely to hit the max subdivision count. However, in this case the cubic is approaching a
392
    // line and the accuracy of the quad point isn't so important. We check if the two middle cubic
393
    // control points are very close to the baseline vector. If so then we just pick quadratic
394
    // points on the control polygon.
395
396
25.2k
    SkVector da = p[0] - p[3];
397
25.2k
    bool doQuads = SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero ||
398
25.2k
                   SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero;
399
25.2k
    if (!doQuads) {
400
25.2k
        SkScalar invDALengthSqd = SkPointPriv::LengthSqd(da);
401
25.2k
        if (invDALengthSqd > SK_ScalarNearlyZero) {
402
25.2k
            invDALengthSqd = SkScalarInvert(invDALengthSqd);
403
            // cross(ab, da)^2/length(da)^2 == sqd distance from b to line from d to a.
404
            // same goes for point c using vector cd.
405
25.2k
            SkScalar detABSqd = ab.cross(da);
406
25.2k
            detABSqd = SkScalarSquare(detABSqd);
407
25.2k
            SkScalar detDCSqd = dc.cross(da);
408
25.2k
            detDCSqd = SkScalarSquare(detDCSqd);
409
25.2k
            if (detABSqd * invDALengthSqd < toleranceSqd &&
410
25.2k
                detDCSqd * invDALengthSqd < toleranceSqd) {
411
4.53k
                doQuads = true;
412
4.53k
            }
413
25.2k
        }
414
25.2k
    }
415
25.2k
    if (doQuads) {
416
4.54k
        SkPoint b = p[0] + ab;
417
4.54k
        SkPoint c = p[3] + dc;
418
4.54k
        SkPoint mid = b + c;
419
4.54k
        mid.scale(SK_ScalarHalf);
420
        // Insert two quadratics to cover the case when ab points away from d and/or dc
421
        // points away from a.
422
4.54k
        if (SkVector::DotProduct(da, dc) < 0 || SkVector::DotProduct(ab, da) > 0) {
423
16
            SkPoint* qpts = quads->push_back_n(6);
424
16
            qpts[0] = p[0];
425
16
            qpts[1] = b;
426
16
            qpts[2] = mid;
427
16
            qpts[3] = mid;
428
16
            qpts[4] = c;
429
16
            qpts[5] = p[3];
430
4.52k
        } else {
431
4.52k
            SkPoint* qpts = quads->push_back_n(3);
432
4.52k
            qpts[0] = p[0];
433
4.52k
            qpts[1] = mid;
434
4.52k
            qpts[2] = p[3];
435
4.52k
        }
436
4.54k
        return;
437
4.54k
    }
438
439
20.6k
    static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
440
20.6k
    static const int kMaxSubdivs = 10;
441
442
20.6k
    ab.scale(kLengthScale);
443
20.6k
    dc.scale(kLengthScale);
444
445
    // c0 and c1 are extrapolations along vectors ab and dc.
446
20.6k
    SkVector c0 = p[0] + ab;
447
20.6k
    SkVector c1 = p[3] + dc;
448
449
20.6k
    SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
450
20.6k
    if (dSqd < toleranceSqd) {
451
10.1k
        SkPoint cAvg = (c0 + c1) * 0.5f;
452
10.1k
        bool subdivide = false;
453
454
10.1k
        if (!is_point_within_cubic_tangents(p[0], ab, dc, p[3], dir, cAvg)) {
455
            // choose a new cAvg that is the intersection of the two tangent lines.
456
6.51k
            ab = SkPointPriv::MakeOrthog(ab);
457
6.51k
            SkScalar z0 = -ab.dot(p[0]);
458
6.51k
            dc = SkPointPriv::MakeOrthog(dc);
459
6.51k
            SkScalar z1 = -dc.dot(p[3]);
460
6.51k
            cAvg.fX = ab.fY * z1 - z0 * dc.fY;
461
6.51k
            cAvg.fY = z0 * dc.fX - ab.fX * z1;
462
6.51k
            SkScalar z = ab.fX * dc.fY - ab.fY * dc.fX;
463
6.51k
            z = sk_ieee_float_divide(1.0f, z);
464
6.51k
            cAvg.fX *= z;
465
6.51k
            cAvg.fY *= z;
466
6.51k
            if (sublevel <= kMaxSubdivs) {
467
3.56k
                SkScalar d0Sqd = SkPointPriv::DistanceToSqd(c0, cAvg);
468
3.56k
                SkScalar d1Sqd = SkPointPriv::DistanceToSqd(c1, cAvg);
469
                // We need to subdivide if d0 + d1 > tolerance but we have the sqd values. We know
470
                // the distances and tolerance can't be negative.
471
                // (d0 + d1)^2 > toleranceSqd
472
                // d0Sqd + 2*d0*d1 + d1Sqd > toleranceSqd
473
3.56k
                SkScalar d0d1 = SkScalarSqrt(d0Sqd * d1Sqd);
474
3.56k
                subdivide = 2 * d0d1 + d0Sqd + d1Sqd > toleranceSqd;
475
3.56k
            }
476
6.51k
        }
477
10.1k
        if (!subdivide) {
478
8.10k
            SkPoint* pts = quads->push_back_n(3);
479
8.10k
            pts[0] = p[0];
480
8.10k
            pts[1] = cAvg;
481
8.10k
            pts[2] = p[3];
482
8.10k
            return;
483
8.10k
        }
484
10.1k
    }
485
12.5k
    SkPoint choppedPts[7];
486
12.5k
    SkChopCubicAtHalf(p, choppedPts);
487
12.5k
    convert_noninflect_cubic_to_quads_with_constraint(
488
12.5k
            choppedPts + 0, toleranceSqd, dir, quads, sublevel + 1);
489
12.5k
    convert_noninflect_cubic_to_quads_with_constraint(
490
12.5k
            choppedPts + 3, toleranceSqd, dir, quads, sublevel + 1);
491
12.5k
}
492
}  // namespace
493
494
void GrPathUtils::convertCubicToQuads(const SkPoint p[4],
495
                                      SkScalar tolScale,
496
2.49k
                                      TArray<SkPoint, true>* quads) {
497
2.49k
    if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
498
795
        return;
499
795
    }
500
1.69k
    if (!SkIsFinite(tolScale)) {
501
0
        return;
502
0
    }
503
1.69k
    SkPoint chopped[10];
504
1.69k
    int count = SkChopCubicAtInflections(p, chopped);
505
506
1.69k
    const SkScalar tolSqd = SkScalarSquare(tolScale);
507
508
3.57k
    for (int i = 0; i < count; ++i) {
509
1.87k
        SkPoint* cubic = chopped + 3*i;
510
1.87k
        convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
511
1.87k
    }
512
1.69k
}
513
514
void GrPathUtils::convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
515
                                                         SkScalar tolScale,
516
                                                         SkPathFirstDirection dir,
517
55
                                                         TArray<SkPoint, true>* quads) {
518
55
    if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
519
0
        return;
520
0
    }
521
55
    if (!SkIsFinite(tolScale)) {
522
0
        return;
523
0
    }
524
55
    SkPoint chopped[10];
525
55
    int count = SkChopCubicAtInflections(p, chopped);
526
527
55
    const SkScalar tolSqd = SkScalarSquare(tolScale);
528
529
110
    for (int i = 0; i < count; ++i) {
530
55
        SkPoint* cubic = chopped + 3*i;
531
55
        convert_noninflect_cubic_to_quads_with_constraint(cubic, tolSqd, dir, quads);
532
55
    }
533
55
}