Clean up GrPathUtils
Removes unused code, including utilities for dealing with KLM
functionals for the implicit cubic function. The implicit has proven
to not be a very good tool for rendering cubics.
Change-Id: I577b50a9eb296c52dc0101a20394480a4a008654
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/329440
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/bench/CubicKLMBench.cpp b/bench/CubicKLMBench.cpp
deleted file mode 100644
index d9ce672..0000000
--- a/bench/CubicKLMBench.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "bench/Benchmark.h"
-
-#include "src/core/SkGeometry.h"
-#include "src/gpu/geometry/GrPathUtils.h"
-
-class CubicKLMBench : public Benchmark {
-public:
- CubicKLMBench(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
- SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3) {
- fPoints[0].set(x0, y0);
- fPoints[1].set(x1, y1);
- fPoints[2].set(x2, y2);
- fPoints[3].set(x3, y3);
-
- fName = "cubic_klm_";
- switch (SkClassifyCubic(fPoints)) {
- case SkCubicType::kSerpentine:
- fName.append("serp");
- break;
- case SkCubicType::kLoop:
- fName.append("loop");
- break;
- default:
- SK_ABORT("Unexpected cubic type");
- break;
- }
- }
-
- bool isSuitableFor(Backend backend) override {
- return backend == kNonRendering_Backend;
- }
-
- const char* onGetName() override {
- return fName.c_str();
- }
-
- void onDraw(int loops, SkCanvas*) override {
- SkPoint dst[10];
- SkMatrix klm;
- int loopIdx;
- for (int i = 0; i < loops * 50000; ++i) {
- GrPathUtils::chopCubicAtLoopIntersection(fPoints, dst, &klm, &loopIdx);
- }
- }
-
-private:
- SkPoint fPoints[4];
- SkString fName;
-
- using INHERITED = Benchmark;
-};
-
-DEF_BENCH( return new CubicKLMBench(285.625f, 499.687f, 411.625f, 808.188f,
- 1064.62f, 135.688f, 1042.63f, 585.187f); )
-DEF_BENCH( return new CubicKLMBench(635.625f, 614.687f, 171.625f, 236.188f,
- 1064.62f, 135.688f, 516.625f, 570.187f); )
diff --git a/gn/bench.gni b/gn/bench.gni
index 79aa70f..6c31fb0 100644
--- a/gn/bench.gni
+++ b/gn/bench.gni
@@ -38,7 +38,6 @@
"$_bench/ControlBench.cpp",
"$_bench/CoverageBench.cpp",
"$_bench/CreateBackendTextureBench.cpp",
- "$_bench/CubicKLMBench.cpp",
"$_bench/CubicMapBench.cpp",
"$_bench/DDLRecorderBench.cpp",
"$_bench/DashBench.cpp",
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index d918c18..7e6c394 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -67,7 +67,6 @@
PrimitiveType fPrimitiveType = PrimitiveType::kCubics;
SkCubicType fCubicType;
- SkMatrix fCubicKLM;
SkPoint fPoints[4] = {
{100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
@@ -154,27 +153,6 @@
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new Impl; }
};
-static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3], SkColor color) {
- SkPoint p1, p2;
- if (SkScalarAbs(line[1]) > SkScalarAbs(line[0])) {
- // Draw from vertical edge to vertical edge.
- p1 = {0, -line[2] / line[1]};
- p2 = {(SkScalar)w, (-line[2] - w * line[0]) / line[1]};
- } else {
- // Draw from horizontal edge to horizontal edge.
- p1 = {-line[2] / line[0], 0};
- p2 = {(-line[2] - h * line[1]) / line[0], (SkScalar)h};
- }
-
- SkPaint linePaint;
- linePaint.setColor(color);
- linePaint.setAlpha(128);
- linePaint.setStyle(SkPaint::kStroke_Style);
- linePaint.setStrokeWidth(0);
- linePaint.setAntiAlias(true);
- canvas->drawLine(p1, p2, linePaint);
-}
-
void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
canvas->clear(SK_ColorBLACK);
@@ -248,12 +226,6 @@
if (PrimitiveType::kCubics == fPrimitiveType) {
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
- if (!fDoStroke) {
- int w = this->width(), h = this->height();
- draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
- draw_klm_line(w, h, canvas, &fCubicKLM[3], SK_ColorBLUE);
- draw_klm_line(w, h, canvas, &fCubicKLM[6], SK_ColorRED);
- }
} else {
canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
@@ -274,8 +246,6 @@
fPath.moveTo(fPoints[0]);
if (PrimitiveType::kCubics == fPrimitiveType) {
- double t[2], s[2];
- fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
GrCCFillGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
diff --git a/src/gpu/geometry/GrPathUtils.cpp b/src/gpu/geometry/GrPathUtils.cpp
index 3b5fc01..3e80318 100644
--- a/src/gpu/geometry/GrPathUtils.cpp
+++ b/src/gpu/geometry/GrPathUtils.cpp
@@ -162,54 +162,6 @@
return a + b;
}
-int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths, SkScalar tol) {
- // You should have called scaleToleranceToSrc, which guarantees this
- SkASSERT(tol >= gMinCurveTol);
-
- int pointCount = 0;
- *subpaths = 1;
-
- bool first = true;
-
- SkPath::Iter iter(path, false);
- SkPath::Verb verb;
-
- SkPoint pts[4];
- while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-
- switch (verb) {
- case SkPath::kLine_Verb:
- pointCount += 1;
- break;
- case SkPath::kConic_Verb: {
- SkScalar weight = iter.conicWeight();
- SkAutoConicToQuads converter;
- const SkPoint* quadPts = converter.computeQuads(pts, weight, tol);
- for (int i = 0; i < converter.countQuads(); ++i) {
- pointCount += quadraticPointCount(quadPts + 2*i, tol);
- }
- [[fallthrough]];
- }
- case SkPath::kQuad_Verb:
- pointCount += quadraticPointCount(pts, tol);
- break;
- case SkPath::kCubic_Verb:
- pointCount += cubicPointCount(pts, tol);
- break;
- case SkPath::kMove_Verb:
- pointCount += 1;
- if (!first) {
- ++(*subpaths);
- }
- break;
- default:
- break;
- }
- first = false;
- }
- return pointCount;
-}
-
void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) {
SkMatrix m;
// We want M such that M * xy_pt = uv_pt
@@ -631,230 +583,3 @@
convert_noninflect_cubic_to_quads_with_constraint(cubic, tolSqd, dir, quads);
}
}
-
-////////////////////////////////////////////////////////////////////////////////
-
-using ExcludedTerm = GrPathUtils::ExcludedTerm;
-
-ExcludedTerm GrPathUtils::calcCubicInverseTransposePowerBasisMatrix(const SkPoint p[4],
- SkMatrix* out) {
- static_assert(SK_SCALAR_IS_FLOAT);
-
- // First convert the bezier coordinates p[0..3] to power basis coefficients X,Y(,W=[0 0 0 1]).
- // M3 is the matrix that does this conversion. The homogeneous equation for the cubic becomes:
- //
- // | X Y 0 |
- // C(t,s) = [t^3 t^2*s t*s^2 s^3] * | . . 0 |
- // | . . 0 |
- // | . . 1 |
- //
- const Sk4f M3[3] = {Sk4f(-1, 3, -3, 1),
- Sk4f(3, -6, 3, 0),
- Sk4f(-3, 3, 0, 0)};
- // 4th col of M3 = Sk4f(1, 0, 0, 0)};
- Sk4f X(p[3].x(), 0, 0, 0);
- Sk4f Y(p[3].y(), 0, 0, 0);
- for (int i = 2; i >= 0; --i) {
- X += M3[i] * p[i].x();
- Y += M3[i] * p[i].y();
- }
-
- // The matrix is 3x4. In order to invert it, we first need to make it square by throwing out one
- // of the middle two rows. We toss the row that leaves us with the largest absolute determinant.
- // Since the right column will be [0 0 1], the respective determinants reduce to x0*y2 - y0*x2
- // and x0*y1 - y0*x1.
- SkScalar dets[4];
- Sk4f D = SkNx_shuffle<0,0,2,1>(X) * SkNx_shuffle<2,1,0,0>(Y);
- D -= SkNx_shuffle<2,3,0,1>(D);
- D.store(dets);
- ExcludedTerm skipTerm = SkScalarAbs(dets[0]) > SkScalarAbs(dets[1]) ?
- ExcludedTerm::kQuadraticTerm : ExcludedTerm::kLinearTerm;
- SkScalar det = dets[ExcludedTerm::kQuadraticTerm == skipTerm ? 0 : 1];
- if (0 == det) {
- return ExcludedTerm::kNonInvertible;
- }
- SkScalar rdet = 1 / det;
-
- // Compute the inverse-transpose of the power basis matrix with the 'skipRow'th row removed.
- // Since W=[0 0 0 1], it follows that our corresponding solution will be equal to:
- //
- // | y1 -x1 x1*y2 - y1*x2 |
- // 1/det * | -y0 x0 -x0*y2 + y0*x2 |
- // | 0 0 det |
- //
- SkScalar x[4], y[4], z[4];
- X.store(x);
- Y.store(y);
- (X * SkNx_shuffle<3,3,3,3>(Y) - Y * SkNx_shuffle<3,3,3,3>(X)).store(z);
-
- int middleRow = ExcludedTerm::kQuadraticTerm == skipTerm ? 2 : 1;
- out->setAll( y[middleRow] * rdet, -x[middleRow] * rdet, z[middleRow] * rdet,
- -y[0] * rdet, x[0] * rdet, -z[0] * rdet,
- 0, 0, 1);
-
- return skipTerm;
-}
-
-inline static void calc_serp_kcoeffs(SkScalar tl, SkScalar sl, SkScalar tm, SkScalar sm,
- ExcludedTerm skipTerm, SkScalar outCoeffs[3]) {
- SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
- outCoeffs[0] = 0;
- outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? sl*sm : -tl*sm - tm*sl;
- outCoeffs[2] = tl*tm;
-}
-
-inline static void calc_serp_lmcoeffs(SkScalar t, SkScalar s, ExcludedTerm skipTerm,
- SkScalar outCoeffs[3]) {
- SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
- outCoeffs[0] = -s*s*s;
- outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? 3*s*s*t : -3*s*t*t;
- outCoeffs[2] = t*t*t;
-}
-
-inline static void calc_loop_kcoeffs(SkScalar td, SkScalar sd, SkScalar te, SkScalar se,
- SkScalar tdse, SkScalar tesd, ExcludedTerm skipTerm,
- SkScalar outCoeffs[3]) {
- SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
- outCoeffs[0] = 0;
- outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? sd*se : -tdse - tesd;
- outCoeffs[2] = td*te;
-}
-
-inline static void calc_loop_lmcoeffs(SkScalar t2, SkScalar s2, SkScalar t1, SkScalar s1,
- SkScalar t2s1, SkScalar t1s2, ExcludedTerm skipTerm,
- SkScalar outCoeffs[3]) {
- SkASSERT(ExcludedTerm::kQuadraticTerm == skipTerm || ExcludedTerm::kLinearTerm == skipTerm);
- outCoeffs[0] = -s2*s2*s1;
- outCoeffs[1] = (ExcludedTerm::kLinearTerm == skipTerm) ? s2 * (2*t2s1 + t1s2)
- : -t2 * (t2s1 + 2*t1s2);
- outCoeffs[2] = t2*t2*t1;
-}
-
-// For the case when a cubic bezier is actually a quadratic. We duplicate k in l so that the
-// implicit becomes:
-//
-// k^3 - l*m == k^3 - l*k == k * (k^2 - l)
-//
-// In the quadratic case we can simply assign fixed values at each control point:
-//
-// | ..K.. | | pts[0] pts[1] pts[2] pts[3] | | 0 1/3 2/3 1 |
-// | ..L.. | * | . . . . | == | 0 0 1/3 1 |
-// | ..K.. | | 1 1 1 1 | | 0 1/3 2/3 1 |
-//
-static void calc_quadratic_klm(const SkPoint pts[4], double d3, SkMatrix* klm) {
- SkMatrix klmAtPts;
- klmAtPts.setAll(0, 1.f/3, 1,
- 0, 0, 1,
- 0, 1.f/3, 1);
-
- SkMatrix inversePts;
- inversePts.setAll(pts[0].x(), pts[1].x(), pts[3].x(),
- pts[0].y(), pts[1].y(), pts[3].y(),
- 1, 1, 1);
- SkAssertResult(inversePts.invert(&inversePts));
-
- klm->setConcat(klmAtPts, inversePts);
-
- // If d3 > 0 we need to flip the orientation of our curve
- // This is done by negating the k and l values
- if (d3 > 0) {
- klm->postScale(-1, -1);
- }
-}
-
-// For the case when a cubic bezier is actually a line. We set K=0, L=1, M=-line, which results in
-// the following implicit:
-//
-// k^3 - l*m == 0^3 - 1*(-line) == -(-line) == line
-//
-static void calc_line_klm(const SkPoint pts[4], SkMatrix* klm) {
- SkScalar ny = pts[0].x() - pts[3].x();
- SkScalar nx = pts[3].y() - pts[0].y();
- SkScalar k = nx * pts[0].x() + ny * pts[0].y();
- klm->setAll( 0, 0, 0,
- 0, 0, 1,
- -nx, -ny, k);
-}
-
-SkCubicType GrPathUtils::getCubicKLM(const SkPoint src[4], SkMatrix* klm, double tt[2],
- double ss[2]) {
- double d[4];
- SkCubicType type = SkClassifyCubic(src, tt, ss, d);
-
- if (SkCubicType::kLineOrPoint == type) {
- calc_line_klm(src, klm);
- return SkCubicType::kLineOrPoint;
- }
-
- if (SkCubicType::kQuadratic == type) {
- calc_quadratic_klm(src, d[3], klm);
- return SkCubicType::kQuadratic;
- }
-
- SkMatrix CIT;
- ExcludedTerm skipTerm = calcCubicInverseTransposePowerBasisMatrix(src, &CIT);
- if (ExcludedTerm::kNonInvertible == skipTerm) {
- // This could technically also happen if the curve were quadratic, but SkClassifyCubic
- // should have detected that case already with tolerance.
- calc_line_klm(src, klm);
- return SkCubicType::kLineOrPoint;
- }
-
- const SkScalar t0 = static_cast<SkScalar>(tt[0]), t1 = static_cast<SkScalar>(tt[1]),
- s0 = static_cast<SkScalar>(ss[0]), s1 = static_cast<SkScalar>(ss[1]);
-
- SkMatrix klmCoeffs;
- switch (type) {
- case SkCubicType::kCuspAtInfinity:
- SkASSERT(1 == t1 && 0 == s1); // Infinity.
- [[fallthrough]];
- case SkCubicType::kLocalCusp:
- case SkCubicType::kSerpentine:
- calc_serp_kcoeffs(t0, s0, t1, s1, skipTerm, &klmCoeffs[0]);
- calc_serp_lmcoeffs(t0, s0, skipTerm, &klmCoeffs[3]);
- calc_serp_lmcoeffs(t1, s1, skipTerm, &klmCoeffs[6]);
- break;
- case SkCubicType::kLoop: {
- const SkScalar tdse = t0 * s1;
- const SkScalar tesd = t1 * s0;
- calc_loop_kcoeffs(t0, s0, t1, s1, tdse, tesd, skipTerm, &klmCoeffs[0]);
- calc_loop_lmcoeffs(t0, s0, t1, s1, tdse, tesd, skipTerm, &klmCoeffs[3]);
- calc_loop_lmcoeffs(t1, s1, t0, s0, tesd, tdse, skipTerm, &klmCoeffs[6]);
- break;
- }
- default:
- SK_ABORT("Unexpected cubic type.");
- break;
- }
-
- klm->setConcat(klmCoeffs, CIT);
- return type;
-}
-
-int GrPathUtils::chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkMatrix* klm,
- int* loopIndex) {
- SkSTArray<2, SkScalar> chops;
- *loopIndex = -1;
-
- double t[2], s[2];
- if (SkCubicType::kLoop == GrPathUtils::getCubicKLM(src, klm, t, s)) {
- SkScalar t0 = static_cast<SkScalar>(t[0] / s[0]);
- SkScalar t1 = static_cast<SkScalar>(t[1] / s[1]);
- SkASSERT(t0 <= t1); // Technically t0 != t1 in a loop, but there may be FP error.
-
- if (t0 < 1 && t1 > 0) {
- *loopIndex = 0;
- if (t0 > 0) {
- chops.push_back(t0);
- *loopIndex = 1;
- }
- if (t1 < 1) {
- chops.push_back(t1);
- *loopIndex = chops.count() - 1;
- }
- }
- }
-
- SkChopCubicAt(src, dst, chops.begin(), chops.count());
- return chops.count() + 1;
-}
diff --git a/src/gpu/geometry/GrPathUtils.h b/src/gpu/geometry/GrPathUtils.h
index 48f537d..6c21cdc 100644
--- a/src/gpu/geometry/GrPathUtils.h
+++ b/src/gpu/geometry/GrPathUtils.h
@@ -19,199 +19,110 @@
* Utilities for evaluating paths.
*/
namespace GrPathUtils {
- // Very small tolerances will be increased to a minimum threshold value, to avoid division
- // problems in subsequent math.
- SkScalar scaleToleranceToSrc(SkScalar devTol,
- const SkMatrix& viewM,
- const SkRect& pathBounds);
- int worstCasePointCount(const SkPath&,
- int* subpaths,
- SkScalar tol);
+// Very small tolerances will be increased to a minimum threshold value, to avoid division problems
+// in subsequent math.
+SkScalar scaleToleranceToSrc(SkScalar devTol,
+ const SkMatrix& viewM,
+ const SkRect& pathBounds);
- uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol);
+uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol);
- uint32_t generateQuadraticPoints(const SkPoint& p0,
- const SkPoint& p1,
- const SkPoint& p2,
- SkScalar tolSqd,
- SkPoint** points,
- uint32_t pointsLeft);
-
- uint32_t cubicPointCount(const SkPoint points[], SkScalar tol);
-
- uint32_t generateCubicPoints(const SkPoint& p0,
+uint32_t generateQuadraticPoints(const SkPoint& p0,
const SkPoint& p1,
const SkPoint& p2,
- const SkPoint& p3,
SkScalar tolSqd,
SkPoint** points,
uint32_t pointsLeft);
- // A 2x3 matrix that goes from the 2d space coordinates to UV space where
- // u^2-v = 0 specifies the quad. The matrix is determined by the control
- // points of the quadratic.
- class QuadUVMatrix {
- public:
- QuadUVMatrix() {}
- // Initialize the matrix from the control pts
- QuadUVMatrix(const SkPoint controlPts[3]) { this->set(controlPts); }
- void set(const SkPoint controlPts[3]);
+uint32_t cubicPointCount(const SkPoint points[], SkScalar tol);
- /**
- * Applies the matrix to vertex positions to compute UV coords.
- *
- * vertices is a pointer to the first vertex.
- * vertexCount is the number of vertices.
- * stride is the size of each vertex.
- * uvOffset is the offset of the UV values within each vertex.
- */
- void apply(void* vertices, int vertexCount, size_t stride, size_t uvOffset) const {
- intptr_t xyPtr = reinterpret_cast<intptr_t>(vertices);
- intptr_t uvPtr = reinterpret_cast<intptr_t>(vertices) + uvOffset;
- float sx = fM[0];
- float kx = fM[1];
- float tx = fM[2];
- float ky = fM[3];
- float sy = fM[4];
- float ty = fM[5];
- for (int i = 0; i < vertexCount; ++i) {
- const SkPoint* xy = reinterpret_cast<const SkPoint*>(xyPtr);
- SkPoint* uv = reinterpret_cast<SkPoint*>(uvPtr);
- uv->fX = sx * xy->fX + kx * xy->fY + tx;
- uv->fY = ky * xy->fX + sy * xy->fY + ty;
- xyPtr += stride;
- uvPtr += stride;
- }
+uint32_t generateCubicPoints(const SkPoint& p0,
+ const SkPoint& p1,
+ const SkPoint& p2,
+ const SkPoint& p3,
+ SkScalar tolSqd,
+ SkPoint** points,
+ uint32_t pointsLeft);
+
+// A 2x3 matrix that goes from the 2d space coordinates to UV space where u^2-v = 0 specifies the
+// quad. The matrix is determined by the control points of the quadratic.
+class QuadUVMatrix {
+public:
+ QuadUVMatrix() {}
+ // Initialize the matrix from the control pts
+ QuadUVMatrix(const SkPoint controlPts[3]) { this->set(controlPts); }
+ void set(const SkPoint controlPts[3]);
+
+ /**
+ * Applies the matrix to vertex positions to compute UV coords.
+ *
+ * vertices is a pointer to the first vertex.
+ * vertexCount is the number of vertices.
+ * stride is the size of each vertex.
+ * uvOffset is the offset of the UV values within each vertex.
+ */
+ void apply(void* vertices, int vertexCount, size_t stride, size_t uvOffset) const {
+ intptr_t xyPtr = reinterpret_cast<intptr_t>(vertices);
+ intptr_t uvPtr = reinterpret_cast<intptr_t>(vertices) + uvOffset;
+ float sx = fM[0];
+ float kx = fM[1];
+ float tx = fM[2];
+ float ky = fM[3];
+ float sy = fM[4];
+ float ty = fM[5];
+ for (int i = 0; i < vertexCount; ++i) {
+ const SkPoint* xy = reinterpret_cast<const SkPoint*>(xyPtr);
+ SkPoint* uv = reinterpret_cast<SkPoint*>(uvPtr);
+ uv->fX = sx * xy->fX + kx * xy->fY + tx;
+ uv->fY = ky * xy->fX + sy * xy->fY + ty;
+ xyPtr += stride;
+ uvPtr += stride;
}
- private:
- float fM[6];
- };
+ }
+private:
+ float fM[6];
+};
- // Input is 3 control points and a weight for a bezier conic. Calculates the
- // three linear functionals (K,L,M) that represent the implicit equation of the
- // conic, k^2 - lm.
- //
- // Output: klm holds the linear functionals K,L,M as row vectors:
- //
- // | ..K.. | | x | | k |
- // | ..L.. | * | y | == | l |
- // | ..M.. | | 1 | | m |
- //
- void getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* klm);
+// Input is 3 control points and a weight for a bezier conic. Calculates the three linear
+// functionals (K,L,M) that represent the implicit equation of the conic, k^2 - lm.
+//
+// Output: klm holds the linear functionals K,L,M as row vectors:
+//
+// | ..K.. | | x | | k |
+// | ..L.. | * | y | == | l |
+// | ..M.. | | 1 | | m |
+//
+void getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* klm);
- // Converts a cubic into a sequence of quads. If working in device space
- // use tolScale = 1, otherwise set based on stretchiness of the matrix. The
- // result is sets of 3 points in quads. This will preserve the starting and
- // ending tangent vectors (modulo FP precision).
- void convertCubicToQuads(const SkPoint p[4],
- SkScalar tolScale,
- SkTArray<SkPoint, true>* quads);
+// Converts a cubic into a sequence of quads. If working in device space use tolScale = 1, otherwise
+// set based on stretchiness of the matrix. The result is sets of 3 points in quads. This will
+// preserve the starting and ending tangent vectors (modulo FP precision).
+void convertCubicToQuads(const SkPoint p[4],
+ SkScalar tolScale,
+ SkTArray<SkPoint, true>* quads);
- // When we approximate a cubic {a,b,c,d} with a quadratic we may have to
- // ensure that the new control point lies between the lines ab and cd. The
- // convex path renderer requires this. It starts with a path where all the
- // control points taken together form a convex polygon. It relies on this
- // property and the quadratic approximation of cubics step cannot alter it.
- // This variation enforces this constraint. The cubic must be simple and dir
- // must specify the orientation of the contour containing the cubic.
- void convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
- SkScalar tolScale,
- SkPathFirstDirection dir,
- SkTArray<SkPoint, true>* quads);
+// When we approximate a cubic {a,b,c,d} with a quadratic we may have to ensure that the new control
+// point lies between the lines ab and cd. The convex path renderer requires this. It starts with a
+// path where all the control points taken together form a convex polygon. It relies on this
+// property and the quadratic approximation of cubics step cannot alter it. This variation enforces
+// this constraint. The cubic must be simple and dir must specify the orientation of the contour
+// containing the cubic.
+void convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
+ SkScalar tolScale,
+ SkPathFirstDirection dir,
+ SkTArray<SkPoint, true>* quads);
- enum class ExcludedTerm {
- kNonInvertible,
- kQuadraticTerm,
- kLinearTerm
- };
+// When tessellating curved paths into linear segments, this defines the maximum distance in screen
+// space which a segment may deviate from the mathematically correct value. Above this value, the
+// segment will be subdivided.
+// This value was chosen to approximate the supersampling accuracy of the raster path (16 samples,
+// or one quarter pixel).
+static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
- // Computes the inverse-transpose of the cubic's power basis matrix, after removing a specific
- // row of coefficients.
- //
- // E.g. if the cubic is defined in power basis form as follows:
- //
- // | x3 y3 0 |
- // C(t,s) = [t^3 t^2*s t*s^2 s^3] * | x2 y2 0 |
- // | x1 y1 0 |
- // | x0 y0 1 |
- //
- // And the excluded term is "kQuadraticTerm", then the resulting inverse-transpose will be:
- //
- // | x3 y3 0 | -1 T
- // | x1 y1 0 |
- // | x0 y0 1 |
- //
- // (The term to exclude is chosen based on maximizing the resulting matrix determinant.)
- //
- // This can be used to find the KLM linear functionals:
- //
- // | ..K.. | | ..kcoeffs.. |
- // | ..L.. | = | ..lcoeffs.. | * inverse_transpose_power_basis_matrix
- // | ..M.. | | ..mcoeffs.. |
- //
- // NOTE: the same term that was excluded here must also be removed from the corresponding column
- // of the klmcoeffs matrix.
- //
- // Returns which row of coefficients was removed, or kNonInvertible if the cubic was degenerate.
- ExcludedTerm calcCubicInverseTransposePowerBasisMatrix(const SkPoint p[4], SkMatrix* out);
+// We guarantee that no quad or cubic will ever produce more than this many points
+static const int kMaxPointsPerCurve = 1 << 10;
- // Computes the KLM linear functionals for the cubic implicit form. The "right" side of the
- // curve (when facing in the direction of increasing parameter values) will be the area that
- // satisfies:
- //
- // k^3 < l*m
- //
- // Output:
- //
- // klm: Holds the linear functionals K,L,M as row vectors:
- //
- // | ..K.. | | x | | k |
- // | ..L.. | * | y | == | l |
- // | ..M.. | | 1 | | m |
- //
- // NOTE: the KLM lines are calculated in the same space as the input control points. If you
- // transform the points the lines will also need to be transformed. This can be done by mapping
- // the lines with the inverse-transpose of the matrix used to map the points.
- //
- // t[],s[]: These are set to the two homogeneous parameter values at which points the lines L&M
- // intersect with K (See SkClassifyCubic).
- //
- // Returns the cubic's classification.
- SkCubicType getCubicKLM(const SkPoint src[4], SkMatrix* klm, double t[2], double s[2]);
-
- // Chops the cubic bezier passed in by src, at the double point (intersection point)
- // if the curve is a cubic loop. If it is a loop, there will be two parametric values for
- // the double point: t1 and t2. We chop the cubic at these values if they are between 0 and 1.
- // Return value:
- // Value of 3: t1 and t2 are both between (0,1), and dst will contain the three cubics,
- // dst[0..3], dst[3..6], and dst[6..9] if dst is not nullptr
- // Value of 2: Only one of t1 and t2 are between (0,1), and dst will contain the two cubics,
- // dst[0..3] and dst[3..6] if dst is not nullptr
- // Value of 1: Neither t1 nor t2 are between (0,1), and dst will contain the one original cubic,
- // src[0..3]
- //
- // Output:
- //
- // klm: Holds the linear functionals K,L,M as row vectors. (See getCubicKLM().)
- //
- // loopIndex: This value will tell the caller which of the chopped sections (if any) are the
- // actual loop. A value of -1 means there is no loop section. The caller can then use
- // this value to decide how/if they want to flip the orientation of this section.
- // The flip should be done by negating the k and l values as follows:
- //
- // KLM.postScale(-1, -1)
- int chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkMatrix* klm,
- int* loopIndex);
-
- // When tessellating curved paths into linear segments, this defines the maximum distance
- // in screen space which a segment may deviate from the mathmatically correct value.
- // Above this value, the segment will be subdivided.
- // This value was chosen to approximate the supersampling accuracy of the raster path (16
- // samples, or one quarter pixel).
- static const SkScalar kDefaultTolerance = SkDoubleToScalar(0.25);
-
- // We guarantee that no quad or cubic will ever produce more than this many points
- static const int kMaxPointsPerCurve = 1 << 10;
} // namespace GrPathUtils
+
#endif