diff options
author | caryclark <caryclark@google.com> | 2015-08-25 13:19:06 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-25 13:19:06 -0700 |
commit | 45398dff710c6cdadbc3c686faa7bf692febd222 (patch) | |
tree | d3738d90e02f0e929709d11850f03c2f1c6137d8 | |
parent | b5fb5af5734dcc31a5e29212db39f0da71f92f2d (diff) |
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
-rw-r--r-- | gm/strokes.cpp | 56 | ||||
-rw-r--r-- | src/core/SkGeometry.cpp | 49 | ||||
-rw-r--r-- | tests/GeometryTest.cpp | 64 |
3 files changed, 150 insertions, 19 deletions
diff --git a/gm/strokes.cpp b/gm/strokes.cpp index 73823907a3..0bcb039783 100644 --- a/gm/strokes.cpp +++ b/gm/strokes.cpp @@ -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); diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp index 6afd9d7ffb..7462009479 100644 --- a/src/core/SkGeometry.cpp +++ b/src/core/SkGeometry.cpp @@ -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]); diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp index 00fc7797dc..5aa80d0b1f 100644 --- a/tests/GeometryTest.cpp +++ b/tests/GeometryTest.cpp @@ -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); } |