Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/core/SkGeometry.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2006 The Android Open Source Project
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/core/SkGeometry.h"
9
10
#include "include/core/SkMatrix.h"
11
#include "include/core/SkPoint3.h"
12
#include "include/core/SkRect.h"
13
#include "include/core/SkScalar.h"
14
#include "include/private/base/SkDebug.h"
15
#include "include/private/base/SkFloatingPoint.h"
16
#include "include/private/base/SkTPin.h"
17
#include "include/private/base/SkTo.h"
18
#include "src/base/SkBezierCurves.h"
19
#include "src/base/SkCubics.h"
20
#include "src/base/SkUtils.h"
21
#include "src/base/SkVx.h"
22
#include "src/core/SkPointPriv.h"
23
24
#include <algorithm>
25
#include <array>
26
#include <cmath>
27
#include <cstddef>
28
#include <cstdint>
29
30
namespace {
31
32
using float2 = skvx::float2;
33
using float4 = skvx::float4;
34
35
81.4M
SkVector to_vector(const float2& x) {
36
81.4M
    SkVector vector;
37
81.4M
    x.store(&vector);
38
81.4M
    return vector;
39
81.4M
}
40
41
////////////////////////////////////////////////////////////////////////
42
43
157M
int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c) {
44
157M
    SkScalar ab = a - b;
45
157M
    SkScalar bc = b - c;
46
157M
    if (ab < 0) {
47
68.1M
        bc = -bc;
48
68.1M
    }
49
157M
    return ab == 0 || bc < 0;
50
157M
}
51
52
////////////////////////////////////////////////////////////////////////
53
54
148M
int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) {
55
148M
    SkASSERT(ratio);
56
57
148M
    if (numer < 0) {
58
69.7M
        numer = -numer;
59
69.7M
        denom = -denom;
60
69.7M
    }
61
62
148M
    if (denom == 0 || numer == 0 || numer >= denom) {
63
115M
        return 0;
64
115M
    }
65
66
33.0M
    SkScalar r = numer / denom;
67
33.0M
    if (SkIsNaN(r)) {
68
1.35k
        return 0;
69
1.35k
    }
70
33.0M
    SkASSERTF(r >= 0 && r < SK_Scalar1, "numer %f, denom %f, r %f", numer, denom, r);
71
33.0M
    if (r == 0) { // catch underflow if numer <<<< denom
72
1.21M
        return 0;
73
1.21M
    }
74
31.7M
    *ratio = r;
75
31.7M
    return 1;
76
33.0M
}
SkGeometry.cpp:(anonymous namespace)::valid_unit_divide(float, float, float*)
Line
Count
Source
54
148M
int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) {
55
148M
    SkASSERT(ratio);
56
57
148M
    if (numer < 0) {
58
69.7M
        numer = -numer;
59
69.7M
        denom = -denom;
60
69.7M
    }
61
62
148M
    if (denom == 0 || numer == 0 || numer >= denom) {
63
115M
        return 0;
64
115M
    }
65
66
33.0M
    SkScalar r = numer / denom;
67
33.0M
    if (SkIsNaN(r)) {
68
1.35k
        return 0;
69
1.35k
    }
70
33.0M
    SkASSERTF(r >= 0 && r < SK_Scalar1, "numer %f, denom %f, r %f", numer, denom, r);
71
33.0M
    if (r == 0) { // catch underflow if numer <<<< denom
72
1.21M
        return 0;
73
1.21M
    }
74
31.7M
    *ratio = r;
75
31.7M
    return 1;
76
33.0M
}
Unexecuted instantiation: SkGeometry.cpp:(anonymous namespace)::valid_unit_divide(float, float, float*)
77
78
// Just returns its argument, but makes it easy to set a break-point to know when
79
// SkFindUnitQuadRoots is going to return 0 (an error).
80
58.7M
int return_check_zero(int value) {
81
58.7M
    if (value == 0) {
82
36.3M
        return 0;
83
36.3M
    }
84
22.4M
    return value;
85
58.7M
}
86
87
} // namespace
88
89
/** From Numerical Recipes in C.
90
91
    Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
92
    x1 = Q / A
93
    x2 = C / Q
94
*/
95
58.7M
int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) {
96
58.7M
    SkASSERT(roots);
97
98
58.7M
    if (A == 0) {
99
2.27M
        return return_check_zero(valid_unit_divide(-C, B, roots));
100
2.27M
    }
101
102
56.5M
    SkScalar* r = roots;
103
104
    // use doubles so we don't overflow temporarily trying to compute R
105
56.5M
    double dr = (double)B * B - 4 * (double)A * C;
106
56.5M
    if (dr < 0) {
107
2.48M
        return return_check_zero(0);
108
2.48M
    }
109
54.0M
    dr = sqrt(dr);
110
54.0M
    SkScalar R = SkDoubleToScalar(dr);
111
54.0M
    if (!SkIsFinite(R)) {
112
3.68M
        return return_check_zero(0);
113
3.68M
    }
114
115
50.3M
    SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
116
50.3M
    r += valid_unit_divide(Q, A, r);
117
50.3M
    r += valid_unit_divide(C, Q, r);
118
50.3M
    if (r - roots == 2) {
119
738k
        if (roots[0] > roots[1]) {
120
734k
            using std::swap;
121
734k
            swap(roots[0], roots[1]);
122
734k
        } else if (roots[0] == roots[1]) { // nearly-equal?
123
3.36k
            r -= 1; // skip the double root
124
3.36k
        }
125
738k
    }
126
50.3M
    return return_check_zero((int)(r - roots));
127
54.0M
}
128
129
///////////////////////////////////////////////////////////////////////////////
130
///////////////////////////////////////////////////////////////////////////////
131
132
25.6M
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) {
133
25.6M
    SkASSERT(src);
134
25.6M
    SkASSERT(t >= 0 && t <= SK_Scalar1);
135
136
25.6M
    if (pt) {
137
25.6M
        *pt = SkEvalQuadAt(src, t);
138
25.6M
    }
139
25.6M
    if (tangent) {
140
24.6M
        *tangent = SkEvalQuadTangentAt(src, t);
141
24.6M
    }
142
25.6M
}
143
144
42.0M
SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) {
145
42.0M
    return to_point(SkQuadCoeff(src).eval(t));
146
42.0M
}
147
148
24.6M
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) {
149
    // The derivative equation is 2(b - a +(a - 2b +c)t). This returns a
150
    // zero tangent vector when t is 0 or 1, and the control point is equal
151
    // to the end point. In this case, use the quad end points to compute the tangent.
152
24.6M
    if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) {
153
180k
        return src[2] - src[0];
154
180k
    }
155
24.4M
    SkASSERT(src);
156
24.4M
    SkASSERT(t >= 0 && t <= SK_Scalar1);
157
158
24.4M
    float2 P0 = from_point(src[0]);
159
24.4M
    float2 P1 = from_point(src[1]);
160
24.4M
    float2 P2 = from_point(src[2]);
161
162
24.4M
    float2 B = P1 - P0;
163
24.4M
    float2 A = P2 - P1 - B;
164
24.4M
    float2 T = A * t + B;
165
166
24.4M
    return to_vector(T + T);
167
24.6M
}
168
169
static inline float2 interp(const float2& v0,
170
                            const float2& v1,
171
120M
                            const float2& t) {
172
120M
    return v0 + (v1 - v0) * t;
173
120M
}
174
175
40.1M
void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) {
176
40.1M
    SkASSERT(t > 0 && t < SK_Scalar1);
177
178
40.1M
    float2 p0 = from_point(src[0]);
179
40.1M
    float2 p1 = from_point(src[1]);
180
40.1M
    float2 p2 = from_point(src[2]);
181
40.1M
    float2 tt(t);
182
183
40.1M
    float2 p01 = interp(p0, p1, tt);
184
40.1M
    float2 p12 = interp(p1, p2, tt);
185
186
40.1M
    dst[0] = to_point(p0);
187
40.1M
    dst[1] = to_point(p01);
188
40.1M
    dst[2] = to_point(interp(p01, p12, tt));
189
40.1M
    dst[3] = to_point(p12);
190
40.1M
    dst[4] = to_point(p2);
191
40.1M
}
192
193
20.5M
void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) {
194
20.5M
    SkChopQuadAt(src, dst, 0.5f);
195
20.5M
}
196
197
0
float SkMeasureAngleBetweenVectors(SkVector a, SkVector b) {
198
0
    float cosTheta = sk_ieee_float_divide(a.dot(b), sqrtf(a.dot(a) * b.dot(b)));
199
    // Pin cosTheta such that if it is NaN (e.g., if a or b was 0), then we return acos(1) = 0.
200
0
    cosTheta = std::max(std::min(1.f, cosTheta), -1.f);
201
0
    return acosf(cosTheta);
202
0
}
203
204
0
SkVector SkFindBisector(SkVector a, SkVector b) {
205
0
    std::array<SkVector, 2> v;
206
0
    if (a.dot(b) >= 0) {
207
        // a,b are within +/-90 degrees apart.
208
0
        v = {a, b};
209
0
    } else if (a.cross(b) >= 0) {
210
        // a,b are >90 degrees apart. Find the bisector of their interior normals instead. (Above 90
211
        // degrees, the original vectors start cancelling each other out which eventually becomes
212
        // unstable.)
213
0
        v[0].set(-a.fY, +a.fX);
214
0
        v[1].set(+b.fY, -b.fX);
215
0
    } else {
216
        // a,b are <-90 degrees apart. Find the bisector of their interior normals instead. (Below
217
        // -90 degrees, the original vectors start cancelling each other out which eventually
218
        // becomes unstable.)
219
0
        v[0].set(+a.fY, -a.fX);
220
0
        v[1].set(-b.fY, +b.fX);
221
0
    }
222
    // Return "normalize(v[0]) + normalize(v[1])".
223
0
    skvx::float2 x0_x1{v[0].fX, v[1].fX};
224
0
    skvx::float2 y0_y1{v[0].fY, v[1].fY};
225
0
    auto invLengths = 1.0f / sqrt(x0_x1 * x0_x1 + y0_y1 * y0_y1);
226
0
    x0_x1 *= invLengths;
227
0
    y0_y1 *= invLengths;
228
0
    return SkPoint{x0_x1[0] + x0_x1[1], y0_y1[0] + y0_y1[1]};
229
0
}
230
231
0
float SkFindQuadMidTangent(const SkPoint src[3]) {
232
    // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
233
    // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
234
    //
235
    //     n dot midtangent = 0
236
    //
237
0
    SkVector tan0 = src[1] - src[0];
238
0
    SkVector tan1 = src[2] - src[1];
239
0
    SkVector bisector = SkFindBisector(tan0, -tan1);
240
241
    // The midtangent can be found where (F' dot bisector) = 0:
242
    //
243
    //   0 = (F'(T) dot bisector) = |2*T 1| * |p0 - 2*p1 + p2| * |bisector.x|
244
    //                                        |-2*p0 + 2*p1  |   |bisector.y|
245
    //
246
    //                     = |2*T 1| * |tan1 - tan0| * |nx|
247
    //                                 |2*tan0     |   |ny|
248
    //
249
    //                     = 2*T * ((tan1 - tan0) dot bisector) + (2*tan0 dot bisector)
250
    //
251
    //   T = (tan0 dot bisector) / ((tan0 - tan1) dot bisector)
252
0
    float T = sk_ieee_float_divide(tan0.dot(bisector), (tan0 - tan1).dot(bisector));
253
0
    if (!(T > 0 && T < 1)) {  // Use "!(positive_logic)" so T=nan will take this branch.
254
0
        T = .5;  // The quadratic was a line or near-line. Just chop at .5.
255
0
    }
256
257
0
    return T;
258
0
}
259
260
/** Quad'(t) = At + B, where
261
    A = 2(a - 2b + c)
262
    B = 2(b - a)
263
    Solve for t, only if it fits between 0 < t < 1
264
*/
265
16.5M
int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) {
266
    /*  At + B == 0
267
        t = -B / A
268
    */
269
16.5M
    return valid_unit_divide(a - b, a - b - b + c, tValue);
270
16.5M
}
271
272
6.51M
static inline void flatten_double_quad_extrema(SkScalar coords[14]) {
273
6.51M
    coords[2] = coords[6] = coords[4];
274
6.51M
}
275
276
/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
277
 stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
278
 */
279
78.4M
int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) {
280
78.4M
    SkASSERT(src);
281
78.4M
    SkASSERT(dst);
282
283
78.4M
    SkScalar a = src[0].fY;
284
78.4M
    SkScalar b = src[1].fY;
285
78.4M
    SkScalar c = src[2].fY;
286
287
78.4M
    if (is_not_monotonic(a, b, c)) {
288
9.59M
        SkScalar    tValue;
289
9.59M
        if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
290
2.16M
            SkChopQuadAt(src, dst, tValue);
291
2.16M
            flatten_double_quad_extrema(&dst[0].fY);
292
2.16M
            return 1;
293
2.16M
        }
294
        // if we get here, we need to force dst to be monotonic, even though
295
        // we couldn't compute a unit_divide value (probably underflow).
296
7.43M
        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
297
7.43M
    }
298
76.2M
    dst[0].set(src[0].fX, a);
299
76.2M
    dst[1].set(src[1].fX, b);
300
76.2M
    dst[2].set(src[2].fX, c);
301
76.2M
    return 0;
302
78.4M
}
SkChopQuadAtYExtrema(SkPoint const*, SkPoint*)
Line
Count
Source
279
78.4M
int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) {
280
78.4M
    SkASSERT(src);
281
78.4M
    SkASSERT(dst);
282
283
78.4M
    SkScalar a = src[0].fY;
284
78.4M
    SkScalar b = src[1].fY;
285
78.4M
    SkScalar c = src[2].fY;
286
287
78.4M
    if (is_not_monotonic(a, b, c)) {
288
9.59M
        SkScalar    tValue;
289
9.59M
        if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
290
2.16M
            SkChopQuadAt(src, dst, tValue);
291
2.16M
            flatten_double_quad_extrema(&dst[0].fY);
292
2.16M
            return 1;
293
2.16M
        }
294
        // if we get here, we need to force dst to be monotonic, even though
295
        // we couldn't compute a unit_divide value (probably underflow).
296
7.43M
        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
297
7.43M
    }
298
76.2M
    dst[0].set(src[0].fX, a);
299
76.2M
    dst[1].set(src[1].fX, b);
300
76.2M
    dst[2].set(src[2].fX, c);
301
76.2M
    return 0;
302
78.4M
}
Unexecuted instantiation: SkChopQuadAtYExtrema(SkPoint const*, SkPoint*)
303
304
/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
305
    stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
306
 */
307
79.1M
int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]) {
308
79.1M
    SkASSERT(src);
309
79.1M
    SkASSERT(dst);
310
311
79.1M
    SkScalar a = src[0].fX;
312
79.1M
    SkScalar b = src[1].fX;
313
79.1M
    SkScalar c = src[2].fX;
314
315
79.1M
    if (is_not_monotonic(a, b, c)) {
316
18.9M
        SkScalar tValue;
317
18.9M
        if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
318
4.34M
            SkChopQuadAt(src, dst, tValue);
319
4.34M
            flatten_double_quad_extrema(&dst[0].fX);
320
4.34M
            return 1;
321
4.34M
        }
322
        // if we get here, we need to force dst to be monotonic, even though
323
        // we couldn't compute a unit_divide value (probably underflow).
324
14.6M
        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
325
14.6M
    }
326
74.7M
    dst[0].set(a, src[0].fY);
327
74.7M
    dst[1].set(b, src[1].fY);
328
74.7M
    dst[2].set(c, src[2].fY);
329
74.7M
    return 0;
330
79.1M
}
SkChopQuadAtXExtrema(SkPoint const*, SkPoint*)
Line
Count
Source
307
79.1M
int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]) {
308
79.1M
    SkASSERT(src);
309
79.1M
    SkASSERT(dst);
310
311
79.1M
    SkScalar a = src[0].fX;
312
79.1M
    SkScalar b = src[1].fX;
313
79.1M
    SkScalar c = src[2].fX;
314
315
79.1M
    if (is_not_monotonic(a, b, c)) {
316
18.9M
        SkScalar tValue;
317
18.9M
        if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
318
4.34M
            SkChopQuadAt(src, dst, tValue);
319
4.34M
            flatten_double_quad_extrema(&dst[0].fX);
320
4.34M
            return 1;
321
4.34M
        }
322
        // if we get here, we need to force dst to be monotonic, even though
323
        // we couldn't compute a unit_divide value (probably underflow).
324
14.6M
        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
325
14.6M
    }
326
74.7M
    dst[0].set(a, src[0].fY);
327
74.7M
    dst[1].set(b, src[1].fY);
328
74.7M
    dst[2].set(c, src[2].fY);
329
74.7M
    return 0;
330
79.1M
}
Unexecuted instantiation: SkChopQuadAtXExtrema(SkPoint const*, SkPoint*)
331
332
//  F(t)    = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2
333
//  F'(t)   = 2 (b - a) + 2 (a - 2b + c) t
334
//  F''(t)  = 2 (a - 2b + c)
335
//
336
//  A = 2 (b - a)
337
//  B = 2 (a - 2b + c)
338
//
339
//  Maximum curvature for a quadratic means solving
340
//  Fx' Fx'' + Fy' Fy'' = 0
341
//
342
//  t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2)
343
//
344
1.61M
SkScalar SkFindQuadMaxCurvature(const SkPoint src[3]) {
345
1.61M
    SkScalar    Ax = src[1].fX - src[0].fX;
346
1.61M
    SkScalar    Ay = src[1].fY - src[0].fY;
347
1.61M
    SkScalar    Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
348
1.61M
    SkScalar    By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
349
350
1.61M
    SkScalar numer = -(Ax * Bx + Ay * By);
351
1.61M
    SkScalar denom = Bx * Bx + By * By;
352
1.61M
    if (denom < 0) {
353
0
        numer = -numer;
354
0
        denom = -denom;
355
0
    }
356
1.61M
    if (numer <= 0) {
357
513k
        return 0;
358
513k
    }
359
1.10M
    if (numer >= denom) {  // Also catches denom=0.
360
471k
        return 1;
361
471k
    }
362
633k
    SkScalar t = numer / denom;
363
633k
    SkASSERT((0 <= t && t < 1) || SkIsNaN(t));
364
633k
    return t;
365
1.10M
}
SkFindQuadMaxCurvature(SkPoint const*)
Line
Count
Source
344
1.61M
SkScalar SkFindQuadMaxCurvature(const SkPoint src[3]) {
345
1.61M
    SkScalar    Ax = src[1].fX - src[0].fX;
346
1.61M
    SkScalar    Ay = src[1].fY - src[0].fY;
347
1.61M
    SkScalar    Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
348
1.61M
    SkScalar    By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
349
350
1.61M
    SkScalar numer = -(Ax * Bx + Ay * By);
351
1.61M
    SkScalar denom = Bx * Bx + By * By;
352
1.61M
    if (denom < 0) {
353
0
        numer = -numer;
354
0
        denom = -denom;
355
0
    }
356
1.61M
    if (numer <= 0) {
357
513k
        return 0;
358
513k
    }
359
1.10M
    if (numer >= denom) {  // Also catches denom=0.
360
471k
        return 1;
361
471k
    }
362
633k
    SkScalar t = numer / denom;
363
633k
    SkASSERT((0 <= t && t < 1) || SkIsNaN(t));
364
633k
    return t;
365
1.10M
}
Unexecuted instantiation: SkFindQuadMaxCurvature(SkPoint const*)
366
367
229k
int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]) {
368
229k
    SkScalar t = SkFindQuadMaxCurvature(src);
369
229k
    if (t > 0 && t < 1) {
370
204k
        SkChopQuadAt(src, dst, t);
371
204k
        return 2;
372
204k
    } else {
373
24.8k
        memcpy(dst, src, 3 * sizeof(SkPoint));
374
24.8k
        return 1;
375
24.8k
    }
376
229k
}
377
378
4.34k
void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]) {
379
4.34k
    float2 scale(SkDoubleToScalar(2.0 / 3.0));
380
4.34k
    float2 s0 = from_point(src[0]);
381
4.34k
    float2 s1 = from_point(src[1]);
382
4.34k
    float2 s2 = from_point(src[2]);
383
384
4.34k
    dst[0] = to_point(s0);
385
4.34k
    dst[1] = to_point(s0 + (s1 - s0) * scale);
386
4.34k
    dst[2] = to_point(s2 + (s1 - s2) * scale);
387
4.34k
    dst[3] = to_point(s2);
388
4.34k
}
389
390
//////////////////////////////////////////////////////////////////////////////
391
///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
392
//////////////////////////////////////////////////////////////////////////////
393
394
41.4M
static SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t) {
395
41.4M
    SkQuadCoeff coeff;
396
41.4M
    float2 P0 = from_point(src[0]);
397
41.4M
    float2 P1 = from_point(src[1]);
398
41.4M
    float2 P2 = from_point(src[2]);
399
41.4M
    float2 P3 = from_point(src[3]);
400
401
41.4M
    coeff.fA = P3 + 3 * (P1 - P2) - P0;
402
41.4M
    coeff.fB = times_2(P2 - times_2(P1) + P0);
403
41.4M
    coeff.fC = P1 - P0;
404
41.4M
    return to_vector(coeff.eval(t));
405
41.4M
}
406
407
0
static SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t) {
408
0
    float2 P0 = from_point(src[0]);
409
0
    float2 P1 = from_point(src[1]);
410
0
    float2 P2 = from_point(src[2]);
411
0
    float2 P3 = from_point(src[3]);
412
0
    float2 A = P3 + 3 * (P1 - P2) - P0;
413
0
    float2 B = P2 - times_2(P1) + P0;
414
415
0
    return to_vector(A * t + B);
416
0
}
417
418
void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc,
419
46.0M
                   SkVector* tangent, SkVector* curvature) {
420
46.0M
    SkASSERT(src);
421
46.0M
    SkASSERT(t >= 0 && t <= SK_Scalar1);
422
423
46.0M
    if (loc) {
424
46.0M
        *loc = to_point(SkCubicCoeff(src).eval(t));
425
46.0M
    }
426
46.0M
    if (tangent) {
427
        // The derivative equation returns a zero tangent vector when t is 0 or 1, and the
428
        // adjacent control point is equal to the end point. In this case, use the
429
        // next control point or the end points to compute the tangent.
430
41.5M
        if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) {
431
242k
            if (t == 0) {
432
62.9k
                *tangent = src[2] - src[0];
433
179k
            } else {
434
179k
                *tangent = src[3] - src[1];
435
179k
            }
436
242k
            if (!tangent->fX && !tangent->fY) {
437
37.6k
                *tangent = src[3] - src[0];
438
37.6k
            }
439
41.3M
        } else {
440
41.3M
            *tangent = eval_cubic_derivative(src, t);
441
41.3M
        }
442
41.5M
    }
443
46.0M
    if (curvature) {
444
0
        *curvature = eval_cubic_2ndDerivative(src, t);
445
0
    }
446
46.0M
}
447
448
/** Cubic'(t) = At^2 + Bt + C, where
449
    A = 3(-a + 3(b - c) + d)
450
    B = 6(a - 2b + c)
451
    C = 3(b - a)
452
    Solve for t, keeping only those that fit betwee 0 < t < 1
453
*/
454
int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
455
15.9M
                       SkScalar tValues[2]) {
456
    // we divide A,B,C by 3 to simplify
457
15.9M
    SkScalar A = d - a + 3*(b - c);
458
15.9M
    SkScalar B = 2*(a - b - b + c);
459
15.9M
    SkScalar C = b - a;
460
461
15.9M
    return SkFindUnitQuadRoots(A, B, C, tValues);
462
15.9M
}
463
464
// This does not return b when t==1, but it otherwise seems to get better precision than
465
// "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points.
466
// The responsibility falls on the caller to check that t != 1 before calling.
467
template<int N, typename T>
468
inline static skvx::Vec<N,T> unchecked_mix(const skvx::Vec<N,T>& a, const skvx::Vec<N,T>& b,
469
68.1M
                                           const skvx::Vec<N,T>& t) {
470
68.1M
    return (b - a)*t + a;
471
68.1M
}
SkGeometry.cpp:skvx::Vec<2, float> unchecked_mix<2, float>(skvx::Vec<2, float> const&, skvx::Vec<2, float> const&, skvx::Vec<2, float> const&)
Line
Count
Source
469
67.7M
                                           const skvx::Vec<N,T>& t) {
470
67.7M
    return (b - a)*t + a;
471
67.7M
}
SkGeometry.cpp:skvx::Vec<4, float> unchecked_mix<4, float>(skvx::Vec<4, float> const&, skvx::Vec<4, float> const&, skvx::Vec<4, float> const&)
Line
Count
Source
469
399k
                                           const skvx::Vec<N,T>& t) {
470
399k
    return (b - a)*t + a;
471
399k
}
472
473
11.2M
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) {
474
11.2M
    SkASSERT(0 <= t && t <= 1);
475
476
11.2M
    if (t == 1) {
477
469
        memcpy(dst, src, sizeof(SkPoint) * 4);
478
469
        dst[4] = dst[5] = dst[6] = src[3];
479
469
        return;
480
469
    }
481
482
11.2M
    float2 p0 = sk_bit_cast<float2>(src[0]);
483
11.2M
    float2 p1 = sk_bit_cast<float2>(src[1]);
484
11.2M
    float2 p2 = sk_bit_cast<float2>(src[2]);
485
11.2M
    float2 p3 = sk_bit_cast<float2>(src[3]);
486
11.2M
    float2 T = t;
487
488
11.2M
    float2 ab = unchecked_mix(p0, p1, T);
489
11.2M
    float2 bc = unchecked_mix(p1, p2, T);
490
11.2M
    float2 cd = unchecked_mix(p2, p3, T);
491
11.2M
    float2 abc = unchecked_mix(ab, bc, T);
492
11.2M
    float2 bcd = unchecked_mix(bc, cd, T);
493
11.2M
    float2 abcd = unchecked_mix(abc, bcd, T);
494
495
11.2M
    dst[0] = sk_bit_cast<SkPoint>(p0);
496
11.2M
    dst[1] = sk_bit_cast<SkPoint>(ab);
497
11.2M
    dst[2] = sk_bit_cast<SkPoint>(abc);
498
11.2M
    dst[3] = sk_bit_cast<SkPoint>(abcd);
499
11.2M
    dst[4] = sk_bit_cast<SkPoint>(bcd);
500
11.2M
    dst[5] = sk_bit_cast<SkPoint>(cd);
501
11.2M
    dst[6] = sk_bit_cast<SkPoint>(p3);
502
11.2M
}
503
504
57.0k
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[10], float t0, float t1) {
505
57.0k
    SkASSERT(0 <= t0 && t0 <= t1 && t1 <= 1);
506
507
57.0k
    if (t1 == 1) {
508
0
        SkChopCubicAt(src, dst, t0);
509
0
        dst[7] = dst[8] = dst[9] = src[3];
510
0
        return;
511
0
    }
512
513
    // Perform both chops in parallel using 4-lane SIMD.
514
57.0k
    float4 p00, p11, p22, p33, T;
515
57.0k
    p00.lo = p00.hi = sk_bit_cast<float2>(src[0]);
516
57.0k
    p11.lo = p11.hi = sk_bit_cast<float2>(src[1]);
517
57.0k
    p22.lo = p22.hi = sk_bit_cast<float2>(src[2]);
518
57.0k
    p33.lo = p33.hi = sk_bit_cast<float2>(src[3]);
519
57.0k
    T.lo = t0;
520
57.0k
    T.hi = t1;
521
522
57.0k
    float4 ab = unchecked_mix(p00, p11, T);
523
57.0k
    float4 bc = unchecked_mix(p11, p22, T);
524
57.0k
    float4 cd = unchecked_mix(p22, p33, T);
525
57.0k
    float4 abc = unchecked_mix(ab, bc, T);
526
57.0k
    float4 bcd = unchecked_mix(bc, cd, T);
527
57.0k
    float4 abcd = unchecked_mix(abc, bcd, T);
528
57.0k
    float4 middle = unchecked_mix(abc, bcd, skvx::shuffle<2,3,0,1>(T));
529
530
57.0k
    dst[0] = sk_bit_cast<SkPoint>(p00.lo);
531
57.0k
    dst[1] = sk_bit_cast<SkPoint>(ab.lo);
532
57.0k
    dst[2] = sk_bit_cast<SkPoint>(abc.lo);
533
57.0k
    dst[3] = sk_bit_cast<SkPoint>(abcd.lo);
534
57.0k
    middle.store(dst + 4);
535
57.0k
    dst[6] = sk_bit_cast<SkPoint>(abcd.hi);
536
57.0k
    dst[7] = sk_bit_cast<SkPoint>(bcd.hi);
537
57.0k
    dst[8] = sk_bit_cast<SkPoint>(cd.hi);
538
57.0k
    dst[9] = sk_bit_cast<SkPoint>(p33.hi);
539
57.0k
}
SkChopCubicAt(SkPoint const*, SkPoint*, float, float)
Line
Count
Source
504
57.0k
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[10], float t0, float t1) {
505
57.0k
    SkASSERT(0 <= t0 && t0 <= t1 && t1 <= 1);
506
507
57.0k
    if (t1 == 1) {
508
0
        SkChopCubicAt(src, dst, t0);
509
0
        dst[7] = dst[8] = dst[9] = src[3];
510
0
        return;
511
0
    }
512
513
    // Perform both chops in parallel using 4-lane SIMD.
514
57.0k
    float4 p00, p11, p22, p33, T;
515
57.0k
    p00.lo = p00.hi = sk_bit_cast<float2>(src[0]);
516
57.0k
    p11.lo = p11.hi = sk_bit_cast<float2>(src[1]);
517
57.0k
    p22.lo = p22.hi = sk_bit_cast<float2>(src[2]);
518
57.0k
    p33.lo = p33.hi = sk_bit_cast<float2>(src[3]);
519
57.0k
    T.lo = t0;
520
57.0k
    T.hi = t1;
521
522
57.0k
    float4 ab = unchecked_mix(p00, p11, T);
523
57.0k
    float4 bc = unchecked_mix(p11, p22, T);
524
57.0k
    float4 cd = unchecked_mix(p22, p33, T);
525
57.0k
    float4 abc = unchecked_mix(ab, bc, T);
526
57.0k
    float4 bcd = unchecked_mix(bc, cd, T);
527
57.0k
    float4 abcd = unchecked_mix(abc, bcd, T);
528
57.0k
    float4 middle = unchecked_mix(abc, bcd, skvx::shuffle<2,3,0,1>(T));
529
530
57.0k
    dst[0] = sk_bit_cast<SkPoint>(p00.lo);
531
57.0k
    dst[1] = sk_bit_cast<SkPoint>(ab.lo);
532
57.0k
    dst[2] = sk_bit_cast<SkPoint>(abc.lo);
533
57.0k
    dst[3] = sk_bit_cast<SkPoint>(abcd.lo);
534
57.0k
    middle.store(dst + 4);
535
57.0k
    dst[6] = sk_bit_cast<SkPoint>(abcd.hi);
536
57.0k
    dst[7] = sk_bit_cast<SkPoint>(bcd.hi);
537
57.0k
    dst[8] = sk_bit_cast<SkPoint>(cd.hi);
538
57.0k
    dst[9] = sk_bit_cast<SkPoint>(p33.hi);
539
57.0k
}
Unexecuted instantiation: SkChopCubicAt(SkPoint const*, SkPoint*, float, float)
540
541
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[],
542
7.15M
                   const SkScalar tValues[], int tCount) {
543
7.15M
    SkASSERT(std::all_of(tValues, tValues + tCount, [](SkScalar t) { return t >= 0 && t <= 1; }));
544
7.15M
    SkASSERT(std::is_sorted(tValues, tValues + tCount));
545
546
7.15M
    if (dst) {
547
7.15M
        if (tCount == 0) { // nothing to chop
548
6.12M
            memcpy(dst, src, 4*sizeof(SkPoint));
549
6.12M
        } else {
550
1.02M
            int i = 0;
551
1.08M
            for (; i < tCount - 1; i += 2) {
552
                // Do two chops at once.
553
57.0k
                float2 tt = float2::Load(tValues + i);
554
57.0k
                if (i != 0) {
555
0
                    float lastT = tValues[i - 1];
556
0
                    tt = skvx::pin((tt - lastT) / (1 - lastT), float2(0), float2(1));
557
0
                }
558
57.0k
                SkChopCubicAt(src, dst, tt[0], tt[1]);
559
57.0k
                src = dst = dst + 6;
560
57.0k
            }
561
1.02M
            if (i < tCount) {
562
                // Chop the final cubic if there was an odd number of chops.
563
973k
                SkASSERT(i + 1 == tCount);
564
973k
                float t = tValues[i];
565
973k
                if (i != 0) {
566
1.29k
                    float lastT = tValues[i - 1];
567
1.29k
                    t = SkTPin(sk_ieee_float_divide(t - lastT, 1 - lastT), 0.f, 1.f);
568
1.29k
                }
569
973k
                SkChopCubicAt(src, dst, t);
570
973k
            }
571
1.02M
        }
572
7.15M
    }
573
7.15M
}
574
575
10.0M
void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]) {
576
10.0M
    SkChopCubicAt(src, dst, 0.5f);
577
10.0M
}
578
579
0
float SkMeasureNonInflectCubicRotation(const SkPoint pts[4]) {
580
0
    SkVector a = pts[1] - pts[0];
581
0
    SkVector b = pts[2] - pts[1];
582
0
    SkVector c = pts[3] - pts[2];
583
0
    if (a.isZero()) {
584
0
        return SkMeasureAngleBetweenVectors(b, c);
585
0
    }
586
0
    if (b.isZero()) {
587
0
        return SkMeasureAngleBetweenVectors(a, c);
588
0
    }
589
0
    if (c.isZero()) {
590
0
        return SkMeasureAngleBetweenVectors(a, b);
591
0
    }
592
    // Postulate: When no points are colocated and there are no inflection points in T=0..1, the
593
    // rotation is: 360 degrees, minus the angle [p0,p1,p2], minus the angle [p1,p2,p3].
594
0
    return 2*SK_ScalarPI - SkMeasureAngleBetweenVectors(a,-b) - SkMeasureAngleBetweenVectors(b,-c);
595
0
}
596
597
0
static skvx::float4 fma(const skvx::float4& f, float m, const skvx::float4& a) {
598
0
    return skvx::fma(f, skvx::float4(m), a);
599
0
}
600
601
// Finds the root nearest 0.5. Returns 0.5 if the roots are undefined or outside 0..1.
602
0
static float solve_quadratic_equation_for_midtangent(float a, float b, float c, float discr) {
603
    // Quadratic formula from Numerical Recipes in C:
604
0
    float q = -.5f * (b + copysignf(sqrtf(discr), b));
605
    // The roots are q/a and c/q. Pick the midtangent closer to T=.5.
606
0
    float _5qa = -.5f*q*a;
607
0
    float T = fabsf(q*q + _5qa) < fabsf(a*c + _5qa) ? sk_ieee_float_divide(q,a)
608
0
                                                    : sk_ieee_float_divide(c,q);
609
0
    if (!(T > 0 && T < 1)) {  // Use "!(positive_logic)" so T=NaN will take this branch.
610
        // Either the curve is a flat line with no rotation or FP precision failed us. Chop at .5.
611
0
        T = .5;
612
0
    }
613
0
    return T;
614
0
}
615
616
0
static float solve_quadratic_equation_for_midtangent(float a, float b, float c) {
617
0
    return solve_quadratic_equation_for_midtangent(a, b, c, b*b - 4*a*c);
618
0
}
619
620
0
float SkFindCubicMidTangent(const SkPoint src[4]) {
621
    // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
622
    // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
623
    //
624
    //     bisector dot midtangent == 0
625
    //
626
0
    SkVector tan0 = (src[0] == src[1]) ? src[2] - src[0] : src[1] - src[0];
627
0
    SkVector tan1 = (src[2] == src[3]) ? src[3] - src[1] : src[3] - src[2];
628
0
    SkVector bisector = SkFindBisector(tan0, -tan1);
629
630
    // Find the T value at the midtangent. This is a simple quadratic equation:
631
    //
632
    //     midtangent dot bisector == 0, or using a tangent matrix C' in power basis form:
633
    //
634
    //                   |C'x  C'y|
635
    //     |T^2  T  1| * |.    .  | * |bisector.x| == 0
636
    //                   |.    .  |   |bisector.y|
637
    //
638
    // The coeffs for the quadratic equation we need to solve are therefore:  C' * bisector
639
0
    static const skvx::float4 kM[4] = {skvx::float4(-1,  2, -1,  0),
640
0
                                       skvx::float4( 3, -4,  1,  0),
641
0
                                       skvx::float4(-3,  2,  0,  0)};
642
0
    auto C_x = fma(kM[0], src[0].fX,
643
0
               fma(kM[1], src[1].fX,
644
0
               fma(kM[2], src[2].fX, skvx::float4(src[3].fX, 0,0,0))));
645
0
    auto C_y = fma(kM[0], src[0].fY,
646
0
               fma(kM[1], src[1].fY,
647
0
               fma(kM[2], src[2].fY, skvx::float4(src[3].fY, 0,0,0))));
648
0
    auto coeffs = C_x * bisector.x() + C_y * bisector.y();
649
650
    // Now solve the quadratic for T.
651
0
    float T = 0;
652
0
    float a=coeffs[0], b=coeffs[1], c=coeffs[2];
653
0
    float discr = b*b - 4*a*c;
654
0
    if (discr > 0) {  // This will only be false if the curve is a line.
655
0
        return solve_quadratic_equation_for_midtangent(a, b, c, discr);
656
0
    } else {
657
        // This is a 0- or 360-degree flat line. It doesn't have single points of midtangent.
658
        // (tangent == midtangent at every point on the curve except the cusp points.)
659
        // Chop in between both cusps instead, if any. There can be up to two cusps on a flat line,
660
        // both where the tangent is perpendicular to the starting tangent:
661
        //
662
        //     tangent dot tan0 == 0
663
        //
664
0
        coeffs = C_x * tan0.x() + C_y * tan0.y();
665
0
        a = coeffs[0];
666
0
        b = coeffs[1];
667
0
        if (a != 0) {
668
            // We want the point in between both cusps. The midpoint of:
669
            //
670
            //     (-b +/- sqrt(b^2 - 4*a*c)) / (2*a)
671
            //
672
            // Is equal to:
673
            //
674
            //     -b / (2*a)
675
0
            T = -b / (2*a);
676
0
        }
677
0
        if (!(T > 0 && T < 1)) {  // Use "!(positive_logic)" so T=NaN will take this branch.
678
            // Either the curve is a flat line with no rotation or FP precision failed us. Chop at
679
            // .5.
680
0
            T = .5;
681
0
        }
682
0
        return T;
683
0
    }
684
0
}
685
686
1.07M
static void flatten_double_cubic_extrema(SkScalar coords[14]) {
687
1.07M
    coords[4] = coords[8] = coords[6];
688
1.07M
}
689
690
/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
691
    the resulting beziers are monotonic in Y. This is called by the scan
692
    converter.  Depending on what is returned, dst[] is treated as follows:
693
    0   dst[0..3] is the original cubic
694
    1   dst[0..3] and dst[3..6] are the two new cubics
695
    2   dst[0..3], dst[3..6], dst[6..9] are the three new cubics
696
    If dst == null, it is ignored and only the count is returned.
697
*/
698
3.30M
int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) {
699
3.30M
    SkScalar    tValues[2];
700
3.30M
    int         roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
701
3.30M
                                           src[3].fY, tValues);
702
703
3.30M
    SkChopCubicAt(src, dst, tValues, roots);
704
3.30M
    if (dst && roots > 0) {
705
        // we do some cleanup to ensure our Y extrema are flat
706
620k
        flatten_double_cubic_extrema(&dst[0].fY);
707
620k
        if (roots == 2) {
708
43.8k
            flatten_double_cubic_extrema(&dst[3].fY);
709
43.8k
        }
710
620k
    }
711
3.30M
    return roots;
712
3.30M
}
713
714
3.85M
int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]) {
715
3.85M
    SkScalar    tValues[2];
716
3.85M
    int         roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
717
3.85M
                                           src[3].fX, tValues);
718
719
3.85M
    SkChopCubicAt(src, dst, tValues, roots);
720
3.85M
    if (dst && roots > 0) {
721
        // we do some cleanup to ensure our Y extrema are flat
722
404k
        flatten_double_cubic_extrema(&dst[0].fX);
723
404k
        if (roots == 2) {
724
10.2k
            flatten_double_cubic_extrema(&dst[3].fX);
725
10.2k
        }
726
404k
    }
727
3.85M
    return roots;
728
3.85M
}
729
730
/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html
731
732
    Inflection means that curvature is zero.
733
    Curvature is [F' x F''] / [F'^3]
734
    So we solve F'x X F''y - F'y X F''y == 0
735
    After some canceling of the cubic term, we get
736
    A = b - a
737
    B = c - 2b + a
738
    C = d - 3c + 3b - a
739
    (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0
740
*/
741
908k
int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]) {
742
908k
    SkScalar    Ax = src[1].fX - src[0].fX;
743
908k
    SkScalar    Ay = src[1].fY - src[0].fY;
744
908k
    SkScalar    Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
745
908k
    SkScalar    By = src[2].fY - 2 * src[1].fY + src[0].fY;
746
908k
    SkScalar    Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
747
908k
    SkScalar    Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
748
749
908k
    return SkFindUnitQuadRoots(Bx*Cy - By*Cx,
750
908k
                               Ax*Cy - Ay*Cx,
751
908k
                               Ax*By - Ay*Bx,
752
908k
                               tValues);
753
908k
}
754
755
1.75k
int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10]) {
756
1.75k
    SkScalar    tValues[2];
757
1.75k
    int         count = SkFindCubicInflections(src, tValues);
758
759
1.75k
    if (dst) {
760
1.75k
        if (count == 0) {
761
1.58k
            memcpy(dst, src, 4 * sizeof(SkPoint));
762
1.58k
        } else {
763
166
            SkChopCubicAt(src, dst, tValues, count);
764
166
        }
765
1.75k
    }
766
1.75k
    return count + 1;
767
1.75k
}
768
769
// Assumes the third component of points is 1.
770
// Calcs p0 . (p1 x p2)
771
1.96M
static double calc_dot_cross_cubic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
772
1.96M
    const double xComp = (double) p0.fX * ((double) p1.fY - (double) p2.fY);
773
1.96M
    const double yComp = (double) p0.fY * ((double) p2.fX - (double) p1.fX);
774
1.96M
    const double wComp = (double) p1.fX * (double) p2.fY - (double) p1.fY * (double) p2.fX;
775
1.96M
    return (xComp + yComp + wComp);
776
1.96M
}
777
778
// Returns a positive power of 2 that, when multiplied by n, and excepting the two edge cases listed
779
// below, shifts the exponent of n to yield a magnitude somewhere inside [1..2).
780
// Returns 2^1023 if abs(n) < 2^-1022 (including 0).
781
// Returns NaN if n is Inf or NaN.
782
656k
inline static double previous_inverse_pow2(double n) {
783
656k
    uint64_t bits;
784
656k
    memcpy(&bits, &n, sizeof(double));
785
656k
    bits = ((1023llu*2 << 52) + ((1llu << 52) - 1)) - bits; // exp=-exp
786
656k
    bits &= (0x7ffllu) << 52; // mantissa=1.0, sign=0
787
656k
    memcpy(&n, &bits, sizeof(double));
788
656k
    return n;
789
656k
}
790
791
inline static void write_cubic_inflection_roots(double t0, double s0, double t1, double s1,
792
656k
                                                double* t, double* s) {
793
656k
    t[0] = t0;
794
656k
    s[0] = s0;
795
796
    // This copysign/abs business orients the implicit function so positive values are always on the
797
    // "left" side of the curve.
798
656k
    t[1] = -copysign(t1, t1 * s1);
799
656k
    s[1] = -fabs(s1);
800
801
    // Ensure t[0]/s[0] <= t[1]/s[1] (s[1] is negative from above).
802
656k
    if (copysign(s[1], s[0]) * t[0] > -fabs(s[0]) * t[1]) {
803
372k
        using std::swap;
804
372k
        swap(t[0], t[1]);
805
372k
        swap(s[0], s[1]);
806
372k
    }
807
656k
}
808
809
656k
SkCubicType SkClassifyCubic(const SkPoint P[4], double t[2], double s[2], double d[4]) {
810
    // Find the cubic's inflection function, I = [T^3  -3T^2  3T  -1] dot D. (D0 will always be 0
811
    // for integral cubics.)
812
    //
813
    // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
814
    // 4.2 Curve Categorization:
815
    //
816
    // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
817
656k
    double A1 = calc_dot_cross_cubic(P[0], P[3], P[2]);
818
656k
    double A2 = calc_dot_cross_cubic(P[1], P[0], P[3]);
819
656k
    double A3 = calc_dot_cross_cubic(P[2], P[1], P[0]);
820
821
656k
    double D3 = 3 * A3;
822
656k
    double D2 = D3 - A2;
823
656k
    double D1 = D2 - A2 + A1;
824
825
    // Shift the exponents in D so the largest magnitude falls somewhere in 1..2. This protects us
826
    // from overflow down the road while solving for roots and KLM functionals.
827
656k
    double Dmax = std::max(std::max(fabs(D1), fabs(D2)), fabs(D3));
828
656k
    double norm = previous_inverse_pow2(Dmax);
829
656k
    D1 *= norm;
830
656k
    D2 *= norm;
831
656k
    D3 *= norm;
832
833
656k
    if (d) {
834
0
        d[3] = D3;
835
0
        d[2] = D2;
836
0
        d[1] = D1;
837
0
        d[0] = 0;
838
0
    }
839
840
    // Now use the inflection function to classify the cubic.
841
    //
842
    // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
843
    // 4.4 Integral Cubics:
844
    //
845
    // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
846
656k
    if (0 != D1) {
847
654k
        double discr = 3*D2*D2 - 4*D1*D3;
848
654k
        if (discr > 0) { // Serpentine.
849
316k
            if (t && s) {
850
316k
                double q = 3*D2 + copysign(sqrt(3*discr), D2);
851
316k
                write_cubic_inflection_roots(q, 6*D1, 2*D3, q, t, s);
852
316k
            }
853
316k
            return SkCubicType::kSerpentine;
854
338k
        } else if (discr < 0) { // Loop.
855
254k
            if (t && s) {
856
254k
                double q = D2 + copysign(sqrt(-discr), D2);
857
254k
                write_cubic_inflection_roots(q, 2*D1, 2*(D2*D2 - D3*D1), D1*q, t, s);
858
254k
            }
859
254k
            return SkCubicType::kLoop;
860
254k
        } else { // Cusp.
861
83.7k
            if (t && s) {
862
83.7k
                write_cubic_inflection_roots(D2, 2*D1, D2, 2*D1, t, s);
863
83.7k
            }
864
83.7k
            return SkCubicType::kLocalCusp;
865
83.7k
        }
866
654k
    } else {
867
1.62k
        if (0 != D2) { // Cusp at T=infinity.
868
1.60k
            if (t && s) {
869
1.60k
                write_cubic_inflection_roots(D3, 3*D2, 1, 0, t, s); // T1=infinity.
870
1.60k
            }
871
1.60k
            return SkCubicType::kCuspAtInfinity;
872
1.60k
        } else { // Degenerate.
873
19
            if (t && s) {
874
19
                write_cubic_inflection_roots(1, 0, 1, 0, t, s); // T0=T1=infinity.
875
19
            }
876
19
            return 0 != D3 ? SkCubicType::kQuadratic : SkCubicType::kLineOrPoint;
877
19
        }
878
1.62k
    }
879
656k
}
880
881
163k
template <typename T> void bubble_sort(T array[], int count) {
882
491k
    for (int i = count - 1; i > 0; --i)
883
819k
        for (int j = i; j > 0; --j)
884
491k
            if (array[j] < array[j-1])
885
107k
            {
886
107k
                T   tmp(array[j]);
887
107k
                array[j] = array[j-1];
888
107k
                array[j-1] = tmp;
889
107k
            }
890
163k
}
891
892
/**
893
 *  Given an array and count, remove all pair-wise duplicates from the array,
894
 *  keeping the existing sorting, and return the new count
895
 */
896
163k
static int collaps_duplicates(SkScalar array[], int count) {
897
491k
    for (int n = count; n > 1; --n) {
898
327k
        if (array[0] == array[1]) {
899
192k
            for (int i = 1; i < n; ++i) {
900
109k
                array[i - 1] = array[i];
901
109k
            }
902
83.0k
            count -= 1;
903
244k
        } else {
904
244k
            array += 1;
905
244k
        }
906
327k
    }
907
163k
    return count;
908
163k
}
909
910
#ifdef SK_DEBUG
911
912
0
#define TEST_COLLAPS_ENTRY(array)   array, std::size(array)
913
914
0
static void test_collaps_duplicates() {
915
0
    static bool gOnce;
916
0
    if (gOnce) { return; }
917
0
    gOnce = true;
918
0
    const SkScalar src0[] = { 0 };
919
0
    const SkScalar src1[] = { 0, 0 };
920
0
    const SkScalar src2[] = { 0, 1 };
921
0
    const SkScalar src3[] = { 0, 0, 0 };
922
0
    const SkScalar src4[] = { 0, 0, 1 };
923
0
    const SkScalar src5[] = { 0, 1, 1 };
924
0
    const SkScalar src6[] = { 0, 1, 2 };
925
0
    const struct {
926
0
        const SkScalar* fData;
927
0
        int fCount;
928
0
        int fCollapsedCount;
929
0
    } data[] = {
930
0
        { TEST_COLLAPS_ENTRY(src0), 1 },
931
0
        { TEST_COLLAPS_ENTRY(src1), 1 },
932
0
        { TEST_COLLAPS_ENTRY(src2), 2 },
933
0
        { TEST_COLLAPS_ENTRY(src3), 1 },
934
0
        { TEST_COLLAPS_ENTRY(src4), 2 },
935
0
        { TEST_COLLAPS_ENTRY(src5), 2 },
936
0
        { TEST_COLLAPS_ENTRY(src6), 3 },
937
0
    };
938
0
    for (size_t i = 0; i < std::size(data); ++i) {
939
0
        SkScalar dst[3];
940
0
        memcpy(dst, data[i].fData, data[i].fCount * sizeof(dst[0]));
941
0
        int count = collaps_duplicates(dst, data[i].fCount);
942
0
        SkASSERT(data[i].fCollapsedCount == count);
943
0
        for (int j = 1; j < count; ++j) {
944
0
            SkASSERT(dst[j-1] < dst[j]);
945
0
        }
946
0
    }
947
0
}
948
#endif
949
950
291k
static SkScalar SkScalarCubeRoot(SkScalar x) {
951
291k
    return SkScalarPow(x, 0.3333333f);
952
291k
}
953
954
/*  Solve coeff(t) == 0, returning the number of roots that
955
    lie withing 0 < t < 1.
956
    coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3]
957
958
    Eliminates repeated roots (so that all tValues are distinct, and are always
959
    in increasing order.
960
*/
961
886k
static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) {
962
886k
    if (SkScalarNearlyZero(coeff[0])) {  // we're just a quadratic
963
430k
        return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
964
430k
    }
965
966
455k
    SkScalar a, b, c, Q, R;
967
968
455k
    {
969
455k
        SkASSERT(coeff[0] != 0);
970
971
455k
        SkScalar inva = SkScalarInvert(coeff[0]);
972
455k
        a = coeff[1] * inva;
973
455k
        b = coeff[2] * inva;
974
455k
        c = coeff[3] * inva;
975
455k
    }
976
455k
    Q = (a*a - b*3) / 9;
977
455k
    R = (2*a*a*a - 9*a*b + 27*c) / 54;
978
979
455k
    SkScalar Q3 = Q * Q * Q;
980
455k
    SkScalar R2MinusQ3 = R * R - Q3;
981
455k
    SkScalar adiv3 = a / 3;
982
983
455k
    if (R2MinusQ3 < 0) { // we have 3 real roots
984
        // the divide/root can, due to finite precisions, be slightly outside of -1...1
985
163k
        SkScalar theta = SkScalarACos(SkTPin(R / SkScalarSqrt(Q3), -1.0f, 1.0f));
986
163k
        SkScalar neg2RootQ = -2 * SkScalarSqrt(Q);
987
988
163k
        tValues[0] = SkTPin(neg2RootQ * SkScalarCos(theta/3) - adiv3, 0.0f, 1.0f);
989
163k
        tValues[1] = SkTPin(neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
990
163k
        tValues[2] = SkTPin(neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
991
163k
        SkDEBUGCODE(test_collaps_duplicates();)
992
993
        // now sort the roots
994
163k
        bubble_sort(tValues, 3);
995
163k
        return collaps_duplicates(tValues, 3);
996
291k
    } else {              // we have 1 real root
997
291k
        SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3);
998
291k
        A = SkScalarCubeRoot(A);
999
291k
        if (R > 0) {
1000
45.5k
            A = -A;
1001
45.5k
        }
1002
291k
        if (A != 0) {
1003
244k
            A += Q / A;
1004
244k
        }
1005
291k
        tValues[0] = SkTPin(A - adiv3, 0.0f, 1.0f);
1006
291k
        return 1;
1007
291k
    }
1008
455k
}
SkGeometry.cpp:solve_cubic_poly(float const*, float*)
Line
Count
Source
961
886k
static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) {
962
886k
    if (SkScalarNearlyZero(coeff[0])) {  // we're just a quadratic
963
430k
        return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
964
430k
    }
965
966
455k
    SkScalar a, b, c, Q, R;
967
968
455k
    {
969
455k
        SkASSERT(coeff[0] != 0);
970
971
455k
        SkScalar inva = SkScalarInvert(coeff[0]);
972
455k
        a = coeff[1] * inva;
973
455k
        b = coeff[2] * inva;
974
455k
        c = coeff[3] * inva;
975
455k
    }
976
455k
    Q = (a*a - b*3) / 9;
977
455k
    R = (2*a*a*a - 9*a*b + 27*c) / 54;
978
979
455k
    SkScalar Q3 = Q * Q * Q;
980
455k
    SkScalar R2MinusQ3 = R * R - Q3;
981
455k
    SkScalar adiv3 = a / 3;
982
983
455k
    if (R2MinusQ3 < 0) { // we have 3 real roots
984
        // the divide/root can, due to finite precisions, be slightly outside of -1...1
985
163k
        SkScalar theta = SkScalarACos(SkTPin(R / SkScalarSqrt(Q3), -1.0f, 1.0f));
986
163k
        SkScalar neg2RootQ = -2 * SkScalarSqrt(Q);
987
988
163k
        tValues[0] = SkTPin(neg2RootQ * SkScalarCos(theta/3) - adiv3, 0.0f, 1.0f);
989
163k
        tValues[1] = SkTPin(neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
990
163k
        tValues[2] = SkTPin(neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
991
163k
        SkDEBUGCODE(test_collaps_duplicates();)
992
993
        // now sort the roots
994
163k
        bubble_sort(tValues, 3);
995
163k
        return collaps_duplicates(tValues, 3);
996
291k
    } else {              // we have 1 real root
997
291k
        SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3);
998
291k
        A = SkScalarCubeRoot(A);
999
291k
        if (R > 0) {
1000
45.5k
            A = -A;
1001
45.5k
        }
1002
291k
        if (A != 0) {
1003
244k
            A += Q / A;
1004
244k
        }
1005
291k
        tValues[0] = SkTPin(A - adiv3, 0.0f, 1.0f);
1006
291k
        return 1;
1007
291k
    }
1008
455k
}
Unexecuted instantiation: SkGeometry.cpp:solve_cubic_poly(float const*, float*)
1009
1010
/*  Looking for F' dot F'' == 0
1011
1012
    A = b - a
1013
    B = c - 2b + a
1014
    C = d - 3c + 3b - a
1015
1016
    F' = 3Ct^2 + 6Bt + 3A
1017
    F'' = 6Ct + 6B
1018
1019
    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
1020
*/
1021
1.77M
static void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4]) {
1022
1.77M
    SkScalar    a = src[2] - src[0];
1023
1.77M
    SkScalar    b = src[4] - 2 * src[2] + src[0];
1024
1.77M
    SkScalar    c = src[6] + 3 * (src[2] - src[4]) - src[0];
1025
1026
1.77M
    coeff[0] = c * c;
1027
1.77M
    coeff[1] = 3 * b * c;
1028
1.77M
    coeff[2] = 2 * b * b + c * a;
1029
1.77M
    coeff[3] = a * b;
1030
1.77M
}
1031
1032
/*  Looking for F' dot F'' == 0
1033
1034
    A = b - a
1035
    B = c - 2b + a
1036
    C = d - 3c + 3b - a
1037
1038
    F' = 3Ct^2 + 6Bt + 3A
1039
    F'' = 6Ct + 6B
1040
1041
    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
1042
*/
1043
886k
int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]) {
1044
886k
    SkScalar coeffX[4], coeffY[4];
1045
886k
    int      i;
1046
1047
886k
    formulate_F1DotF2(&src[0].fX, coeffX);
1048
886k
    formulate_F1DotF2(&src[0].fY, coeffY);
1049
1050
4.43M
    for (i = 0; i < 4; i++) {
1051
3.54M
        coeffX[i] += coeffY[i];
1052
3.54M
    }
1053
1054
886k
    int numRoots = solve_cubic_poly(coeffX, tValues);
1055
    // now remove extrema where the curvature is zero (mins)
1056
    // !!!! need a test for this !!!!
1057
886k
    return numRoots;
1058
886k
}
1059
1060
int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13],
1061
9.34k
                              SkScalar tValues[3]) {
1062
9.34k
    SkScalar    t_storage[3];
1063
1064
9.34k
    if (tValues == nullptr) {
1065
0
        tValues = t_storage;
1066
0
    }
1067
1068
9.34k
    SkScalar roots[3];
1069
9.34k
    int rootCount = SkFindCubicMaxCurvature(src, roots);
1070
1071
    // Throw out values not inside 0..1.
1072
9.34k
    int count = 0;
1073
24.5k
    for (int i = 0; i < rootCount; ++i) {
1074
15.2k
        if (0 < roots[i] && roots[i] < 1) {
1075
8.14k
            tValues[count++] = roots[i];
1076
8.14k
        }
1077
15.2k
    }
1078
1079
9.34k
    if (dst) {
1080
9.34k
        if (count == 0) {
1081
5.38k
            memcpy(dst, src, 4 * sizeof(SkPoint));
1082
5.38k
        } else {
1083
3.96k
            SkChopCubicAt(src, dst, tValues, count);
1084
3.96k
        }
1085
9.34k
    }
1086
9.34k
    return count + 1;
1087
9.34k
}
1088
1089
// Returns a constant proportional to the dimensions of the cubic.
1090
// Constant found through experimentation -- maybe there's a better way....
1091
86.4k
static SkScalar calc_cubic_precision(const SkPoint src[4]) {
1092
86.4k
    return (SkPointPriv::DistanceToSqd(src[1], src[0]) + SkPointPriv::DistanceToSqd(src[2], src[1])
1093
86.4k
            + SkPointPriv::DistanceToSqd(src[3], src[2])) * 1e-8f;
1094
86.4k
}
1095
1096
// Returns true if both points src[testIndex], src[testIndex+1] are in the same half plane defined
1097
// by the line segment src[lineIndex], src[lineIndex+1].
1098
968k
static bool on_same_side(const SkPoint src[4], int testIndex, int lineIndex) {
1099
968k
    SkPoint origin = src[lineIndex];
1100
968k
    SkVector line = src[lineIndex + 1] - origin;
1101
968k
    SkScalar crosses[2];
1102
2.90M
    for (int index = 0; index < 2; ++index) {
1103
1.93M
        SkVector testLine = src[testIndex + index] - origin;
1104
1.93M
        crosses[index] = line.cross(testLine);
1105
1.93M
    }
1106
968k
    return crosses[0] * crosses[1] >= 0;
1107
968k
}
1108
1109
// Return location (in t) of cubic cusp, if there is one.
1110
// Note that classify cubic code does not reliably return all cusp'd cubics, so
1111
// it is not called here.
1112
906k
SkScalar SkFindCubicCusp(const SkPoint src[4]) {
1113
    // When the adjacent control point matches the end point, it behaves as if
1114
    // the cubic has a cusp: there's a point of max curvature where the derivative
1115
    // goes to zero. Ideally, this would be where t is zero or one, but math
1116
    // error makes not so. It is not uncommon to create cubics this way; skip them.
1117
906k
    if (src[0] == src[1]) {
1118
12.6k
        return -1;
1119
12.6k
    }
1120
893k
    if (src[2] == src[3]) {
1121
89.7k
        return -1;
1122
89.7k
    }
1123
    // Cubics only have a cusp if the line segments formed by the control and end points cross.
1124
    // Detect crossing if line ends are on opposite sides of plane formed by the other line.
1125
804k
    if (on_same_side(src, 0, 2) || on_same_side(src, 2, 0)) {
1126
672k
        return -1;
1127
672k
    }
1128
    // Cubics may have multiple points of maximum curvature, although at most only
1129
    // one is a cusp.
1130
132k
    SkScalar maxCurvature[3];
1131
132k
    int roots = SkFindCubicMaxCurvature(src, maxCurvature);
1132
164k
    for (int index = 0; index < roots; ++index) {
1133
99.4k
        SkScalar testT = maxCurvature[index];
1134
99.4k
        if (0 >= testT || testT >= 1) {  // no need to consider max curvature on the end
1135
12.9k
            continue;
1136
12.9k
        }
1137
        // A cusp is at the max curvature, and also has a derivative close to zero.
1138
        // Choose the 'close to zero' meaning by comparing the derivative length
1139
        // with the overall cubic size.
1140
86.4k
        SkVector dPt = eval_cubic_derivative(src, testT);
1141
86.4k
        SkScalar dPtMagnitude = SkPointPriv::LengthSqd(dPt);
1142
86.4k
        SkScalar precision = calc_cubic_precision(src);
1143
86.4k
        if (dPtMagnitude < precision) {
1144
            // All three max curvature t values may be close to the cusp;
1145
            // return the first one.
1146
66.6k
            return testT;
1147
66.6k
        }
1148
86.4k
    }
1149
65.4k
    return -1;
1150
132k
}
1151
1152
2.73M
static bool close_enough_to_zero(double x) {
1153
2.73M
    return std::fabs(x) < 0.00001;
1154
2.73M
}
1155
1156
static bool first_axis_intersection(const double coefficients[8], bool yDirection,
1157
2.09M
                                    double axisIntercept, double* solution) {
1158
2.09M
    auto [A, B, C, D] = SkBezierCubic::ConvertToPolynomial(coefficients, yDirection);
1159
2.09M
    D -= axisIntercept;
1160
2.09M
    double roots[3] = {0, 0, 0};
1161
2.09M
    int count = SkCubics::RootsValidT(A, B, C, D, roots);
1162
2.09M
    if (count == 0) {
1163
21.9k
        return false;
1164
21.9k
    }
1165
    // Verify that at least one of the roots is accurate.
1166
2.73M
    for (int i = 0; i < count; i++) {
1167
2.07M
        if (close_enough_to_zero(SkCubics::EvalAt(A, B, C, D, roots[i]))) {
1168
1.42M
            *solution = roots[i];
1169
1.42M
            return true;
1170
1.42M
        }
1171
2.07M
    }
1172
    // None of the roots returned by our normal cubic solver were correct enough
1173
    // (e.g. https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=55732)
1174
    // So we need to fallback to a more accurate solution.
1175
655k
    count = SkCubics::BinarySearchRootsValidT(A, B, C, D, roots);
1176
655k
    if (count == 0) {
1177
0
        return false;
1178
0
    }
1179
655k
    for (int i = 0; i < count; i++) {
1180
655k
        if (close_enough_to_zero(SkCubics::EvalAt(A, B, C, D, roots[i]))) {
1181
655k
            *solution = roots[i];
1182
655k
            return true;
1183
655k
        }
1184
655k
    }
1185
0
    return false;
1186
655k
}
1187
1188
723k
bool SkChopMonoCubicAtY(const SkPoint src[4], SkScalar y, SkPoint dst[7]) {
1189
723k
    double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1190
723k
                              src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1191
723k
    double solution = 0;
1192
723k
    if (first_axis_intersection(coefficients, true, y, &solution)) {
1193
710k
        double cubicPair[14];
1194
710k
        SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1195
5.68M
        for (int i = 0; i < 7; i ++) {
1196
4.97M
            dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1197
4.97M
            dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1198
4.97M
        }
1199
710k
        return true;
1200
710k
    }
1201
12.7k
    return false;
1202
723k
}
1203
1204
1.37M
bool SkChopMonoCubicAtX(const SkPoint src[4], SkScalar x, SkPoint dst[7]) {
1205
1.37M
    double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1206
1.37M
                                  src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1207
1.37M
    double solution = 0;
1208
1.37M
    if (first_axis_intersection(coefficients, false, x, &solution)) {
1209
1.36M
        double cubicPair[14];
1210
1.36M
        SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1211
10.9M
        for (int i = 0; i < 7; i ++) {
1212
9.55M
            dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1213
9.55M
            dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1214
9.55M
        }
1215
1.36M
        return true;
1216
1.36M
    }
1217
9.20k
    return false;
1218
1.37M
}
1219
1220
///////////////////////////////////////////////////////////////////////////////
1221
//
1222
// NURB representation for conics.  Helpful explanations at:
1223
//
1224
// http://citeseerx.ist.psu.edu/viewdoc/
1225
//   download?doi=10.1.1.44.5740&rep=rep1&type=ps
1226
// and
1227
// http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/NURBS/RB-conics.html
1228
//
1229
// F = (A (1 - t)^2 + C t^2 + 2 B (1 - t) t w)
1230
//     ------------------------------------------
1231
//         ((1 - t)^2 + t^2 + 2 (1 - t) t w)
1232
//
1233
//   = {t^2 (P0 + P2 - 2 P1 w), t (-2 P0 + 2 P1 w), P0}
1234
//     ------------------------------------------------
1235
//             {t^2 (2 - 2 w), t (-2 + 2 w), 1}
1236
//
1237
1238
// F' = 2 (C t (1 + t (-1 + w)) - A (-1 + t) (t (-1 + w) - w) + B (1 - 2 t) w)
1239
//
1240
//  t^2 : (2 P0 - 2 P2 - 2 P0 w + 2 P2 w)
1241
//  t^1 : (-2 P0 + 2 P2 + 4 P0 w - 4 P1 w)
1242
//  t^0 : -2 P0 w + 2 P1 w
1243
//
1244
//  We disregard magnitude, so we can freely ignore the denominator of F', and
1245
//  divide the numerator by 2
1246
//
1247
//    coeff[0] for t^2
1248
//    coeff[1] for t^1
1249
//    coeff[2] for t^0
1250
//
1251
static void conic_deriv_coeff(const SkScalar src[],
1252
                              SkScalar w,
1253
18.2M
                              SkScalar coeff[3]) {
1254
18.2M
    const SkScalar P20 = src[4] - src[0];
1255
18.2M
    const SkScalar P10 = src[2] - src[0];
1256
18.2M
    const SkScalar wP10 = w * P10;
1257
18.2M
    coeff[0] = w * P20 - P20;
1258
18.2M
    coeff[1] = P20 - 2 * wP10;
1259
18.2M
    coeff[2] = wP10;
1260
18.2M
}
1261
1262
18.2M
static bool conic_find_extrema(const SkScalar src[], SkScalar w, SkScalar* t) {
1263
18.2M
    SkScalar coeff[3];
1264
18.2M
    conic_deriv_coeff(src, w, coeff);
1265
1266
18.2M
    SkScalar tValues[2];
1267
18.2M
    int roots = SkFindUnitQuadRoots(coeff[0], coeff[1], coeff[2], tValues);
1268
18.2M
    SkASSERT(0 == roots || 1 == roots);
1269
1270
18.2M
    if (1 == roots) {
1271
215k
        *t = tValues[0];
1272
215k
        return true;
1273
215k
    }
1274
17.9M
    return false;
1275
18.2M
}
1276
1277
// We only interpolate one dimension at a time (the first, at +0, +3, +6).
1278
787k
static void p3d_interp(const SkScalar src[7], SkScalar dst[7], SkScalar t) {
1279
787k
    SkScalar ab = SkScalarInterp(src[0], src[3], t);
1280
787k
    SkScalar bc = SkScalarInterp(src[3], src[6], t);
1281
787k
    dst[0] = ab;
1282
787k
    dst[3] = SkScalarInterp(ab, bc, t);
1283
787k
    dst[6] = bc;
1284
787k
}
1285
1286
2.28M
static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkPoint3 dst[3]) {
1287
2.28M
    dst[0].set(src[0].fX * 1, src[0].fY * 1, 1);
1288
2.28M
    dst[1].set(src[1].fX * w, src[1].fY * w, w);
1289
2.28M
    dst[2].set(src[2].fX * 1, src[2].fY * 1, 1);
1290
2.28M
}
1291
1292
787k
static SkPoint project_down(const SkPoint3& src) {
1293
787k
    return {src.fX / src.fZ, src.fY / src.fZ};
1294
787k
}
1295
1296
// return false if infinity or NaN is generated; caller must check
1297
262k
bool SkConic::chopAt(SkScalar t, SkConic dst[2]) const {
1298
262k
    SkPoint3 tmp[3], tmp2[3];
1299
1300
262k
    ratquad_mapTo3D(fPts, fW, tmp);
1301
1302
262k
    p3d_interp(&tmp[0].fX, &tmp2[0].fX, t);
1303
262k
    p3d_interp(&tmp[0].fY, &tmp2[0].fY, t);
1304
262k
    p3d_interp(&tmp[0].fZ, &tmp2[0].fZ, t);
1305
1306
262k
    dst[0].fPts[0] = fPts[0];
1307
262k
    dst[0].fPts[1] = project_down(tmp2[0]);
1308
262k
    dst[0].fPts[2] = project_down(tmp2[1]); dst[1].fPts[0] = dst[0].fPts[2];
1309
262k
    dst[1].fPts[1] = project_down(tmp2[2]);
1310
262k
    dst[1].fPts[2] = fPts[2];
1311
1312
    // to put in "standard form", where w0 and w2 are both 1, we compute the
1313
    // new w1 as sqrt(w1*w1/w0*w2)
1314
    // or
1315
    // w1 /= sqrt(w0*w2)
1316
    //
1317
    // However, in our case, we know that for dst[0]:
1318
    //     w0 == 1, and for dst[1], w2 == 1
1319
    //
1320
262k
    SkScalar root = SkScalarSqrt(tmp2[1].fZ);
1321
262k
    dst[0].fW = tmp2[0].fZ / root;
1322
262k
    dst[1].fW = tmp2[2].fZ / root;
1323
262k
    SkASSERT(sizeof(dst[0]) == sizeof(SkScalar) * 7);
1324
262k
    SkASSERT(0 == offsetof(SkConic, fPts[0].fX));
1325
262k
    return SkIsFinite(&dst[0].fPts[0].fX, 7 * 2);
1326
262k
}
1327
1328
3.15M
void SkConic::chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const {
1329
3.15M
    if (0 == t1 || 1 == t2) {
1330
0
        if (0 == t1 && 1 == t2) {
1331
0
            *dst = *this;
1332
0
            return;
1333
0
        } else {
1334
0
            SkConic pair[2];
1335
0
            if (this->chopAt(t1 ? t1 : t2, pair)) {
1336
0
                *dst = pair[SkToBool(t1)];
1337
0
                return;
1338
0
            }
1339
0
        }
1340
0
    }
1341
3.15M
    SkConicCoeff coeff(*this);
1342
3.15M
    float2 tt1(t1);
1343
3.15M
    float2 aXY = coeff.fNumer.eval(tt1);
1344
3.15M
    float2 aZZ = coeff.fDenom.eval(tt1);
1345
3.15M
    float2 midTT((t1 + t2) / 2);
1346
3.15M
    float2 dXY = coeff.fNumer.eval(midTT);
1347
3.15M
    float2 dZZ = coeff.fDenom.eval(midTT);
1348
3.15M
    float2 tt2(t2);
1349
3.15M
    float2 cXY = coeff.fNumer.eval(tt2);
1350
3.15M
    float2 cZZ = coeff.fDenom.eval(tt2);
1351
3.15M
    float2 bXY = times_2(dXY) - (aXY + cXY) * 0.5f;
1352
3.15M
    float2 bZZ = times_2(dZZ) - (aZZ + cZZ) * 0.5f;
1353
3.15M
    dst->fPts[0] = to_point(aXY / aZZ);
1354
3.15M
    dst->fPts[1] = to_point(bXY / bZZ);
1355
3.15M
    dst->fPts[2] = to_point(cXY / cZZ);
1356
3.15M
    float2 ww = bZZ / sqrt(aZZ * cZZ);
1357
3.15M
    dst->fW = ww[0];
1358
3.15M
}
1359
1360
57.8M
SkPoint SkConic::evalAt(SkScalar t) const {
1361
57.8M
    return to_point(SkConicCoeff(*this).eval(t));
1362
57.8M
}
1363
1364
15.7M
SkVector SkConic::evalTangentAt(SkScalar t) const {
1365
    // The derivative equation returns a zero tangent vector when t is 0 or 1,
1366
    // and the control point is equal to the end point.
1367
    // In this case, use the conic endpoints to compute the tangent.
1368
15.7M
    if ((t == 0 && fPts[0] == fPts[1]) || (t == 1 && fPts[1] == fPts[2])) {
1369
195k
        return fPts[2] - fPts[0];
1370
195k
    }
1371
15.5M
    float2 p0 = from_point(fPts[0]);
1372
15.5M
    float2 p1 = from_point(fPts[1]);
1373
15.5M
    float2 p2 = from_point(fPts[2]);
1374
15.5M
    float2 ww(fW);
1375
1376
15.5M
    float2 p20 = p2 - p0;
1377
15.5M
    float2 p10 = p1 - p0;
1378
1379
15.5M
    float2 C = ww * p10;
1380
15.5M
    float2 A = ww * p20 - p20;
1381
15.5M
    float2 B = p20 - C - C;
1382
1383
15.5M
    return to_vector(SkQuadCoeff(A, B, C).eval(t));
1384
15.7M
}
1385
1386
26.3M
void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const {
1387
26.3M
    SkASSERT(t >= 0 && t <= SK_Scalar1);
1388
1389
26.3M
    if (pt) {
1390
26.3M
        *pt = this->evalAt(t);
1391
26.3M
    }
1392
26.3M
    if (tangent) {
1393
15.7M
        *tangent = this->evalTangentAt(t);
1394
15.7M
    }
1395
26.3M
}
1396
1397
84.1M
static SkScalar subdivide_w_value(SkScalar w) {
1398
84.1M
    return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf);
1399
84.1M
}
1400
1401
#if defined(SK_SUPPORT_LEGACY_CONIC_CHOP)
1402
void SkConic::chop(SkConic * SK_RESTRICT dst) const {
1403
    float2 scale = SkScalarInvert(SK_Scalar1 + fW);
1404
    SkScalar newW = subdivide_w_value(fW);
1405
1406
    float2 p0 = from_point(fPts[0]);
1407
    float2 p1 = from_point(fPts[1]);
1408
    float2 p2 = from_point(fPts[2]);
1409
    float2 ww(fW);
1410
1411
    float2 wp1 = ww * p1;
1412
    float2 m = (p0 + times_2(wp1) + p2) * scale * 0.5f;
1413
    SkPoint mPt = to_point(m);
1414
    if (!mPt.isFinite()) {
1415
        double w_d = fW;
1416
        double w_2 = w_d * 2;
1417
        double scale_half = 1 / (1 + w_d) * 0.5;
1418
        mPt.fX = SkDoubleToScalar((fPts[0].fX + w_2 * fPts[1].fX + fPts[2].fX) * scale_half);
1419
        mPt.fY = SkDoubleToScalar((fPts[0].fY + w_2 * fPts[1].fY + fPts[2].fY) * scale_half);
1420
    }
1421
    dst[0].fPts[0] = fPts[0];
1422
    dst[0].fPts[1] = to_point((p0 + wp1) * scale);
1423
    dst[0].fPts[2] = dst[1].fPts[0] = mPt;
1424
    dst[1].fPts[1] = to_point((wp1 + p2) * scale);
1425
    dst[1].fPts[2] = fPts[2];
1426
1427
    dst[0].fW = dst[1].fW = newW;
1428
}
1429
#else
1430
84.1M
void SkConic::chop(SkConic * SK_RESTRICT dst) const {
1431
1432
    // Observe that scale will always be smaller than 1 because fW > 0.
1433
84.1M
    const float scale = SkScalarInvert(SK_Scalar1 + fW);
1434
1435
    // The subdivided control points below are the sums of the following three terms. Because the
1436
    // terms are multiplied by something <1, and the resulting control points lie within the
1437
    // control points of the original then the terms and the sums below will not overflow. Note
1438
    // that fW * scale approaches 1 as fW becomes very large.
1439
84.1M
    float2 t0 = from_point(fPts[0]) * scale;
1440
84.1M
    float2 t1 = from_point(fPts[1]) * (fW * scale);
1441
84.1M
    float2 t2 = from_point(fPts[2]) * scale;
1442
1443
    // Calculate the subdivided control points
1444
84.1M
    const SkPoint p1 = to_point(t0 + t1);
1445
84.1M
    const SkPoint p3 = to_point(t1 + t2);
1446
1447
    // p2 = (t0 + 2*t1 + t2) / 2. Divide the terms by 2 before the sum to keep the sum for p2
1448
    // from overflowing.
1449
84.1M
    const SkPoint p2 = to_point(0.5f * t0 + t1 + 0.5f * t2);
1450
1451
84.1M
    SkASSERT(p1.isFinite() && p2.isFinite() && p3.isFinite());
1452
1453
84.1M
    dst[0].fPts[0] = fPts[0];
1454
84.1M
    dst[0].fPts[1] = p1;
1455
84.1M
    dst[0].fPts[2] = p2;
1456
84.1M
    dst[1].fPts[0] = p2;
1457
84.1M
    dst[1].fPts[1] = p3;
1458
84.1M
    dst[1].fPts[2] = fPts[2];
1459
1460
    // Update w.
1461
84.1M
    dst[0].fW = dst[1].fW = subdivide_w_value(fW);
1462
84.1M
}
1463
#endif  // SK_SUPPORT_LEGACY_CONIC_CHOP
1464
1465
/*
1466
 *  "High order approximation of conic sections by quadratic splines"
1467
 *      by Michael Floater, 1993
1468
 */
1469
#define AS_QUAD_ERROR_SETUP                                         \
1470
12.1M
    SkScalar a = fW - 1;                                            \
1471
12.1M
    SkScalar k = a / (4 * (2 + a));                                 \
1472
12.1M
    SkScalar x = k * (fPts[0].fX - 2 * fPts[1].fX + fPts[2].fX);    \
1473
12.1M
    SkScalar y = k * (fPts[0].fY - 2 * fPts[1].fY + fPts[2].fY);
1474
1475
0
void SkConic::computeAsQuadError(SkVector* err) const {
1476
0
    AS_QUAD_ERROR_SETUP
1477
0
    err->set(x, y);
1478
0
}
1479
1480
0
bool SkConic::asQuadTol(SkScalar tol) const {
1481
0
    AS_QUAD_ERROR_SETUP
1482
0
    return (x * x + y * y) <= tol * tol;
1483
0
}
1484
1485
// Limit the number of suggested quads to approximate a conic
1486
40.8M
#define kMaxConicToQuadPOW2     5
1487
1488
12.1M
int SkConic::computeQuadPOW2(SkScalar tol) const {
1489
12.1M
    if (tol < 0 || !SkIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3)) {
1490
728
        return 0;
1491
728
    }
1492
1493
12.1M
    AS_QUAD_ERROR_SETUP
1494
1495
12.1M
    SkScalar error = SkScalarSqrt(x * x + y * y);
1496
12.1M
    int pow2;
1497
28.7M
    for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
1498
26.4M
        if (error <= tol) {
1499
9.83M
            break;
1500
9.83M
        }
1501
16.5M
        error *= 0.25f;
1502
16.5M
    }
1503
    // float version -- using ceil gives the same results as the above.
1504
12.1M
    if ((false)) {
1505
0
        SkScalar err = SkScalarSqrt(x * x + y * y);
1506
0
        if (err <= tol) {
1507
0
            return 0;
1508
0
        }
1509
0
        SkScalar tol2 = tol * tol;
1510
0
        if (tol2 == 0) {
1511
0
            return kMaxConicToQuadPOW2;
1512
0
        }
1513
0
        SkScalar fpow2 = SkScalarLog2((x * x + y * y) / tol2) * 0.25f;
1514
0
        int altPow2 = SkScalarCeilToInt(fpow2);
1515
0
        if (altPow2 != pow2) {
1516
0
            SkDebugf("pow2 %d altPow2 %d fbits %g err %g tol %g\n", pow2, altPow2, fpow2, err, tol);
1517
0
        }
1518
0
        pow2 = altPow2;
1519
0
    }
1520
12.1M
    return pow2;
1521
12.1M
}
1522
1523
// This was originally developed and tested for pathops: see SkOpTypes.h
1524
// returns true if (a <= b <= c) || (a >= b >= c)
1525
326M
static bool between(SkScalar a, SkScalar b, SkScalar c) {
1526
326M
    return (a - b) * (c - b) <= 0;
1527
326M
}
1528
1529
175M
static SkPoint* subdivide(const SkConic& src, SkPoint pts[], int level) {
1530
175M
    SkASSERT(level >= 0);
1531
1532
175M
    if (0 == level) {
1533
93.9M
        memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint));
1534
93.9M
        return pts + 2;
1535
93.9M
    } else {
1536
81.8M
        SkConic dst[2];
1537
81.8M
        src.chop(dst);
1538
81.8M
        const SkScalar startY = src.fPts[0].fY;
1539
81.8M
        SkScalar endY = src.fPts[2].fY;
1540
81.8M
        if (between(startY, src.fPts[1].fY, endY)) {
1541
            // If the input is monotonic and the output is not, the scan converter hangs.
1542
            // Ensure that the chopped conics maintain their y-order.
1543
81.5M
            SkScalar midY = dst[0].fPts[2].fY;
1544
81.5M
            if (!between(startY, midY, endY)) {
1545
                // If the computed midpoint is outside the ends, move it to the closer one.
1546
694k
                SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY;
1547
694k
                dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY;
1548
694k
            }
1549
81.5M
            if (!between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)) {
1550
                // If the 1st control is not between the start and end, put it at the start.
1551
                // This also reduces the quad to a line.
1552
849k
                dst[0].fPts[1].fY = startY;
1553
849k
            }
1554
81.5M
            if (!between(dst[1].fPts[0].fY, dst[1].fPts[1].fY, endY)) {
1555
                // If the 2nd control is not between the start and end, put it at the end.
1556
                // This also reduces the quad to a line.
1557
859k
                dst[1].fPts[1].fY = endY;
1558
859k
            }
1559
            // Verify that all five points are in order.
1560
81.5M
            SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY));
1561
81.5M
            SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY));
1562
81.5M
            SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY));
1563
81.5M
        }
1564
81.8M
        --level;
1565
81.8M
        pts = subdivide(dst[0], pts, level);
1566
81.8M
        return subdivide(dst[1], pts, level);
1567
81.8M
    }
1568
175M
}
1569
1570
12.1M
int SkConic::chopIntoQuadsPOW2(SkPoint pts[], int pow2) const {
1571
12.1M
    SkASSERT(pow2 >= 0);
1572
12.1M
    *pts = fPts[0];
1573
12.1M
    SkDEBUGCODE(SkPoint* endPts);
1574
12.1M
    if (pow2 == kMaxConicToQuadPOW2) {  // If an extreme weight generates many quads ...
1575
2.30M
        SkConic dst[2];
1576
2.30M
        this->chop(dst);
1577
        // check to see if the first chop generates a pair of lines
1578
2.30M
        if (SkPointPriv::EqualsWithinTolerance(dst[0].fPts[1], dst[0].fPts[2]) &&
1579
2.30M
                SkPointPriv::EqualsWithinTolerance(dst[1].fPts[0], dst[1].fPts[1])) {
1580
92.7k
            pts[1] = pts[2] = pts[3] = dst[0].fPts[1];  // set ctrl == end to make lines
1581
92.7k
            pts[4] = dst[1].fPts[2];
1582
92.7k
            pow2 = 1;
1583
92.7k
            SkDEBUGCODE(endPts = &pts[5]);
1584
92.7k
            goto commonFinitePtCheck;
1585
92.7k
        }
1586
2.30M
    }
1587
12.0M
    SkDEBUGCODE(endPts = ) subdivide(*this, pts + 1, pow2);
1588
12.1M
commonFinitePtCheck:
1589
12.1M
    const int quadCount = 1 << pow2;
1590
12.1M
    const int ptCount = 2 * quadCount + 1;
1591
12.1M
    SkASSERT(endPts - pts == ptCount);
1592
12.1M
    if (!SkPointPriv::AreFinite(pts, ptCount)) {
1593
        // if we generated a non-finite, pin ourselves to the middle of the hull,
1594
        // as our first and last are already on the first/last pts of the hull.
1595
31.4k
        for (int i = 1; i < ptCount - 1; ++i) {
1596
28.6k
            pts[i] = fPts[1];
1597
28.6k
        }
1598
2.81k
    }
1599
12.1M
    return 1 << pow2;
1600
12.0M
}
1601
1602
0
float SkConic::findMidTangent() const {
1603
    // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
1604
    // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
1605
    //
1606
    //     bisector dot midtangent = 0
1607
    //
1608
0
    SkVector tan0 = fPts[1] - fPts[0];
1609
0
    SkVector tan1 = fPts[2] - fPts[1];
1610
0
    SkVector bisector = SkFindBisector(tan0, -tan1);
1611
1612
    // Start by finding the tangent function's power basis coefficients. These define a tangent
1613
    // direction (scaled by some uniform value) as:
1614
    //                                                |T^2|
1615
    //     Tangent_Direction(T) = dx,dy = |A  B  C| * |T  |
1616
    //                                    |.  .  .|   |1  |
1617
    //
1618
    // The derivative of a conic has a cumbersome order-4 denominator. However, this isn't necessary
1619
    // if we are only interested in a vector in the same *direction* as a given tangent line. Since
1620
    // the denominator scales dx and dy uniformly, we can throw it out completely after evaluating
1621
    // the derivative with the standard quotient rule. This leaves us with a simpler quadratic
1622
    // function that we use to find a tangent.
1623
0
    SkVector A = (fPts[2] - fPts[0]) * (fW - 1);
1624
0
    SkVector B = (fPts[2] - fPts[0]) - (fPts[1] - fPts[0]) * (fW*2);
1625
0
    SkVector C = (fPts[1] - fPts[0]) * fW;
1626
1627
    // Now solve for "bisector dot midtangent = 0":
1628
    //
1629
    //                            |T^2|
1630
    //     bisector * |A  B  C| * |T  | = 0
1631
    //                |.  .  .|   |1  |
1632
    //
1633
0
    float a = bisector.dot(A);
1634
0
    float b = bisector.dot(B);
1635
0
    float c = bisector.dot(C);
1636
0
    return solve_quadratic_equation_for_midtangent(a, b, c);
1637
0
}
1638
1639
9.10M
bool SkConic::findXExtrema(SkScalar* t) const {
1640
9.10M
    return conic_find_extrema(&fPts[0].fX, fW, t);
1641
9.10M
}
1642
1643
9.10M
bool SkConic::findYExtrema(SkScalar* t) const {
1644
9.10M
    return conic_find_extrema(&fPts[0].fY, fW, t);
1645
9.10M
}
1646
1647
0
bool SkConic::chopAtXExtrema(SkConic dst[2]) const {
1648
0
    SkScalar t;
1649
0
    if (this->findXExtrema(&t)) {
1650
0
        if (!this->chopAt(t, dst)) {
1651
            // if chop can't return finite values, don't chop
1652
0
            return false;
1653
0
        }
1654
        // now clean-up the middle, since we know t was meant to be at
1655
        // an X-extrema
1656
0
        SkScalar value = dst[0].fPts[2].fX;
1657
0
        dst[0].fPts[1].fX = value;
1658
0
        dst[1].fPts[0].fX = value;
1659
0
        dst[1].fPts[1].fX = value;
1660
0
        return true;
1661
0
    }
1662
0
    return false;
1663
0
}
1664
1665
0
bool SkConic::chopAtYExtrema(SkConic dst[2]) const {
1666
0
    SkScalar t;
1667
0
    if (this->findYExtrema(&t)) {
1668
0
        if (!this->chopAt(t, dst)) {
1669
            // if chop can't return finite values, don't chop
1670
0
            return false;
1671
0
        }
1672
        // now clean-up the middle, since we know t was meant to be at
1673
        // an Y-extrema
1674
0
        SkScalar value = dst[0].fPts[2].fY;
1675
0
        dst[0].fPts[1].fY = value;
1676
0
        dst[1].fPts[0].fY = value;
1677
0
        dst[1].fPts[1].fY = value;
1678
0
        return true;
1679
0
    }
1680
0
    return false;
1681
0
}
1682
1683
0
void SkConic::computeTightBounds(SkRect* bounds) const {
1684
0
    SkPoint pts[4];
1685
0
    pts[0] = fPts[0];
1686
0
    pts[1] = fPts[2];
1687
0
    int count = 2;
1688
1689
0
    SkScalar t;
1690
0
    if (this->findXExtrema(&t)) {
1691
0
        this->evalAt(t, &pts[count++]);
1692
0
    }
1693
0
    if (this->findYExtrema(&t)) {
1694
0
        this->evalAt(t, &pts[count++]);
1695
0
    }
1696
0
    bounds->setBounds(pts, count);
1697
0
}
1698
1699
0
void SkConic::computeFastBounds(SkRect* bounds) const {
1700
0
    bounds->setBounds(fPts, 3);
1701
0
}
1702
1703
#if 0  // unimplemented
1704
bool SkConic::findMaxCurvature(SkScalar* t) const {
1705
    // TODO: Implement me
1706
    return false;
1707
}
1708
#endif
1709
1710
2.02M
SkScalar SkConic::TransformW(const SkPoint pts[3], SkScalar w, const SkMatrix& matrix) {
1711
2.02M
    if (!matrix.hasPerspective()) {
1712
0
        return w;
1713
0
    }
1714
1715
2.02M
    SkPoint3 src[3], dst[3];
1716
1717
2.02M
    ratquad_mapTo3D(pts, w, src);
1718
1719
2.02M
    matrix.mapHomogeneousPoints(dst, src, 3);
1720
1721
    // w' = sqrt(w1*w1/w0*w2)
1722
    // use doubles temporarily, to handle small numer/denom
1723
2.02M
    double w0 = dst[0].fZ;
1724
2.02M
    double w1 = dst[1].fZ;
1725
2.02M
    double w2 = dst[2].fZ;
1726
2.02M
    return sk_double_to_float(sqrt(sk_ieee_double_divide(w1 * w1, w0 * w2)));
1727
2.02M
}
1728
1729
int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir,
1730
430k
                          const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) {
1731
    // rotate by x,y so that uStart is (1.0)
1732
430k
    SkScalar x = SkPoint::DotProduct(uStart, uStop);
1733
430k
    SkScalar y = SkPoint::CrossProduct(uStart, uStop);
1734
1735
430k
    SkScalar absY = SkScalarAbs(y);
1736
1737
    // check for (effectively) coincident vectors
1738
    // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
1739
    // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
1740
430k
    if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) ||
1741
2.69k
                                                 (y <= 0 && kCCW_SkRotationDirection == dir))) {
1742
1.39k
        return 0;
1743
1.39k
    }
1744
1745
428k
    if (dir == kCCW_SkRotationDirection) {
1746
234k
        y = -y;
1747
234k
    }
1748
1749
    // We decide to use 1-conic per quadrant of a circle. What quadrant does [xy] lie in?
1750
    //      0 == [0  .. 90)
1751
    //      1 == [90 ..180)
1752
    //      2 == [180..270)
1753
    //      3 == [270..360)
1754
    //
1755
428k
    int quadrant = 0;
1756
428k
    if (0 == y) {
1757
67.2k
        quadrant = 2;        // 180
1758
67.2k
        SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
1759
361k
    } else if (0 == x) {
1760
28.7k
        SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
1761
28.7k
        quadrant = y > 0 ? 1 : 3; // 90 : 270
1762
332k
    } else {
1763
332k
        if (y < 0) {
1764
6.31k
            quadrant += 2;
1765
6.31k
        }
1766
332k
        if ((x < 0) != (y < 0)) {
1767
239k
            quadrant += 1;
1768
239k
        }
1769
332k
    }
1770
1771
428k
    const SkPoint quadrantPts[] = {
1772
428k
        { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }
1773
428k
    };
1774
428k
    const SkScalar quadrantWeight = SK_ScalarRoot2Over2;
1775
1776
428k
    int conicCount = quadrant;
1777
844k
    for (int i = 0; i < conicCount; ++i) {
1778
415k
        dst[i].set(&quadrantPts[i * 2], quadrantWeight);
1779
415k
    }
1780
1781
    // Now compute any remaing (sub-90-degree) arc for the last conic
1782
428k
    const SkPoint finalP = { x, y };
1783
428k
    const SkPoint& lastQ = quadrantPts[quadrant * 2];  // will already be a unit-vector
1784
428k
    const SkScalar dot = SkVector::DotProduct(lastQ, finalP);
1785
428k
    if (SkIsNaN(dot)) {
1786
66
        return 0;
1787
66
    }
1788
428k
    SkASSERT(0 <= dot && dot <= SK_Scalar1 + SK_ScalarNearlyZero);
1789
1790
428k
    if (dot < 1) {
1791
320k
        SkVector offCurve = { lastQ.x() + x, lastQ.y() + y };
1792
        // compute the bisector vector, and then rescale to be the off-curve point.
1793
        // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get
1794
        // length = sqrt(2 / (1 + cos(theta)). We already have cos() when to computed the dot.
1795
        // This is nice, since our computed weight is cos(theta/2) as well!
1796
        //
1797
320k
        const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2);
1798
320k
        offCurve.setLength(SkScalarInvert(cosThetaOver2));
1799
320k
        if (!SkPointPriv::EqualsWithinTolerance(lastQ, offCurve)) {
1800
317k
            dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2);
1801
317k
            conicCount += 1;
1802
317k
        }
1803
320k
    }
1804
1805
    // now handle counter-clockwise and the initial unitStart rotation
1806
428k
    SkMatrix    matrix;
1807
428k
    matrix.setSinCos(uStart.fY, uStart.fX);
1808
428k
    if (dir == kCCW_SkRotationDirection) {
1809
234k
        matrix.preScale(SK_Scalar1, -SK_Scalar1);
1810
234k
    }
1811
428k
    if (userMatrix) {
1812
428k
        matrix.postConcat(*userMatrix);
1813
428k
    }
1814
1.16M
    for (int i = 0; i < conicCount; ++i) {
1815
733k
        matrix.mapPoints(dst[i].fPts, 3);
1816
733k
    }
1817
428k
    return conicCount;
1818
428k
}