/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 | } |