Flutter Engine
The Flutter Engine
Macros | Functions
SkGeometry.cpp File Reference
#include "src/core/SkGeometry.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPoint3.h"
#include "include/core/SkRect.h"
#include "include/core/SkScalar.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkFloatingPoint.h"
#include "include/private/base/SkTPin.h"
#include "include/private/base/SkTo.h"
#include "src/base/SkBezierCurves.h"
#include "src/base/SkCubics.h"
#include "src/base/SkUtils.h"
#include "src/base/SkVx.h"
#include "src/core/SkPointPriv.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>

Go to the source code of this file.

Macros

#define AS_QUAD_ERROR_SETUP
 
#define kMaxConicToQuadPOW2   5
 

Functions

int SkFindUnitQuadRoots (SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
 
void SkEvalQuadAt (const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
 
SkPoint SkEvalQuadAt (const SkPoint src[3], SkScalar t)
 
SkVector SkEvalQuadTangentAt (const SkPoint src[3], SkScalar t)
 
static float2 interp (const float2 &v0, const float2 &v1, const float2 &t)
 
void SkChopQuadAt (const SkPoint src[3], SkPoint dst[5], SkScalar t)
 
void SkChopQuadAtHalf (const SkPoint src[3], SkPoint dst[5])
 
float SkMeasureAngleBetweenVectors (SkVector a, SkVector b)
 
SkVector SkFindBisector (SkVector a, SkVector b)
 
float SkFindQuadMidTangent (const SkPoint src[3])
 
int SkFindQuadExtrema (SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
 
static void flatten_double_quad_extrema (SkScalar coords[14])
 
int SkChopQuadAtYExtrema (const SkPoint src[3], SkPoint dst[5])
 
int SkChopQuadAtXExtrema (const SkPoint src[3], SkPoint dst[5])
 
SkScalar SkFindQuadMaxCurvature (const SkPoint src[3])
 
int SkChopQuadAtMaxCurvature (const SkPoint src[3], SkPoint dst[5])
 
void SkConvertQuadToCubic (const SkPoint src[3], SkPoint dst[4])
 
static SkVector eval_cubic_derivative (const SkPoint src[4], SkScalar t)
 
static SkVector eval_cubic_2ndDerivative (const SkPoint src[4], SkScalar t)
 
void SkEvalCubicAt (const SkPoint src[4], SkScalar t, SkPoint *loc, SkVector *tangent, SkVector *curvature)
 
int SkFindCubicExtrema (SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
 
template<int N, typename T >
static skvx::Vec< N, Tunchecked_mix (const skvx::Vec< N, T > &a, const skvx::Vec< N, T > &b, const skvx::Vec< N, T > &t)
 
void SkChopCubicAt (const SkPoint src[4], SkPoint dst[7], SkScalar t)
 
void SkChopCubicAt (const SkPoint src[4], SkPoint dst[10], float t0, float t1)
 
void SkChopCubicAt (const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int tCount)
 
void SkChopCubicAtHalf (const SkPoint src[4], SkPoint dst[7])
 
float SkMeasureNonInflectCubicRotation (const SkPoint pts[4])
 
static skvx::float4 fma (const skvx::float4 &f, float m, const skvx::float4 &a)
 
static float solve_quadratic_equation_for_midtangent (float a, float b, float c, float discr)
 
static float solve_quadratic_equation_for_midtangent (float a, float b, float c)
 
float SkFindCubicMidTangent (const SkPoint src[4])
 
static void flatten_double_cubic_extrema (SkScalar coords[14])
 
int SkChopCubicAtYExtrema (const SkPoint src[4], SkPoint dst[10])
 
int SkChopCubicAtXExtrema (const SkPoint src[4], SkPoint dst[10])
 
int SkFindCubicInflections (const SkPoint src[4], SkScalar tValues[2])
 
int SkChopCubicAtInflections (const SkPoint src[4], SkPoint dst[10])
 
static double calc_dot_cross_cubic (const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
 
static double previous_inverse_pow2 (double n)
 
static void write_cubic_inflection_roots (double t0, double s0, double t1, double s1, double *t, double *s)
 
SkCubicType SkClassifyCubic (const SkPoint P[4], double t[2], double s[2], double d[4])
 
template<typename T >
void bubble_sort (T array[], int count)
 
static int collaps_duplicates (SkScalar array[], int count)
 
static SkScalar SkScalarCubeRoot (SkScalar x)
 
static int solve_cubic_poly (const SkScalar coeff[4], SkScalar tValues[3])
 
static void formulate_F1DotF2 (const SkScalar src[], SkScalar coeff[4])
 
int SkFindCubicMaxCurvature (const SkPoint src[4], SkScalar tValues[3])
 
int SkChopCubicAtMaxCurvature (const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
 
static SkScalar calc_cubic_precision (const SkPoint src[4])
 
static bool on_same_side (const SkPoint src[4], int testIndex, int lineIndex)
 
SkScalar SkFindCubicCusp (const SkPoint src[4])
 
static bool close_enough_to_zero (double x)
 
static bool first_axis_intersection (const double coefficients[8], bool yDirection, double axisIntercept, double *solution)
 
bool SkChopMonoCubicAtY (const SkPoint src[4], SkScalar y, SkPoint dst[7])
 
bool SkChopMonoCubicAtX (const SkPoint src[4], SkScalar x, SkPoint dst[7])
 
static void conic_deriv_coeff (const SkScalar src[], SkScalar w, SkScalar coeff[3])
 
static bool conic_find_extrema (const SkScalar src[], SkScalar w, SkScalar *t)
 
static void p3d_interp (const SkScalar src[7], SkScalar dst[7], SkScalar t)
 
static void ratquad_mapTo3D (const SkPoint src[3], SkScalar w, SkPoint3 dst[3])
 
static SkPoint project_down (const SkPoint3 &src)
 
static SkScalar subdivide_w_value (SkScalar w)
 
static bool between (SkScalar a, SkScalar b, SkScalar c)
 
static SkPointsubdivide (const SkConic &src, SkPoint pts[], int level)
 

Macro Definition Documentation

◆ AS_QUAD_ERROR_SETUP

#define AS_QUAD_ERROR_SETUP
Value:
SkScalar a = fW - 1; \
SkScalar k = a / (4 * (2 + a)); \
SkScalar x = k * (fPts[0].fX - 2 * fPts[1].fX + fPts[2].fX); \
SkScalar y = k * (fPts[0].fY - 2 * fPts[1].fY + fPts[2].fY);
SkPoint fPts[2]
float SkScalar
Definition: extension.cpp:12
struct MyStruct a[10]
double y
double x
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165

Definition at line 1469 of file SkGeometry.cpp.

◆ kMaxConicToQuadPOW2

#define kMaxConicToQuadPOW2   5

Definition at line 1486 of file SkGeometry.cpp.

Function Documentation

◆ between()

static bool between ( SkScalar  a,
SkScalar  b,
SkScalar  c 
)
static

Definition at line 1525 of file SkGeometry.cpp.

1525 {
1526 return (a - b) * (c - b) <= 0;
1527}
static bool b

◆ bubble_sort()

template<typename T >
void bubble_sort ( T  array[],
int  count 
)

Definition at line 881 of file SkGeometry.cpp.

881 {
882 for (int i = count - 1; i > 0; --i)
883 for (int j = i; j > 0; --j)
884 if (array[j] < array[j-1])
885 {
886 T tmp(array[j]);
887 array[j] = array[j-1];
888 array[j-1] = tmp;
889 }
890}
int count
Definition: FontMgrTest.cpp:50
#define T
Definition: precompiler.cc:65

◆ calc_cubic_precision()

static SkScalar calc_cubic_precision ( const SkPoint  src[4])
static

Definition at line 1091 of file SkGeometry.cpp.

1091 {
1093 + SkPointPriv::DistanceToSqd(src[3], src[2])) * 1e-8f;
1094}
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
Definition: SkPointPriv.h:48

◆ calc_dot_cross_cubic()

static double calc_dot_cross_cubic ( const SkPoint p0,
const SkPoint p1,
const SkPoint p2 
)
static

Definition at line 771 of file SkGeometry.cpp.

771 {
772 const double xComp = (double) p0.fX * ((double) p1.fY - (double) p2.fY);
773 const double yComp = (double) p0.fY * ((double) p2.fX - (double) p1.fX);
774 const double wComp = (double) p1.fX * (double) p2.fY - (double) p1.fY * (double) p2.fX;
775 return (xComp + yComp + wComp);
776}

◆ close_enough_to_zero()

static bool close_enough_to_zero ( double  x)
static

Definition at line 1152 of file SkGeometry.cpp.

1152 {
1153 return std::fabs(x) < 0.00001;
1154}

◆ collaps_duplicates()

static int collaps_duplicates ( SkScalar  array[],
int  count 
)
static

Given an array and count, remove all pair-wise duplicates from the array, keeping the existing sorting, and return the new count

Definition at line 896 of file SkGeometry.cpp.

896 {
897 for (int n = count; n > 1; --n) {
898 if (array[0] == array[1]) {
899 for (int i = 1; i < n; ++i) {
900 array[i - 1] = array[i];
901 }
902 count -= 1;
903 } else {
904 array += 1;
905 }
906 }
907 return count;
908}

◆ conic_deriv_coeff()

static void conic_deriv_coeff ( const SkScalar  src[],
SkScalar  w,
SkScalar  coeff[3] 
)
static

Definition at line 1251 of file SkGeometry.cpp.

1253 {
1254 const SkScalar P20 = src[4] - src[0];
1255 const SkScalar P10 = src[2] - src[0];
1256 const SkScalar wP10 = w * P10;
1257 coeff[0] = w * P20 - P20;
1258 coeff[1] = P20 - 2 * wP10;
1259 coeff[2] = wP10;
1260}
SkScalar w

◆ conic_find_extrema()

static bool conic_find_extrema ( const SkScalar  src[],
SkScalar  w,
SkScalar t 
)
static

Definition at line 1262 of file SkGeometry.cpp.

1262 {
1263 SkScalar coeff[3];
1264 conic_deriv_coeff(src, w, coeff);
1265
1266 SkScalar tValues[2];
1267 int roots = SkFindUnitQuadRoots(coeff[0], coeff[1], coeff[2], tValues);
1268 SkASSERT(0 == roots || 1 == roots);
1269
1270 if (1 == roots) {
1271 *t = tValues[0];
1272 return true;
1273 }
1274 return false;
1275}
#define SkASSERT(cond)
Definition: SkAssert.h:116
int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
Definition: SkGeometry.cpp:95
static void conic_deriv_coeff(const SkScalar src[], SkScalar w, SkScalar coeff[3])

◆ eval_cubic_2ndDerivative()

static SkVector eval_cubic_2ndDerivative ( const SkPoint  src[4],
SkScalar  t 
)
static

Definition at line 407 of file SkGeometry.cpp.

407 {
408 float2 P0 = from_point(src[0]);
409 float2 P1 = from_point(src[1]);
410 float2 P2 = from_point(src[2]);
411 float2 P3 = from_point(src[3]);
412 float2 A = P3 + 3 * (P1 - P2) - P0;
413 float2 B = P2 - times_2(P1) + P0;
414
415 return to_vector(A * t + B);
416}
static skvx::float2 times_2(const skvx::float2 &value)
Definition: SkGeometry.h:32
static skvx::float2 from_point(const SkPoint &point)
Definition: SkGeometry.h:22
Definition: SkVx.h:83

◆ eval_cubic_derivative()

static SkVector eval_cubic_derivative ( const SkPoint  src[4],
SkScalar  t 
)
static

Definition at line 394 of file SkGeometry.cpp.

394 {
395 SkQuadCoeff coeff;
396 float2 P0 = from_point(src[0]);
397 float2 P1 = from_point(src[1]);
398 float2 P2 = from_point(src[2]);
399 float2 P3 = from_point(src[3]);
400
401 coeff.fA = P3 + 3 * (P1 - P2) - P0;
402 coeff.fB = times_2(P2 - times_2(P1) + P0);
403 coeff.fC = P1 - P0;
404 return to_vector(coeff.eval(t));
405}

◆ first_axis_intersection()

static bool first_axis_intersection ( const double  coefficients[8],
bool  yDirection,
double  axisIntercept,
double *  solution 
)
static

Definition at line 1156 of file SkGeometry.cpp.

1157 {
1158 auto [A, B, C, D] = SkBezierCubic::ConvertToPolynomial(coefficients, yDirection);
1159 D -= axisIntercept;
1160 double roots[3] = {0, 0, 0};
1161 int count = SkCubics::RootsValidT(A, B, C, D, roots);
1162 if (count == 0) {
1163 return false;
1164 }
1165 // Verify that at least one of the roots is accurate.
1166 for (int i = 0; i < count; i++) {
1168 *solution = roots[i];
1169 return true;
1170 }
1171 }
1172 // None of the roots returned by our normal cubic solver were correct enough
1173 // (e.g. https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=55732)
1174 // So we need to fallback to a more accurate solution.
1176 if (count == 0) {
1177 return false;
1178 }
1179 for (int i = 0; i < count; i++) {
1181 *solution = roots[i];
1182 return true;
1183 }
1184 }
1185 return false;
1186}
static bool close_enough_to_zero(double x)
static std::array< double, 4 > ConvertToPolynomial(const double curve[8], bool yValues)
static int BinarySearchRootsValidT(double A, double B, double C, double D, double solution[3])
Definition: SkCubics.cpp:208
static double EvalAt(double A, double B, double C, double D, double t)
Definition: SkCubics.h:51
static int RootsValidT(double A, double B, double C, double D, double solution[3])
Definition: SkCubics.cpp:127
#define C(TEST_CATEGORY)
Definition: colrv1.cpp:248
#define B

◆ flatten_double_cubic_extrema()

static void flatten_double_cubic_extrema ( SkScalar  coords[14])
static

Definition at line 686 of file SkGeometry.cpp.

686 {
687 coords[4] = coords[8] = coords[6];
688}

◆ flatten_double_quad_extrema()

static void flatten_double_quad_extrema ( SkScalar  coords[14])
inlinestatic

Definition at line 272 of file SkGeometry.cpp.

272 {
273 coords[2] = coords[6] = coords[4];
274}

◆ fma()

static skvx::float4 fma ( const skvx::float4 f,
float  m,
const skvx::float4 a 
)
static

Definition at line 597 of file SkGeometry.cpp.

597 {
598 return skvx::fma(f, skvx::float4(m), a);
599}
SIN Vec< N, float > fma(const Vec< N, float > &x, const Vec< N, float > &y, const Vec< N, float > &z)
Definition: SkVx.h:708

◆ formulate_F1DotF2()

static void formulate_F1DotF2 ( const SkScalar  src[],
SkScalar  coeff[4] 
)
static

Definition at line 1021 of file SkGeometry.cpp.

1021 {
1022 SkScalar a = src[2] - src[0];
1023 SkScalar b = src[4] - 2 * src[2] + src[0];
1024 SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0];
1025
1026 coeff[0] = c * c;
1027 coeff[1] = 3 * b * c;
1028 coeff[2] = 2 * b * b + c * a;
1029 coeff[3] = a * b;
1030}

◆ interp()

static float2 interp ( const float2 v0,
const float2 v1,
const float2 t 
)
inlinestatic

Definition at line 169 of file SkGeometry.cpp.

171 {
172 return v0 + (v1 - v0) * t;
173}

◆ on_same_side()

static bool on_same_side ( const SkPoint  src[4],
int  testIndex,
int  lineIndex 
)
static

Definition at line 1098 of file SkGeometry.cpp.

1098 {
1099 SkPoint origin = src[lineIndex];
1100 SkVector line = src[lineIndex + 1] - origin;
1101 SkScalar crosses[2];
1102 for (int index = 0; index < 2; ++index) {
1103 SkVector testLine = src[testIndex + index] - origin;
1104 crosses[index] = line.cross(testLine);
1105 }
1106 return crosses[0] * crosses[1] >= 0;
1107}

◆ p3d_interp()

static void p3d_interp ( const SkScalar  src[7],
SkScalar  dst[7],
SkScalar  t 
)
static

Definition at line 1278 of file SkGeometry.cpp.

1278 {
1279 SkScalar ab = SkScalarInterp(src[0], src[3], t);
1280 SkScalar bc = SkScalarInterp(src[3], src[6], t);
1281 dst[0] = ab;
1282 dst[3] = SkScalarInterp(ab, bc, t);
1283 dst[6] = bc;
1284}
static SkScalar SkScalarInterp(SkScalar A, SkScalar B, SkScalar t)
Definition: SkScalar.h:131
Definition: ab.py:1
const CatchEntryMove ab[]
dst
Definition: cp.py:12

◆ previous_inverse_pow2()

static double previous_inverse_pow2 ( double  n)
inlinestatic

Definition at line 782 of file SkGeometry.cpp.

782 {
783 uint64_t bits;
784 memcpy(&bits, &n, sizeof(double));
785 bits = ((1023llu*2 << 52) + ((1llu << 52) - 1)) - bits; // exp=-exp
786 bits &= (0x7ffllu) << 52; // mantissa=1.0, sign=0
787 memcpy(&n, &bits, sizeof(double));
788 return n;
789}

◆ project_down()

static SkPoint project_down ( const SkPoint3 src)
static

Definition at line 1292 of file SkGeometry.cpp.

1292 {
1293 return {src.fX / src.fZ, src.fY / src.fZ};
1294}

◆ ratquad_mapTo3D()

static void ratquad_mapTo3D ( const SkPoint  src[3],
SkScalar  w,
SkPoint3  dst[3] 
)
static

Definition at line 1286 of file SkGeometry.cpp.

1286 {
1287 dst[0].set(src[0].fX * 1, src[0].fY * 1, 1);
1288 dst[1].set(src[1].fX * w, src[1].fY * w, w);
1289 dst[2].set(src[2].fX * 1, src[2].fY * 1, 1);
1290}

◆ SkChopCubicAt() [1/3]

void SkChopCubicAt ( const SkPoint  src[4],
SkPoint  dst[10],
float  t0,
float  t1 
)

Given a src cubic bezier, chop it at the specified t0 and t1 values, where 0 <= t0 <= t1 <= 1, and return the three new cubics in dst: dst[0..3], dst[3..6], and dst[6..9]

Definition at line 504 of file SkGeometry.cpp.

504 {
505 SkASSERT(0 <= t0 && t0 <= t1 && t1 <= 1);
506
507 if (t1 == 1) {
508 SkChopCubicAt(src, dst, t0);
509 dst[7] = dst[8] = dst[9] = src[3];
510 return;
511 }
512
513 // Perform both chops in parallel using 4-lane SIMD.
514 float4 p00, p11, p22, p33, T;
515 p00.lo = p00.hi = sk_bit_cast<float2>(src[0]);
516 p11.lo = p11.hi = sk_bit_cast<float2>(src[1]);
517 p22.lo = p22.hi = sk_bit_cast<float2>(src[2]);
518 p33.lo = p33.hi = sk_bit_cast<float2>(src[3]);
519 T.lo = t0;
520 T.hi = t1;
521
522 float4 ab = unchecked_mix(p00, p11, T);
523 float4 bc = unchecked_mix(p11, p22, T);
524 float4 cd = unchecked_mix(p22, p33, T);
525 float4 abc = unchecked_mix(ab, bc, T);
526 float4 bcd = unchecked_mix(bc, cd, T);
527 float4 abcd = unchecked_mix(abc, bcd, T);
528 float4 middle = unchecked_mix(abc, bcd, skvx::shuffle<2,3,0,1>(T));
529
530 dst[0] = sk_bit_cast<SkPoint>(p00.lo);
531 dst[1] = sk_bit_cast<SkPoint>(ab.lo);
532 dst[2] = sk_bit_cast<SkPoint>(abc.lo);
533 dst[3] = sk_bit_cast<SkPoint>(abcd.lo);
534 middle.store(dst + 4);
535 dst[6] = sk_bit_cast<SkPoint>(abcd.hi);
536 dst[7] = sk_bit_cast<SkPoint>(bcd.hi);
537 dst[8] = sk_bit_cast<SkPoint>(cd.hi);
538 dst[9] = sk_bit_cast<SkPoint>(p33.hi);
539}
static skvx::Vec< N, T > unchecked_mix(const skvx::Vec< N, T > &a, const skvx::Vec< N, T > &b, const skvx::Vec< N, T > &t)
Definition: SkGeometry.cpp:468
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
Definition: SkGeometry.cpp:473
SKVX_ALWAYS_INLINE void store(void *ptr) const
Definition: SkVx.h:112
Vec< N/2, T > hi
Definition: SkVx.h:117
Vec< N/2, T > lo
Definition: SkVx.h:117

◆ SkChopCubicAt() [2/3]

void SkChopCubicAt ( const SkPoint  src[4],
SkPoint  dst[7],
SkScalar  t 
)

Given a src cubic bezier, chop it at the specified t value, where 0 <= t <= 1, and return the two new cubics in dst: dst[0..3] and dst[3..6]

Definition at line 473 of file SkGeometry.cpp.

473 {
474 SkASSERT(0 <= t && t <= 1);
475
476 if (t == 1) {
477 memcpy(dst, src, sizeof(SkPoint) * 4);
478 dst[4] = dst[5] = dst[6] = src[3];
479 return;
480 }
481
482 float2 p0 = sk_bit_cast<float2>(src[0]);
483 float2 p1 = sk_bit_cast<float2>(src[1]);
484 float2 p2 = sk_bit_cast<float2>(src[2]);
485 float2 p3 = sk_bit_cast<float2>(src[3]);
486 float2 T = t;
487
488 float2 ab = unchecked_mix(p0, p1, T);
489 float2 bc = unchecked_mix(p1, p2, T);
490 float2 cd = unchecked_mix(p2, p3, T);
491 float2 abc = unchecked_mix(ab, bc, T);
492 float2 bcd = unchecked_mix(bc, cd, T);
493 float2 abcd = unchecked_mix(abc, bcd, T);
494
495 dst[0] = sk_bit_cast<SkPoint>(p0);
496 dst[1] = sk_bit_cast<SkPoint>(ab);
497 dst[2] = sk_bit_cast<SkPoint>(abc);
498 dst[3] = sk_bit_cast<SkPoint>(abcd);
499 dst[4] = sk_bit_cast<SkPoint>(bcd);
500 dst[5] = sk_bit_cast<SkPoint>(cd);
501 dst[6] = sk_bit_cast<SkPoint>(p3);
502}

◆ SkChopCubicAt() [3/3]

void SkChopCubicAt ( const SkPoint  src[4],
SkPoint  dst[],
const SkScalar  t[],
int  t_count 
)

Given a src cubic bezier, chop it at the specified t values, where 0 <= t0 <= t1 <= ... <= 1, and return the new cubics in dst: dst[0..3],dst[3..6],...,dst[3*t_count..3*(t_count+1)]

Definition at line 541 of file SkGeometry.cpp.

542 {
543 SkASSERT(std::all_of(tValues, tValues + tCount, [](SkScalar t) { return t >= 0 && t <= 1; }));
544 SkASSERT(std::is_sorted(tValues, tValues + tCount));
545
546 if (dst) {
547 if (tCount == 0) { // nothing to chop
548 memcpy(dst, src, 4*sizeof(SkPoint));
549 } else {
550 int i = 0;
551 for (; i < tCount - 1; i += 2) {
552 // Do two chops at once.
553 float2 tt = float2::Load(tValues + i);
554 if (i != 0) {
555 float lastT = tValues[i - 1];
556 tt = skvx::pin((tt - lastT) / (1 - lastT), float2(0), float2(1));
557 }
558 SkChopCubicAt(src, dst, tt[0], tt[1]);
559 src = dst = dst + 6;
560 }
561 if (i < tCount) {
562 // Chop the final cubic if there was an odd number of chops.
563 SkASSERT(i + 1 == tCount);
564 float t = tValues[i];
565 if (i != 0) {
566 float lastT = tValues[i - 1];
567 t = SkTPin(sk_ieee_float_divide(t - lastT, 1 - lastT), 0.f, 1.f);
568 }
569 SkChopCubicAt(src, dst, t);
570 }
571 }
572 }
573}
static constexpr float sk_ieee_float_divide(float numer, float denom)
skvx::float2 float2
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
SINT Vec< N, T > pin(const Vec< N, T > &x, const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition: SkVx.h:655
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109

◆ SkChopCubicAtHalf()

void SkChopCubicAtHalf ( const SkPoint  src[4],
SkPoint  dst[7] 
)

Given a src cubic bezier, chop it at the specified t == 1/2, The new cubics are returned in dst[0..3] and dst[3..6]

Definition at line 575 of file SkGeometry.cpp.

575 {
576 SkChopCubicAt(src, dst, 0.5f);
577}

◆ SkChopCubicAtInflections()

int SkChopCubicAtInflections ( const SkPoint  src[4],
SkPoint  dst[10] 
)

Return 1 for no chop, 2 for having chopped the cubic at a single inflection point, 3 for having chopped at 2 inflection points. dst will hold the resulting 1, 2, or 3 cubics.

Definition at line 755 of file SkGeometry.cpp.

755 {
756 SkScalar tValues[2];
757 int count = SkFindCubicInflections(src, tValues);
758
759 if (dst) {
760 if (count == 0) {
761 memcpy(dst, src, 4 * sizeof(SkPoint));
762 } else {
763 SkChopCubicAt(src, dst, tValues, count);
764 }
765 }
766 return count + 1;
767}
int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[2])
Definition: SkGeometry.cpp:741

◆ SkChopCubicAtMaxCurvature()

int SkChopCubicAtMaxCurvature ( const SkPoint  src[4],
SkPoint  dst[13],
SkScalar  tValues[3] 
)

Definition at line 1060 of file SkGeometry.cpp.

1061 {
1062 SkScalar t_storage[3];
1063
1064 if (tValues == nullptr) {
1065 tValues = t_storage;
1066 }
1067
1068 SkScalar roots[3];
1069 int rootCount = SkFindCubicMaxCurvature(src, roots);
1070
1071 // Throw out values not inside 0..1.
1072 int count = 0;
1073 for (int i = 0; i < rootCount; ++i) {
1074 if (0 < roots[i] && roots[i] < 1) {
1075 tValues[count++] = roots[i];
1076 }
1077 }
1078
1079 if (dst) {
1080 if (count == 0) {
1081 memcpy(dst, src, 4 * sizeof(SkPoint));
1082 } else {
1083 SkChopCubicAt(src, dst, tValues, count);
1084 }
1085 }
1086 return count + 1;
1087}
int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])

◆ SkChopCubicAtXExtrema()

int SkChopCubicAtXExtrema ( const SkPoint  src[4],
SkPoint  dst[10] 
)

Definition at line 714 of file SkGeometry.cpp.

714 {
715 SkScalar tValues[2];
716 int roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
717 src[3].fX, tValues);
718
719 SkChopCubicAt(src, dst, tValues, roots);
720 if (dst && roots > 0) {
721 // we do some cleanup to ensure our Y extrema are flat
723 if (roots == 2) {
725 }
726 }
727 return roots;
728}
static void flatten_double_cubic_extrema(SkScalar coords[14])
Definition: SkGeometry.cpp:686
int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
Definition: SkGeometry.cpp:454

◆ SkChopCubicAtYExtrema()

int SkChopCubicAtYExtrema ( const SkPoint  src[4],
SkPoint  dst[10] 
)

Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that the resulting beziers are monotonic in Y. This is called by the scan converter. Depending on what is returned, dst[] is treated as follows: 0 dst[0..3] is the original cubic 1 dst[0..3] and dst[3..6] are the two new cubics 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics If dst == null, it is ignored and only the count is returned.

Definition at line 698 of file SkGeometry.cpp.

698 {
699 SkScalar tValues[2];
700 int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
701 src[3].fY, tValues);
702
703 SkChopCubicAt(src, dst, tValues, roots);
704 if (dst && roots > 0) {
705 // we do some cleanup to ensure our Y extrema are flat
707 if (roots == 2) {
709 }
710 }
711 return roots;
712}

◆ SkChopMonoCubicAtX()

bool SkChopMonoCubicAtX ( const SkPoint  src[4],
SkScalar  x,
SkPoint  dst[7] 
)

Given a monotonically increasing or decreasing cubic bezier src, chop it where the X value is the specified value. The returned cubics will be in dst, sharing the middle point. That is, the first cubic is dst[0..3] and the second dst[3..6].

If the cubic provided is not monotone, it will be chopped at the first time the curve has the specified X value.

If the cubic never reaches the specified value, the function returns false.

Definition at line 1204 of file SkGeometry.cpp.

1204 {
1205 double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1206 src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1207 double solution = 0;
1208 if (first_axis_intersection(coefficients, false, x, &solution)) {
1209 double cubicPair[14];
1210 SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1211 for (int i = 0; i < 7; i ++) {
1212 dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1213 dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1214 }
1215 return true;
1216 }
1217 return false;
1218}
static constexpr float sk_double_to_float(double x)
static bool first_axis_intersection(const double coefficients[8], bool yDirection, double axisIntercept, double *solution)
static void Subdivide(const double curve[8], double t, double twoCurves[14])

◆ SkChopMonoCubicAtY()

bool SkChopMonoCubicAtY ( const SkPoint  src[4],
SkScalar  y,
SkPoint  dst[7] 
)

Given a monotonically increasing or decreasing cubic bezier src, chop it where the Y value is the specified value. The returned cubics will be in dst, sharing the middle point. That is, the first cubic is dst[0..3] and the second dst[3..6].

If the cubic provided is not monotone, it will be chopped at the first time the curve has the specified Y value.

If the cubic never reaches the specified value, the function returns false.

Definition at line 1188 of file SkGeometry.cpp.

1188 {
1189 double coefficients[8] = {src[0].fX, src[0].fY, src[1].fX, src[1].fY,
1190 src[2].fX, src[2].fY, src[3].fX, src[3].fY};
1191 double solution = 0;
1192 if (first_axis_intersection(coefficients, true, y, &solution)) {
1193 double cubicPair[14];
1194 SkBezierCubic::Subdivide(coefficients, solution, cubicPair);
1195 for (int i = 0; i < 7; i ++) {
1196 dst[i].fX = sk_double_to_float(cubicPair[i*2]);
1197 dst[i].fY = sk_double_to_float(cubicPair[i*2 + 1]);
1198 }
1199 return true;
1200 }
1201 return false;
1202}

◆ SkChopQuadAt()

void SkChopQuadAt ( const SkPoint  src[3],
SkPoint  dst[5],
SkScalar  t 
)

Given a src quadratic bezier, chop it at the specified t value, where 0 < t < 1, and return the two new quadratics in dst: dst[0..2] and dst[2..4]

Definition at line 175 of file SkGeometry.cpp.

175 {
176 SkASSERT(t > 0 && t < SK_Scalar1);
177
178 float2 p0 = from_point(src[0]);
179 float2 p1 = from_point(src[1]);
180 float2 p2 = from_point(src[2]);
181 float2 tt(t);
182
183 float2 p01 = interp(p0, p1, tt);
184 float2 p12 = interp(p1, p2, tt);
185
186 dst[0] = to_point(p0);
187 dst[1] = to_point(p01);
188 dst[2] = to_point(interp(p01, p12, tt));
189 dst[3] = to_point(p12);
190 dst[4] = to_point(p2);
191}
static float2 interp(const float2 &v0, const float2 &v1, const float2 &t)
Definition: SkGeometry.cpp:169
#define SK_Scalar1
Definition: SkScalar.h:18
static SkPoint to_point(SkIPoint p)
Definition: editor.cpp:75

◆ SkChopQuadAtHalf()

void SkChopQuadAtHalf ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Given a src quadratic bezier, chop it at the specified t == 1/2, The new quads are returned in dst[0..2] and dst[2..4]

Definition at line 193 of file SkGeometry.cpp.

193 {
194 SkChopQuadAt(src, dst, 0.5f);
195}
void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)
Definition: SkGeometry.cpp:175

◆ SkChopQuadAtMaxCurvature()

int SkChopQuadAtMaxCurvature ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Given 3 points on a quadratic bezier, divide it into 2 quadratics if the point of maximum curvature exists on the quad segment. Depending on what is returned, dst[] is treated as follows 1 dst[0..2] is the original quad 2 dst[0..2] and dst[2..4] are the two new quads If dst == null, it is ignored and only the count is returned.

Definition at line 367 of file SkGeometry.cpp.

367 {
369 if (t > 0 && t < 1) {
370 SkChopQuadAt(src, dst, t);
371 return 2;
372 } else {
373 memcpy(dst, src, 3 * sizeof(SkPoint));
374 return 1;
375 }
376}
SkScalar SkFindQuadMaxCurvature(const SkPoint src[3])
Definition: SkGeometry.cpp:344

◆ SkChopQuadAtXExtrema()

int SkChopQuadAtXExtrema ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Definition at line 307 of file SkGeometry.cpp.

307 {
308 SkASSERT(src);
309 SkASSERT(dst);
310
311 SkScalar a = src[0].fX;
312 SkScalar b = src[1].fX;
313 SkScalar c = src[2].fX;
314
315 if (is_not_monotonic(a, b, c)) {
316 SkScalar tValue;
317 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
318 SkChopQuadAt(src, dst, tValue);
320 return 1;
321 }
322 // if we get here, we need to force dst to be monotonic, even though
323 // we couldn't compute a unit_divide value (probably underflow).
324 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
325 }
326 dst[0].set(a, src[0].fY);
327 dst[1].set(b, src[1].fY);
328 dst[2].set(c, src[2].fY);
329 return 0;
330}
static void flatten_double_quad_extrema(SkScalar coords[14])
Definition: SkGeometry.cpp:272
static int valid_unit_divide(double numer, double denom, double *ratio)
#define SkScalarAbs(x)
Definition: SkScalar.h:39

◆ SkChopQuadAtYExtrema()

int SkChopQuadAtYExtrema ( const SkPoint  src[3],
SkPoint  dst[5] 
)

Given 3 points on a quadratic bezier, chop it into 1, 2 beziers such that the resulting beziers are monotonic in Y. This is called by the scan converter. Depending on what is returned, dst[] is treated as follows 0 dst[0..2] is the original quad 1 dst[0..2] and dst[2..4] are the two new quads

Definition at line 279 of file SkGeometry.cpp.

279 {
280 SkASSERT(src);
281 SkASSERT(dst);
282
283 SkScalar a = src[0].fY;
284 SkScalar b = src[1].fY;
285 SkScalar c = src[2].fY;
286
287 if (is_not_monotonic(a, b, c)) {
288 SkScalar tValue;
289 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
290 SkChopQuadAt(src, dst, tValue);
292 return 1;
293 }
294 // if we get here, we need to force dst to be monotonic, even though
295 // we couldn't compute a unit_divide value (probably underflow).
296 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
297 }
298 dst[0].set(src[0].fX, a);
299 dst[1].set(src[1].fX, b);
300 dst[2].set(src[2].fX, c);
301 return 0;
302}

◆ SkClassifyCubic()

SkCubicType SkClassifyCubic ( const SkPoint  p[4],
double  t[2] = nullptr,
double  s[2] = nullptr,
double  d[4] = nullptr 
)

Returns the cubic classification.

t[],s[] are set to the two homogeneous parameter values at which points the lines L & M intersect with K, sorted from smallest to largest and oriented so positive values of the implicit are on the "left" side. For a serpentine curve they are the inflection points. For a loop they are the double point. For a local cusp, they are both equal and denote the cusp point. For a cusp at an infinite parameter value, one will be the local inflection point and the other +inf (t,s = 1,0). If the curve is degenerate (i.e. quadratic or linear) they are both set to a parameter value of +inf (t,s = 1,0).

d[] is filled with the cubic inflection function coefficients. See "Resolution Independent Curve Rendering using Programmable Graphics Hardware", 4.2 Curve Categorization:

If the input points contain infinities or NaN, the return values are undefined.

https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf

Definition at line 809 of file SkGeometry.cpp.

809 {
810 // Find the cubic's inflection function, I = [T^3 -3T^2 3T -1] dot D. (D0 will always be 0
811 // for integral cubics.)
812 //
813 // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
814 // 4.2 Curve Categorization:
815 //
816 // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
817 double A1 = calc_dot_cross_cubic(P[0], P[3], P[2]);
818 double A2 = calc_dot_cross_cubic(P[1], P[0], P[3]);
819 double A3 = calc_dot_cross_cubic(P[2], P[1], P[0]);
820
821 double D3 = 3 * A3;
822 double D2 = D3 - A2;
823 double D1 = D2 - A2 + A1;
824
825 // Shift the exponents in D so the largest magnitude falls somewhere in 1..2. This protects us
826 // from overflow down the road while solving for roots and KLM functionals.
827 double Dmax = std::max(std::max(fabs(D1), fabs(D2)), fabs(D3));
828 double norm = previous_inverse_pow2(Dmax);
829 D1 *= norm;
830 D2 *= norm;
831 D3 *= norm;
832
833 if (d) {
834 d[3] = D3;
835 d[2] = D2;
836 d[1] = D1;
837 d[0] = 0;
838 }
839
840 // Now use the inflection function to classify the cubic.
841 //
842 // See "Resolution Independent Curve Rendering using Programmable Graphics Hardware",
843 // 4.4 Integral Cubics:
844 //
845 // https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
846 if (0 != D1) {
847 double discr = 3*D2*D2 - 4*D1*D3;
848 if (discr > 0) { // Serpentine.
849 if (t && s) {
850 double q = 3*D2 + copysign(sqrt(3*discr), D2);
851 write_cubic_inflection_roots(q, 6*D1, 2*D3, q, t, s);
852 }
854 } else if (discr < 0) { // Loop.
855 if (t && s) {
856 double q = D2 + copysign(sqrt(-discr), D2);
857 write_cubic_inflection_roots(q, 2*D1, 2*(D2*D2 - D3*D1), D1*q, t, s);
858 }
859 return SkCubicType::kLoop;
860 } else { // Cusp.
861 if (t && s) {
863 }
865 }
866 } else {
867 if (0 != D2) { // Cusp at T=infinity.
868 if (t && s) {
869 write_cubic_inflection_roots(D3, 3*D2, 1, 0, t, s); // T1=infinity.
870 }
872 } else { // Degenerate.
873 if (t && s) {
874 write_cubic_inflection_roots(1, 0, 1, 0, t, s); // T0=T1=infinity.
875 }
877 }
878 }
879}
static double calc_dot_cross_cubic(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2)
Definition: SkGeometry.cpp:771
static double previous_inverse_pow2(double n)
Definition: SkGeometry.cpp:782
static void write_cubic_inflection_roots(double t0, double s0, double t1, double s1, double *t, double *s)
Definition: SkGeometry.cpp:791
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
struct MyStruct s
static float max(float r, float g, float b)
Definition: hsl.cpp:49
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706

◆ SkConvertQuadToCubic()

void SkConvertQuadToCubic ( const SkPoint  src[3],
SkPoint  dst[4] 
)

Given 3 points on a quadratic bezier, use degree elevation to convert it into the cubic fitting the same curve. The new cubic curve is returned in dst[0..3].

Definition at line 378 of file SkGeometry.cpp.

378 {
379 float2 scale(SkDoubleToScalar(2.0 / 3.0));
380 float2 s0 = from_point(src[0]);
381 float2 s1 = from_point(src[1]);
382 float2 s2 = from_point(src[2]);
383
384 dst[0] = to_point(s0);
385 dst[1] = to_point(s0 + (s1 - s0) * scale);
386 dst[2] = to_point(s2 + (s1 - s2) * scale);
387 dst[3] = to_point(s2);
388}
#define SkDoubleToScalar(x)
Definition: SkScalar.h:64
const Scalar scale

◆ SkEvalCubicAt()

void SkEvalCubicAt ( const SkPoint  src[4],
SkScalar  t,
SkPoint locOrNull,
SkVector tangentOrNull,
SkVector curvatureOrNull 
)

Set pt to the point on the src cubic specified by t. t must be 0 <= t <= 1.0

Definition at line 418 of file SkGeometry.cpp.

419 {
420 SkASSERT(src);
421 SkASSERT(t >= 0 && t <= SK_Scalar1);
422
423 if (loc) {
424 *loc = to_point(SkCubicCoeff(src).eval(t));
425 }
426 if (tangent) {
427 // The derivative equation returns a zero tangent vector when t is 0 or 1, and the
428 // adjacent control point is equal to the end point. In this case, use the
429 // next control point or the end points to compute the tangent.
430 if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) {
431 if (t == 0) {
432 *tangent = src[2] - src[0];
433 } else {
434 *tangent = src[3] - src[1];
435 }
436 if (!tangent->fX && !tangent->fY) {
437 *tangent = src[3] - src[0];
438 }
439 } else {
440 *tangent = eval_cubic_derivative(src, t);
441 }
442 }
443 if (curvature) {
444 *curvature = eval_cubic_2ndDerivative(src, t);
445 }
446}
static SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t)
Definition: SkGeometry.cpp:407
static SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t)
Definition: SkGeometry.cpp:394

◆ SkEvalQuadAt() [1/2]

SkPoint SkEvalQuadAt ( const SkPoint  src[3],
SkScalar  t 
)

Definition at line 144 of file SkGeometry.cpp.

144 {
145 return to_point(SkQuadCoeff(src).eval(t));
146}

◆ SkEvalQuadAt() [2/2]

void SkEvalQuadAt ( const SkPoint  src[3],
SkScalar  t,
SkPoint pt,
SkVector tangent = nullptr 
)

Set pt to the point on the src quadratic specified by t. t must be 0 <= t <= 1.0

Definition at line 132 of file SkGeometry.cpp.

132 {
133 SkASSERT(src);
134 SkASSERT(t >= 0 && t <= SK_Scalar1);
135
136 if (pt) {
137 *pt = SkEvalQuadAt(src, t);
138 }
139 if (tangent) {
140 *tangent = SkEvalQuadTangentAt(src, t);
141 }
142}
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t)
Definition: SkGeometry.cpp:148
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
Definition: SkGeometry.cpp:132

◆ SkEvalQuadTangentAt()

SkVector SkEvalQuadTangentAt ( const SkPoint  src[3],
SkScalar  t 
)

Definition at line 148 of file SkGeometry.cpp.

148 {
149 // The derivative equation is 2(b - a +(a - 2b +c)t). This returns a
150 // zero tangent vector when t is 0 or 1, and the control point is equal
151 // to the end point. In this case, use the quad end points to compute the tangent.
152 if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) {
153 return src[2] - src[0];
154 }
155 SkASSERT(src);
156 SkASSERT(t >= 0 && t <= SK_Scalar1);
157
158 float2 P0 = from_point(src[0]);
159 float2 P1 = from_point(src[1]);
160 float2 P2 = from_point(src[2]);
161
162 float2 B = P1 - P0;
163 float2 A = P2 - P1 - B;
164 float2 T = A * t + B;
165
166 return to_vector(T + T);
167}

◆ SkFindBisector()

SkVector SkFindBisector ( SkVector  a,
SkVector  b 
)

Returns a new, arbitrarily scaled vector that bisects the given vectors. The returned bisector will always point toward the interior of the provided vectors.

Definition at line 204 of file SkGeometry.cpp.

204 {
205 std::array<SkVector, 2> v;
206 if (a.dot(b) >= 0) {
207 // a,b are within +/-90 degrees apart.
208 v = {a, b};
209 } else if (a.cross(b) >= 0) {
210 // a,b are >90 degrees apart. Find the bisector of their interior normals instead. (Above 90
211 // degrees, the original vectors start cancelling each other out which eventually becomes
212 // unstable.)
213 v[0].set(-a.fY, +a.fX);
214 v[1].set(+b.fY, -b.fX);
215 } else {
216 // a,b are <-90 degrees apart. Find the bisector of their interior normals instead. (Below
217 // -90 degrees, the original vectors start cancelling each other out which eventually
218 // becomes unstable.)
219 v[0].set(+a.fY, -a.fX);
220 v[1].set(-b.fY, +b.fX);
221 }
222 // Return "normalize(v[0]) + normalize(v[1])".
223 skvx::float2 x0_x1{v[0].fX, v[1].fX};
224 skvx::float2 y0_y1{v[0].fY, v[1].fY};
225 auto invLengths = 1.0f / sqrt(x0_x1 * x0_x1 + y0_y1 * y0_y1);
226 x0_x1 *= invLengths;
227 y0_y1 *= invLengths;
228 return SkPoint{x0_x1[0] + x0_x1[1], y0_y1[0] + y0_y1[1]};
229}

◆ SkFindCubicCusp()

SkScalar SkFindCubicCusp ( const SkPoint  src[4])

Returns t value of cusp if cubic has one; returns -1 otherwise.

Definition at line 1112 of file SkGeometry.cpp.

1112 {
1113 // When the adjacent control point matches the end point, it behaves as if
1114 // the cubic has a cusp: there's a point of max curvature where the derivative
1115 // goes to zero. Ideally, this would be where t is zero or one, but math
1116 // error makes not so. It is not uncommon to create cubics this way; skip them.
1117 if (src[0] == src[1]) {
1118 return -1;
1119 }
1120 if (src[2] == src[3]) {
1121 return -1;
1122 }
1123 // Cubics only have a cusp if the line segments formed by the control and end points cross.
1124 // Detect crossing if line ends are on opposite sides of plane formed by the other line.
1125 if (on_same_side(src, 0, 2) || on_same_side(src, 2, 0)) {
1126 return -1;
1127 }
1128 // Cubics may have multiple points of maximum curvature, although at most only
1129 // one is a cusp.
1130 SkScalar maxCurvature[3];
1131 int roots = SkFindCubicMaxCurvature(src, maxCurvature);
1132 for (int index = 0; index < roots; ++index) {
1133 SkScalar testT = maxCurvature[index];
1134 if (0 >= testT || testT >= 1) { // no need to consider max curvature on the end
1135 continue;
1136 }
1137 // A cusp is at the max curvature, and also has a derivative close to zero.
1138 // Choose the 'close to zero' meaning by comparing the derivative length
1139 // with the overall cubic size.
1140 SkVector dPt = eval_cubic_derivative(src, testT);
1141 SkScalar dPtMagnitude = SkPointPriv::LengthSqd(dPt);
1142 SkScalar precision = calc_cubic_precision(src);
1143 if (dPtMagnitude < precision) {
1144 // All three max curvature t values may be close to the cusp;
1145 // return the first one.
1146 return testT;
1147 }
1148 }
1149 return -1;
1150}
static SkScalar calc_cubic_precision(const SkPoint src[4])
static bool on_same_side(const SkPoint src[4], int testIndex, int lineIndex)
static SkScalar LengthSqd(const SkPoint &pt)
Definition: SkPointPriv.h:63

◆ SkFindCubicExtrema()

int SkFindCubicExtrema ( SkScalar  a,
SkScalar  b,
SkScalar  c,
SkScalar  d,
SkScalar  tValues[2] 
)

Cubic'(t) = At^2 + Bt + C, where A = 3(-a + 3(b - c) + d) B = 6(a - 2b + c) C = 3(b - a) Solve for t, keeping only those that fit betwee 0 < t < 1

Definition at line 454 of file SkGeometry.cpp.

455 {
456 // we divide A,B,C by 3 to simplify
457 SkScalar A = d - a + 3*(b - c);
458 SkScalar B = 2*(a - b - b + c);
459 SkScalar C = b - a;
460
461 return SkFindUnitQuadRoots(A, B, C, tValues);
462}

◆ SkFindCubicInflections()

int SkFindCubicInflections ( const SkPoint  src[4],
SkScalar  tValues[2] 
)

http://www.faculty.idc.ac.il/arik/quality/appendixA.html

Inflection means that curvature is zero. Curvature is [F' x F''] / [F'^3] So we solve F'x X F''y - F'y X F''y == 0 After some canceling of the cubic term, we get A = b - a B = c - 2b + a C = d - 3c + 3b - a (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0

Definition at line 741 of file SkGeometry.cpp.

741 {
742 SkScalar Ax = src[1].fX - src[0].fX;
743 SkScalar Ay = src[1].fY - src[0].fY;
744 SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
745 SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY;
746 SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
747 SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
748
749 return SkFindUnitQuadRoots(Bx*Cy - By*Cx,
750 Ax*Cy - Ay*Cx,
751 Ax*By - Ay*Bx,
752 tValues);
753}

◆ SkFindCubicMaxCurvature()

int SkFindCubicMaxCurvature ( const SkPoint  src[4],
SkScalar  tValues[3] 
)

Definition at line 1043 of file SkGeometry.cpp.

1043 {
1044 SkScalar coeffX[4], coeffY[4];
1045 int i;
1046
1047 formulate_F1DotF2(&src[0].fX, coeffX);
1048 formulate_F1DotF2(&src[0].fY, coeffY);
1049
1050 for (i = 0; i < 4; i++) {
1051 coeffX[i] += coeffY[i];
1052 }
1053
1054 int numRoots = solve_cubic_poly(coeffX, tValues);
1055 // now remove extrema where the curvature is zero (mins)
1056 // !!!! need a test for this !!!!
1057 return numRoots;
1058}
static void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4])
static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3])
Definition: SkGeometry.cpp:961

◆ SkFindCubicMidTangent()

float SkFindCubicMidTangent ( const SkPoint  src[4])

Given a src cubic bezier, returns the T value whose tangent angle is halfway between the tangents at p0 and p3.

Definition at line 620 of file SkGeometry.cpp.

620 {
621 // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
622 // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
623 //
624 // bisector dot midtangent == 0
625 //
626 SkVector tan0 = (src[0] == src[1]) ? src[2] - src[0] : src[1] - src[0];
627 SkVector tan1 = (src[2] == src[3]) ? src[3] - src[1] : src[3] - src[2];
628 SkVector bisector = SkFindBisector(tan0, -tan1);
629
630 // Find the T value at the midtangent. This is a simple quadratic equation:
631 //
632 // midtangent dot bisector == 0, or using a tangent matrix C' in power basis form:
633 //
634 // |C'x C'y|
635 // |T^2 T 1| * |. . | * |bisector.x| == 0
636 // |. . | |bisector.y|
637 //
638 // The coeffs for the quadratic equation we need to solve are therefore: C' * bisector
639 static const skvx::float4 kM[4] = {skvx::float4(-1, 2, -1, 0),
640 skvx::float4( 3, -4, 1, 0),
641 skvx::float4(-3, 2, 0, 0)};
642 auto C_x = fma(kM[0], src[0].fX,
643 fma(kM[1], src[1].fX,
644 fma(kM[2], src[2].fX, skvx::float4(src[3].fX, 0,0,0))));
645 auto C_y = fma(kM[0], src[0].fY,
646 fma(kM[1], src[1].fY,
647 fma(kM[2], src[2].fY, skvx::float4(src[3].fY, 0,0,0))));
648 auto coeffs = C_x * bisector.x() + C_y * bisector.y();
649
650 // Now solve the quadratic for T.
651 float T = 0;
652 float a=coeffs[0], b=coeffs[1], c=coeffs[2];
653 float discr = b*b - 4*a*c;
654 if (discr > 0) { // This will only be false if the curve is a line.
656 } else {
657 // This is a 0- or 360-degree flat line. It doesn't have single points of midtangent.
658 // (tangent == midtangent at every point on the curve except the cusp points.)
659 // Chop in between both cusps instead, if any. There can be up to two cusps on a flat line,
660 // both where the tangent is perpendicular to the starting tangent:
661 //
662 // tangent dot tan0 == 0
663 //
664 coeffs = C_x * tan0.x() + C_y * tan0.y();
665 a = coeffs[0];
666 b = coeffs[1];
667 if (a != 0) {
668 // We want the point in between both cusps. The midpoint of:
669 //
670 // (-b +/- sqrt(b^2 - 4*a*c)) / (2*a)
671 //
672 // Is equal to:
673 //
674 // -b / (2*a)
675 T = -b / (2*a);
676 }
677 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=NaN will take this branch.
678 // Either the curve is a flat line with no rotation or FP precision failed us. Chop at
679 // .5.
680 T = .5;
681 }
682 return T;
683 }
684}
SkVector SkFindBisector(SkVector a, SkVector b)
Definition: SkGeometry.cpp:204
static float solve_quadratic_equation_for_midtangent(float a, float b, float c, float discr)
Definition: SkGeometry.cpp:602
static skvx::float4 fma(const skvx::float4 &f, float m, const skvx::float4 &a)
Definition: SkGeometry.cpp:597
Vec< 4, float > float4
Definition: SkVx.h:1146
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181

◆ SkFindQuadExtrema()

int SkFindQuadExtrema ( SkScalar  a,
SkScalar  b,
SkScalar  c,
SkScalar  tValue[1] 
)

Quad'(t) = At + B, where A = 2(a - 2b + c) B = 2(b - a) Solve for t, only if it fits between 0 < t < 1

Definition at line 265 of file SkGeometry.cpp.

265 {
266 /* At + B == 0
267 t = -B / A
268 */
269 return valid_unit_divide(a - b, a - b - b + c, tValue);
270}

◆ SkFindQuadMaxCurvature()

SkScalar SkFindQuadMaxCurvature ( const SkPoint  src[3])

Given 3 points on a quadratic bezier, if the point of maximum curvature exists on the segment, returns the t value for this point along the curve. Otherwise it will return a value of 0.

Definition at line 344 of file SkGeometry.cpp.

344 {
345 SkScalar Ax = src[1].fX - src[0].fX;
346 SkScalar Ay = src[1].fY - src[0].fY;
347 SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
348 SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
349
350 SkScalar numer = -(Ax * Bx + Ay * By);
351 SkScalar denom = Bx * Bx + By * By;
352 if (denom < 0) {
353 numer = -numer;
354 denom = -denom;
355 }
356 if (numer <= 0) {
357 return 0;
358 }
359 if (numer >= denom) { // Also catches denom=0.
360 return 1;
361 }
362 SkScalar t = numer / denom;
363 SkASSERT((0 <= t && t < 1) || SkIsNaN(t));
364 return t;
365}
static constexpr bool SkIsNaN(T x)

◆ SkFindQuadMidTangent()

float SkFindQuadMidTangent ( const SkPoint  src[3])

Given a src quadratic bezier, returns the T value whose tangent angle is halfway between the tangents at p0 and p3.

Definition at line 231 of file SkGeometry.cpp.

231 {
232 // Tangents point in the direction of increasing T, so tan0 and -tan1 both point toward the
233 // midtangent. The bisector of tan0 and -tan1 is orthogonal to the midtangent:
234 //
235 // n dot midtangent = 0
236 //
237 SkVector tan0 = src[1] - src[0];
238 SkVector tan1 = src[2] - src[1];
239 SkVector bisector = SkFindBisector(tan0, -tan1);
240
241 // The midtangent can be found where (F' dot bisector) = 0:
242 //
243 // 0 = (F'(T) dot bisector) = |2*T 1| * |p0 - 2*p1 + p2| * |bisector.x|
244 // |-2*p0 + 2*p1 | |bisector.y|
245 //
246 // = |2*T 1| * |tan1 - tan0| * |nx|
247 // |2*tan0 | |ny|
248 //
249 // = 2*T * ((tan1 - tan0) dot bisector) + (2*tan0 dot bisector)
250 //
251 // T = (tan0 dot bisector) / ((tan0 - tan1) dot bisector)
252 float T = sk_ieee_float_divide(tan0.dot(bisector), (tan0 - tan1).dot(bisector));
253 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=nan will take this branch.
254 T = .5; // The quadratic was a line or near-line. Just chop at .5.
255 }
256
257 return T;
258}
float dot(const SkVector &vec) const
Definition: SkPoint_impl.h:554

◆ SkFindUnitQuadRoots()

int SkFindUnitQuadRoots ( SkScalar  A,
SkScalar  B,
SkScalar  C,
SkScalar  roots[2] 
)

From Numerical Recipes in C.

Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) x1 = Q / A x2 = C / Q

Definition at line 95 of file SkGeometry.cpp.

95 {
97
98 if (A == 0) {
99 return return_check_zero(valid_unit_divide(-C, B, roots));
100 }
101
102 SkScalar* r = roots;
103
104 // use doubles so we don't overflow temporarily trying to compute R
105 double dr = (double)B * B - 4 * (double)A * C;
106 if (dr < 0) {
107 return return_check_zero(0);
108 }
109 dr = sqrt(dr);
111 if (!SkIsFinite(R)) {
112 return return_check_zero(0);
113 }
114
115 SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
116 r += valid_unit_divide(Q, A, r);
117 r += valid_unit_divide(C, Q, r);
118 if (r - roots == 2) {
119 if (roots[0] > roots[1]) {
120 using std::swap;
121 swap(roots[0], roots[1]);
122 } else if (roots[0] == roots[1]) { // nearly-equal?
123 r -= 1; // skip the double root
124 }
125 }
126 return return_check_zero((int)(r - roots));
127}
static bool SkIsFinite(T x, Pack... values)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
#define R(r)

◆ SkMeasureAngleBetweenVectors()

float SkMeasureAngleBetweenVectors ( SkVector  a,
SkVector  b 
)

Measures the angle between two vectors, in the range [0, pi].

Definition at line 197 of file SkGeometry.cpp.

197 {
198 float cosTheta = sk_ieee_float_divide(a.dot(b), sqrtf(a.dot(a) * b.dot(b)));
199 // Pin cosTheta such that if it is NaN (e.g., if a or b was 0), then we return acos(1) = 0.
200 cosTheta = std::max(std::min(1.f, cosTheta), -1.f);
201 return acosf(cosTheta);
202}
static float min(float r, float g, float b)
Definition: hsl.cpp:48

◆ SkMeasureNonInflectCubicRotation()

float SkMeasureNonInflectCubicRotation ( const  SkPoint[4])

Given a cubic curve with no inflection points, this method measures the rotation in radians.

Rotation is perhaps easiest described via a driving analogy: If you drive your car along the curve from p0 to p3, then by the time you arrive at p3, how many radians will your car have rotated? This is not quite the same as the vector inside the tangents at the endpoints, even without inflection, because the curve might rotate around the outside of the tangents (>= 180 degrees) or the inside (<= 180 degrees).

Cubics can have rotations in the range [0, 2*pi].

NOTE: The caller must either call SkChopCubicAtInflections or otherwise prove that the provided cubic has no inflection points prior to calling this method.

Definition at line 579 of file SkGeometry.cpp.

579 {
580 SkVector a = pts[1] - pts[0];
581 SkVector b = pts[2] - pts[1];
582 SkVector c = pts[3] - pts[2];
583 if (a.isZero()) {
585 }
586 if (b.isZero()) {
588 }
589 if (c.isZero()) {
591 }
592 // Postulate: When no points are colocated and there are no inflection points in T=0..1, the
593 // rotation is: 360 degrees, minus the angle [p0,p1,p2], minus the angle [p1,p2,p3].
595}
float SkMeasureAngleBetweenVectors(SkVector a, SkVector b)
Definition: SkGeometry.cpp:197
#define SK_ScalarPI
Definition: SkScalar.h:21
bool isZero() const
Definition: SkPoint_impl.h:193

◆ SkScalarCubeRoot()

static SkScalar SkScalarCubeRoot ( SkScalar  x)
static

Definition at line 950 of file SkGeometry.cpp.

950 {
951 return SkScalarPow(x, 0.3333333f);
952}
#define SkScalarPow(b, e)
Definition: SkScalar.h:43

◆ solve_cubic_poly()

static int solve_cubic_poly ( const SkScalar  coeff[4],
SkScalar  tValues[3] 
)
static

Definition at line 961 of file SkGeometry.cpp.

961 {
962 if (SkScalarNearlyZero(coeff[0])) { // we're just a quadratic
963 return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
964 }
965
966 SkScalar a, b, c, Q, R;
967
968 {
969 SkASSERT(coeff[0] != 0);
970
971 SkScalar inva = SkScalarInvert(coeff[0]);
972 a = coeff[1] * inva;
973 b = coeff[2] * inva;
974 c = coeff[3] * inva;
975 }
976 Q = (a*a - b*3) / 9;
977 R = (2*a*a*a - 9*a*b + 27*c) / 54;
978
979 SkScalar Q3 = Q * Q * Q;
980 SkScalar R2MinusQ3 = R * R - Q3;
981 SkScalar adiv3 = a / 3;
982
983 if (R2MinusQ3 < 0) { // we have 3 real roots
984 // the divide/root can, due to finite precisions, be slightly outside of -1...1
985 SkScalar theta = SkScalarACos(SkTPin(R / SkScalarSqrt(Q3), -1.0f, 1.0f));
986 SkScalar neg2RootQ = -2 * SkScalarSqrt(Q);
987
988 tValues[0] = SkTPin(neg2RootQ * SkScalarCos(theta/3) - adiv3, 0.0f, 1.0f);
989 tValues[1] = SkTPin(neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
990 tValues[2] = SkTPin(neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3, 0.0f, 1.0f);
991 SkDEBUGCODE(test_collaps_duplicates();)
992
993 // now sort the roots
994 bubble_sort(tValues, 3);
995 return collaps_duplicates(tValues, 3);
996 } else { // we have 1 real root
997 SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3);
999 if (R > 0) {
1000 A = -A;
1001 }
1002 if (A != 0) {
1003 A += Q / A;
1004 }
1005 tValues[0] = SkTPin(A - adiv3, 0.0f, 1.0f);
1006 return 1;
1007 }
1008}
static int collaps_duplicates(SkScalar array[], int count)
Definition: SkGeometry.cpp:896
static SkScalar SkScalarCubeRoot(SkScalar x)
Definition: SkGeometry.cpp:950
void bubble_sort(T array[], int count)
Definition: SkGeometry.cpp:881
#define SkScalarInvert(x)
Definition: SkScalar.h:73
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SkScalarCos(radians)
Definition: SkScalar.h:46
#define SkScalarSqrt(x)
Definition: SkScalar.h:42
#define SkScalarACos(val)
Definition: SkScalar.h:49
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()

◆ solve_quadratic_equation_for_midtangent() [1/2]

static float solve_quadratic_equation_for_midtangent ( float  a,
float  b,
float  c 
)
static

Definition at line 616 of file SkGeometry.cpp.

616 {
617 return solve_quadratic_equation_for_midtangent(a, b, c, b*b - 4*a*c);
618}

◆ solve_quadratic_equation_for_midtangent() [2/2]

static float solve_quadratic_equation_for_midtangent ( float  a,
float  b,
float  c,
float  discr 
)
static

Definition at line 602 of file SkGeometry.cpp.

602 {
603 // Quadratic formula from Numerical Recipes in C:
604 float q = -.5f * (b + copysignf(sqrtf(discr), b));
605 // The roots are q/a and c/q. Pick the midtangent closer to T=.5.
606 float _5qa = -.5f*q*a;
607 float T = fabsf(q*q + _5qa) < fabsf(a*c + _5qa) ? sk_ieee_float_divide(q,a)
609 if (!(T > 0 && T < 1)) { // Use "!(positive_logic)" so T=NaN will take this branch.
610 // Either the curve is a flat line with no rotation or FP precision failed us. Chop at .5.
611 T = .5;
612 }
613 return T;
614}

◆ subdivide()

static SkPoint * subdivide ( const SkConic src,
SkPoint  pts[],
int  level 
)
static

Definition at line 1529 of file SkGeometry.cpp.

1529 {
1530 SkASSERT(level >= 0);
1531
1532 if (0 == level) {
1533 memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint));
1534 return pts + 2;
1535 } else {
1536 SkConic dst[2];
1537 src.chop(dst);
1538 const SkScalar startY = src.fPts[0].fY;
1539 SkScalar endY = src.fPts[2].fY;
1540 if (between(startY, src.fPts[1].fY, endY)) {
1541 // If the input is monotonic and the output is not, the scan converter hangs.
1542 // Ensure that the chopped conics maintain their y-order.
1543 SkScalar midY = dst[0].fPts[2].fY;
1544 if (!between(startY, midY, endY)) {
1545 // If the computed midpoint is outside the ends, move it to the closer one.
1546 SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY;
1547 dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY;
1548 }
1549 if (!between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)) {
1550 // If the 1st control is not between the start and end, put it at the start.
1551 // This also reduces the quad to a line.
1552 dst[0].fPts[1].fY = startY;
1553 }
1554 if (!between(dst[1].fPts[0].fY, dst[1].fPts[1].fY, endY)) {
1555 // If the 2nd control is not between the start and end, put it at the end.
1556 // This also reduces the quad to a line.
1557 dst[1].fPts[1].fY = endY;
1558 }
1559 // Verify that all five points are in order.
1560 SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY));
1561 SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY));
1562 SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY));
1563 }
1564 --level;
1565 pts = subdivide(dst[0], pts, level);
1566 return subdivide(dst[1], pts, level);
1567 }
1568}
static bool between(SkScalar a, SkScalar b, SkScalar c)
static SkPoint * subdivide(const SkConic &src, SkPoint pts[], int level)
static T SkTAbs(T value)
Definition: SkTemplates.h:43

◆ subdivide_w_value()

static SkScalar subdivide_w_value ( SkScalar  w)
static

Definition at line 1397 of file SkGeometry.cpp.

1397 {
1399}
#define SK_ScalarHalf
Definition: SkScalar.h:19

◆ unchecked_mix()

template<int N, typename T >
static skvx::Vec< N, T > unchecked_mix ( const skvx::Vec< N, T > &  a,
const skvx::Vec< N, T > &  b,
const skvx::Vec< N, T > &  t 
)
inlinestatic

Definition at line 468 of file SkGeometry.cpp.

469 {
470 return (b - a)*t + a;
471}

◆ write_cubic_inflection_roots()

static void write_cubic_inflection_roots ( double  t0,
double  s0,
double  t1,
double  s1,
double *  t,
double *  s 
)
inlinestatic

Definition at line 791 of file SkGeometry.cpp.

792 {
793 t[0] = t0;
794 s[0] = s0;
795
796 // This copysign/abs business orients the implicit function so positive values are always on the
797 // "left" side of the curve.
798 t[1] = -copysign(t1, t1 * s1);
799 s[1] = -fabs(s1);
800
801 // Ensure t[0]/s[0] <= t[1]/s[1] (s[1] is negative from above).
802 if (copysign(s[1], s[0]) * t[0] > -fabs(s[0]) * t[1]) {
803 using std::swap;
804 swap(t[0], t[1]);
805 swap(s[0], s[1]);
806 }
807}