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>
This commit is contained in:
parent
81b270a659
commit
0e13db707f
@ -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); )
|
@ -38,7 +38,6 @@ bench_sources = [
|
||||
"$_bench/ControlBench.cpp",
|
||||
"$_bench/CoverageBench.cpp",
|
||||
"$_bench/CreateBackendTextureBench.cpp",
|
||||
"$_bench/CubicKLMBench.cpp",
|
||||
"$_bench/CubicMapBench.cpp",
|
||||
"$_bench/DDLRecorderBench.cpp",
|
||||
"$_bench/DashBench.cpp",
|
||||
|
@ -67,7 +67,6 @@ class CCPRGeometryView : public Sample {
|
||||
|
||||
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 @@ private:
|
||||
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 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
|
||||
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 @@ void CCPRGeometryView::updateGpuData() {
|
||||
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);
|
||||
|
@ -162,54 +162,6 @@ uint32_t GrPathUtils::generateCubicPoints(const SkPoint& p0,
|
||||
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 @@ void GrPathUtils::convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
|
||||
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;
|
||||
}
|
||||
|
@ -19,199 +19,110 @@ class SkMatrix;
|
||||
* 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
|
||||
|
Loading…
Reference in New Issue
Block a user