1 /* libs/graphics/sgl/SkGeometry.cpp
2 **
3 ** Copyright 2006, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 ** http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17
18 #include "SkGeometry.h"
19 #include "Sk64.h"
20 #include "SkMatrix.h"
21
22 /** If defined, this makes eval_quad and eval_cubic do more setup (sometimes
23 involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul.
24 May also introduce overflow of fixed when we compute our setup.
25 */
26 #ifdef SK_SCALAR_IS_FIXED
27 #define DIRECT_EVAL_OF_POLYNOMIALS
28 #endif
29
30 ////////////////////////////////////////////////////////////////////////
31
32 #ifdef SK_SCALAR_IS_FIXED
is_not_monotonic(int a,int b,int c,int d)33 static int is_not_monotonic(int a, int b, int c, int d)
34 {
35 return (((a - b) | (b - c) | (c - d)) & ((b - a) | (c - b) | (d - c))) >> 31;
36 }
37
is_not_monotonic(int a,int b,int c)38 static int is_not_monotonic(int a, int b, int c)
39 {
40 return (((a - b) | (b - c)) & ((b - a) | (c - b))) >> 31;
41 }
42 #else
is_not_monotonic(float a,float b,float c)43 static int is_not_monotonic(float a, float b, float c)
44 {
45 float ab = a - b;
46 float bc = b - c;
47 if (ab < 0)
48 bc = -bc;
49 return ab == 0 || bc < 0;
50 }
51 #endif
52
53 ////////////////////////////////////////////////////////////////////////
54
is_unit_interval(SkScalar x)55 static bool is_unit_interval(SkScalar x)
56 {
57 return x > 0 && x < SK_Scalar1;
58 }
59
valid_unit_divide(SkScalar numer,SkScalar denom,SkScalar * ratio)60 static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio)
61 {
62 SkASSERT(ratio);
63
64 if (numer < 0)
65 {
66 numer = -numer;
67 denom = -denom;
68 }
69
70 if (denom == 0 || numer == 0 || numer >= denom)
71 return 0;
72
73 SkScalar r = SkScalarDiv(numer, denom);
74 SkASSERT(r >= 0 && r < SK_Scalar1);
75 if (r == 0) // catch underflow if numer <<<< denom
76 return 0;
77 *ratio = r;
78 return 1;
79 }
80
81 /** From Numerical Recipes in C.
82
83 Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
84 x1 = Q / A
85 x2 = C / Q
86 */
SkFindUnitQuadRoots(SkScalar A,SkScalar B,SkScalar C,SkScalar roots[2])87 int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
88 {
89 SkASSERT(roots);
90
91 if (A == 0)
92 return valid_unit_divide(-C, B, roots);
93
94 SkScalar* r = roots;
95
96 #ifdef SK_SCALAR_IS_FLOAT
97 float R = B*B - 4*A*C;
98 if (R < 0) // complex roots
99 return 0;
100 R = sk_float_sqrt(R);
101 #else
102 Sk64 RR, tmp;
103
104 RR.setMul(B,B);
105 tmp.setMul(A,C);
106 tmp.shiftLeft(2);
107 RR.sub(tmp);
108 if (RR.isNeg())
109 return 0;
110 SkFixed R = RR.getSqrt();
111 #endif
112
113 SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
114 r += valid_unit_divide(Q, A, r);
115 r += valid_unit_divide(C, Q, r);
116 if (r - roots == 2)
117 {
118 if (roots[0] > roots[1])
119 SkTSwap<SkScalar>(roots[0], roots[1]);
120 else if (roots[0] == roots[1]) // nearly-equal?
121 r -= 1; // skip the double root
122 }
123 return (int)(r - roots);
124 }
125
126 #ifdef SK_SCALAR_IS_FIXED
127 /** Trim A/B/C down so that they are all <= 32bits
128 and then call SkFindUnitQuadRoots()
129 */
Sk64FindFixedQuadRoots(const Sk64 & A,const Sk64 & B,const Sk64 & C,SkFixed roots[2])130 static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2])
131 {
132 int na = A.shiftToMake32();
133 int nb = B.shiftToMake32();
134 int nc = C.shiftToMake32();
135
136 int shift = SkMax32(na, SkMax32(nb, nc));
137 SkASSERT(shift >= 0);
138
139 return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots);
140 }
141 #endif
142
143 /////////////////////////////////////////////////////////////////////////////////////
144 /////////////////////////////////////////////////////////////////////////////////////
145
eval_quad(const SkScalar src[],SkScalar t)146 static SkScalar eval_quad(const SkScalar src[], SkScalar t)
147 {
148 SkASSERT(src);
149 SkASSERT(t >= 0 && t <= SK_Scalar1);
150
151 #ifdef DIRECT_EVAL_OF_POLYNOMIALS
152 SkScalar C = src[0];
153 SkScalar A = src[4] - 2 * src[2] + C;
154 SkScalar B = 2 * (src[2] - C);
155 return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
156 #else
157 SkScalar ab = SkScalarInterp(src[0], src[2], t);
158 SkScalar bc = SkScalarInterp(src[2], src[4], t);
159 return SkScalarInterp(ab, bc, t);
160 #endif
161 }
162
eval_quad_derivative(const SkScalar src[],SkScalar t)163 static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t)
164 {
165 SkScalar A = src[4] - 2 * src[2] + src[0];
166 SkScalar B = src[2] - src[0];
167
168 return 2 * SkScalarMulAdd(A, t, B);
169 }
170
eval_quad_derivative_at_half(const SkScalar src[])171 static SkScalar eval_quad_derivative_at_half(const SkScalar src[])
172 {
173 SkScalar A = src[4] - 2 * src[2] + src[0];
174 SkScalar B = src[2] - src[0];
175 return A + 2 * B;
176 }
177
SkEvalQuadAt(const SkPoint src[3],SkScalar t,SkPoint * pt,SkVector * tangent)178 void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent)
179 {
180 SkASSERT(src);
181 SkASSERT(t >= 0 && t <= SK_Scalar1);
182
183 if (pt)
184 pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t));
185 if (tangent)
186 tangent->set(eval_quad_derivative(&src[0].fX, t),
187 eval_quad_derivative(&src[0].fY, t));
188 }
189
SkEvalQuadAtHalf(const SkPoint src[3],SkPoint * pt,SkVector * tangent)190 void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent)
191 {
192 SkASSERT(src);
193
194 if (pt)
195 {
196 SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
197 SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
198 SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
199 SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
200 pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
201 }
202 if (tangent)
203 tangent->set(eval_quad_derivative_at_half(&src[0].fX),
204 eval_quad_derivative_at_half(&src[0].fY));
205 }
206
interp_quad_coords(const SkScalar * src,SkScalar * dst,SkScalar t)207 static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
208 {
209 SkScalar ab = SkScalarInterp(src[0], src[2], t);
210 SkScalar bc = SkScalarInterp(src[2], src[4], t);
211
212 dst[0] = src[0];
213 dst[2] = ab;
214 dst[4] = SkScalarInterp(ab, bc, t);
215 dst[6] = bc;
216 dst[8] = src[4];
217 }
218
SkChopQuadAt(const SkPoint src[3],SkPoint dst[5],SkScalar t)219 void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)
220 {
221 SkASSERT(t > 0 && t < SK_Scalar1);
222
223 interp_quad_coords(&src[0].fX, &dst[0].fX, t);
224 interp_quad_coords(&src[0].fY, &dst[0].fY, t);
225 }
226
SkChopQuadAtHalf(const SkPoint src[3],SkPoint dst[5])227 void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5])
228 {
229 SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
230 SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
231 SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
232 SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
233
234 dst[0] = src[0];
235 dst[1].set(x01, y01);
236 dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
237 dst[3].set(x12, y12);
238 dst[4] = src[2];
239 }
240
241 /** Quad'(t) = At + B, where
242 A = 2(a - 2b + c)
243 B = 2(b - a)
244 Solve for t, only if it fits between 0 < t < 1
245 */
SkFindQuadExtrema(SkScalar a,SkScalar b,SkScalar c,SkScalar tValue[1])246 int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
247 {
248 /* At + B == 0
249 t = -B / A
250 */
251 #ifdef SK_SCALAR_IS_FIXED
252 return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue);
253 #else
254 return valid_unit_divide(a - b, a - b - b + c, tValue);
255 #endif
256 }
257
flatten_double_quad_extrema(SkScalar coords[14])258 static inline void flatten_double_quad_extrema(SkScalar coords[14])
259 {
260 coords[2] = coords[6] = coords[4];
261 }
262
force_quad_monotonic_in_y(SkPoint pts[3])263 static inline void force_quad_monotonic_in_y(SkPoint pts[3])
264 {
265 // zap pts[1].fY to the nearest value
266 SkScalar ab = SkScalarAbs(pts[0].fY - pts[1].fY);
267 SkScalar bc = SkScalarAbs(pts[1].fY - pts[2].fY);
268 pts[1].fY = ab < bc ? pts[0].fY : pts[2].fY;
269 }
270
271 /* Returns 0 for 1 quad, and 1 for two quads, either way the answer is
272 stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
273 */
SkChopQuadAtYExtrema(const SkPoint src[3],SkPoint dst[5])274 int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5])
275 {
276 SkASSERT(src);
277 SkASSERT(dst);
278
279 #if 0
280 static bool once = true;
281 if (once)
282 {
283 once = false;
284 SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 };
285 SkPoint d[6];
286
287 int n = SkChopQuadAtYExtrema(s, d);
288 SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY);
289 }
290 #endif
291
292 SkScalar a = src[0].fY;
293 SkScalar b = src[1].fY;
294 SkScalar c = src[2].fY;
295
296 if (is_not_monotonic(a, b, c))
297 {
298 SkScalar tValue;
299 if (valid_unit_divide(a - b, a - b - b + c, &tValue))
300 {
301 SkChopQuadAt(src, dst, tValue);
302 flatten_double_quad_extrema(&dst[0].fY);
303 return 1;
304 }
305 // if we get here, we need to force dst to be monotonic, even though
306 // we couldn't compute a unit_divide value (probably underflow).
307 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
308 }
309 dst[0].set(src[0].fX, a);
310 dst[1].set(src[1].fX, b);
311 dst[2].set(src[2].fX, c);
312 return 0;
313 }
314
315 // F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2
316 // F'(t) = 2 (b - a) + 2 (a - 2b + c) t
317 // F''(t) = 2 (a - 2b + c)
318 //
319 // A = 2 (b - a)
320 // B = 2 (a - 2b + c)
321 //
322 // Maximum curvature for a quadratic means solving
323 // Fx' Fx'' + Fy' Fy'' = 0
324 //
325 // t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2)
326 //
SkChopQuadAtMaxCurvature(const SkPoint src[3],SkPoint dst[5])327 int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5])
328 {
329 SkScalar Ax = src[1].fX - src[0].fX;
330 SkScalar Ay = src[1].fY - src[0].fY;
331 SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
332 SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
333 SkScalar t = 0; // 0 means don't chop
334
335 #ifdef SK_SCALAR_IS_FLOAT
336 (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t);
337 #else
338 // !!! should I use SkFloat here? seems like it
339 Sk64 numer, denom, tmp;
340
341 numer.setMul(Ax, -Bx);
342 tmp.setMul(Ay, -By);
343 numer.add(tmp);
344
345 if (numer.isPos()) // do nothing if numer <= 0
346 {
347 denom.setMul(Bx, Bx);
348 tmp.setMul(By, By);
349 denom.add(tmp);
350 SkASSERT(!denom.isNeg());
351 if (numer < denom)
352 {
353 t = numer.getFixedDiv(denom);
354 SkASSERT(t >= 0 && t <= SK_Fixed1); // assert that we're numerically stable (ha!)
355 if ((unsigned)t >= SK_Fixed1) // runtime check for numerical stability
356 t = 0; // ignore the chop
357 }
358 }
359 #endif
360
361 if (t == 0)
362 {
363 memcpy(dst, src, 3 * sizeof(SkPoint));
364 return 1;
365 }
366 else
367 {
368 SkChopQuadAt(src, dst, t);
369 return 2;
370 }
371 }
372
373 ////////////////////////////////////////////////////////////////////////////////////////
374 ///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
375 ////////////////////////////////////////////////////////////////////////////////////////
376
get_cubic_coeff(const SkScalar pt[],SkScalar coeff[4])377 static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4])
378 {
379 coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0];
380 coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]);
381 coeff[2] = 3*(pt[2] - pt[0]);
382 coeff[3] = pt[0];
383 }
384
SkGetCubicCoeff(const SkPoint pts[4],SkScalar cx[4],SkScalar cy[4])385 void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4])
386 {
387 SkASSERT(pts);
388
389 if (cx)
390 get_cubic_coeff(&pts[0].fX, cx);
391 if (cy)
392 get_cubic_coeff(&pts[0].fY, cy);
393 }
394
eval_cubic(const SkScalar src[],SkScalar t)395 static SkScalar eval_cubic(const SkScalar src[], SkScalar t)
396 {
397 SkASSERT(src);
398 SkASSERT(t >= 0 && t <= SK_Scalar1);
399
400 if (t == 0)
401 return src[0];
402
403 #ifdef DIRECT_EVAL_OF_POLYNOMIALS
404 SkScalar D = src[0];
405 SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
406 SkScalar B = 3*(src[4] - src[2] - src[2] + D);
407 SkScalar C = 3*(src[2] - D);
408
409 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
410 #else
411 SkScalar ab = SkScalarInterp(src[0], src[2], t);
412 SkScalar bc = SkScalarInterp(src[2], src[4], t);
413 SkScalar cd = SkScalarInterp(src[4], src[6], t);
414 SkScalar abc = SkScalarInterp(ab, bc, t);
415 SkScalar bcd = SkScalarInterp(bc, cd, t);
416 return SkScalarInterp(abc, bcd, t);
417 #endif
418 }
419
420 /** return At^2 + Bt + C
421 */
eval_quadratic(SkScalar A,SkScalar B,SkScalar C,SkScalar t)422 static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t)
423 {
424 SkASSERT(t >= 0 && t <= SK_Scalar1);
425
426 return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
427 }
428
eval_cubic_derivative(const SkScalar src[],SkScalar t)429 static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t)
430 {
431 SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
432 SkScalar B = 2*(src[4] - 2 * src[2] + src[0]);
433 SkScalar C = src[2] - src[0];
434
435 return eval_quadratic(A, B, C, t);
436 }
437
eval_cubic_2ndDerivative(const SkScalar src[],SkScalar t)438 static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t)
439 {
440 SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
441 SkScalar B = src[4] - 2 * src[2] + src[0];
442
443 return SkScalarMulAdd(A, t, B);
444 }
445
SkEvalCubicAt(const SkPoint src[4],SkScalar t,SkPoint * loc,SkVector * tangent,SkVector * curvature)446 void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkVector* tangent, SkVector* curvature)
447 {
448 SkASSERT(src);
449 SkASSERT(t >= 0 && t <= SK_Scalar1);
450
451 if (loc)
452 loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t));
453 if (tangent)
454 tangent->set(eval_cubic_derivative(&src[0].fX, t),
455 eval_cubic_derivative(&src[0].fY, t));
456 if (curvature)
457 curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t),
458 eval_cubic_2ndDerivative(&src[0].fY, t));
459 }
460
461 /** Cubic'(t) = At^2 + Bt + C, where
462 A = 3(-a + 3(b - c) + d)
463 B = 6(a - 2b + c)
464 C = 3(b - a)
465 Solve for t, keeping only those that fit betwee 0 < t < 1
466 */
SkFindCubicExtrema(SkScalar a,SkScalar b,SkScalar c,SkScalar d,SkScalar tValues[2])467 int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
468 {
469 #ifdef SK_SCALAR_IS_FIXED
470 if (!is_not_monotonic(a, b, c, d))
471 return 0;
472 #endif
473
474 // we divide A,B,C by 3 to simplify
475 SkScalar A = d - a + 3*(b - c);
476 SkScalar B = 2*(a - b - b + c);
477 SkScalar C = b - a;
478
479 return SkFindUnitQuadRoots(A, B, C, tValues);
480 }
481
interp_cubic_coords(const SkScalar * src,SkScalar * dst,SkScalar t)482 static void interp_cubic_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
483 {
484 SkScalar ab = SkScalarInterp(src[0], src[2], t);
485 SkScalar bc = SkScalarInterp(src[2], src[4], t);
486 SkScalar cd = SkScalarInterp(src[4], src[6], t);
487 SkScalar abc = SkScalarInterp(ab, bc, t);
488 SkScalar bcd = SkScalarInterp(bc, cd, t);
489 SkScalar abcd = SkScalarInterp(abc, bcd, t);
490
491 dst[0] = src[0];
492 dst[2] = ab;
493 dst[4] = abc;
494 dst[6] = abcd;
495 dst[8] = bcd;
496 dst[10] = cd;
497 dst[12] = src[6];
498 }
499
SkChopCubicAt(const SkPoint src[4],SkPoint dst[7],SkScalar t)500 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
501 {
502 SkASSERT(t > 0 && t < SK_Scalar1);
503
504 interp_cubic_coords(&src[0].fX, &dst[0].fX, t);
505 interp_cubic_coords(&src[0].fY, &dst[0].fY, t);
506 }
507
508 /* http://code.google.com/p/skia/issues/detail?id=32
509
510 This test code would fail when we didn't check the return result of
511 valid_unit_divide in SkChopCubicAt(... tValues[], int roots). The reason is
512 that after the first chop, the parameters to valid_unit_divide are equal
513 (thanks to finite float precision and rounding in the subtracts). Thus
514 even though the 2nd tValue looks < 1.0, after we renormalize it, we end
515 up with 1.0, hence the need to check and just return the last cubic as
516 a degenerate clump of 4 points in the sampe place.
517
518 static void test_cubic() {
519 SkPoint src[4] = {
520 { 556.25000, 523.03003 },
521 { 556.23999, 522.96002 },
522 { 556.21997, 522.89001 },
523 { 556.21997, 522.82001 }
524 };
525 SkPoint dst[10];
526 SkScalar tval[] = { 0.33333334f, 0.99999994f };
527 SkChopCubicAt(src, dst, tval, 2);
528 }
529 */
530
SkChopCubicAt(const SkPoint src[4],SkPoint dst[],const SkScalar tValues[],int roots)531 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots)
532 {
533 #ifdef SK_DEBUG
534 {
535 for (int i = 0; i < roots - 1; i++)
536 {
537 SkASSERT(is_unit_interval(tValues[i]));
538 SkASSERT(is_unit_interval(tValues[i+1]));
539 SkASSERT(tValues[i] < tValues[i+1]);
540 }
541 }
542 #endif
543
544 if (dst)
545 {
546 if (roots == 0) // nothing to chop
547 memcpy(dst, src, 4*sizeof(SkPoint));
548 else
549 {
550 SkScalar t = tValues[0];
551 SkPoint tmp[4];
552
553 for (int i = 0; i < roots; i++)
554 {
555 SkChopCubicAt(src, dst, t);
556 if (i == roots - 1)
557 break;
558
559 dst += 3;
560 // have src point to the remaining cubic (after the chop)
561 memcpy(tmp, dst, 4 * sizeof(SkPoint));
562 src = tmp;
563
564 // watch out in case the renormalized t isn't in range
565 if (!valid_unit_divide(tValues[i+1] - tValues[i],
566 SK_Scalar1 - tValues[i], &t)) {
567 // if we can't, just create a degenerate cubic
568 dst[4] = dst[5] = dst[6] = src[3];
569 break;
570 }
571 }
572 }
573 }
574 }
575
SkChopCubicAtHalf(const SkPoint src[4],SkPoint dst[7])576 void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
577 {
578 SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
579 SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
580 SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
581 SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
582 SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX);
583 SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY);
584
585 SkScalar x012 = SkScalarAve(x01, x12);
586 SkScalar y012 = SkScalarAve(y01, y12);
587 SkScalar x123 = SkScalarAve(x12, x23);
588 SkScalar y123 = SkScalarAve(y12, y23);
589
590 dst[0] = src[0];
591 dst[1].set(x01, y01);
592 dst[2].set(x012, y012);
593 dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123));
594 dst[4].set(x123, y123);
595 dst[5].set(x23, y23);
596 dst[6] = src[3];
597 }
598
flatten_double_cubic_extrema(SkScalar coords[14])599 static void flatten_double_cubic_extrema(SkScalar coords[14])
600 {
601 coords[4] = coords[8] = coords[6];
602 }
603
604 /** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
605 the resulting beziers are monotonic in Y. This is called by the scan converter.
606 Depending on what is returned, dst[] is treated as follows
607 0 dst[0..3] is the original cubic
608 1 dst[0..3] and dst[3..6] are the two new cubics
609 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics
610 If dst == null, it is ignored and only the count is returned.
611 */
SkChopCubicAtYExtrema(const SkPoint src[4],SkPoint dst[10])612 int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10])
613 {
614 SkScalar tValues[2];
615 int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, tValues);
616
617 SkChopCubicAt(src, dst, tValues, roots);
618 if (dst && roots > 0)
619 {
620 // we do some cleanup to ensure our Y extrema are flat
621 flatten_double_cubic_extrema(&dst[0].fY);
622 if (roots == 2)
623 flatten_double_cubic_extrema(&dst[3].fY);
624 }
625 return roots;
626 }
627
628 /** http://www.faculty.idc.ac.il/arik/quality/appendixA.html
629
630 Inflection means that curvature is zero.
631 Curvature is [F' x F''] / [F'^3]
632 So we solve F'x X F''y - F'y X F''y == 0
633 After some canceling of the cubic term, we get
634 A = b - a
635 B = c - 2b + a
636 C = d - 3c + 3b - a
637 (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0
638 */
SkFindCubicInflections(const SkPoint src[4],SkScalar tValues[])639 int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[])
640 {
641 SkScalar Ax = src[1].fX - src[0].fX;
642 SkScalar Ay = src[1].fY - src[0].fY;
643 SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
644 SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY;
645 SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
646 SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
647 int count;
648
649 #ifdef SK_SCALAR_IS_FLOAT
650 count = SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues);
651 #else
652 Sk64 A, B, C, tmp;
653
654 A.setMul(Bx, Cy);
655 tmp.setMul(By, Cx);
656 A.sub(tmp);
657
658 B.setMul(Ax, Cy);
659 tmp.setMul(Ay, Cx);
660 B.sub(tmp);
661
662 C.setMul(Ax, By);
663 tmp.setMul(Ay, Bx);
664 C.sub(tmp);
665
666 count = Sk64FindFixedQuadRoots(A, B, C, tValues);
667 #endif
668
669 return count;
670 }
671
SkChopCubicAtInflections(const SkPoint src[],SkPoint dst[10])672 int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10])
673 {
674 SkScalar tValues[2];
675 int count = SkFindCubicInflections(src, tValues);
676
677 if (dst)
678 {
679 if (count == 0)
680 memcpy(dst, src, 4 * sizeof(SkPoint));
681 else
682 SkChopCubicAt(src, dst, tValues, count);
683 }
684 return count + 1;
685 }
686
bubble_sort(T array[],int count)687 template <typename T> void bubble_sort(T array[], int count)
688 {
689 for (int i = count - 1; i > 0; --i)
690 for (int j = i; j > 0; --j)
691 if (array[j] < array[j-1])
692 {
693 T tmp(array[j]);
694 array[j] = array[j-1];
695 array[j-1] = tmp;
696 }
697 }
698
699 #include "SkFP.h"
700
701 // newton refinement
702 #if 0
703 static SkScalar refine_cubic_root(const SkFP coeff[4], SkScalar root)
704 {
705 // x1 = x0 - f(t) / f'(t)
706
707 SkFP T = SkScalarToFloat(root);
708 SkFP N, D;
709
710 // f' = 3*coeff[0]*T^2 + 2*coeff[1]*T + coeff[2]
711 D = SkFPMul(SkFPMul(coeff[0], SkFPMul(T,T)), 3);
712 D = SkFPAdd(D, SkFPMulInt(SkFPMul(coeff[1], T), 2));
713 D = SkFPAdd(D, coeff[2]);
714
715 if (D == 0)
716 return root;
717
718 // f = coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
719 N = SkFPMul(SkFPMul(SkFPMul(T, T), T), coeff[0]);
720 N = SkFPAdd(N, SkFPMul(SkFPMul(T, T), coeff[1]));
721 N = SkFPAdd(N, SkFPMul(T, coeff[2]));
722 N = SkFPAdd(N, coeff[3]);
723
724 if (N)
725 {
726 SkScalar delta = SkFPToScalar(SkFPDiv(N, D));
727
728 if (delta)
729 root -= delta;
730 }
731 return root;
732 }
733 #endif
734
735 #if defined _WIN32 && _MSC_VER >= 1300 && defined SK_SCALAR_IS_FIXED // disable warning : unreachable code if building fixed point for windows desktop
736 #pragma warning ( disable : 4702 )
737 #endif
738
739 /* Solve coeff(t) == 0, returning the number of roots that
740 lie withing 0 < t < 1.
741 coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3]
742 */
solve_cubic_polynomial(const SkFP coeff[4],SkScalar tValues[3])743 static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3])
744 {
745 #ifndef SK_SCALAR_IS_FLOAT
746 return 0; // this is not yet implemented for software float
747 #endif
748
749 if (SkScalarNearlyZero(coeff[0])) // we're just a quadratic
750 {
751 return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
752 }
753
754 SkFP a, b, c, Q, R;
755
756 {
757 SkASSERT(coeff[0] != 0);
758
759 SkFP inva = SkFPInvert(coeff[0]);
760 a = SkFPMul(coeff[1], inva);
761 b = SkFPMul(coeff[2], inva);
762 c = SkFPMul(coeff[3], inva);
763 }
764 Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9);
765 // R = (2*a*a*a - 9*a*b + 27*c) / 54;
766 R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2);
767 R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9));
768 R = SkFPAdd(R, SkFPMulInt(c, 27));
769 R = SkFPDivInt(R, 54);
770
771 SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q);
772 SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3);
773 SkFP adiv3 = SkFPDivInt(a, 3);
774
775 SkScalar* roots = tValues;
776 SkScalar r;
777
778 if (SkFPLT(R2MinusQ3, 0)) // we have 3 real roots
779 {
780 #ifdef SK_SCALAR_IS_FLOAT
781 float theta = sk_float_acos(R / sk_float_sqrt(Q3));
782 float neg2RootQ = -2 * sk_float_sqrt(Q);
783
784 r = neg2RootQ * sk_float_cos(theta/3) - adiv3;
785 if (is_unit_interval(r))
786 *roots++ = r;
787
788 r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3;
789 if (is_unit_interval(r))
790 *roots++ = r;
791
792 r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3;
793 if (is_unit_interval(r))
794 *roots++ = r;
795
796 // now sort the roots
797 bubble_sort(tValues, (int)(roots - tValues));
798 #endif
799 }
800 else // we have 1 real root
801 {
802 SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3));
803 A = SkFPCubeRoot(A);
804 if (SkFPGT(R, 0))
805 A = SkFPNeg(A);
806
807 if (A != 0)
808 A = SkFPAdd(A, SkFPDiv(Q, A));
809 r = SkFPToScalar(SkFPSub(A, adiv3));
810 if (is_unit_interval(r))
811 *roots++ = r;
812 }
813
814 return (int)(roots - tValues);
815 }
816
817 /* Looking for F' dot F'' == 0
818
819 A = b - a
820 B = c - 2b + a
821 C = d - 3c + 3b - a
822
823 F' = 3Ct^2 + 6Bt + 3A
824 F'' = 6Ct + 6B
825
826 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
827 */
formulate_F1DotF2(const SkScalar src[],SkFP coeff[4])828 static void formulate_F1DotF2(const SkScalar src[], SkFP coeff[4])
829 {
830 SkScalar a = src[2] - src[0];
831 SkScalar b = src[4] - 2 * src[2] + src[0];
832 SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0];
833
834 SkFP A = SkScalarToFP(a);
835 SkFP B = SkScalarToFP(b);
836 SkFP C = SkScalarToFP(c);
837
838 coeff[0] = SkFPMul(C, C);
839 coeff[1] = SkFPMulInt(SkFPMul(B, C), 3);
840 coeff[2] = SkFPMulInt(SkFPMul(B, B), 2);
841 coeff[2] = SkFPAdd(coeff[2], SkFPMul(C, A));
842 coeff[3] = SkFPMul(A, B);
843 }
844
845 // EXPERIMENTAL: can set this to zero to accept all t-values 0 < t < 1
846 //#define kMinTValueForChopping (SK_Scalar1 / 256)
847 #define kMinTValueForChopping 0
848
849 /* Looking for F' dot F'' == 0
850
851 A = b - a
852 B = c - 2b + a
853 C = d - 3c + 3b - a
854
855 F' = 3Ct^2 + 6Bt + 3A
856 F'' = 6Ct + 6B
857
858 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
859 */
SkFindCubicMaxCurvature(const SkPoint src[4],SkScalar tValues[3])860 int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])
861 {
862 SkFP coeffX[4], coeffY[4];
863 int i;
864
865 formulate_F1DotF2(&src[0].fX, coeffX);
866 formulate_F1DotF2(&src[0].fY, coeffY);
867
868 for (i = 0; i < 4; i++)
869 coeffX[i] = SkFPAdd(coeffX[i],coeffY[i]);
870
871 SkScalar t[3];
872 int count = solve_cubic_polynomial(coeffX, t);
873 int maxCount = 0;
874
875 // now remove extrema where the curvature is zero (mins)
876 // !!!! need a test for this !!!!
877 for (i = 0; i < count; i++)
878 {
879 // if (not_min_curvature())
880 if (t[i] > kMinTValueForChopping && t[i] < SK_Scalar1 - kMinTValueForChopping)
881 tValues[maxCount++] = t[i];
882 }
883 return maxCount;
884 }
885
SkChopCubicAtMaxCurvature(const SkPoint src[4],SkPoint dst[13],SkScalar tValues[3])886 int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
887 {
888 SkScalar t_storage[3];
889
890 if (tValues == NULL)
891 tValues = t_storage;
892
893 int count = SkFindCubicMaxCurvature(src, tValues);
894
895 if (dst)
896 {
897 if (count == 0)
898 memcpy(dst, src, 4 * sizeof(SkPoint));
899 else
900 SkChopCubicAt(src, dst, tValues, count);
901 }
902 return count + 1;
903 }
904
905 ////////////////////////////////////////////////////////////////////////////////
906
907 /* Find t value for quadratic [a, b, c] = d.
908 Return 0 if there is no solution within [0, 1)
909 */
quad_solve(SkScalar a,SkScalar b,SkScalar c,SkScalar d)910 static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d)
911 {
912 // At^2 + Bt + C = d
913 SkScalar A = a - 2 * b + c;
914 SkScalar B = 2 * (b - a);
915 SkScalar C = a - d;
916
917 SkScalar roots[2];
918 int count = SkFindUnitQuadRoots(A, B, C, roots);
919
920 SkASSERT(count <= 1);
921 return count == 1 ? roots[0] : 0;
922 }
923
924 /* given a quad-curve and a point (x,y), chop the quad at that point and return
925 the new quad's offCurve point. Should only return false if the computed pos
926 is the start of the curve (i.e. root == 0)
927 */
quad_pt2OffCurve(const SkPoint quad[3],SkScalar x,SkScalar y,SkPoint * offCurve)928 static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve)
929 {
930 const SkScalar* base;
931 SkScalar value;
932
933 if (SkScalarAbs(x) < SkScalarAbs(y)) {
934 base = &quad[0].fX;
935 value = x;
936 } else {
937 base = &quad[0].fY;
938 value = y;
939 }
940
941 // note: this returns 0 if it thinks value is out of range, meaning the
942 // root might return something outside of [0, 1)
943 SkScalar t = quad_solve(base[0], base[2], base[4], value);
944
945 if (t > 0)
946 {
947 SkPoint tmp[5];
948 SkChopQuadAt(quad, tmp, t);
949 *offCurve = tmp[1];
950 return true;
951 } else {
952 /* t == 0 means either the value triggered a root outside of [0, 1)
953 For our purposes, we can ignore the <= 0 roots, but we want to
954 catch the >= 1 roots (which given our caller, will basically mean
955 a root of 1, give-or-take numerical instability). If we are in the
956 >= 1 case, return the existing offCurve point.
957
958 The test below checks to see if we are close to the "end" of the
959 curve (near base[4]). Rather than specifying a tolerance, I just
960 check to see if value is on to the right/left of the middle point
961 (depending on the direction/sign of the end points).
962 */
963 if ((base[0] < base[4] && value > base[2]) ||
964 (base[0] > base[4] && value < base[2])) // should root have been 1
965 {
966 *offCurve = quad[1];
967 return true;
968 }
969 }
970 return false;
971 }
972
973 static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = {
974 { SK_Scalar1, 0 },
975 { SK_Scalar1, SK_ScalarTanPIOver8 },
976 { SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
977 { SK_ScalarTanPIOver8, SK_Scalar1 },
978
979 { 0, SK_Scalar1 },
980 { -SK_ScalarTanPIOver8, SK_Scalar1 },
981 { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
982 { -SK_Scalar1, SK_ScalarTanPIOver8 },
983
984 { -SK_Scalar1, 0 },
985 { -SK_Scalar1, -SK_ScalarTanPIOver8 },
986 { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 },
987 { -SK_ScalarTanPIOver8, -SK_Scalar1 },
988
989 { 0, -SK_Scalar1 },
990 { SK_ScalarTanPIOver8, -SK_Scalar1 },
991 { SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 },
992 { SK_Scalar1, -SK_ScalarTanPIOver8 },
993
994 { SK_Scalar1, 0 }
995 };
996
SkBuildQuadArc(const SkVector & uStart,const SkVector & uStop,SkRotationDirection dir,const SkMatrix * userMatrix,SkPoint quadPoints[])997 int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop,
998 SkRotationDirection dir, const SkMatrix* userMatrix,
999 SkPoint quadPoints[])
1000 {
1001 // rotate by x,y so that uStart is (1.0)
1002 SkScalar x = SkPoint::DotProduct(uStart, uStop);
1003 SkScalar y = SkPoint::CrossProduct(uStart, uStop);
1004
1005 SkScalar absX = SkScalarAbs(x);
1006 SkScalar absY = SkScalarAbs(y);
1007
1008 int pointCount;
1009
1010 // check for (effectively) coincident vectors
1011 // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
1012 // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
1013 if (absY <= SK_ScalarNearlyZero && x > 0 &&
1014 ((y >= 0 && kCW_SkRotationDirection == dir) ||
1015 (y <= 0 && kCCW_SkRotationDirection == dir))) {
1016
1017 // just return the start-point
1018 quadPoints[0].set(SK_Scalar1, 0);
1019 pointCount = 1;
1020 } else {
1021 if (dir == kCCW_SkRotationDirection)
1022 y = -y;
1023
1024 // what octant (quadratic curve) is [xy] in?
1025 int oct = 0;
1026 bool sameSign = true;
1027
1028 if (0 == y)
1029 {
1030 oct = 4; // 180
1031 SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
1032 }
1033 else if (0 == x)
1034 {
1035 SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
1036 if (y > 0)
1037 oct = 2; // 90
1038 else
1039 oct = 6; // 270
1040 }
1041 else
1042 {
1043 if (y < 0)
1044 oct += 4;
1045 if ((x < 0) != (y < 0))
1046 {
1047 oct += 2;
1048 sameSign = false;
1049 }
1050 if ((absX < absY) == sameSign)
1051 oct += 1;
1052 }
1053
1054 int wholeCount = oct << 1;
1055 memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint));
1056
1057 const SkPoint* arc = &gQuadCirclePts[wholeCount];
1058 if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1]))
1059 {
1060 quadPoints[wholeCount + 2].set(x, y);
1061 wholeCount += 2;
1062 }
1063 pointCount = wholeCount + 1;
1064 }
1065
1066 // now handle counter-clockwise and the initial unitStart rotation
1067 SkMatrix matrix;
1068 matrix.setSinCos(uStart.fY, uStart.fX);
1069 if (dir == kCCW_SkRotationDirection) {
1070 matrix.preScale(SK_Scalar1, -SK_Scalar1);
1071 }
1072 if (userMatrix) {
1073 matrix.postConcat(*userMatrix);
1074 }
1075 matrix.mapPoints(quadPoints, pointCount);
1076 return pointCount;
1077 }
1078
1079