Flutter Engine
The Flutter Engine
SkGeometry.cpp
Go to the documentation of this file.
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
9
12#include "include/core/SkRect.h"
19#include "src/base/SkCubics.h"
20#include "src/base/SkUtils.h"
21#include "src/base/SkVx.h"
23
24#include <algorithm>
25#include <array>
26#include <cmath>
27#include <cstddef>
28#include <cstdint>
29
30namespace {
31
32using float2 = skvx::float2;
33using float4 = skvx::float4;
34
35SkVector to_vector(const float2& x) {
36 SkVector vector;
37 x.store(&vector);
38 return vector;
39}
40
41////////////////////////////////////////////////////////////////////////
42
43int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c) {
44 SkScalar ab = a - b;
45 SkScalar bc = b - c;
46 if (ab < 0) {
47 bc = -bc;
48 }
49 return ab == 0 || bc < 0;
50}
51
52////////////////////////////////////////////////////////////////////////
53
54int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) {
55 SkASSERT(ratio);
56
57 if (numer < 0) {
58 numer = -numer;
59 denom = -denom;
60 }
61
62 if (denom == 0 || numer == 0 || numer >= denom) {
63 return 0;
64 }
65
66 SkScalar r = numer / denom;
67 if (SkIsNaN(r)) {
68 return 0;
69 }
70 SkASSERTF(r >= 0 && r < SK_Scalar1, "numer %f, denom %f, r %f", numer, denom, r);
71 if (r == 0) { // catch underflow if numer <<<< denom
72 return 0;
73 }
74 *ratio = r;
75 return 1;
76}
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).
80int return_check_zero(int value) {
81 if (value == 0) {
82 return 0;
83 }
84 return value;
85}
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*/
97
98 if (A == 0) {
99 return return_check_zero(valid_unit_divide(-C, B, roots));
100 }
101
102 SkScalar* r = roots;
103
104 // use doubles so we don't overflow temporarily trying to compute R
105 double dr = (double)B * B - 4 * (double)A * C;
106 if (dr < 0) {
107 return return_check_zero(0);
108 }
109 dr = sqrt(dr);
111 if (!SkIsFinite(R)) {
112 return return_check_zero(0);
113 }
114
115 SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
116 r += valid_unit_divide(Q, A, r);
117 r += valid_unit_divide(C, Q, r);
118 if (r - roots == 2) {
119 if (roots[0] > roots[1]) {
120 using std::swap;
121 swap(roots[0], roots[1]);
122 } else if (roots[0] == roots[1]) { // nearly-equal?
123 r -= 1; // skip the double root
124 }
125 }
126 return return_check_zero((int)(r - roots));
127}
128
129///////////////////////////////////////////////////////////////////////////////
130///////////////////////////////////////////////////////////////////////////////
131
132void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) {
133 SkASSERT(src);
134 SkASSERT(t >= 0 && t <= SK_Scalar1);
135
136 if (pt) {
137 *pt = SkEvalQuadAt(src, t);
138 }
139 if (tangent) {
140 *tangent = SkEvalQuadTangentAt(src, t);
141 }
142}
143
145 return to_point(SkQuadCoeff(src).eval(t));
146}
147
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 if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) {
153 return src[2] - src[0];
154 }
155 SkASSERT(src);
156 SkASSERT(t >= 0 && t <= SK_Scalar1);
157
158 float2 P0 = from_point(src[0]);
159 float2 P1 = from_point(src[1]);
160 float2 P2 = from_point(src[2]);
161
162 float2 B = P1 - P0;
163 float2 A = P2 - P1 - B;
164 float2 T = A * t + B;
165
166 return to_vector(T + T);
167}
168
169static inline float2 interp(const float2& v0,
170 const float2& v1,
171 const float2& t) {
172 return v0 + (v1 - v0) * t;
173}
174
176 SkASSERT(t > 0 && t < SK_Scalar1);
177
178 float2 p0 = from_point(src[0]);
179 float2 p1 = from_point(src[1]);
180 float2 p2 = from_point(src[2]);
181 float2 tt(t);
182
183 float2 p01 = interp(p0, p1, tt);
184 float2 p12 = interp(p1, p2, tt);
185
186 dst[0] = to_point(p0);
187 dst[1] = to_point(p01);
188 dst[2] = to_point(interp(p01, p12, tt));
189 dst[3] = to_point(p12);
190 dst[4] = to_point(p2);
191}
192
194 SkChopQuadAt(src, dst, 0.5f);
195}
196
198 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 cosTheta = std::max(std::min(1.f, cosTheta), -1.f);
201 return acosf(cosTheta);
202}
203
205 std::array<SkVector, 2> v;
206 if (a.dot(b) >= 0) {
207 // a,b are within +/-90 degrees apart.
208 v = {a, b};
209 } 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 v[0].set(-a.fY, +a.fX);
214 v[1].set(+b.fY, -b.fX);
215 } 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 v[0].set(+a.fY, -a.fX);
220 v[1].set(-b.fY, +b.fX);
221 }
222 // Return "normalize(v[0]) + normalize(v[1])".
223 skvx::float2 x0_x1{v[0].fX, v[1].fX};
224 skvx::float2 y0_y1{v[0].fY, v[1].fY};
225 auto invLengths = 1.0f / sqrt(x0_x1 * x0_x1 + y0_y1 * y0_y1);
226 x0_x1 *= invLengths;
227 y0_y1 *= invLengths;
228 return SkPoint{x0_x1[0] + x0_x1[1], y0_y1[0] + y0_y1[1]};
229}
230
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 SkVector tan0 = src[1] - src[0];
238 SkVector tan1 = src[2] - src[1];
239 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 float T = sk_ieee_float_divide(tan0.dot(bisector), (tan0 - tan1).dot(bisector));
253 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=nan will take this branch.
254 T = .5; // The quadratic was a line or near-line. Just chop at .5.
255 }
256
257 return T;
258}
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*/
266 /* At + B == 0
267 t = -B / A
268 */
269 return valid_unit_divide(a - b, a - b - b + c, tValue);
270}
271
272static inline void flatten_double_quad_extrema(SkScalar coords[14]) {
273 coords[2] = coords[6] = coords[4];
274}
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 */
280 SkASSERT(src);
281 SkASSERT(dst);
282
283 SkScalar a = src[0].fY;
284 SkScalar b = src[1].fY;
285 SkScalar c = src[2].fY;
286
287 if (is_not_monotonic(a, b, c)) {
288 SkScalar tValue;
289 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
290 SkChopQuadAt(src, dst, tValue);
292 return 1;
293 }
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 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
297 }
298 dst[0].set(src[0].fX, a);
299 dst[1].set(src[1].fX, b);
300 dst[2].set(src[2].fX, c);
301 return 0;
302}
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 */
308 SkASSERT(src);
309 SkASSERT(dst);
310
311 SkScalar a = src[0].fX;
312 SkScalar b = src[1].fX;
313 SkScalar c = src[2].fX;
314
315 if (is_not_monotonic(a, b, c)) {
316 SkScalar tValue;
317 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
318 SkChopQuadAt(src, dst, tValue);
320 return 1;
321 }
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 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
325 }
326 dst[0].set(a, src[0].fY);
327 dst[1].set(b, src[1].fY);
328 dst[2].set(c, src[2].fY);
329 return 0;
330}
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//
345 SkScalar Ax = src[1].fX - src[0].fX;
346 SkScalar Ay = src[1].fY - src[0].fY;
347 SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
348 SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
349
350 SkScalar numer = -(Ax * Bx + Ay * By);
351 SkScalar denom = Bx * Bx + By * By;
352 if (denom < 0) {
353 numer = -numer;
354 denom = -denom;
355 }
356 if (numer <= 0) {
357 return 0;
358 }
359 if (numer >= denom) { // Also catches denom=0.
360 return 1;
361 }
362 SkScalar t = numer / denom;
363 SkASSERT((0 <= t && t < 1) || SkIsNaN(t));
364 return t;
365}
366
369 if (t > 0 && t < 1) {
370 SkChopQuadAt(src, dst, t);
371 return 2;
372 } else {
373 memcpy(dst, src, 3 * sizeof(SkPoint));
374 return 1;
375 }
376}
377
379 float2 scale(SkDoubleToScalar(2.0 / 3.0));
380 float2 s0 = from_point(src[0]);
381 float2 s1 = from_point(src[1]);
382 float2 s2 = from_point(src[2]);
383
384 dst[0] = to_point(s0);
385 dst[1] = to_point(s0 + (s1 - s0) * scale);
386 dst[2] = to_point(s2 + (s1 - s2) * scale);
387 dst[3] = to_point(s2);
388}
389
390//////////////////////////////////////////////////////////////////////////////
391///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
392//////////////////////////////////////////////////////////////////////////////
393
395 SkQuadCoeff coeff;
396 float2 P0 = from_point(src[0]);
397 float2 P1 = from_point(src[1]);
398 float2 P2 = from_point(src[2]);
399 float2 P3 = from_point(src[3]);
400
401 coeff.fA = P3 + 3 * (P1 - P2) - P0;
402 coeff.fB = times_2(P2 - times_2(P1) + P0);
403 coeff.fC = P1 - P0;
404 return to_vector(coeff.eval(t));
405}
406
408 float2 P0 = from_point(src[0]);
409 float2 P1 = from_point(src[1]);
410 float2 P2 = from_point(src[2]);
411 float2 P3 = from_point(src[3]);
412 float2 A = P3 + 3 * (P1 - P2) - P0;
413 float2 B = P2 - times_2(P1) + P0;
414
415 return to_vector(A * t + B);
416}
417
418void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc,
419 SkVector* tangent, SkVector* curvature) {
420 SkASSERT(src);
421 SkASSERT(t >= 0 && t <= SK_Scalar1);
422
423 if (loc) {
424 *loc = to_point(SkCubicCoeff(src).eval(t));
425 }
426 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 if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) {
431 if (t == 0) {
432 *tangent = src[2] - src[0];
433 } else {
434 *tangent = src[3] - src[1];
435 }
436 if (!tangent->fX && !tangent->fY) {
437 *tangent = src[3] - src[0];
438 }
439 } else {
440 *tangent = eval_cubic_derivative(src, t);
441 }
442 }
443 if (curvature) {
444 *curvature = eval_cubic_2ndDerivative(src, t);
445 }
446}
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*/
455 SkScalar tValues[2]) {
456 // we divide A,B,C by 3 to simplify
457 SkScalar A = d - a + 3*(b - c);
458 SkScalar B = 2*(a - b - b + c);
459 SkScalar C = b - a;
460
461 return SkFindUnitQuadRoots(A, B, C, tValues);
462}
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.
467template<int N, typename T>
469 const skvx::Vec<N,T>& t) {
470 return (b - a)*t + a;
471}
472
474 SkASSERT(0 <= t && t <= 1);
475
476 if (t == 1) {
477 memcpy(dst, src, sizeof(SkPoint) * 4);
478 dst[4] = dst[5] = dst[6] = src[3];
479 return;
480 }
481
482 float2 p0 = sk_bit_cast<float2>(src[0]);
483 float2 p1 = sk_bit_cast<float2>(src[1]);
484 float2 p2 = sk_bit_cast<float2>(src[2]);
485 float2 p3 = sk_bit_cast<float2>(src[3]);
486 float2 T = t;
487
488 float2 ab = unchecked_mix(p0, p1, T);
489 float2 bc = unchecked_mix(p1, p2, T);
490 float2 cd = unchecked_mix(p2, p3, T);
491 float2 abc = unchecked_mix(ab, bc, T);
492 float2 bcd = unchecked_mix(bc, cd, T);
493 float2 abcd = unchecked_mix(abc, bcd, T);
494
495 dst[0] = sk_bit_cast<SkPoint>(p0);
496 dst[1] = sk_bit_cast<SkPoint>(ab);
497 dst[2] = sk_bit_cast<SkPoint>(abc);
498 dst[3] = sk_bit_cast<SkPoint>(abcd);
499 dst[4] = sk_bit_cast<SkPoint>(bcd);
500 dst[5] = sk_bit_cast<SkPoint>(cd);
501 dst[6] = sk_bit_cast<SkPoint>(p3);
502}
503
504void SkChopCubicAt(const SkPoint src[4], SkPoint dst[10], float t0, float t1) {
505 SkASSERT(0 <= t0 && t0 <= t1 && t1 <= 1);
506
507 if (t1 == 1) {
508 SkChopCubicAt(src, dst, t0);
509 dst[7] = dst[8] = dst[9] = src[3];
510 return;
511 }
512
513 // Perform both chops in parallel using 4-lane SIMD.
514 float4 p00, p11, p22, p33, T;
515 p00.lo = p00.hi = sk_bit_cast<float2>(src[0]);
516 p11.lo = p11.hi = sk_bit_cast<float2>(src[1]);
517 p22.lo = p22.hi = sk_bit_cast<float2>(src[2]);
518 p33.lo = p33.hi = sk_bit_cast<float2>(src[3]);
519 T.lo = t0;
520 T.hi = t1;
521
522 float4 ab = unchecked_mix(p00, p11, T);
523 float4 bc = unchecked_mix(p11, p22, T);
524 float4 cd = unchecked_mix(p22, p33, T);
525 float4 abc = unchecked_mix(ab, bc, T);
526 float4 bcd = unchecked_mix(bc, cd, T);
527 float4 abcd = unchecked_mix(abc, bcd, T);
528 float4 middle = unchecked_mix(abc, bcd, skvx::shuffle<2,3,0,1>(T));
529
530 dst[0] = sk_bit_cast<SkPoint>(p00.lo);
531 dst[1] = sk_bit_cast<SkPoint>(ab.lo);
532 dst[2] = sk_bit_cast<SkPoint>(abc.lo);
533 dst[3] = sk_bit_cast<SkPoint>(abcd.lo);
534 middle.store(dst + 4);
535 dst[6] = sk_bit_cast<SkPoint>(abcd.hi);
536 dst[7] = sk_bit_cast<SkPoint>(bcd.hi);
537 dst[8] = sk_bit_cast<SkPoint>(cd.hi);
538 dst[9] = sk_bit_cast<SkPoint>(p33.hi);
539}
540
542 const SkScalar tValues[], int tCount) {
543 SkASSERT(std::all_of(tValues, tValues + tCount, [](SkScalar t) { return t >= 0 && t <= 1; }));
544 SkASSERT(std::is_sorted(tValues, tValues + tCount));
545
546 if (dst) {
547 if (tCount == 0) { // nothing to chop
548 memcpy(dst, src, 4*sizeof(SkPoint));
549 } else {
550 int i = 0;
551 for (; i < tCount - 1; i += 2) {
552 // Do two chops at once.
553 float2 tt = float2::Load(tValues + i);
554 if (i != 0) {
555 float lastT = tValues[i - 1];
556 tt = skvx::pin((tt - lastT) / (1 - lastT), float2(0), float2(1));
557 }
558 SkChopCubicAt(src, dst, tt[0], tt[1]);
559 src = dst = dst + 6;
560 }
561 if (i < tCount) {
562 // Chop the final cubic if there was an odd number of chops.
563 SkASSERT(i + 1 == tCount);
564 float t = tValues[i];
565 if (i != 0) {
566 float lastT = tValues[i - 1];
567 t = SkTPin(sk_ieee_float_divide(t - lastT, 1 - lastT), 0.f, 1.f);
568 }
569 SkChopCubicAt(src, dst, t);
570 }
571 }
572 }
573}
574
576 SkChopCubicAt(src, dst, 0.5f);
577}
578
580 SkVector a = pts[1] - pts[0];
581 SkVector b = pts[2] - pts[1];
582 SkVector c = pts[3] - pts[2];
583 if (a.isZero()) {
585 }
586 if (b.isZero()) {
588 }
589 if (c.isZero()) {
591 }
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].
595}
596
597static skvx::float4 fma(const skvx::float4& f, float m, const skvx::float4& a) {
598 return skvx::fma(f, skvx::float4(m), a);
599}
600
601// Finds the root nearest 0.5. Returns 0.5 if the roots are undefined or outside 0..1.
602static float solve_quadratic_equation_for_midtangent(float a, float b, float c, float discr) {
603 // Quadratic formula from Numerical Recipes in C:
604 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 float _5qa = -.5f*q*a;
607 float T = fabsf(q*q + _5qa) < fabsf(a*c + _5qa) ? sk_ieee_float_divide(q,a)
609 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 T = .5;
612 }
613 return T;
614}
615
616static float solve_quadratic_equation_for_midtangent(float a, float b, float c) {
617 return solve_quadratic_equation_for_midtangent(a, b, c, b*b - 4*a*c);
618}
619
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 SkVector tan0 = (src[0] == src[1]) ? src[2] - src[0] : src[1] - src[0];
627 SkVector tan1 = (src[2] == src[3]) ? src[3] - src[1] : src[3] - src[2];
628 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 static const skvx::float4 kM[4] = {skvx::float4(-1, 2, -1, 0),
640 skvx::float4( 3, -4, 1, 0),
641 skvx::float4(-3, 2, 0, 0)};
642 auto C_x = fma(kM[0], src[0].fX,
643 fma(kM[1], src[1].fX,
644 fma(kM[2], src[2].fX, skvx::float4(src[3].fX, 0,0,0))));
645 auto C_y = fma(kM[0], src[0].fY,
646 fma(kM[1], src[1].fY,
647 fma(kM[2], src[2].fY, skvx::float4(src[3].fY, 0,0,0))));
648 auto coeffs = C_x * bisector.x() + C_y * bisector.y();
649
650 // Now solve the quadratic for T.
651 float T = 0;
652 float a=coeffs[0], b=coeffs[1], c=coeffs[2];
653 float discr = b*b - 4*a*c;
654 if (discr > 0) { // This will only be false if the curve is a line.
656 } 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 coeffs = C_x * tan0.x() + C_y * tan0.y();
665 a = coeffs[0];
666 b = coeffs[1];
667 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 T = -b / (2*a);
676 }
677 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 T = .5;
681 }
682 return T;
683 }
684}
685
686static void flatten_double_cubic_extrema(SkScalar coords[14]) {
687 coords[4] = coords[8] = coords[6];
688}
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*/
699 SkScalar tValues[2];
700 int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
701 src[3].fY, tValues);
702
703 SkChopCubicAt(src, dst, tValues, roots);
704 if (dst && roots > 0) {
705 // we do some cleanup to ensure our Y extrema are flat
707 if (roots == 2) {
709 }
710 }
711 return roots;
712}
713
715 SkScalar tValues[2];
716 int roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
717 src[3].fX, tValues);
718
719 SkChopCubicAt(src, dst, tValues, roots);
720 if (dst && roots > 0) {
721 // we do some cleanup to ensure our Y extrema are flat
723 if (roots == 2) {
725 }
726 }
727 return roots;
728}
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*/
741int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]) {
742 SkScalar Ax = src[1].fX - src[0].fX;
743 SkScalar Ay = src[1].fY - src[0].fY;
744 SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
745 SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY;
746 SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
747 SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
748
749 return SkFindUnitQuadRoots(Bx*Cy - By*Cx,
750 Ax*Cy - Ay*Cx,
751 Ax*By - Ay*Bx,
752 tValues);
753}
754
756 SkScalar tValues[2];
757 int count = SkFindCubicInflections(src, tValues);
758
759 if (dst) {
760 if (count == 0) {
761 memcpy(dst, src, 4 * sizeof(SkPoint));
762 } else {
763 SkChopCubicAt(src, dst, tValues, count);
764 }
765 }
766 return count + 1;
767}
768
769// Assumes the third component of points is 1.
770// Calcs p0 . (p1 x p2)
771static double calc_dot_cross_cubic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
772 const double xComp = (double) p0.fX * ((double) p1.fY - (double) p2.fY);
773 const double yComp = (double) p0.fY * ((double) p2.fX - (double) p1.fX);
774 const double wComp = (double) p1.fX * (double) p2.fY - (double) p1.fY * (double) p2.fX;
775 return (xComp + yComp + wComp);
776}
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.
782inline static double previous_inverse_pow2(double n) {
783 uint64_t bits;
784 memcpy(&bits, &n, sizeof(double));
785 bits = ((1023llu*2 << 52) + ((1llu << 52) - 1)) - bits; // exp=-exp
786 bits &= (0x7ffllu) << 52; // mantissa=1.0, sign=0
787 memcpy(&n, &bits, sizeof(double));
788 return n;
789}
790
791inline static void write_cubic_inflection_roots(double t0, double s0, double t1, double s1,
792 double* t, double* s) {
793 t[0] = t0;
794 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 t[1] = -copysign(t1, t1 * s1);
799 s[1] = -fabs(s1);
800
801 // Ensure t[0]/s[0] <= t[1]/s[1] (s[1] is negative from above).
802 if (copysign(s[1], s[0]) * t[0] > -fabs(s[0]) * t[1]) {
803 using std::swap;
804 swap(t[0], t[1]);
805 swap(s[0], s[1]);
806 }
807}
808
809SkCubicType 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 double A1 = calc_dot_cross_cubic(P[0], P[3], P[2]);
818 double A2 = calc_dot_cross_cubic(P[1], P[0], P[3]);
819 double A3 = calc_dot_cross_cubic(P[2], P[1], P[0]);
820
821 double D3 = 3 * A3;
822 double D2 = D3 - A2;
823 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 double Dmax = std::max(std::max(fabs(D1), fabs(D2)), fabs(D3));
828 double norm = previous_inverse_pow2(Dmax);
829 D1 *= norm;
830 D2 *= norm;
831 D3 *= norm;
832
833 if (d) {
834 d[3] = D3;
835 d[2] = D2;
836 d[1] = D1;
837 d[0] = 0;
838 }
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 if (0 != D1) {
847 double discr = 3*D2*D2 - 4*D1*D3;
848 if (discr > 0) { // Serpentine.
849 if (t && s) {
850 double q = 3*D2 + copysign(sqrt(3*discr), D2);
851 write_cubic_inflection_roots(q, 6*D1, 2*D3, q, t, s);
852 }
854 } else if (discr < 0) { // Loop.
855 if (t && s) {
856 double q = D2 + copysign(sqrt(-discr), D2);
857 write_cubic_inflection_roots(q, 2*D1, 2*(D2*D2 - D3*D1), D1*q, t, s);
858 }
859 return SkCubicType::kLoop;
860 } else { // Cusp.
861 if (t && s) {
863 }
865 }
866 } else {
867 if (0 != D2) { // Cusp at T=infinity.
868 if (t && s) {
869 write_cubic_inflection_roots(D3, 3*D2, 1, 0, t, s); // T1=infinity.
870 }
872 } else { // Degenerate.
873 if (t && s) {
874 write_cubic_inflection_roots(1, 0, 1, 0, t, s); // T0=T1=infinity.
875 }
877 }
878 }
879}
880
881template <typename T> void bubble_sort(T array[], int count) {
882 for (int i = count - 1; i > 0; --i)
883 for (int j = i; j > 0; --j)
884 if (array[j] < array[j-1])
885 {
886 T tmp(array[j]);
887 array[j] = array[j-1];
888 array[j-1] = tmp;
889 }
890}
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 */
896static int collaps_duplicates(SkScalar array[], int count) {
897 for (int n = count; n > 1; --n) {
898 if (array[0] == array[1]) {
899 for (int i = 1; i < n; ++i) {
900 array[i - 1] = array[i];
901 }
902 count -= 1;
903 } else {
904 array += 1;
905 }
906 }
907 return count;
908}
909
910#ifdef SK_DEBUG
911
912#define TEST_COLLAPS_ENTRY(array) array, std::size(array)
913
914static void test_collaps_duplicates() {
915 static bool gOnce;
916 if (gOnce) { return; }
917 gOnce = true;
918 const SkScalar src0[] = { 0 };
919 const SkScalar src1[] = { 0, 0 };
920 const SkScalar src2[] = { 0, 1 };
921 const SkScalar src3[] = { 0, 0, 0 };
922 const SkScalar src4[] = { 0, 0, 1 };
923 const SkScalar src5[] = { 0, 1, 1 };
924 const SkScalar src6[] = { 0, 1, 2 };
925 const struct {
926 const SkScalar* fData;
927 int fCount;
928 int fCollapsedCount;
929 } data[] = {
930 { TEST_COLLAPS_ENTRY(src0), 1 },
931 { TEST_COLLAPS_ENTRY(src1), 1 },
932 { TEST_COLLAPS_ENTRY(src2), 2 },
933 { TEST_COLLAPS_ENTRY(src3), 1 },
934 { TEST_COLLAPS_ENTRY(src4), 2 },
935 { TEST_COLLAPS_ENTRY(src5), 2 },
936 { TEST_COLLAPS_ENTRY(src6), 3 },
937 };
938 for (size_t i = 0; i < std::size(data); ++i) {
939 SkScalar dst[3];
940 memcpy(dst, data[i].fData, data[i].fCount * sizeof(dst[0]));
942 SkASSERT(data[i].fCollapsedCount == count);
943 for (int j = 1; j < count; ++j) {
944 SkASSERT(dst[j-1] < dst[j]);
945 }
946 }
947}
948#endif
949
951 return SkScalarPow(x, 0.3333333f);
952}
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*/
961static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) {
962 if (SkScalarNearlyZero(coeff[0])) { // we're just a quadratic
963 return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
964 }
965
966 SkScalar a, b, c, Q, R;
967
968 {
969 SkASSERT(coeff[0] != 0);
970
971 SkScalar inva = SkScalarInvert(coeff[0]);
972 a = coeff[1] * inva;
973 b = coeff[2] * inva;
974 c = coeff[3] * inva;
975 }
976 Q = (a*a - b*3) / 9;
977 R = (2*a*a*a - 9*a*b + 27*c) / 54;
978
979 SkScalar Q3 = Q * Q * Q;
980 SkScalar R2MinusQ3 = R * R - Q3;
981 SkScalar adiv3 = a / 3;
982
983 if (R2MinusQ3 < 0) { // we have 3 real roots
984 // the divide/root can, due to finite precisions, be slightly outside of -1...1
985 SkScalar theta = SkScalarACos(SkTPin(R / SkScalarSqrt(Q3), -1.0f, 1.0f));
986 SkScalar neg2RootQ = -2 * SkScalarSqrt(Q);
987
988 tValues[0] = SkTPin(neg2RootQ * SkScalarCos(theta/3) - adiv3, 0.0f, 1.0f);
989 tValues[1] = SkTPin(neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
990 tValues[2] = SkTPin(neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
991 SkDEBUGCODE(test_collaps_duplicates();)
992
993 // now sort the roots
994 bubble_sort(tValues, 3);
995 return collaps_duplicates(tValues, 3);
996 } else { // we have 1 real root
997 SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3);
999 if (R > 0) {
1000 A = -A;
1001 }
1002 if (A != 0) {
1003 A += Q / A;
1004 }
1005 tValues[0] = SkTPin(A - adiv3, 0.0f, 1.0f);
1006 return 1;
1007 }
1008}
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*/
1021static void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4]) {
1022 SkScalar a = src[2] - src[0];
1023 SkScalar b = src[4] - 2 * src[2] + src[0];
1024 SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0];
1025
1026 coeff[0] = c * c;
1027 coeff[1] = 3 * b * c;
1028 coeff[2] = 2 * b * b + c * a;
1029 coeff[3] = a * b;
1030}
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*/
1043int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]) {
1044 SkScalar coeffX[4], coeffY[4];
1045 int i;
1046
1047 formulate_F1DotF2(&src[0].fX, coeffX);
1048 formulate_F1DotF2(&src[0].fY, coeffY);
1049
1050 for (i = 0; i < 4; i++) {
1051 coeffX[i] += coeffY[i];
1052 }
1053
1054 int numRoots = solve_cubic_poly(coeffX, tValues);
1055 // now remove extrema where the curvature is zero (mins)
1056 // !!!! need a test for this !!!!
1057 return numRoots;
1058}
1059
1061 SkScalar tValues[3]) {
1062 SkScalar t_storage[3];
1063
1064 if (tValues == nullptr) {
1065 tValues = t_storage;
1066 }
1067
1068 SkScalar roots[3];
1069 int rootCount = SkFindCubicMaxCurvature(src, roots);
1070
1071 // Throw out values not inside 0..1.
1072 int count = 0;
1073 for (int i = 0; i < rootCount; ++i) {
1074 if (0 < roots[i] && roots[i] < 1) {
1075 tValues[count++] = roots[i];
1076 }
1077 }
1078
1079 if (dst) {
1080 if (count == 0) {
1081 memcpy(dst, src, 4 * sizeof(SkPoint));
1082 } else {
1083 SkChopCubicAt(src, dst, tValues, count);
1084 }
1085 }
1086 return count + 1;
1087}
1088
1089// Returns a constant proportional to the dimensions of the cubic.
1090// Constant found through experimentation -- maybe there's a better way....
1093 + SkPointPriv::DistanceToSqd(src[3], src[2])) * 1e-8f;
1094}
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].
1098static bool on_same_side(const SkPoint src[4], int testIndex, int lineIndex) {
1099 SkPoint origin = src[lineIndex];
1100 SkVector line = src[lineIndex + 1] - origin;
1101 SkScalar crosses[2];
1102 for (int index = 0; index < 2; ++index) {
1103 SkVector testLine = src[testIndex + index] - origin;
1104 crosses[index] = line.cross(testLine);
1105 }
1106 return crosses[0] * crosses[1] >= 0;
1107}
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.
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 if (src[0] == src[1]) {
1118 return -1;
1119 }
1120 if (src[2] == src[3]) {
1121 return -1;
1122 }
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 if (on_same_side(src, 0, 2) || on_same_side(src, 2, 0)) {
1126 return -1;
1127 }
1128 // Cubics may have multiple points of maximum curvature, although at most only
1129 // one is a cusp.
1130 SkScalar maxCurvature[3];
1131 int roots = SkFindCubicMaxCurvature(src, maxCurvature);
1132 for (int index = 0; index < roots; ++index) {
1133 SkScalar testT = maxCurvature[index];
1134 if (0 >= testT || testT >= 1) { // no need to consider max curvature on the end
1135 continue;
1136 }
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 SkVector dPt = eval_cubic_derivative(src, testT);
1141 SkScalar dPtMagnitude = SkPointPriv::LengthSqd(dPt);
1142 SkScalar precision = calc_cubic_precision(src);
1143 if (dPtMagnitude < precision) {
1144 // All three max curvature t values may be close to the cusp;
1145 // return the first one.
1146 return testT;
1147 }
1148 }
1149 return -1;
1150}
1151
1152static bool close_enough_to_zero(double x) {
1153 return std::fabs(x) < 0.00001;
1154}
1155
1156static bool first_axis_intersection(const double coefficients[8], bool yDirection,
1157 double axisIntercept, double* solution) {
1158 auto [A, B, C, D] = SkBezierCubic::ConvertToPolynomial(coefficients, yDirection);
1159 D -= axisIntercept;
1160 double roots[3] = {0, 0, 0};
1161 int count = SkCubics::RootsValidT(A, B, C, D, roots);
1162 if (count == 0) {
1163 return false;
1164 }
1165 // Verify that at least one of the roots is accurate.
1166 for (int i = 0; i < count; i++) {
1168 *solution = roots[i];
1169 return true;
1170 }
1171 }
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.
1176 if (count == 0) {
1177 return false;
1178 }
1179 for (int i = 0; i < count; i++) {
1181 *solution = roots[i];
1182 return true;
1183 }
1184 }
1185 return false;
1186}
1187
1189 double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1190 src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1191 double solution = 0;
1192 if (first_axis_intersection(coefficients, true, y, &solution)) {
1193 double cubicPair[14];
1194 SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1195 for (int i = 0; i < 7; i ++) {
1196 dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1197 dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1198 }
1199 return true;
1200 }
1201 return false;
1202}
1203
1205 double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1206 src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1207 double solution = 0;
1208 if (first_axis_intersection(coefficients, false, x, &solution)) {
1209 double cubicPair[14];
1210 SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1211 for (int i = 0; i < 7; i ++) {
1212 dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1213 dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1214 }
1215 return true;
1216 }
1217 return false;
1218}
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//
1251static void conic_deriv_coeff(const SkScalar src[],
1252 SkScalar w,
1253 SkScalar coeff[3]) {
1254 const SkScalar P20 = src[4] - src[0];
1255 const SkScalar P10 = src[2] - src[0];
1256 const SkScalar wP10 = w * P10;
1257 coeff[0] = w * P20 - P20;
1258 coeff[1] = P20 - 2 * wP10;
1259 coeff[2] = wP10;
1260}
1261
1263 SkScalar coeff[3];
1264 conic_deriv_coeff(src, w, coeff);
1265
1266 SkScalar tValues[2];
1267 int roots = SkFindUnitQuadRoots(coeff[0], coeff[1], coeff[2], tValues);
1268 SkASSERT(0 == roots || 1 == roots);
1269
1270 if (1 == roots) {
1271 *t = tValues[0];
1272 return true;
1273 }
1274 return false;
1275}
1276
1277// We only interpolate one dimension at a time (the first, at +0, +3, +6).
1278static void p3d_interp(const SkScalar src[7], SkScalar dst[7], SkScalar t) {
1279 SkScalar ab = SkScalarInterp(src[0], src[3], t);
1280 SkScalar bc = SkScalarInterp(src[3], src[6], t);
1281 dst[0] = ab;
1282 dst[3] = SkScalarInterp(ab, bc, t);
1283 dst[6] = bc;
1284}
1285
1286static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkPoint3 dst[3]) {
1287 dst[0].set(src[0].fX * 1, src[0].fY * 1, 1);
1288 dst[1].set(src[1].fX * w, src[1].fY * w, w);
1289 dst[2].set(src[2].fX * 1, src[2].fY * 1, 1);
1290}
1291
1293 return {src.fX / src.fZ, src.fY / src.fZ};
1294}
1295
1296// return false if infinity or NaN is generated; caller must check
1298 SkPoint3 tmp[3], tmp2[3];
1299
1300 ratquad_mapTo3D(fPts, fW, tmp);
1301
1302 p3d_interp(&tmp[0].fX, &tmp2[0].fX, t);
1303 p3d_interp(&tmp[0].fY, &tmp2[0].fY, t);
1304 p3d_interp(&tmp[0].fZ, &tmp2[0].fZ, t);
1305
1306 dst[0].fPts[0] = fPts[0];
1307 dst[0].fPts[1] = project_down(tmp2[0]);
1308 dst[0].fPts[2] = project_down(tmp2[1]); dst[1].fPts[0] = dst[0].fPts[2];
1309 dst[1].fPts[1] = project_down(tmp2[2]);
1310 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 SkScalar root = SkScalarSqrt(tmp2[1].fZ);
1321 dst[0].fW = tmp2[0].fZ / root;
1322 dst[1].fW = tmp2[2].fZ / root;
1323 SkASSERT(sizeof(dst[0]) == sizeof(SkScalar) * 7);
1324 SkASSERT(0 == offsetof(SkConic, fPts[0].fX));
1325 return SkIsFinite(&dst[0].fPts[0].fX, 7 * 2);
1326}
1327
1329 if (0 == t1 || 1 == t2) {
1330 if (0 == t1 && 1 == t2) {
1331 *dst = *this;
1332 return;
1333 } else {
1334 SkConic pair[2];
1335 if (this->chopAt(t1 ? t1 : t2, pair)) {
1336 *dst = pair[SkToBool(t1)];
1337 return;
1338 }
1339 }
1340 }
1341 SkConicCoeff coeff(*this);
1342 float2 tt1(t1);
1343 float2 aXY = coeff.fNumer.eval(tt1);
1344 float2 aZZ = coeff.fDenom.eval(tt1);
1345 float2 midTT((t1 + t2) / 2);
1346 float2 dXY = coeff.fNumer.eval(midTT);
1347 float2 dZZ = coeff.fDenom.eval(midTT);
1348 float2 tt2(t2);
1349 float2 cXY = coeff.fNumer.eval(tt2);
1350 float2 cZZ = coeff.fDenom.eval(tt2);
1351 float2 bXY = times_2(dXY) - (aXY + cXY) * 0.5f;
1352 float2 bZZ = times_2(dZZ) - (aZZ + cZZ) * 0.5f;
1353 dst->fPts[0] = to_point(aXY / aZZ);
1354 dst->fPts[1] = to_point(bXY / bZZ);
1355 dst->fPts[2] = to_point(cXY / cZZ);
1356 float2 ww = bZZ / sqrt(aZZ * cZZ);
1357 dst->fW = ww[0];
1358}
1359
1361 return to_point(SkConicCoeff(*this).eval(t));
1362}
1363
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 if ((t == 0 && fPts[0] == fPts[1]) || (t == 1 && fPts[1] == fPts[2])) {
1369 return fPts[2] - fPts[0];
1370 }
1371 float2 p0 = from_point(fPts[0]);
1372 float2 p1 = from_point(fPts[1]);
1373 float2 p2 = from_point(fPts[2]);
1374 float2 ww(fW);
1375
1376 float2 p20 = p2 - p0;
1377 float2 p10 = p1 - p0;
1378
1379 float2 C = ww * p10;
1380 float2 A = ww * p20 - p20;
1381 float2 B = p20 - C - C;
1382
1383 return to_vector(SkQuadCoeff(A, B, C).eval(t));
1384}
1385
1386void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const {
1387 SkASSERT(t >= 0 && t <= SK_Scalar1);
1388
1389 if (pt) {
1390 *pt = this->evalAt(t);
1391 }
1392 if (tangent) {
1393 *tangent = this->evalTangentAt(t);
1394 }
1395}
1396
1399}
1400
1401#if defined(SK_SUPPORT_LEGACY_CONIC_CHOP)
1402void SkConic::chop(SkConic * SK_RESTRICT dst) const {
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
1431
1432 // Observe that scale will always be smaller than 1 because fW > 0.
1433 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 float2 t0 = from_point(fPts[0]) * scale;
1440 float2 t1 = from_point(fPts[1]) * (fW * scale);
1441 float2 t2 = from_point(fPts[2]) * scale;
1442
1443 // Calculate the subdivided control points
1444 const SkPoint p1 = to_point(t0 + t1);
1445 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 const SkPoint p2 = to_point(0.5f * t0 + t1 + 0.5f * t2);
1450
1451 SkASSERT(p1.isFinite() && p2.isFinite() && p3.isFinite());
1452
1453 dst[0].fPts[0] = fPts[0];
1454 dst[0].fPts[1] = p1;
1455 dst[0].fPts[2] = p2;
1456 dst[1].fPts[0] = p2;
1457 dst[1].fPts[1] = p3;
1458 dst[1].fPts[2] = fPts[2];
1459
1460 // Update w.
1461 dst[0].fW = dst[1].fW = subdivide_w_value(fW);
1462}
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 SkScalar a = fW - 1; \
1471 SkScalar k = a / (4 * (2 + a)); \
1472 SkScalar x = k * (fPts[0].fX - 2 * fPts[1].fX + fPts[2].fX); \
1473 SkScalar y = k * (fPts[0].fY - 2 * fPts[1].fY + fPts[2].fY);
1474
1477 err->set(x, y);
1478}
1479
1482 return (x * x + y * y) <= tol * tol;
1483}
1484
1485// Limit the number of suggested quads to approximate a conic
1486#define kMaxConicToQuadPOW2 5
1487
1489 if (tol < 0 || !SkIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3)) {
1490 return 0;
1491 }
1492
1494
1495 SkScalar error = SkScalarSqrt(x * x + y * y);
1496 int pow2;
1497 for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
1498 if (error <= tol) {
1499 break;
1500 }
1501 error *= 0.25f;
1502 }
1503 // float version -- using ceil gives the same results as the above.
1504 if ((false)) {
1505 SkScalar err = SkScalarSqrt(x * x + y * y);
1506 if (err <= tol) {
1507 return 0;
1508 }
1509 SkScalar tol2 = tol * tol;
1510 if (tol2 == 0) {
1511 return kMaxConicToQuadPOW2;
1512 }
1513 SkScalar fpow2 = SkScalarLog2((x * x + y * y) / tol2) * 0.25f;
1514 int altPow2 = SkScalarCeilToInt(fpow2);
1515 if (altPow2 != pow2) {
1516 SkDebugf("pow2 %d altPow2 %d fbits %g err %g tol %g\n", pow2, altPow2, fpow2, err, tol);
1517 }
1518 pow2 = altPow2;
1519 }
1520 return pow2;
1521}
1522
1523// This was originally developed and tested for pathops: see SkOpTypes.h
1524// returns true if (a <= b <= c) || (a >= b >= c)
1526 return (a - b) * (c - b) <= 0;
1527}
1528
1529static SkPoint* subdivide(const SkConic& src, SkPoint pts[], int level) {
1530 SkASSERT(level >= 0);
1531
1532 if (0 == level) {
1533 memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint));
1534 return pts + 2;
1535 } else {
1536 SkConic dst[2];
1537 src.chop(dst);
1538 const SkScalar startY = src.fPts[0].fY;
1539 SkScalar endY = src.fPts[2].fY;
1540 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 SkScalar midY = dst[0].fPts[2].fY;
1544 if (!between(startY, midY, endY)) {
1545 // If the computed midpoint is outside the ends, move it to the closer one.
1546 SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY;
1547 dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY;
1548 }
1549 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 dst[0].fPts[1].fY = startY;
1553 }
1554 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 dst[1].fPts[1].fY = endY;
1558 }
1559 // Verify that all five points are in order.
1560 SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY));
1561 SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY));
1562 SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY));
1563 }
1564 --level;
1565 pts = subdivide(dst[0], pts, level);
1566 return subdivide(dst[1], pts, level);
1567 }
1568}
1569
1570int SkConic::chopIntoQuadsPOW2(SkPoint pts[], int pow2) const {
1571 SkASSERT(pow2 >= 0);
1572 *pts = fPts[0];
1573 SkDEBUGCODE(SkPoint* endPts);
1574 if (pow2 == kMaxConicToQuadPOW2) { // If an extreme weight generates many quads ...
1575 SkConic dst[2];
1576 this->chop(dst);
1577 // check to see if the first chop generates a pair of lines
1580 pts[1] = pts[2] = pts[3] = dst[0].fPts[1]; // set ctrl == end to make lines
1581 pts[4] = dst[1].fPts[2];
1582 pow2 = 1;
1583 SkDEBUGCODE(endPts = &pts[5]);
1584 goto commonFinitePtCheck;
1585 }
1586 }
1587 SkDEBUGCODE(endPts = ) subdivide(*this, pts + 1, pow2);
1588commonFinitePtCheck:
1589 const int quadCount = 1 << pow2;
1590 const int ptCount = 2 * quadCount + 1;
1591 SkASSERT(endPts - pts == ptCount);
1592 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 for (int i = 1; i < ptCount - 1; ++i) {
1596 pts[i] = fPts[1];
1597 }
1598 }
1599 return 1 << pow2;
1600}
1601
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 SkVector tan0 = fPts[1] - fPts[0];
1609 SkVector tan1 = fPts[2] - fPts[1];
1610 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 SkVector A = (fPts[2] - fPts[0]) * (fW - 1);
1624 SkVector B = (fPts[2] - fPts[0]) - (fPts[1] - fPts[0]) * (fW*2);
1625 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 float a = bisector.dot(A);
1634 float b = bisector.dot(B);
1635 float c = bisector.dot(C);
1637}
1638
1640 return conic_find_extrema(&fPts[0].fX, fW, t);
1641}
1642
1644 return conic_find_extrema(&fPts[0].fY, fW, t);
1645}
1646
1648 SkScalar t;
1649 if (this->findXExtrema(&t)) {
1650 if (!this->chopAt(t, dst)) {
1651 // if chop can't return finite values, don't chop
1652 return false;
1653 }
1654 // now clean-up the middle, since we know t was meant to be at
1655 // an X-extrema
1656 SkScalar value = dst[0].fPts[2].fX;
1657 dst[0].fPts[1].fX = value;
1658 dst[1].fPts[0].fX = value;
1659 dst[1].fPts[1].fX = value;
1660 return true;
1661 }
1662 return false;
1663}
1664
1666 SkScalar t;
1667 if (this->findYExtrema(&t)) {
1668 if (!this->chopAt(t, dst)) {
1669 // if chop can't return finite values, don't chop
1670 return false;
1671 }
1672 // now clean-up the middle, since we know t was meant to be at
1673 // an Y-extrema
1674 SkScalar value = dst[0].fPts[2].fY;
1675 dst[0].fPts[1].fY = value;
1676 dst[1].fPts[0].fY = value;
1677 dst[1].fPts[1].fY = value;
1678 return true;
1679 }
1680 return false;
1681}
1682
1684 SkPoint pts[4];
1685 pts[0] = fPts[0];
1686 pts[1] = fPts[2];
1687 int count = 2;
1688
1689 SkScalar t;
1690 if (this->findXExtrema(&t)) {
1691 this->evalAt(t, &pts[count++]);
1692 }
1693 if (this->findYExtrema(&t)) {
1694 this->evalAt(t, &pts[count++]);
1695 }
1696 bounds->setBounds(pts, count);
1697}
1698
1700 bounds->setBounds(fPts, 3);
1701}
1702
1703#if 0 // unimplemented
1704bool SkConic::findMaxCurvature(SkScalar* t) const {
1705 // TODO: Implement me
1706 return false;
1707}
1708#endif
1709
1711 if (!matrix.hasPerspective()) {
1712 return w;
1713 }
1714
1715 SkPoint3 src[3], dst[3];
1716
1717 ratquad_mapTo3D(pts, w, src);
1718
1719 matrix.mapHomogeneousPoints(dst, src, 3);
1720
1721 // w' = sqrt(w1*w1/w0*w2)
1722 // use doubles temporarily, to handle small numer/denom
1723 double w0 = dst[0].fZ;
1724 double w1 = dst[1].fZ;
1725 double w2 = dst[2].fZ;
1726 return sk_double_to_float(sqrt(sk_ieee_double_divide(w1 * w1, w0 * w2)));
1727}
1728
1730 const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) {
1731 // rotate by x,y so that uStart is (1.0)
1732 SkScalar x = SkPoint::DotProduct(uStart, uStop);
1733 SkScalar y = SkPoint::CrossProduct(uStart, uStop);
1734
1735 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 if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) ||
1741 (y <= 0 && kCCW_SkRotationDirection == dir))) {
1742 return 0;
1743 }
1744
1746 y = -y;
1747 }
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 int quadrant = 0;
1756 if (0 == y) {
1757 quadrant = 2; // 180
1759 } else if (0 == x) {
1761 quadrant = y > 0 ? 1 : 3; // 90 : 270
1762 } else {
1763 if (y < 0) {
1764 quadrant += 2;
1765 }
1766 if ((x < 0) != (y < 0)) {
1767 quadrant += 1;
1768 }
1769 }
1770
1771 const SkPoint quadrantPts[] = {
1772 { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }
1773 };
1774 const SkScalar quadrantWeight = SK_ScalarRoot2Over2;
1775
1776 int conicCount = quadrant;
1777 for (int i = 0; i < conicCount; ++i) {
1778 dst[i].set(&quadrantPts[i * 2], quadrantWeight);
1779 }
1780
1781 // Now compute any remaing (sub-90-degree) arc for the last conic
1782 const SkPoint finalP = { x, y };
1783 const SkPoint& lastQ = quadrantPts[quadrant * 2]; // will already be a unit-vector
1784 const SkScalar dot = SkVector::DotProduct(lastQ, finalP);
1785 if (SkIsNaN(dot)) {
1786 return 0;
1787 }
1789
1790 if (dot < 1) {
1791 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 const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2);
1798 offCurve.setLength(SkScalarInvert(cosThetaOver2));
1799 if (!SkPointPriv::EqualsWithinTolerance(lastQ, offCurve)) {
1800 dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2);
1801 conicCount += 1;
1802 }
1803 }
1804
1805 // now handle counter-clockwise and the initial unitStart rotation
1807 matrix.setSinCos(uStart.fY, uStart.fX);
1809 matrix.preScale(SK_Scalar1, -SK_Scalar1);
1810 }
1811 if (userMatrix) {
1812 matrix.postConcat(*userMatrix);
1813 }
1814 for (int i = 0; i < conicCount; ++i) {
1815 matrix.mapPoints(dst[i].fPts, 3);
1816 }
1817 return conicCount;
1818}
SkPoint fPts[2]
int count
Definition: FontMgrTest.cpp:50
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkASSERTF(cond, fmt,...)
Definition: SkAssert.h:117
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SK_RESTRICT
Definition: SkFeatures.h:42
static constexpr float sk_double_to_float(double x)
static constexpr bool SkIsNaN(T x)
static bool SkIsFinite(T x, Pack... values)
static constexpr double sk_ieee_double_divide(double numer, double denom)
static constexpr float sk_ieee_float_divide(float numer, float denom)
static SkScalar subdivide_w_value(SkScalar w)
static skvx::Vec< N, T > unchecked_mix(const skvx::Vec< N, T > &a, const skvx::Vec< N, T > &b, const skvx::Vec< N, T > &t)
Definition: SkGeometry.cpp:468
void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)
Definition: SkGeometry.cpp:175
float SkFindQuadMidTangent(const SkPoint src[3])
Definition: SkGeometry.cpp:231
static SkPoint project_down(const SkPoint3 &src)
int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5])
Definition: SkGeometry.cpp:307
SkVector SkFindBisector(SkVector a, SkVector b)
Definition: SkGeometry.cpp:204
static int collaps_duplicates(SkScalar array[], int count)
Definition: SkGeometry.cpp:896
int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10])
Definition: SkGeometry.cpp:698
static bool first_axis_intersection(const double coefficients[8], bool yDirection, double axisIntercept, double *solution)
static float solve_quadratic_equation_for_midtangent(float a, float b, float c, float discr)
Definition: SkGeometry.cpp:602
void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
Definition: SkGeometry.cpp:575
int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10])
Definition: SkGeometry.cpp:755
float SkMeasureNonInflectCubicRotation(const SkPoint pts[4])
Definition: SkGeometry.cpp:579
static void flatten_double_cubic_extrema(SkScalar coords[14])
Definition: SkGeometry.cpp:686
static bool conic_find_extrema(const SkScalar src[], SkScalar w, SkScalar *t)
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
Definition: SkGeometry.cpp:473
void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint *loc, SkVector *tangent, SkVector *curvature)
Definition: SkGeometry.cpp:418
int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
Definition: SkGeometry.cpp:95
void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5])
Definition: SkGeometry.cpp:193
SkScalar SkFindCubicCusp(const SkPoint src[4])
void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4])
Definition: SkGeometry.cpp:378
static void conic_deriv_coeff(const SkScalar src[], SkScalar w, SkScalar coeff[3])
SkScalar SkFindQuadMaxCurvature(const SkPoint src[3])
Definition: SkGeometry.cpp:344
static SkScalar SkScalarCubeRoot(SkScalar x)
Definition: SkGeometry.cpp:950
int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
Definition: SkGeometry.cpp:454
static SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t)
Definition: SkGeometry.cpp:407
static float2 interp(const float2 &v0, const float2 &v1, const float2 &t)
Definition: SkGeometry.cpp:169
float SkMeasureAngleBetweenVectors(SkVector a, SkVector b)
Definition: SkGeometry.cpp:197
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t)
Definition: SkGeometry.cpp:148
static void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4])
static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkPoint3 dst[3])
SkCubicType SkClassifyCubic(const SkPoint P[4], double t[2], double s[2], double d[4])
Definition: SkGeometry.cpp:809
static skvx::float4 fma(const skvx::float4 &f, float m, const skvx::float4 &a)
Definition: SkGeometry.cpp:597
static double calc_dot_cross_cubic(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
Definition: SkGeometry.cpp:771
#define kMaxConicToQuadPOW2
int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
Definition: SkGeometry.cpp:132
#define AS_QUAD_ERROR_SETUP
bool SkChopMonoCubicAtY(const SkPoint src[4], SkScalar y, SkPoint dst[7])
int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10])
Definition: SkGeometry.cpp:714
int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
Definition: SkGeometry.cpp:265
float SkFindCubicMidTangent(const SkPoint src[4])
Definition: SkGeometry.cpp:620
int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
static double previous_inverse_pow2(double n)
Definition: SkGeometry.cpp:782
int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2])
Definition: SkGeometry.cpp:741
static SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t)
Definition: SkGeometry.cpp:394
static bool between(SkScalar a, SkScalar b, SkScalar c)
static void p3d_interp(const SkScalar src[7], SkScalar dst[7], SkScalar t)
static SkPoint * subdivide(const SkConic &src, SkPoint pts[], int level)
static SkScalar calc_cubic_precision(const SkPoint src[4])
static bool close_enough_to_zero(double x)
bool SkChopMonoCubicAtX(const SkPoint src[4], SkScalar x, SkPoint dst[7])
static bool on_same_side(const SkPoint src[4], int testIndex, int lineIndex)
void bubble_sort(T array[], int count)
Definition: SkGeometry.cpp:881
static void write_cubic_inflection_roots(double t0, double s0, double t1, double s1, double *t, double *s)
Definition: SkGeometry.cpp:791
static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3])
Definition: SkGeometry.cpp:961
static void flatten_double_quad_extrema(SkScalar coords[14])
Definition: SkGeometry.cpp:272
int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5])
Definition: SkGeometry.cpp:367
int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5])
Definition: SkGeometry.cpp:279
static skvx::float2 times_2(const skvx::float2 &value)
Definition: SkGeometry.h:32
SkRotationDirection
Definition: SkGeometry.h:321
@ kCW_SkRotationDirection
Definition: SkGeometry.h:322
@ kCCW_SkRotationDirection
Definition: SkGeometry.h:323
static skvx::float2 from_point(const SkPoint &point)
Definition: SkGeometry.h:22
SkCubicType
Definition: SkGeometry.h:264
static int valid_unit_divide(double numer, double denom, double *ratio)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
#define SkScalarInvert(x)
Definition: SkScalar.h:73
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarHalf
Definition: SkScalar.h:19
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
#define SkScalarCos(radians)
Definition: SkScalar.h:46
static SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t)
Definition: SkScalar.h:131
#define SkScalarSqrt(x)
Definition: SkScalar.h:42
#define SK_ScalarRoot2Over2
Definition: SkScalar.h:23
#define SkScalarPow(b, e)
Definition: SkScalar.h:43
#define SkScalarAbs(x)
Definition: SkScalar.h:39
#define SK_ScalarPI
Definition: SkScalar.h:21
#define SkScalarACos(val)
Definition: SkScalar.h:49
#define SkScalarLog2(x)
Definition: SkScalar.h:53
skvx::float2 float2
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
static T SkTAbs(T value)
Definition: SkTemplates.h:43
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
static std::array< double, 4 > ConvertToPolynomial(const double curve[8], bool yValues)
static void Subdivide(const double curve[8], double t, double twoCurves[14])
static int BinarySearchRootsValidT(double A, double B, double C, double D, double solution[3])
Definition: SkCubics.cpp:208
static double EvalAt(double A, double B, double C, double D, double t)
Definition: SkCubics.h:51
static int RootsValidT(double A, double B, double C, double D, double solution[3])
Definition: SkCubics.cpp:127
static SkScalar LengthSqd(const SkPoint &pt)
Definition: SkPointPriv.h:63
static bool AreFinite(const SkPoint array[], int count)
Definition: SkPointPriv.h:22
static bool EqualsWithinTolerance(const SkPoint &p1, const SkPoint &p2)
Definition: SkPointPriv.h:54
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
Definition: SkPointPriv.h:48
#define C(TEST_CATEGORY)
Definition: colrv1.cpp:248
static SkPoint to_point(SkIPoint p)
Definition: editor.cpp:75
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
#define R(r)
#define B
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
Definition: ab.py:1
const CatchEntryMove ab[]
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
string root
Definition: scale_cpu.py:20
SINT T dot(const Vec< N, T > &a, const Vec< N, T > &b)
Definition: SkVx.h:964
SIN Vec< N, float > fma(const Vec< N, float > &x, const Vec< N, float > &y, const Vec< N, float > &z)
Definition: SkVx.h:708
Vec< 4, float > float4
Definition: SkVx.h:1146
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
Vec< 2, float > float2
Definition: SkVx.h:1145
SINT Vec< N, T > pin(const Vec< N, T > &x, const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition: SkVx.h:655
SkScalar w
#define T
Definition: precompiler.cc:65
const Scalar scale
bool findXExtrema(SkScalar *t) const
int SK_SPI chopIntoQuadsPOW2(SkPoint pts[], int pow2) const
SkVector evalTangentAt(SkScalar t) const
bool chopAtYExtrema(SkConic dst[2]) const
void computeTightBounds(SkRect *bounds) const
int SK_SPI computeQuadPOW2(SkScalar tol) const
static SkScalar TransformW(const SkPoint[3], SkScalar w, const SkMatrix &)
SkScalar fW
Definition: SkGeometry.h:337
void computeAsQuadError(SkVector *err) const
bool findYExtrema(SkScalar *t) const
bool chopAtXExtrema(SkConic dst[2]) const
float findMidTangent() const
void evalAt(SkScalar t, SkPoint *pos, SkVector *tangent=nullptr) const
bool chopAt(SkScalar t, SkConic dst[2]) const
bool asQuadTol(SkScalar tol) const
void chop(SkConic dst[2]) const
SkPoint fPts[3]
Definition: SkGeometry.h:336
static int BuildUnitArc(const SkVector &start, const SkVector &stop, SkRotationDirection, const SkMatrix *, SkConic conics[kMaxConicsForArc])
void computeFastBounds(SkRect *bounds) const
SkScalar fZ
Definition: SkPoint3.h:16
static float CrossProduct(const SkVector &a, const SkVector &b)
Definition: SkPoint_impl.h:532
bool isZero() const
Definition: SkPoint_impl.h:193
bool setLength(float length)
Definition: SkPoint.cpp:30
float fX
x-axis value
Definition: SkPoint_impl.h:164
bool isFinite() const
Definition: SkPoint_impl.h:412
static float DotProduct(const SkVector &a, const SkVector &b)
Definition: SkPoint_impl.h:518
float dot(const SkVector &vec) const
Definition: SkPoint_impl.h:554
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
Definition: SkVx.h:83
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109
SKVX_ALWAYS_INLINE void store(void *ptr) const
Definition: SkVx.h:112
Vec< N/2, T > hi
Definition: SkVx.h:117
Vec< N/2, T > lo
Definition: SkVx.h:117
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63