1 /*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #ifndef SkGeometry_DEFINED
9 #define SkGeometry_DEFINED
10
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkScalar.h"
13 #include "include/core/SkTypes.h"
14 #include "src/base/SkVx.h"
15
16 #include <cstring>
17
18 class SkMatrix;
19 struct SkRect;
20
from_point(const SkPoint & point)21 static inline skvx::float2 from_point(const SkPoint& point) {
22 return skvx::float2::Load(&point);
23 }
24
to_point(const skvx::float2 & x)25 static inline SkPoint to_point(const skvx::float2& x) {
26 SkPoint point;
27 x.store(&point);
28 return point;
29 }
30
times_2(const skvx::float2 & value)31 static skvx::float2 times_2(const skvx::float2& value) {
32 return value + value;
33 }
34
35 /** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the
36 equation.
37 */
38 int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]);
39
40 /** Measures the angle between two vectors, in the range [0, pi].
41 */
42 float SkMeasureAngleBetweenVectors(SkVector, SkVector);
43
44 /** Returns a new, arbitrarily scaled vector that bisects the given vectors. The returned bisector
45 will always point toward the interior of the provided vectors.
46 */
47 SkVector SkFindBisector(SkVector, SkVector);
48
49 ///////////////////////////////////////////////////////////////////////////////
50
51 SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t);
52 SkPoint SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t);
53
54 /** Set pt to the point on the src quadratic specified by t. t must be
55 0 <= t <= 1.0
56 */
57 void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = nullptr);
58
59 /** Given a src quadratic bezier, chop it at the specified t value,
60 where 0 < t < 1, and return the two new quadratics in dst:
61 dst[0..2] and dst[2..4]
62 */
63 void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t);
64
65 /** Given a src quadratic bezier, chop it at the specified t == 1/2,
66 The new quads are returned in dst[0..2] and dst[2..4]
67 */
68 void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]);
69
70 /** Measures the rotation of the given quadratic curve in radians.
71
72 Rotation is perhaps easiest described via a driving analogy: If you drive your car along the
73 curve from p0 to p2, then by the time you arrive at p2, how many radians will your car have
74 rotated? For a quadratic this is the same as the vector inside the tangents at the endpoints.
75
76 Quadratics can have rotations in the range [0, pi].
77 */
SkMeasureQuadRotation(const SkPoint pts[3])78 inline float SkMeasureQuadRotation(const SkPoint pts[3]) {
79 return SkMeasureAngleBetweenVectors(pts[1] - pts[0], pts[2] - pts[1]);
80 }
81
82 /** Given a src quadratic bezier, returns the T value whose tangent angle is halfway between the
83 tangents at p0 and p3.
84 */
85 float SkFindQuadMidTangent(const SkPoint src[3]);
86
87 /** Given a src quadratic bezier, chop it at the tangent whose angle is halfway between the
88 tangents at p0 and p2. The new quads are returned in dst[0..2] and dst[2..4].
89 */
SkChopQuadAtMidTangent(const SkPoint src[3],SkPoint dst[5])90 inline void SkChopQuadAtMidTangent(const SkPoint src[3], SkPoint dst[5]) {
91 SkChopQuadAt(src, dst, SkFindQuadMidTangent(src));
92 }
93
94 /** Given the 3 coefficients for a quadratic bezier (either X or Y values), look
95 for extrema, and return the number of t-values that are found that represent
96 these extrema. If the quadratic has no extrema betwee (0..1) exclusive, the
97 function returns 0.
98 Returned count tValues[]
99 0 ignored
100 1 0 < tValues[0] < 1
101 */
102 int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValues[1]);
103
104 /** Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that
105 the resulting beziers are monotonic in Y. This is called by the scan converter.
106 Depending on what is returned, dst[] is treated as follows
107 0 dst[0..2] is the original quad
108 1 dst[0..2] and dst[2..4] are the two new quads
109 */
110 int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]);
111 int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]);
112
113 /** Given 3 points on a quadratic bezier, if the point of maximum
114 curvature exists on the segment, returns the t value for this
115 point along the curve. Otherwise it will return a value of 0.
116 */
117 SkScalar SkFindQuadMaxCurvature(const SkPoint src[3]);
118
119 /** Given 3 points on a quadratic bezier, divide it into 2 quadratics
120 if the point of maximum curvature exists on the quad segment.
121 Depending on what is returned, dst[] is treated as follows
122 1 dst[0..2] is the original quad
123 2 dst[0..2] and dst[2..4] are the two new quads
124 If dst == null, it is ignored and only the count is returned.
125 */
126 int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]);
127
128 /** Given 3 points on a quadratic bezier, use degree elevation to
129 convert it into the cubic fitting the same curve. The new cubic
130 curve is returned in dst[0..3].
131 */
132 void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]);
133
134 ///////////////////////////////////////////////////////////////////////////////
135
136 /** Set pt to the point on the src cubic specified by t. t must be
137 0 <= t <= 1.0
138 */
139 void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* locOrNull,
140 SkVector* tangentOrNull, SkVector* curvatureOrNull);
141
142 /** Given a src cubic bezier, chop it at the specified t value,
143 where 0 <= t <= 1, and return the two new cubics in dst:
144 dst[0..3] and dst[3..6]
145 */
146 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t);
147
148 /** Given a src cubic bezier, chop it at the specified t0 and t1 values,
149 where 0 <= t0 <= t1 <= 1, and return the three new cubics in dst:
150 dst[0..3], dst[3..6], and dst[6..9]
151 */
152 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[10], float t0, float t1);
153
154 /** Given a src cubic bezier, chop it at the specified t values,
155 where 0 <= t0 <= t1 <= ... <= 1, and return the new cubics in dst:
156 dst[0..3],dst[3..6],...,dst[3*t_count..3*(t_count+1)]
157 */
158 void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar t[],
159 int t_count);
160
161 /** Given a src cubic bezier, chop it at the specified t == 1/2,
162 The new cubics are returned in dst[0..3] and dst[3..6]
163 */
164 void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]);
165
166 /** Given a cubic curve with no inflection points, this method measures the rotation in radians.
167
168 Rotation is perhaps easiest described via a driving analogy: If you drive your car along the
169 curve from p0 to p3, then by the time you arrive at p3, how many radians will your car have
170 rotated? This is not quite the same as the vector inside the tangents at the endpoints, even
171 without inflection, because the curve might rotate around the outside of the
172 tangents (>= 180 degrees) or the inside (<= 180 degrees).
173
174 Cubics can have rotations in the range [0, 2*pi].
175
176 NOTE: The caller must either call SkChopCubicAtInflections or otherwise prove that the provided
177 cubic has no inflection points prior to calling this method.
178 */
179 float SkMeasureNonInflectCubicRotation(const SkPoint[4]);
180
181 /** Given a src cubic bezier, returns the T value whose tangent angle is halfway between the
182 tangents at p0 and p3.
183 */
184 float SkFindCubicMidTangent(const SkPoint src[4]);
185
186 /** Given a src cubic bezier, chop it at the tangent whose angle is halfway between the
187 tangents at p0 and p3. The new cubics are returned in dst[0..3] and dst[3..6].
188
189 NOTE: 0- and 360-degree flat lines don't have single points of midtangent.
190 (tangent == midtangent at every point on these curves except the cusp points.)
191 If this is the case then we simply chop at a point which guarantees neither side rotates more
192 than 180 degrees.
193 */
SkChopCubicAtMidTangent(const SkPoint src[4],SkPoint dst[7])194 inline void SkChopCubicAtMidTangent(const SkPoint src[4], SkPoint dst[7]) {
195 SkChopCubicAt(src, dst, SkFindCubicMidTangent(src));
196 }
197
198 /** Given the 4 coefficients for a cubic bezier (either X or Y values), look
199 for extrema, and return the number of t-values that are found that represent
200 these extrema. If the cubic has no extrema betwee (0..1) exclusive, the
201 function returns 0.
202 Returned count tValues[]
203 0 ignored
204 1 0 < tValues[0] < 1
205 2 0 < tValues[0] < tValues[1] < 1
206 */
207 int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
208 SkScalar tValues[2]);
209
210 /** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
211 the resulting beziers are monotonic in Y. This is called by the scan converter.
212 Depending on what is returned, dst[] is treated as follows
213 0 dst[0..3] is the original cubic
214 1 dst[0..3] and dst[3..6] are the two new cubics
215 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics
216 If dst == null, it is ignored and only the count is returned.
217 */
218 int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]);
219 int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]);
220
221 /** Given a cubic bezier, return 0, 1, or 2 t-values that represent the
222 inflection points.
223 */
224 int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2]);
225
226 /** Return 1 for no chop, 2 for having chopped the cubic at a single
227 inflection point, 3 for having chopped at 2 inflection points.
228 dst will hold the resulting 1, 2, or 3 cubics.
229 */
230 int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10]);
231
232 int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]);
233 int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13],
234 SkScalar tValues[3] = nullptr);
235 /** Returns t value of cusp if cubic has one; returns -1 otherwise.
236 */
237 SkScalar SkFindCubicCusp(const SkPoint src[4]);
238
239 /** Given a monotonically increasing or decreasing cubic bezier src, chop it
240 * where the X value is the specified value. The returned cubics will be in
241 * dst, sharing the middle point. That is, the first cubic is dst[0..3] and
242 * the second dst[3..6].
243 *
244 * If the cubic provided is *not* monotone, it will be chopped at the first
245 * time the curve has the specified X value.
246 *
247 * If the cubic never reaches the specified value, the function returns false.
248 */
249 bool SkChopMonoCubicAtX(const SkPoint src[4], SkScalar x, SkPoint dst[7]);
250
251 /** Given a monotonically increasing or decreasing cubic bezier src, chop it
252 * where the Y value is the specified value. The returned cubics will be in
253 * dst, sharing the middle point. That is, the first cubic is dst[0..3] and
254 * the second dst[3..6].
255 *
256 * If the cubic provided is *not* monotone, it will be chopped at the first
257 * time the curve has the specified Y value.
258 *
259 * If the cubic never reaches the specified value, the function returns false.
260 */
261 bool SkChopMonoCubicAtY(const SkPoint src[4], SkScalar y, SkPoint dst[7]);
262
263 enum class SkCubicType {
264 kSerpentine,
265 kLoop,
266 kLocalCusp, // Cusp at a non-infinite parameter value with an inflection at t=infinity.
267 kCuspAtInfinity, // Cusp with a cusp at t=infinity and a local inflection.
268 kQuadratic,
269 kLineOrPoint
270 };
271
SkCubicIsDegenerate(SkCubicType type)272 static inline bool SkCubicIsDegenerate(SkCubicType type) {
273 switch (type) {
274 case SkCubicType::kSerpentine:
275 case SkCubicType::kLoop:
276 case SkCubicType::kLocalCusp:
277 case SkCubicType::kCuspAtInfinity:
278 return false;
279 case SkCubicType::kQuadratic:
280 case SkCubicType::kLineOrPoint:
281 return true;
282 }
283 SK_ABORT("Invalid SkCubicType");
284 }
285
SkCubicTypeName(SkCubicType type)286 static inline const char* SkCubicTypeName(SkCubicType type) {
287 switch (type) {
288 case SkCubicType::kSerpentine: return "kSerpentine";
289 case SkCubicType::kLoop: return "kLoop";
290 case SkCubicType::kLocalCusp: return "kLocalCusp";
291 case SkCubicType::kCuspAtInfinity: return "kCuspAtInfinity";
292 case SkCubicType::kQuadratic: return "kQuadratic";
293 case SkCubicType::kLineOrPoint: return "kLineOrPoint";
294 }
295 SK_ABORT("Invalid SkCubicType");
296 }
297
298 /** Returns the cubic classification.
299
300 t[],s[] are set to the two homogeneous parameter values at which points the lines L & M
301 intersect with K, sorted from smallest to largest and oriented so positive values of the
302 implicit are on the "left" side. For a serpentine curve they are the inflection points. For a
303 loop they are the double point. For a local cusp, they are both equal and denote the cusp point.
304 For a cusp at an infinite parameter value, one will be the local inflection point and the other
305 +inf (t,s = 1,0). If the curve is degenerate (i.e. quadratic or linear) they are both set to a
306 parameter value of +inf (t,s = 1,0).
307
308 d[] is filled with the cubic inflection function coefficients. See "Resolution Independent
309 Curve Rendering using Programmable Graphics Hardware", 4.2 Curve Categorization:
310
311 If the input points contain infinities or NaN, the return values are undefined.
312
313 https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
314 */
315 SkCubicType SkClassifyCubic(const SkPoint p[4], double t[2] = nullptr, double s[2] = nullptr,
316 double d[4] = nullptr);
317
318 ///////////////////////////////////////////////////////////////////////////////
319
320 enum SkRotationDirection {
321 kCW_SkRotationDirection,
322 kCCW_SkRotationDirection
323 };
324
325 struct SkConic {
SkConicSkConic326 SkConic() {}
SkConicSkConic327 SkConic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, SkScalar w) {
328 fPts[0] = p0;
329 fPts[1] = p1;
330 fPts[2] = p2;
331 fW = w;
332 }
SkConicSkConic333 SkConic(const SkPoint pts[3], SkScalar w) {
334 memcpy(fPts, pts, sizeof(fPts));
335 fW = w;
336 }
337
338 SkPoint fPts[3];
339 SkScalar fW;
340
setSkConic341 void set(const SkPoint pts[3], SkScalar w) {
342 memcpy(fPts, pts, 3 * sizeof(SkPoint));
343 fW = w;
344 }
345
setSkConic346 void set(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, SkScalar w) {
347 fPts[0] = p0;
348 fPts[1] = p1;
349 fPts[2] = p2;
350 fW = w;
351 }
352
353 /**
354 * Given a t-value [0...1] return its position and/or tangent.
355 * If pos is not null, return its position at the t-value.
356 * If tangent is not null, return its tangent at the t-value. NOTE the
357 * tangent value's length is arbitrary, and only its direction should
358 * be used.
359 */
360 void evalAt(SkScalar t, SkPoint* pos, SkVector* tangent = nullptr) const;
361 bool SK_WARN_UNUSED_RESULT chopAt(SkScalar t, SkConic dst[2]) const;
362 void chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const;
363 void chop(SkConic dst[2]) const;
364
365 SkPoint evalAt(SkScalar t) const;
366 SkVector evalTangentAt(SkScalar t) const;
367
368 void computeAsQuadError(SkVector* err) const;
369 bool asQuadTol(SkScalar tol) const;
370
371 /**
372 * return the power-of-2 number of quads needed to approximate this conic
373 * with a sequence of quads. Will be >= 0.
374 */
375 int SK_SPI computeQuadPOW2(SkScalar tol) const;
376
377 /**
378 * Chop this conic into N quads, stored continguously in pts[], where
379 * N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
380 */
381 int SK_SPI SK_WARN_UNUSED_RESULT chopIntoQuadsPOW2(SkPoint pts[], int pow2) const;
382
383 float findMidTangent() const;
384 bool findXExtrema(SkScalar* t) const;
385 bool findYExtrema(SkScalar* t) const;
386 bool chopAtXExtrema(SkConic dst[2]) const;
387 bool chopAtYExtrema(SkConic dst[2]) const;
388
389 void computeTightBounds(SkRect* bounds) const;
390 void computeFastBounds(SkRect* bounds) const;
391
392 /** Find the parameter value where the conic takes on its maximum curvature.
393 *
394 * @param t output scalar for max curvature. Will be unchanged if
395 * max curvature outside 0..1 range.
396 *
397 * @return true if max curvature found inside 0..1 range, false otherwise
398 */
399 // bool findMaxCurvature(SkScalar* t) const; // unimplemented
400
401 static SkScalar TransformW(const SkPoint[3], SkScalar w, const SkMatrix&);
402
403 enum {
404 kMaxConicsForArc = 5
405 };
406 static int BuildUnitArc(const SkVector& start, const SkVector& stop, SkRotationDirection,
407 const SkMatrix*, SkConic conics[kMaxConicsForArc]);
408 };
409
410 // inline helpers are contained in a namespace to avoid external leakage to fragile SkVx members
411 namespace { // NOLINT(google-build-namespaces)
412
413 /**
414 * use for : eval(t) == A * t^2 + B * t + C
415 */
416 struct SkQuadCoeff {
SkQuadCoeffSkQuadCoeff417 SkQuadCoeff() {}
418
SkQuadCoeffSkQuadCoeff419 SkQuadCoeff(const skvx::float2& A, const skvx::float2& B, const skvx::float2& C)
420 : fA(A)
421 , fB(B)
422 , fC(C)
423 {
424 }
425
SkQuadCoeffSkQuadCoeff426 SkQuadCoeff(const SkPoint src[3]) {
427 fC = from_point(src[0]);
428 auto P1 = from_point(src[1]);
429 auto P2 = from_point(src[2]);
430 fB = times_2(P1 - fC);
431 fA = P2 - times_2(P1) + fC;
432 }
433
evalSkQuadCoeff434 skvx::float2 eval(const skvx::float2& tt) {
435 return (fA * tt + fB) * tt + fC;
436 }
437
438 skvx::float2 fA;
439 skvx::float2 fB;
440 skvx::float2 fC;
441 };
442
443 struct SkConicCoeff {
SkConicCoeffSkConicCoeff444 SkConicCoeff(const SkConic& conic) {
445 skvx::float2 p0 = from_point(conic.fPts[0]);
446 skvx::float2 p1 = from_point(conic.fPts[1]);
447 skvx::float2 p2 = from_point(conic.fPts[2]);
448 skvx::float2 ww(conic.fW);
449
450 auto p1w = p1 * ww;
451 fNumer.fC = p0;
452 fNumer.fA = p2 - times_2(p1w) + p0;
453 fNumer.fB = times_2(p1w - p0);
454
455 fDenom.fC = 1;
456 fDenom.fB = times_2(ww - fDenom.fC);
457 fDenom.fA = 0 - fDenom.fB;
458 }
459
evalSkConicCoeff460 skvx::float2 eval(SkScalar t) {
461 skvx::float2 tt(t);
462 skvx::float2 numer = fNumer.eval(tt);
463 skvx::float2 denom = fDenom.eval(tt);
464 return numer / denom;
465 }
466
467 SkQuadCoeff fNumer;
468 SkQuadCoeff fDenom;
469 };
470
471 struct SkCubicCoeff {
SkCubicCoeffSkCubicCoeff472 SkCubicCoeff(const SkPoint src[4]) {
473 skvx::float2 P0 = from_point(src[0]);
474 skvx::float2 P1 = from_point(src[1]);
475 skvx::float2 P2 = from_point(src[2]);
476 skvx::float2 P3 = from_point(src[3]);
477 skvx::float2 three(3);
478 fA = P3 + three * (P1 - P2) - P0;
479 fB = three * (P2 - times_2(P1) + P0);
480 fC = three * (P1 - P0);
481 fD = P0;
482 }
483
evalSkCubicCoeff484 skvx::float2 eval(const skvx::float2& t) {
485 return ((fA * t + fB) * t + fC) * t + fD;
486 }
487
488 skvx::float2 fA;
489 skvx::float2 fB;
490 skvx::float2 fC;
491 skvx::float2 fD;
492 };
493
494 } // namespace
495
496 #include "include/private/base/SkTemplates.h"
497
498 /**
499 * Help class to allocate storage for approximating a conic with N quads.
500 */
501 class SkAutoConicToQuads {
502 public:
SkAutoConicToQuads()503 SkAutoConicToQuads() : fQuadCount(0) {}
504
505 /**
506 * Given a conic and a tolerance, return the array of points for the
507 * approximating quad(s). Call countQuads() to know the number of quads
508 * represented in these points.
509 *
510 * The quads are allocated to share end-points. e.g. if there are 4 quads,
511 * there will be 9 points allocated as follows
512 * quad[0] == pts[0..2]
513 * quad[1] == pts[2..4]
514 * quad[2] == pts[4..6]
515 * quad[3] == pts[6..8]
516 */
computeQuads(const SkConic & conic,SkScalar tol)517 const SkPoint* computeQuads(const SkConic& conic, SkScalar tol) {
518 int pow2 = conic.computeQuadPOW2(tol);
519 fQuadCount = 1 << pow2;
520 SkPoint* pts = fStorage.reset(1 + 2 * fQuadCount);
521 fQuadCount = conic.chopIntoQuadsPOW2(pts, pow2);
522 return pts;
523 }
524
computeQuads(const SkPoint pts[3],SkScalar weight,SkScalar tol)525 const SkPoint* computeQuads(const SkPoint pts[3], SkScalar weight,
526 SkScalar tol) {
527 SkConic conic;
528 conic.set(pts, weight);
529 return computeQuads(conic, tol);
530 }
531
countQuads()532 int countQuads() const { return fQuadCount; }
533
534 private:
535 enum {
536 kQuadCount = 8, // should handle most conics
537 kPointCount = 1 + 2 * kQuadCount,
538 };
539 skia_private::AutoSTMalloc<kPointCount, SkPoint> fStorage;
540 int fQuadCount; // #quads for current usage
541 };
542
543 #endif
544