• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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