Reland of ix zero-length tangent (patchset #1 id:1 of https://codereview.chromium.org/1312243002/ )

Reason for revert:
Layout suppression has landed, and verified that Skia gm test changes are correct.

Original issue's description:
> Revert of fix zero-length tangent (patchset #2 id:20001 of https://codereview.chromium.org/1311273002/ )
>
> Reason for revert:
> causes layout test to draw differently -- new drawing is more correct. Reverting until layout test ignore is landed.
>
> Original issue's description:
> > fix zero-length tangent
> >
> > If the end point and the control point are the same, computing
> > the tangent will result in (0, 0). In this case, use the prior
> > control point instead.
> >
> > R=reed@google.com
> >
> > BUG=skia:4191
> >
> > Committed: https://skia.googlesource.com/skia/+/7544124fb8ee744f68f549a353f8a9163cd7432d
>
> TBR=reed@google.com
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=skia:4191
>
> Committed: https://skia.googlesource.com/skia/+/91298b47c547b2ab4697038c04685af957bd1416

TBR=reed@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:4191

Review URL: https://codereview.chromium.org/1320473002
This commit is contained in:
caryclark 2015-08-25 13:19:06 -07:00 committed by Commit bot
parent b5fb5af573
commit 45398dff71
3 changed files with 150 additions and 19 deletions

View File

@ -266,6 +266,60 @@ private:
typedef skiagm::GM INHERITED;
};
// Test stroking for curves that produce degenerate tangents when t is 0 or 1 (see bug 4191)
class Strokes5GM : public skiagm::GM {
public:
Strokes5GM() {}
protected:
SkString onShortName() override {
return SkString("zero_control_stroke");
}
SkISize onISize() override {
return SkISize::Make(W, H*2);
}
void onDraw(SkCanvas* canvas) override {
SkPaint p;
p.setColor(SK_ColorRED);
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(40);
p.setStrokeCap(SkPaint::kButt_Cap);
SkPath path;
path.moveTo(157.474f,111.753f);
path.cubicTo(128.5f,111.5f,35.5f,29.5f,35.5f,29.5f);
canvas->drawPath(path, p);
path.reset();
path.moveTo(250, 50);
path.quadTo(280, 80, 280, 80);
canvas->drawPath(path, p);
path.reset();
path.moveTo(150, 50);
path.conicTo(180, 80, 180, 80, 0.707f);
canvas->drawPath(path, p);
path.reset();
path.moveTo(157.474f,311.753f);
path.cubicTo(157.474f,311.753f,85.5f,229.5f,35.5f,229.5f);
canvas->drawPath(path, p);
path.reset();
path.moveTo(280, 250);
path.quadTo(280, 250, 310, 280);
canvas->drawPath(path, p);
path.reset();
path.moveTo(180, 250);
path.conicTo(180, 250, 210, 280, 0.707f);
canvas->drawPath(path, p);
}
private:
typedef skiagm::GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
@ -273,8 +327,10 @@ static skiagm::GM* F0(void*) { return new StrokesGM; }
static skiagm::GM* F1(void*) { return new Strokes2GM; }
static skiagm::GM* F2(void*) { return new Strokes3GM; }
static skiagm::GM* F3(void*) { return new Strokes4GM; }
static skiagm::GM* F4(void*) { return new Strokes5GM; }
static skiagm::GMRegistry R0(F0);
static skiagm::GMRegistry R1(F1);
static skiagm::GMRegistry R2(F2);
static skiagm::GMRegistry R3(F3);
static skiagm::GMRegistry R4(F4);

View File

@ -130,13 +130,6 @@ static SkScalar eval_quad(const SkScalar src[], SkScalar t) {
#endif
}
static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t) {
SkScalar A = src[4] - 2 * src[2] + src[0];
SkScalar B = src[2] - src[0];
return 2 * SkScalarMulAdd(A, t, B);
}
void SkQuadToCoeff(const SkPoint pts[3], SkPoint coeff[3]) {
Sk2s p0 = from_point(pts[0]);
Sk2s p1 = from_point(pts[1]);
@ -157,8 +150,7 @@ void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tange
pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t));
}
if (tangent) {
tangent->set(eval_quad_derivative(&src[0].fX, t),
eval_quad_derivative(&src[0].fY, t));
*tangent = SkEvalQuadTangentAt(src, t);
}
}
@ -179,6 +171,12 @@ SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) {
}
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) {
// The derivative equation is 2(b - a +(a - 2b +c)t). This returns a
// zero tangent vector when t is 0 or 1, and the control point is equal
// to the end point. In this case, use the quad end points to compute the tangent.
if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) {
return src[2] - src[0];
}
SkASSERT(src);
SkASSERT(t >= 0 && t <= SK_Scalar1);
@ -398,8 +396,22 @@ void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc,
loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t));
}
if (tangent) {
tangent->set(eval_cubic_derivative(&src[0].fX, t),
eval_cubic_derivative(&src[0].fY, t));
// The derivative equation returns a zero tangent vector when t is 0 or 1, and the
// adjacent control point is equal to the end point. In this case, use the
// next control point or the end points to compute the tangent.
if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) {
if (t == 0) {
*tangent = src[2] - src[0];
} else {
*tangent = src[3] - src[1];
}
if (!tangent->fX && !tangent->fY) {
*tangent = src[3] - src[0];
}
} else {
tangent->set(eval_cubic_derivative(&src[0].fX, t),
eval_cubic_derivative(&src[0].fY, t));
}
}
if (curvature) {
curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t),
@ -1176,12 +1188,6 @@ static void conic_deriv_coeff(const SkScalar src[],
coeff[2] = wP10;
}
static SkScalar conic_eval_tan(const SkScalar coord[], SkScalar w, SkScalar t) {
SkScalar coeff[3];
conic_deriv_coeff(coord, w, coeff);
return t * (t * coeff[0] + coeff[1]) + coeff[2];
}
static bool conic_find_extrema(const SkScalar src[], SkScalar w, SkScalar* t) {
SkScalar coeff[3];
conic_deriv_coeff(src, w, coeff);
@ -1232,8 +1238,7 @@ void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const {
conic_eval_pos(&fPts[0].fY, fW, t));
}
if (tangent) {
tangent->set(conic_eval_tan(&fPts[0].fX, fW, t),
conic_eval_tan(&fPts[0].fY, fW, t));
*tangent = evalTangentAt(t);
}
}
@ -1291,6 +1296,12 @@ SkPoint SkConic::evalAt(SkScalar t) const {
}
SkVector SkConic::evalTangentAt(SkScalar t) const {
// The derivative equation returns a zero tangent vector when t is 0 or 1,
// and the control point is equal to the end point.
// In this case, use the conic endpoints to compute the tangent.
if ((t == 0 && fPts[0] == fPts[1]) || (t == 1 && fPts[1] == fPts[2])) {
return fPts[2] - fPts[0];
}
Sk2s p0 = from_point(fPts[0]);
Sk2s p1 = from_point(fPts[1]);
Sk2s p2 = from_point(fPts[2]);

View File

@ -105,6 +105,67 @@ static void test_conic(skiatest::Reporter* reporter) {
}
}
static void test_quad_tangents(skiatest::Reporter* reporter) {
SkPoint pts[] = {
{10, 20}, {10, 20}, {20, 30},
{10, 20}, {15, 25}, {20, 30},
{10, 20}, {20, 30}, {20, 30},
};
int count = (int) SK_ARRAY_COUNT(pts) / 3;
for (int index = 0; index < count; ++index) {
SkConic conic(&pts[index * 3], 0.707f);
SkVector start = SkEvalQuadTangentAt(&pts[index * 3], 0);
SkVector mid = SkEvalQuadTangentAt(&pts[index * 3], .5f);
SkVector end = SkEvalQuadTangentAt(&pts[index * 3], 1);
REPORTER_ASSERT(reporter, start.fX && start.fY);
REPORTER_ASSERT(reporter, mid.fX && mid.fY);
REPORTER_ASSERT(reporter, end.fX && end.fY);
REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid)));
REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end)));
}
}
static void test_conic_tangents(skiatest::Reporter* reporter) {
SkPoint pts[] = {
{ 10, 20}, {10, 20}, {20, 30},
{ 10, 20}, {15, 25}, {20, 30},
{ 10, 20}, {20, 30}, {20, 30}
};
int count = (int) SK_ARRAY_COUNT(pts) / 3;
for (int index = 0; index < count; ++index) {
SkConic conic(&pts[index * 3], 0.707f);
SkVector start = conic.evalTangentAt(0);
SkVector mid = conic.evalTangentAt(.5f);
SkVector end = conic.evalTangentAt(1);
REPORTER_ASSERT(reporter, start.fX && start.fY);
REPORTER_ASSERT(reporter, mid.fX && mid.fY);
REPORTER_ASSERT(reporter, end.fX && end.fY);
REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid)));
REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end)));
}
}
static void test_cubic_tangents(skiatest::Reporter* reporter) {
SkPoint pts[] = {
{ 10, 20}, {10, 20}, {20, 30}, {30, 40},
{ 10, 20}, {15, 25}, {20, 30}, {30, 40},
{ 10, 20}, {20, 30}, {30, 40}, {30, 40},
};
int count = (int) SK_ARRAY_COUNT(pts) / 4;
for (int index = 0; index < count; ++index) {
SkConic conic(&pts[index * 3], 0.707f);
SkVector start, mid, end;
SkEvalCubicAt(&pts[index * 4], 0, NULL, &start, NULL);
SkEvalCubicAt(&pts[index * 4], .5f, NULL, &mid, NULL);
SkEvalCubicAt(&pts[index * 4], 1, NULL, &end, NULL);
REPORTER_ASSERT(reporter, start.fX && start.fY);
REPORTER_ASSERT(reporter, mid.fX && mid.fY);
REPORTER_ASSERT(reporter, end.fX && end.fY);
REPORTER_ASSERT(reporter, SkScalarNearlyZero(start.cross(mid)));
REPORTER_ASSERT(reporter, SkScalarNearlyZero(mid.cross(end)));
}
}
DEF_TEST(Geometry, reporter) {
SkPoint pts[3], dst[5];
@ -129,4 +190,7 @@ DEF_TEST(Geometry, reporter) {
testChopCubic(reporter);
test_evalquadat(reporter);
test_conic(reporter);
test_cubic_tangents(reporter);
test_quad_tangents(reporter);
test_conic_tangents(reporter);
}