CCPR: Check for flat lines before crunching on curves

Bug: skia:
Change-Id: Ib99f3f83aee4c261ef1cfc4e1045b2be3113e1ae
Reviewed-on: https://skia-review.googlesource.com/43100
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Chris Dalton 2017-09-07 10:36:51 -06:00 committed by Skia Commit-Bot
parent 6c251d24cf
commit 900cd05037

View File

@ -40,6 +40,7 @@ void GrCCPRGeometry::beginContour(const SkPoint& devPt) {
void GrCCPRGeometry::lineTo(const SkPoint& devPt) {
SkASSERT(fBuildingContour);
SkASSERT(fCurrFanPoint == fPoints.back());
fCurrFanPoint = devPt;
fPoints.push_back(devPt);
fVerbs.push_back(Verb::kLineTo);
@ -56,6 +57,22 @@ static inline float dot(const Sk2f& a, const Sk2f& b) {
return product[0] + product[1];
}
static inline bool are_collinear(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
static constexpr float kFlatnessTolerance = 16; // 1/16 of a pixel.
// Area (times 2) of the triangle.
Sk2f a = (p0 - p1) * SkNx_shuffle<1,0>(p1 - p2);
a = (a - SkNx_shuffle<1,0>(a)).abs();
// Bounding box of the triangle.
Sk2f bbox0 = Sk2f::Min(Sk2f::Min(p0, p1), p2);
Sk2f bbox1 = Sk2f::Max(Sk2f::Max(p0, p1), p2);
// The triangle is linear if its area is within a fraction of the largest bounding box
// dimension, or else if its area is within a fraction of a pixel.
return (a * (kFlatnessTolerance/2) < Sk2f::Max(bbox1 - bbox0, 1)).anyTrue();
}
// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& startTan,
const Sk2f& endPt, const Sk2f& endTan) {
@ -75,12 +92,20 @@ static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) {
void GrCCPRGeometry::quadraticTo(const SkPoint& devP0, const SkPoint& devP1) {
SkASSERT(fBuildingContour);
SkASSERT(fCurrFanPoint == fPoints.back());
Sk2f p0 = Sk2f::Load(&fCurrFanPoint);
Sk2f p1 = Sk2f::Load(&devP0);
Sk2f p2 = Sk2f::Load(&devP1);
fCurrFanPoint = devP1;
// Don't send curves to the GPU if we know they are flat (or just very small).
if (are_collinear(p0, p1, p2)) {
p2.store(&fPoints.push_back());
fVerbs.push_back(Verb::kLineTo);
return;
}
Sk2f tan0 = p1 - p0;
Sk2f tan1 = p2 - p1;
// This should almost always be this case for well-behaved curves in the real world.
@ -230,6 +255,7 @@ static inline void calc_loop_intersect_padding_pts(float padRadius, const Sk2f&
void GrCCPRGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const SkPoint& devP3,
float inflectPad, float loopIntersectPad) {
SkASSERT(fBuildingContour);
SkASSERT(fCurrFanPoint == fPoints.back());
SkPoint devPts[4] = {fCurrFanPoint, devP1, devP2, devP3};
Sk2f p0 = Sk2f::Load(&fCurrFanPoint);
@ -238,6 +264,15 @@ void GrCCPRGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const S
Sk2f p3 = Sk2f::Load(&devP3);
fCurrFanPoint = devP3;
// Don't crunch on the curve and inflate geometry if it is already flat (or just very small).
if (are_collinear(p0, p1, p2) &&
are_collinear(p1, p2, p3) &&
are_collinear(p0, (p1 + p2) * .5f, p3)) {
p3.store(&fPoints.push_back());
fVerbs.push_back(Verb::kLineTo);
return;
}
double tt[2], ss[2];
fCurrCubicType = SkClassifyCubic(devPts, tt, ss);
if (SkCubicIsDegenerate(fCurrCubicType)) {
@ -251,8 +286,8 @@ void GrCCPRGeometry::cubicTo(const SkPoint& devP1, const SkPoint& devP2, const S
if (ExcludedTerm::kNonInvertible == skipTerm) {
// This could technically also happen if the curve were a quadratic, but SkClassifyCubic
// should have detected that case already with tolerance.
fCurrCubicType = SkCubicType::kLineOrPoint;
this->appendCubicApproximation(p0, p1, p2, p3, /*maxSubdivisions=*/0);
p3.store(&fPoints.push_back());
fVerbs.push_back(Verb::kLineTo);
return;
}
SkASSERT(0 == CIT[6]);