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