1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrPathUtils.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/gpu/GrTypes.h" 11cb93a386Sopenharmony_ci#include "src/core/SkMathPriv.h" 12cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 13cb93a386Sopenharmony_ci#include "src/core/SkUtils.h" 14cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_cistatic const SkScalar kMinCurveTol = 0.0001f; 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_cistatic float tolerance_to_wangs_precision(float srcTol) { 19cb93a386Sopenharmony_ci // You should have called scaleToleranceToSrc, which guarantees this 20cb93a386Sopenharmony_ci SkASSERT(srcTol >= kMinCurveTol); 21cb93a386Sopenharmony_ci 22cb93a386Sopenharmony_ci // The GrPathUtil API defines tolerance as the max distance the linear segment can be from 23cb93a386Sopenharmony_ci // the real curve. Wang's formula guarantees the linear segments will be within 1/precision 24cb93a386Sopenharmony_ci // of the true curve, so precision = 1/srcTol 25cb93a386Sopenharmony_ci return 1.f / srcTol; 26cb93a386Sopenharmony_ci} 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ciuint32_t max_bezier_vertices(uint32_t chopCount) { 29cb93a386Sopenharmony_ci static constexpr uint32_t kMaxChopsPerCurve = 10; 30cb93a386Sopenharmony_ci static_assert((1 << kMaxChopsPerCurve) == GrPathUtils::kMaxPointsPerCurve); 31cb93a386Sopenharmony_ci return 1 << std::min(chopCount, kMaxChopsPerCurve); 32cb93a386Sopenharmony_ci} 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_ciSkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol, 35cb93a386Sopenharmony_ci const SkMatrix& viewM, 36cb93a386Sopenharmony_ci const SkRect& pathBounds) { 37cb93a386Sopenharmony_ci // In order to tesselate the path we get a bound on how much the matrix can 38cb93a386Sopenharmony_ci // scale when mapping to screen coordinates. 39cb93a386Sopenharmony_ci SkScalar stretch = viewM.getMaxScale(); 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci if (stretch < 0) { 42cb93a386Sopenharmony_ci // take worst case mapRadius amoung four corners. 43cb93a386Sopenharmony_ci // (less than perfect) 44cb93a386Sopenharmony_ci for (int i = 0; i < 4; ++i) { 45cb93a386Sopenharmony_ci SkMatrix mat; 46cb93a386Sopenharmony_ci mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight, 47cb93a386Sopenharmony_ci (i < 2) ? pathBounds.fTop : pathBounds.fBottom); 48cb93a386Sopenharmony_ci mat.postConcat(viewM); 49cb93a386Sopenharmony_ci stretch = std::max(stretch, mat.mapRadius(SK_Scalar1)); 50cb93a386Sopenharmony_ci } 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci SkScalar srcTol = 0; 53cb93a386Sopenharmony_ci if (stretch <= 0) { 54cb93a386Sopenharmony_ci // We have degenerate bounds or some degenerate matrix. Thus we set the tolerance to be the 55cb93a386Sopenharmony_ci // max of the path pathBounds width and height. 56cb93a386Sopenharmony_ci srcTol = std::max(pathBounds.width(), pathBounds.height()); 57cb93a386Sopenharmony_ci } else { 58cb93a386Sopenharmony_ci srcTol = devTol / stretch; 59cb93a386Sopenharmony_ci } 60cb93a386Sopenharmony_ci if (srcTol < kMinCurveTol) { 61cb93a386Sopenharmony_ci srcTol = kMinCurveTol; 62cb93a386Sopenharmony_ci } 63cb93a386Sopenharmony_ci return srcTol; 64cb93a386Sopenharmony_ci} 65cb93a386Sopenharmony_ci 66cb93a386Sopenharmony_ciuint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) { 67cb93a386Sopenharmony_ci return max_bezier_vertices(skgpu::wangs_formula::quadratic_log2( 68cb93a386Sopenharmony_ci tolerance_to_wangs_precision(tol), points)); 69cb93a386Sopenharmony_ci} 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ciuint32_t GrPathUtils::generateQuadraticPoints(const SkPoint& p0, 72cb93a386Sopenharmony_ci const SkPoint& p1, 73cb93a386Sopenharmony_ci const SkPoint& p2, 74cb93a386Sopenharmony_ci SkScalar tolSqd, 75cb93a386Sopenharmony_ci SkPoint** points, 76cb93a386Sopenharmony_ci uint32_t pointsLeft) { 77cb93a386Sopenharmony_ci if (pointsLeft < 2 || 78cb93a386Sopenharmony_ci (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p2)) < tolSqd) { 79cb93a386Sopenharmony_ci (*points)[0] = p2; 80cb93a386Sopenharmony_ci *points += 1; 81cb93a386Sopenharmony_ci return 1; 82cb93a386Sopenharmony_ci } 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_ci SkPoint q[] = { 85cb93a386Sopenharmony_ci { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, 86cb93a386Sopenharmony_ci { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, 87cb93a386Sopenharmony_ci }; 88cb93a386Sopenharmony_ci SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }; 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci pointsLeft >>= 1; 91cb93a386Sopenharmony_ci uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft); 92cb93a386Sopenharmony_ci uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft); 93cb93a386Sopenharmony_ci return a + b; 94cb93a386Sopenharmony_ci} 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ciuint32_t GrPathUtils::cubicPointCount(const SkPoint points[], SkScalar tol) { 97cb93a386Sopenharmony_ci return max_bezier_vertices(skgpu::wangs_formula::cubic_log2( 98cb93a386Sopenharmony_ci tolerance_to_wangs_precision(tol), points)); 99cb93a386Sopenharmony_ci} 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ciuint32_t GrPathUtils::generateCubicPoints(const SkPoint& p0, 102cb93a386Sopenharmony_ci const SkPoint& p1, 103cb93a386Sopenharmony_ci const SkPoint& p2, 104cb93a386Sopenharmony_ci const SkPoint& p3, 105cb93a386Sopenharmony_ci SkScalar tolSqd, 106cb93a386Sopenharmony_ci SkPoint** points, 107cb93a386Sopenharmony_ci uint32_t pointsLeft) { 108cb93a386Sopenharmony_ci if (pointsLeft < 2 || 109cb93a386Sopenharmony_ci (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p3) < tolSqd && 110cb93a386Sopenharmony_ci SkPointPriv::DistanceToLineSegmentBetweenSqd(p2, p0, p3) < tolSqd)) { 111cb93a386Sopenharmony_ci (*points)[0] = p3; 112cb93a386Sopenharmony_ci *points += 1; 113cb93a386Sopenharmony_ci return 1; 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci SkPoint q[] = { 116cb93a386Sopenharmony_ci { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) }, 117cb93a386Sopenharmony_ci { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) }, 118cb93a386Sopenharmony_ci { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) } 119cb93a386Sopenharmony_ci }; 120cb93a386Sopenharmony_ci SkPoint r[] = { 121cb93a386Sopenharmony_ci { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) }, 122cb93a386Sopenharmony_ci { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) } 123cb93a386Sopenharmony_ci }; 124cb93a386Sopenharmony_ci SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) }; 125cb93a386Sopenharmony_ci pointsLeft >>= 1; 126cb93a386Sopenharmony_ci uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft); 127cb93a386Sopenharmony_ci uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft); 128cb93a386Sopenharmony_ci return a + b; 129cb93a386Sopenharmony_ci} 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_civoid GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) { 132cb93a386Sopenharmony_ci SkMatrix m; 133cb93a386Sopenharmony_ci // We want M such that M * xy_pt = uv_pt 134cb93a386Sopenharmony_ci // We know M * control_pts = [0 1/2 1] 135cb93a386Sopenharmony_ci // [0 0 1] 136cb93a386Sopenharmony_ci // [1 1 1] 137cb93a386Sopenharmony_ci // And control_pts = [x0 x1 x2] 138cb93a386Sopenharmony_ci // [y0 y1 y2] 139cb93a386Sopenharmony_ci // [1 1 1 ] 140cb93a386Sopenharmony_ci // We invert the control pt matrix and post concat to both sides to get M. 141cb93a386Sopenharmony_ci // Using the known form of the control point matrix and the result, we can 142cb93a386Sopenharmony_ci // optimize and improve precision. 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci double x0 = qPts[0].fX; 145cb93a386Sopenharmony_ci double y0 = qPts[0].fY; 146cb93a386Sopenharmony_ci double x1 = qPts[1].fX; 147cb93a386Sopenharmony_ci double y1 = qPts[1].fY; 148cb93a386Sopenharmony_ci double x2 = qPts[2].fX; 149cb93a386Sopenharmony_ci double y2 = qPts[2].fY; 150cb93a386Sopenharmony_ci double det = x0*y1 - y0*x1 + x2*y0 - y2*x0 + x1*y2 - y1*x2; 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ci if (!sk_float_isfinite(det) 153cb93a386Sopenharmony_ci || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) { 154cb93a386Sopenharmony_ci // The quad is degenerate. Hopefully this is rare. Find the pts that are 155cb93a386Sopenharmony_ci // farthest apart to compute a line (unless it is really a pt). 156cb93a386Sopenharmony_ci SkScalar maxD = SkPointPriv::DistanceToSqd(qPts[0], qPts[1]); 157cb93a386Sopenharmony_ci int maxEdge = 0; 158cb93a386Sopenharmony_ci SkScalar d = SkPointPriv::DistanceToSqd(qPts[1], qPts[2]); 159cb93a386Sopenharmony_ci if (d > maxD) { 160cb93a386Sopenharmony_ci maxD = d; 161cb93a386Sopenharmony_ci maxEdge = 1; 162cb93a386Sopenharmony_ci } 163cb93a386Sopenharmony_ci d = SkPointPriv::DistanceToSqd(qPts[2], qPts[0]); 164cb93a386Sopenharmony_ci if (d > maxD) { 165cb93a386Sopenharmony_ci maxD = d; 166cb93a386Sopenharmony_ci maxEdge = 2; 167cb93a386Sopenharmony_ci } 168cb93a386Sopenharmony_ci // We could have a tolerance here, not sure if it would improve anything 169cb93a386Sopenharmony_ci if (maxD > 0) { 170cb93a386Sopenharmony_ci // Set the matrix to give (u = 0, v = distance_to_line) 171cb93a386Sopenharmony_ci SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge]; 172cb93a386Sopenharmony_ci // when looking from the point 0 down the line we want positive 173cb93a386Sopenharmony_ci // distances to be to the left. This matches the non-degenerate 174cb93a386Sopenharmony_ci // case. 175cb93a386Sopenharmony_ci lineVec = SkPointPriv::MakeOrthog(lineVec, SkPointPriv::kLeft_Side); 176cb93a386Sopenharmony_ci // first row 177cb93a386Sopenharmony_ci fM[0] = 0; 178cb93a386Sopenharmony_ci fM[1] = 0; 179cb93a386Sopenharmony_ci fM[2] = 0; 180cb93a386Sopenharmony_ci // second row 181cb93a386Sopenharmony_ci fM[3] = lineVec.fX; 182cb93a386Sopenharmony_ci fM[4] = lineVec.fY; 183cb93a386Sopenharmony_ci fM[5] = -lineVec.dot(qPts[maxEdge]); 184cb93a386Sopenharmony_ci } else { 185cb93a386Sopenharmony_ci // It's a point. It should cover zero area. Just set the matrix such 186cb93a386Sopenharmony_ci // that (u, v) will always be far away from the quad. 187cb93a386Sopenharmony_ci fM[0] = 0; fM[1] = 0; fM[2] = 100.f; 188cb93a386Sopenharmony_ci fM[3] = 0; fM[4] = 0; fM[5] = 100.f; 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci } else { 191cb93a386Sopenharmony_ci double scale = 1.0/det; 192cb93a386Sopenharmony_ci 193cb93a386Sopenharmony_ci // compute adjugate matrix 194cb93a386Sopenharmony_ci double a2, a3, a4, a5, a6, a7, a8; 195cb93a386Sopenharmony_ci a2 = x1*y2-x2*y1; 196cb93a386Sopenharmony_ci 197cb93a386Sopenharmony_ci a3 = y2-y0; 198cb93a386Sopenharmony_ci a4 = x0-x2; 199cb93a386Sopenharmony_ci a5 = x2*y0-x0*y2; 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci a6 = y0-y1; 202cb93a386Sopenharmony_ci a7 = x1-x0; 203cb93a386Sopenharmony_ci a8 = x0*y1-x1*y0; 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ci // this performs the uv_pts*adjugate(control_pts) multiply, 206cb93a386Sopenharmony_ci // then does the scale by 1/det afterwards to improve precision 207cb93a386Sopenharmony_ci m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale); 208cb93a386Sopenharmony_ci m[SkMatrix::kMSkewX] = (float)((0.5*a4 + a7)*scale); 209cb93a386Sopenharmony_ci m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale); 210cb93a386Sopenharmony_ci 211cb93a386Sopenharmony_ci m[SkMatrix::kMSkewY] = (float)(a6*scale); 212cb93a386Sopenharmony_ci m[SkMatrix::kMScaleY] = (float)(a7*scale); 213cb93a386Sopenharmony_ci m[SkMatrix::kMTransY] = (float)(a8*scale); 214cb93a386Sopenharmony_ci 215cb93a386Sopenharmony_ci // kMPersp0 & kMPersp1 should algebraically be zero 216cb93a386Sopenharmony_ci m[SkMatrix::kMPersp0] = 0.0f; 217cb93a386Sopenharmony_ci m[SkMatrix::kMPersp1] = 0.0f; 218cb93a386Sopenharmony_ci m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale); 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ci // It may not be normalized to have 1.0 in the bottom right 221cb93a386Sopenharmony_ci float m33 = m.get(SkMatrix::kMPersp2); 222cb93a386Sopenharmony_ci if (1.f != m33) { 223cb93a386Sopenharmony_ci m33 = 1.f / m33; 224cb93a386Sopenharmony_ci fM[0] = m33 * m.get(SkMatrix::kMScaleX); 225cb93a386Sopenharmony_ci fM[1] = m33 * m.get(SkMatrix::kMSkewX); 226cb93a386Sopenharmony_ci fM[2] = m33 * m.get(SkMatrix::kMTransX); 227cb93a386Sopenharmony_ci fM[3] = m33 * m.get(SkMatrix::kMSkewY); 228cb93a386Sopenharmony_ci fM[4] = m33 * m.get(SkMatrix::kMScaleY); 229cb93a386Sopenharmony_ci fM[5] = m33 * m.get(SkMatrix::kMTransY); 230cb93a386Sopenharmony_ci } else { 231cb93a386Sopenharmony_ci fM[0] = m.get(SkMatrix::kMScaleX); 232cb93a386Sopenharmony_ci fM[1] = m.get(SkMatrix::kMSkewX); 233cb93a386Sopenharmony_ci fM[2] = m.get(SkMatrix::kMTransX); 234cb93a386Sopenharmony_ci fM[3] = m.get(SkMatrix::kMSkewY); 235cb93a386Sopenharmony_ci fM[4] = m.get(SkMatrix::kMScaleY); 236cb93a386Sopenharmony_ci fM[5] = m.get(SkMatrix::kMTransY); 237cb93a386Sopenharmony_ci } 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci} 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////////// 242cb93a386Sopenharmony_ci 243cb93a386Sopenharmony_ci// k = (y2 - y0, x0 - x2, x2*y0 - x0*y2) 244cb93a386Sopenharmony_ci// l = (y1 - y0, x0 - x1, x1*y0 - x0*y1) * 2*w 245cb93a386Sopenharmony_ci// m = (y2 - y1, x1 - x2, x2*y1 - x1*y2) * 2*w 246cb93a386Sopenharmony_civoid GrPathUtils::getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* out) { 247cb93a386Sopenharmony_ci SkMatrix& klm = *out; 248cb93a386Sopenharmony_ci const SkScalar w2 = 2.f * weight; 249cb93a386Sopenharmony_ci klm[0] = p[2].fY - p[0].fY; 250cb93a386Sopenharmony_ci klm[1] = p[0].fX - p[2].fX; 251cb93a386Sopenharmony_ci klm[2] = p[2].fX * p[0].fY - p[0].fX * p[2].fY; 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci klm[3] = w2 * (p[1].fY - p[0].fY); 254cb93a386Sopenharmony_ci klm[4] = w2 * (p[0].fX - p[1].fX); 255cb93a386Sopenharmony_ci klm[5] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY); 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci klm[6] = w2 * (p[2].fY - p[1].fY); 258cb93a386Sopenharmony_ci klm[7] = w2 * (p[1].fX - p[2].fX); 259cb93a386Sopenharmony_ci klm[8] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY); 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci // scale the max absolute value of coeffs to 10 262cb93a386Sopenharmony_ci SkScalar scale = 0.f; 263cb93a386Sopenharmony_ci for (int i = 0; i < 9; ++i) { 264cb93a386Sopenharmony_ci scale = std::max(scale, SkScalarAbs(klm[i])); 265cb93a386Sopenharmony_ci } 266cb93a386Sopenharmony_ci SkASSERT(scale > 0.f); 267cb93a386Sopenharmony_ci scale = 10.f / scale; 268cb93a386Sopenharmony_ci for (int i = 0; i < 9; ++i) { 269cb93a386Sopenharmony_ci klm[i] *= scale; 270cb93a386Sopenharmony_ci } 271cb93a386Sopenharmony_ci} 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////////// 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_cinamespace { 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_ci// a is the first control point of the cubic. 278cb93a386Sopenharmony_ci// ab is the vector from a to the second control point. 279cb93a386Sopenharmony_ci// dc is the vector from the fourth to the third control point. 280cb93a386Sopenharmony_ci// d is the fourth control point. 281cb93a386Sopenharmony_ci// p is the candidate quadratic control point. 282cb93a386Sopenharmony_ci// this assumes that the cubic doesn't inflect and is simple 283cb93a386Sopenharmony_cibool is_point_within_cubic_tangents(const SkPoint& a, 284cb93a386Sopenharmony_ci const SkVector& ab, 285cb93a386Sopenharmony_ci const SkVector& dc, 286cb93a386Sopenharmony_ci const SkPoint& d, 287cb93a386Sopenharmony_ci SkPathFirstDirection dir, 288cb93a386Sopenharmony_ci const SkPoint p) { 289cb93a386Sopenharmony_ci SkVector ap = p - a; 290cb93a386Sopenharmony_ci SkScalar apXab = ap.cross(ab); 291cb93a386Sopenharmony_ci if (SkPathFirstDirection::kCW == dir) { 292cb93a386Sopenharmony_ci if (apXab > 0) { 293cb93a386Sopenharmony_ci return false; 294cb93a386Sopenharmony_ci } 295cb93a386Sopenharmony_ci } else { 296cb93a386Sopenharmony_ci SkASSERT(SkPathFirstDirection::kCCW == dir); 297cb93a386Sopenharmony_ci if (apXab < 0) { 298cb93a386Sopenharmony_ci return false; 299cb93a386Sopenharmony_ci } 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci 302cb93a386Sopenharmony_ci SkVector dp = p - d; 303cb93a386Sopenharmony_ci SkScalar dpXdc = dp.cross(dc); 304cb93a386Sopenharmony_ci if (SkPathFirstDirection::kCW == dir) { 305cb93a386Sopenharmony_ci if (dpXdc < 0) { 306cb93a386Sopenharmony_ci return false; 307cb93a386Sopenharmony_ci } 308cb93a386Sopenharmony_ci } else { 309cb93a386Sopenharmony_ci SkASSERT(SkPathFirstDirection::kCCW == dir); 310cb93a386Sopenharmony_ci if (dpXdc > 0) { 311cb93a386Sopenharmony_ci return false; 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci } 314cb93a386Sopenharmony_ci return true; 315cb93a386Sopenharmony_ci} 316cb93a386Sopenharmony_ci 317cb93a386Sopenharmony_civoid convert_noninflect_cubic_to_quads(const SkPoint p[4], 318cb93a386Sopenharmony_ci SkScalar toleranceSqd, 319cb93a386Sopenharmony_ci SkTArray<SkPoint, true>* quads, 320cb93a386Sopenharmony_ci int sublevel = 0, 321cb93a386Sopenharmony_ci bool preserveFirstTangent = true, 322cb93a386Sopenharmony_ci bool preserveLastTangent = true) { 323cb93a386Sopenharmony_ci // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is 324cb93a386Sopenharmony_ci // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1]. 325cb93a386Sopenharmony_ci SkVector ab = p[1] - p[0]; 326cb93a386Sopenharmony_ci SkVector dc = p[2] - p[3]; 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ci if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) { 329cb93a386Sopenharmony_ci if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) { 330cb93a386Sopenharmony_ci SkPoint* degQuad = quads->push_back_n(3); 331cb93a386Sopenharmony_ci degQuad[0] = p[0]; 332cb93a386Sopenharmony_ci degQuad[1] = p[0]; 333cb93a386Sopenharmony_ci degQuad[2] = p[3]; 334cb93a386Sopenharmony_ci return; 335cb93a386Sopenharmony_ci } 336cb93a386Sopenharmony_ci ab = p[2] - p[0]; 337cb93a386Sopenharmony_ci } 338cb93a386Sopenharmony_ci if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) { 339cb93a386Sopenharmony_ci dc = p[1] - p[3]; 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci 342cb93a386Sopenharmony_ci static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2; 343cb93a386Sopenharmony_ci static const int kMaxSubdivs = 10; 344cb93a386Sopenharmony_ci 345cb93a386Sopenharmony_ci ab.scale(kLengthScale); 346cb93a386Sopenharmony_ci dc.scale(kLengthScale); 347cb93a386Sopenharmony_ci 348cb93a386Sopenharmony_ci // c0 and c1 are extrapolations along vectors ab and dc. 349cb93a386Sopenharmony_ci SkPoint c0 = p[0] + ab; 350cb93a386Sopenharmony_ci SkPoint c1 = p[3] + dc; 351cb93a386Sopenharmony_ci 352cb93a386Sopenharmony_ci SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1); 353cb93a386Sopenharmony_ci if (dSqd < toleranceSqd) { 354cb93a386Sopenharmony_ci SkPoint newC; 355cb93a386Sopenharmony_ci if (preserveFirstTangent == preserveLastTangent) { 356cb93a386Sopenharmony_ci // We used to force a split when both tangents need to be preserved and c0 != c1. 357cb93a386Sopenharmony_ci // This introduced a large performance regression for tiny paths for no noticeable 358cb93a386Sopenharmony_ci // quality improvement. However, we aren't quite fulfilling our contract of guaranteeing 359cb93a386Sopenharmony_ci // the two tangent vectors and this could introduce a missed pixel in 360cb93a386Sopenharmony_ci // AAHairlinePathRenderer. 361cb93a386Sopenharmony_ci newC = (c0 + c1) * 0.5f; 362cb93a386Sopenharmony_ci } else if (preserveFirstTangent) { 363cb93a386Sopenharmony_ci newC = c0; 364cb93a386Sopenharmony_ci } else { 365cb93a386Sopenharmony_ci newC = c1; 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci 368cb93a386Sopenharmony_ci SkPoint* pts = quads->push_back_n(3); 369cb93a386Sopenharmony_ci pts[0] = p[0]; 370cb93a386Sopenharmony_ci pts[1] = newC; 371cb93a386Sopenharmony_ci pts[2] = p[3]; 372cb93a386Sopenharmony_ci return; 373cb93a386Sopenharmony_ci } 374cb93a386Sopenharmony_ci SkPoint choppedPts[7]; 375cb93a386Sopenharmony_ci SkChopCubicAtHalf(p, choppedPts); 376cb93a386Sopenharmony_ci convert_noninflect_cubic_to_quads( 377cb93a386Sopenharmony_ci choppedPts + 0, toleranceSqd, quads, sublevel + 1, preserveFirstTangent, false); 378cb93a386Sopenharmony_ci convert_noninflect_cubic_to_quads( 379cb93a386Sopenharmony_ci choppedPts + 3, toleranceSqd, quads, sublevel + 1, false, preserveLastTangent); 380cb93a386Sopenharmony_ci} 381cb93a386Sopenharmony_ci 382cb93a386Sopenharmony_civoid convert_noninflect_cubic_to_quads_with_constraint(const SkPoint p[4], 383cb93a386Sopenharmony_ci SkScalar toleranceSqd, 384cb93a386Sopenharmony_ci SkPathFirstDirection dir, 385cb93a386Sopenharmony_ci SkTArray<SkPoint, true>* quads, 386cb93a386Sopenharmony_ci int sublevel = 0) { 387cb93a386Sopenharmony_ci // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is 388cb93a386Sopenharmony_ci // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1]. 389cb93a386Sopenharmony_ci 390cb93a386Sopenharmony_ci SkVector ab = p[1] - p[0]; 391cb93a386Sopenharmony_ci SkVector dc = p[2] - p[3]; 392cb93a386Sopenharmony_ci 393cb93a386Sopenharmony_ci if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) { 394cb93a386Sopenharmony_ci if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) { 395cb93a386Sopenharmony_ci SkPoint* degQuad = quads->push_back_n(3); 396cb93a386Sopenharmony_ci degQuad[0] = p[0]; 397cb93a386Sopenharmony_ci degQuad[1] = p[0]; 398cb93a386Sopenharmony_ci degQuad[2] = p[3]; 399cb93a386Sopenharmony_ci return; 400cb93a386Sopenharmony_ci } 401cb93a386Sopenharmony_ci ab = p[2] - p[0]; 402cb93a386Sopenharmony_ci } 403cb93a386Sopenharmony_ci if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) { 404cb93a386Sopenharmony_ci dc = p[1] - p[3]; 405cb93a386Sopenharmony_ci } 406cb93a386Sopenharmony_ci 407cb93a386Sopenharmony_ci // When the ab and cd tangents are degenerate or nearly parallel with vector from d to a the 408cb93a386Sopenharmony_ci // constraint that the quad point falls between the tangents becomes hard to enforce and we are 409cb93a386Sopenharmony_ci // likely to hit the max subdivision count. However, in this case the cubic is approaching a 410cb93a386Sopenharmony_ci // line and the accuracy of the quad point isn't so important. We check if the two middle cubic 411cb93a386Sopenharmony_ci // control points are very close to the baseline vector. If so then we just pick quadratic 412cb93a386Sopenharmony_ci // points on the control polygon. 413cb93a386Sopenharmony_ci 414cb93a386Sopenharmony_ci SkVector da = p[0] - p[3]; 415cb93a386Sopenharmony_ci bool doQuads = SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero || 416cb93a386Sopenharmony_ci SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero; 417cb93a386Sopenharmony_ci if (!doQuads) { 418cb93a386Sopenharmony_ci SkScalar invDALengthSqd = SkPointPriv::LengthSqd(da); 419cb93a386Sopenharmony_ci if (invDALengthSqd > SK_ScalarNearlyZero) { 420cb93a386Sopenharmony_ci invDALengthSqd = SkScalarInvert(invDALengthSqd); 421cb93a386Sopenharmony_ci // cross(ab, da)^2/length(da)^2 == sqd distance from b to line from d to a. 422cb93a386Sopenharmony_ci // same goes for point c using vector cd. 423cb93a386Sopenharmony_ci SkScalar detABSqd = ab.cross(da); 424cb93a386Sopenharmony_ci detABSqd = SkScalarSquare(detABSqd); 425cb93a386Sopenharmony_ci SkScalar detDCSqd = dc.cross(da); 426cb93a386Sopenharmony_ci detDCSqd = SkScalarSquare(detDCSqd); 427cb93a386Sopenharmony_ci if (detABSqd * invDALengthSqd < toleranceSqd && 428cb93a386Sopenharmony_ci detDCSqd * invDALengthSqd < toleranceSqd) { 429cb93a386Sopenharmony_ci doQuads = true; 430cb93a386Sopenharmony_ci } 431cb93a386Sopenharmony_ci } 432cb93a386Sopenharmony_ci } 433cb93a386Sopenharmony_ci if (doQuads) { 434cb93a386Sopenharmony_ci SkPoint b = p[0] + ab; 435cb93a386Sopenharmony_ci SkPoint c = p[3] + dc; 436cb93a386Sopenharmony_ci SkPoint mid = b + c; 437cb93a386Sopenharmony_ci mid.scale(SK_ScalarHalf); 438cb93a386Sopenharmony_ci // Insert two quadratics to cover the case when ab points away from d and/or dc 439cb93a386Sopenharmony_ci // points away from a. 440cb93a386Sopenharmony_ci if (SkVector::DotProduct(da, dc) < 0 || SkVector::DotProduct(ab, da) > 0) { 441cb93a386Sopenharmony_ci SkPoint* qpts = quads->push_back_n(6); 442cb93a386Sopenharmony_ci qpts[0] = p[0]; 443cb93a386Sopenharmony_ci qpts[1] = b; 444cb93a386Sopenharmony_ci qpts[2] = mid; 445cb93a386Sopenharmony_ci qpts[3] = mid; 446cb93a386Sopenharmony_ci qpts[4] = c; 447cb93a386Sopenharmony_ci qpts[5] = p[3]; 448cb93a386Sopenharmony_ci } else { 449cb93a386Sopenharmony_ci SkPoint* qpts = quads->push_back_n(3); 450cb93a386Sopenharmony_ci qpts[0] = p[0]; 451cb93a386Sopenharmony_ci qpts[1] = mid; 452cb93a386Sopenharmony_ci qpts[2] = p[3]; 453cb93a386Sopenharmony_ci } 454cb93a386Sopenharmony_ci return; 455cb93a386Sopenharmony_ci } 456cb93a386Sopenharmony_ci 457cb93a386Sopenharmony_ci static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2; 458cb93a386Sopenharmony_ci static const int kMaxSubdivs = 10; 459cb93a386Sopenharmony_ci 460cb93a386Sopenharmony_ci ab.scale(kLengthScale); 461cb93a386Sopenharmony_ci dc.scale(kLengthScale); 462cb93a386Sopenharmony_ci 463cb93a386Sopenharmony_ci // c0 and c1 are extrapolations along vectors ab and dc. 464cb93a386Sopenharmony_ci SkVector c0 = p[0] + ab; 465cb93a386Sopenharmony_ci SkVector c1 = p[3] + dc; 466cb93a386Sopenharmony_ci 467cb93a386Sopenharmony_ci SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1); 468cb93a386Sopenharmony_ci if (dSqd < toleranceSqd) { 469cb93a386Sopenharmony_ci SkPoint cAvg = (c0 + c1) * 0.5f; 470cb93a386Sopenharmony_ci bool subdivide = false; 471cb93a386Sopenharmony_ci 472cb93a386Sopenharmony_ci if (!is_point_within_cubic_tangents(p[0], ab, dc, p[3], dir, cAvg)) { 473cb93a386Sopenharmony_ci // choose a new cAvg that is the intersection of the two tangent lines. 474cb93a386Sopenharmony_ci ab = SkPointPriv::MakeOrthog(ab); 475cb93a386Sopenharmony_ci SkScalar z0 = -ab.dot(p[0]); 476cb93a386Sopenharmony_ci dc = SkPointPriv::MakeOrthog(dc); 477cb93a386Sopenharmony_ci SkScalar z1 = -dc.dot(p[3]); 478cb93a386Sopenharmony_ci cAvg.fX = ab.fY * z1 - z0 * dc.fY; 479cb93a386Sopenharmony_ci cAvg.fY = z0 * dc.fX - ab.fX * z1; 480cb93a386Sopenharmony_ci SkScalar z = ab.fX * dc.fY - ab.fY * dc.fX; 481cb93a386Sopenharmony_ci z = SkScalarInvert(z); 482cb93a386Sopenharmony_ci cAvg.fX *= z; 483cb93a386Sopenharmony_ci cAvg.fY *= z; 484cb93a386Sopenharmony_ci if (sublevel <= kMaxSubdivs) { 485cb93a386Sopenharmony_ci SkScalar d0Sqd = SkPointPriv::DistanceToSqd(c0, cAvg); 486cb93a386Sopenharmony_ci SkScalar d1Sqd = SkPointPriv::DistanceToSqd(c1, cAvg); 487cb93a386Sopenharmony_ci // We need to subdivide if d0 + d1 > tolerance but we have the sqd values. We know 488cb93a386Sopenharmony_ci // the distances and tolerance can't be negative. 489cb93a386Sopenharmony_ci // (d0 + d1)^2 > toleranceSqd 490cb93a386Sopenharmony_ci // d0Sqd + 2*d0*d1 + d1Sqd > toleranceSqd 491cb93a386Sopenharmony_ci SkScalar d0d1 = SkScalarSqrt(d0Sqd * d1Sqd); 492cb93a386Sopenharmony_ci subdivide = 2 * d0d1 + d0Sqd + d1Sqd > toleranceSqd; 493cb93a386Sopenharmony_ci } 494cb93a386Sopenharmony_ci } 495cb93a386Sopenharmony_ci if (!subdivide) { 496cb93a386Sopenharmony_ci SkPoint* pts = quads->push_back_n(3); 497cb93a386Sopenharmony_ci pts[0] = p[0]; 498cb93a386Sopenharmony_ci pts[1] = cAvg; 499cb93a386Sopenharmony_ci pts[2] = p[3]; 500cb93a386Sopenharmony_ci return; 501cb93a386Sopenharmony_ci } 502cb93a386Sopenharmony_ci } 503cb93a386Sopenharmony_ci SkPoint choppedPts[7]; 504cb93a386Sopenharmony_ci SkChopCubicAtHalf(p, choppedPts); 505cb93a386Sopenharmony_ci convert_noninflect_cubic_to_quads_with_constraint( 506cb93a386Sopenharmony_ci choppedPts + 0, toleranceSqd, dir, quads, sublevel + 1); 507cb93a386Sopenharmony_ci convert_noninflect_cubic_to_quads_with_constraint( 508cb93a386Sopenharmony_ci choppedPts + 3, toleranceSqd, dir, quads, sublevel + 1); 509cb93a386Sopenharmony_ci} 510cb93a386Sopenharmony_ci} // namespace 511cb93a386Sopenharmony_ci 512cb93a386Sopenharmony_civoid GrPathUtils::convertCubicToQuads(const SkPoint p[4], 513cb93a386Sopenharmony_ci SkScalar tolScale, 514cb93a386Sopenharmony_ci SkTArray<SkPoint, true>* quads) { 515cb93a386Sopenharmony_ci if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) { 516cb93a386Sopenharmony_ci return; 517cb93a386Sopenharmony_ci } 518cb93a386Sopenharmony_ci if (!SkScalarIsFinite(tolScale)) { 519cb93a386Sopenharmony_ci return; 520cb93a386Sopenharmony_ci } 521cb93a386Sopenharmony_ci SkPoint chopped[10]; 522cb93a386Sopenharmony_ci int count = SkChopCubicAtInflections(p, chopped); 523cb93a386Sopenharmony_ci 524cb93a386Sopenharmony_ci const SkScalar tolSqd = SkScalarSquare(tolScale); 525cb93a386Sopenharmony_ci 526cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 527cb93a386Sopenharmony_ci SkPoint* cubic = chopped + 3*i; 528cb93a386Sopenharmony_ci convert_noninflect_cubic_to_quads(cubic, tolSqd, quads); 529cb93a386Sopenharmony_ci } 530cb93a386Sopenharmony_ci} 531cb93a386Sopenharmony_ci 532cb93a386Sopenharmony_civoid GrPathUtils::convertCubicToQuadsConstrainToTangents(const SkPoint p[4], 533cb93a386Sopenharmony_ci SkScalar tolScale, 534cb93a386Sopenharmony_ci SkPathFirstDirection dir, 535cb93a386Sopenharmony_ci SkTArray<SkPoint, true>* quads) { 536cb93a386Sopenharmony_ci if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) { 537cb93a386Sopenharmony_ci return; 538cb93a386Sopenharmony_ci } 539cb93a386Sopenharmony_ci if (!SkScalarIsFinite(tolScale)) { 540cb93a386Sopenharmony_ci return; 541cb93a386Sopenharmony_ci } 542cb93a386Sopenharmony_ci SkPoint chopped[10]; 543cb93a386Sopenharmony_ci int count = SkChopCubicAtInflections(p, chopped); 544cb93a386Sopenharmony_ci 545cb93a386Sopenharmony_ci const SkScalar tolSqd = SkScalarSquare(tolScale); 546cb93a386Sopenharmony_ci 547cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 548cb93a386Sopenharmony_ci SkPoint* cubic = chopped + 3*i; 549cb93a386Sopenharmony_ci convert_noninflect_cubic_to_quads_with_constraint(cubic, tolSqd, dir, quads); 550cb93a386Sopenharmony_ci } 551cb93a386Sopenharmony_ci} 552cb93a386Sopenharmony_ci 553cb93a386Sopenharmony_ciint GrPathUtils::findCubicConvex180Chops(const SkPoint pts[], float T[2], bool* areCusps) { 554cb93a386Sopenharmony_ci using grvx::float2; 555cb93a386Sopenharmony_ci SkASSERT(pts); 556cb93a386Sopenharmony_ci SkASSERT(T); 557cb93a386Sopenharmony_ci SkASSERT(areCusps); 558cb93a386Sopenharmony_ci 559cb93a386Sopenharmony_ci // If a chop falls within a distance of "kEpsilon" from 0 or 1, throw it out. Tangents become 560cb93a386Sopenharmony_ci // unstable when we chop too close to the boundary. This works out because the tessellation 561cb93a386Sopenharmony_ci // shaders don't allow more than 2^10 parametric segments, and they snap the beginning and 562cb93a386Sopenharmony_ci // ending edges at 0 and 1. So if we overstep an inflection or point of 180-degree rotation by a 563cb93a386Sopenharmony_ci // fraction of a tessellation segment, it just gets snapped. 564cb93a386Sopenharmony_ci constexpr static float kEpsilon = 1.f / (1 << 11); 565cb93a386Sopenharmony_ci // Floating-point representation of "1 - 2*kEpsilon". 566cb93a386Sopenharmony_ci constexpr static uint32_t kIEEE_one_minus_2_epsilon = (127 << 23) - 2 * (1 << (24 - 11)); 567cb93a386Sopenharmony_ci // Unfortunately we don't have a way to static_assert this, but we can runtime assert that the 568cb93a386Sopenharmony_ci // kIEEE_one_minus_2_epsilon bits are correct. 569cb93a386Sopenharmony_ci SkASSERT(sk_bit_cast<float>(kIEEE_one_minus_2_epsilon) == 1 - 2*kEpsilon); 570cb93a386Sopenharmony_ci 571cb93a386Sopenharmony_ci float2 p0 = skvx::bit_pun<float2>(pts[0]); 572cb93a386Sopenharmony_ci float2 p1 = skvx::bit_pun<float2>(pts[1]); 573cb93a386Sopenharmony_ci float2 p2 = skvx::bit_pun<float2>(pts[2]); 574cb93a386Sopenharmony_ci float2 p3 = skvx::bit_pun<float2>(pts[3]); 575cb93a386Sopenharmony_ci 576cb93a386Sopenharmony_ci // Find the cubic's power basis coefficients. These define the bezier curve as: 577cb93a386Sopenharmony_ci // 578cb93a386Sopenharmony_ci // |T^3| 579cb93a386Sopenharmony_ci // Cubic(T) = x,y = |A 3B 3C| * |T^2| + P0 580cb93a386Sopenharmony_ci // |. . .| |T | 581cb93a386Sopenharmony_ci // 582cb93a386Sopenharmony_ci // And the tangent direction (scaled by a uniform 1/3) will be: 583cb93a386Sopenharmony_ci // 584cb93a386Sopenharmony_ci // |T^2| 585cb93a386Sopenharmony_ci // Tangent_Direction(T) = dx,dy = |A 2B C| * |T | 586cb93a386Sopenharmony_ci // |. . .| |1 | 587cb93a386Sopenharmony_ci // 588cb93a386Sopenharmony_ci float2 C = p1 - p0; 589cb93a386Sopenharmony_ci float2 D = p2 - p1; 590cb93a386Sopenharmony_ci float2 E = p3 - p0; 591cb93a386Sopenharmony_ci float2 B = D - C; 592cb93a386Sopenharmony_ci float2 A = -3*D + E; 593cb93a386Sopenharmony_ci 594cb93a386Sopenharmony_ci // Now find the cubic's inflection function. There are inflections where F' x F'' == 0. 595cb93a386Sopenharmony_ci // We formulate this as a quadratic equation: F' x F'' == aT^2 + bT + c == 0. 596cb93a386Sopenharmony_ci // See: https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf 597cb93a386Sopenharmony_ci // NOTE: We only need the roots, so a uniform scale factor does not affect the solution. 598cb93a386Sopenharmony_ci float a = grvx::cross(A,B); 599cb93a386Sopenharmony_ci float b = grvx::cross(A,C); 600cb93a386Sopenharmony_ci float c = grvx::cross(B,C); 601cb93a386Sopenharmony_ci float b_over_minus_2 = -.5f * b; 602cb93a386Sopenharmony_ci float discr_over_4 = b_over_minus_2*b_over_minus_2 - a*c; 603cb93a386Sopenharmony_ci 604cb93a386Sopenharmony_ci // If -cuspThreshold <= discr_over_4 <= cuspThreshold, it means the two roots are within 605cb93a386Sopenharmony_ci // kEpsilon of one another (in parametric space). This is close enough for our purposes to 606cb93a386Sopenharmony_ci // consider them a single cusp. 607cb93a386Sopenharmony_ci float cuspThreshold = a * (kEpsilon/2); 608cb93a386Sopenharmony_ci cuspThreshold *= cuspThreshold; 609cb93a386Sopenharmony_ci 610cb93a386Sopenharmony_ci if (discr_over_4 < -cuspThreshold) { 611cb93a386Sopenharmony_ci // The curve does not inflect or cusp. This means it might rotate more than 180 degrees 612cb93a386Sopenharmony_ci // instead. Chop were rotation == 180 deg. (This is the 2nd root where the tangent is 613cb93a386Sopenharmony_ci // parallel to tan0.) 614cb93a386Sopenharmony_ci // 615cb93a386Sopenharmony_ci // Tangent_Direction(T) x tan0 == 0 616cb93a386Sopenharmony_ci // (AT^2 x tan0) + (2BT x tan0) + (C x tan0) == 0 617cb93a386Sopenharmony_ci // (A x C)T^2 + (2B x C)T + (C x C) == 0 [[because tan0 == P1 - P0 == C]] 618cb93a386Sopenharmony_ci // bT^2 + 2cT + 0 == 0 [[because A x C == b, B x C == c]] 619cb93a386Sopenharmony_ci // T = [0, -2c/b] 620cb93a386Sopenharmony_ci // 621cb93a386Sopenharmony_ci // NOTE: if C == 0, then C != tan0. But this is fine because the curve is definitely 622cb93a386Sopenharmony_ci // convex-180 if any points are colocated, and T[0] will equal NaN which returns 0 chops. 623cb93a386Sopenharmony_ci *areCusps = false; 624cb93a386Sopenharmony_ci float root = sk_ieee_float_divide(c, b_over_minus_2); 625cb93a386Sopenharmony_ci // Is "root" inside the range [kEpsilon, 1 - kEpsilon)? 626cb93a386Sopenharmony_ci if (sk_bit_cast<uint32_t>(root - kEpsilon) < kIEEE_one_minus_2_epsilon) { 627cb93a386Sopenharmony_ci T[0] = root; 628cb93a386Sopenharmony_ci return 1; 629cb93a386Sopenharmony_ci } 630cb93a386Sopenharmony_ci return 0; 631cb93a386Sopenharmony_ci } 632cb93a386Sopenharmony_ci 633cb93a386Sopenharmony_ci *areCusps = (discr_over_4 <= cuspThreshold); 634cb93a386Sopenharmony_ci if (*areCusps) { 635cb93a386Sopenharmony_ci // The two roots are close enough that we can consider them a single cusp. 636cb93a386Sopenharmony_ci if (a != 0 || b_over_minus_2 != 0 || c != 0) { 637cb93a386Sopenharmony_ci // Pick the average of both roots. 638cb93a386Sopenharmony_ci float root = sk_ieee_float_divide(b_over_minus_2, a); 639cb93a386Sopenharmony_ci // Is "root" inside the range [kEpsilon, 1 - kEpsilon)? 640cb93a386Sopenharmony_ci if (sk_bit_cast<uint32_t>(root - kEpsilon) < kIEEE_one_minus_2_epsilon) { 641cb93a386Sopenharmony_ci T[0] = root; 642cb93a386Sopenharmony_ci return 1; 643cb93a386Sopenharmony_ci } 644cb93a386Sopenharmony_ci return 0; 645cb93a386Sopenharmony_ci } 646cb93a386Sopenharmony_ci 647cb93a386Sopenharmony_ci // The curve is a flat line. The standard inflection function doesn't detect cusps from flat 648cb93a386Sopenharmony_ci // lines. Find cusps by searching instead for points where the tangent is perpendicular to 649cb93a386Sopenharmony_ci // tan0. This will find any cusp point. 650cb93a386Sopenharmony_ci // 651cb93a386Sopenharmony_ci // dot(tan0, Tangent_Direction(T)) == 0 652cb93a386Sopenharmony_ci // 653cb93a386Sopenharmony_ci // |T^2| 654cb93a386Sopenharmony_ci // tan0 * |A 2B C| * |T | == 0 655cb93a386Sopenharmony_ci // |. . .| |1 | 656cb93a386Sopenharmony_ci // 657cb93a386Sopenharmony_ci float2 tan0 = skvx::if_then_else(C != 0, C, p2 - p0); 658cb93a386Sopenharmony_ci a = grvx::dot(tan0, A); 659cb93a386Sopenharmony_ci b_over_minus_2 = -grvx::dot(tan0, B); 660cb93a386Sopenharmony_ci c = grvx::dot(tan0, C); 661cb93a386Sopenharmony_ci discr_over_4 = std::max(b_over_minus_2*b_over_minus_2 - a*c, 0.f); 662cb93a386Sopenharmony_ci } 663cb93a386Sopenharmony_ci 664cb93a386Sopenharmony_ci // Solve our quadratic equation to find where to chop. See the quadratic formula from 665cb93a386Sopenharmony_ci // Numerical Recipes in C. 666cb93a386Sopenharmony_ci float q = sqrtf(discr_over_4); 667cb93a386Sopenharmony_ci q = copysignf(q, b_over_minus_2); 668cb93a386Sopenharmony_ci q = q + b_over_minus_2; 669cb93a386Sopenharmony_ci float2 roots = float2{q,c} / float2{a,q}; 670cb93a386Sopenharmony_ci 671cb93a386Sopenharmony_ci auto inside = (roots > kEpsilon) & (roots < (1 - kEpsilon)); 672cb93a386Sopenharmony_ci if (inside[0]) { 673cb93a386Sopenharmony_ci if (inside[1] && roots[0] != roots[1]) { 674cb93a386Sopenharmony_ci if (roots[0] > roots[1]) { 675cb93a386Sopenharmony_ci roots = skvx::shuffle<1,0>(roots); // Sort. 676cb93a386Sopenharmony_ci } 677cb93a386Sopenharmony_ci roots.store(T); 678cb93a386Sopenharmony_ci return 2; 679cb93a386Sopenharmony_ci } 680cb93a386Sopenharmony_ci T[0] = roots[0]; 681cb93a386Sopenharmony_ci return 1; 682cb93a386Sopenharmony_ci } 683cb93a386Sopenharmony_ci if (inside[1]) { 684cb93a386Sopenharmony_ci T[0] = roots[1]; 685cb93a386Sopenharmony_ci return 1; 686cb93a386Sopenharmony_ci } 687cb93a386Sopenharmony_ci return 0; 688cb93a386Sopenharmony_ci} 689