|  | /* | 
|  | * Copyright 2011 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "SkPathMeasure.h" | 
|  | #include "Test.h" | 
|  |  | 
|  | static void test_small_segment3() { | 
|  | SkPath path; | 
|  | const SkPoint pts[] = { | 
|  | { 0, 0 }, | 
|  | { 100000000000.0f, 100000000000.0f }, { 0, 0 }, { 10, 10 }, | 
|  | { 10, 10 }, { 0, 0 }, { 10, 10 } | 
|  | }; | 
|  |  | 
|  | path.moveTo(pts[0]); | 
|  | for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 3) { | 
|  | path.cubicTo(pts[i], pts[i + 1], pts[i + 2]); | 
|  | } | 
|  |  | 
|  | SkPathMeasure meas(path, false); | 
|  | meas.getLength(); | 
|  | } | 
|  |  | 
|  | static void test_small_segment2() { | 
|  | SkPath path; | 
|  | const SkPoint pts[] = { | 
|  | { 0, 0 }, | 
|  | { 100000000000.0f, 100000000000.0f }, { 0, 0 }, | 
|  | { 10, 10 }, { 0, 0 }, | 
|  | }; | 
|  |  | 
|  | path.moveTo(pts[0]); | 
|  | for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 2) { | 
|  | path.quadTo(pts[i], pts[i + 1]); | 
|  | } | 
|  | SkPathMeasure meas(path, false); | 
|  | meas.getLength(); | 
|  | } | 
|  |  | 
|  | static void test_small_segment() { | 
|  | SkPath path; | 
|  | const SkPoint pts[] = { | 
|  | { 100000, 100000}, | 
|  | // big jump between these points, makes a big segment | 
|  | { 1.0005f, 0.9999f }, | 
|  | // tiny (non-zero) jump between these points | 
|  | { SK_Scalar1, SK_Scalar1 }, | 
|  | }; | 
|  |  | 
|  | path.moveTo(pts[0]); | 
|  | for (size_t i = 1; i < SK_ARRAY_COUNT(pts); ++i) { | 
|  | path.lineTo(pts[i]); | 
|  | } | 
|  | SkPathMeasure meas(path, false); | 
|  |  | 
|  | /*  this would assert (before a fix) because we added a segment with | 
|  | the same length as the prev segment, due to the follow (bad) pattern | 
|  |  | 
|  | d = distance(pts[0], pts[1]); | 
|  | distance += d; | 
|  | seg->fDistance = distance; | 
|  |  | 
|  | SkASSERT(d > 0);    // TRUE | 
|  | SkASSERT(seg->fDistance > prevSeg->fDistance);  // FALSE | 
|  |  | 
|  | This 2nd assert failes because (distance += d) didn't affect distance | 
|  | because distance >>> d. | 
|  | */ | 
|  | meas.getLength(); | 
|  | } | 
|  |  | 
|  | DEF_TEST(PathMeasure, reporter) { | 
|  | SkPath  path; | 
|  |  | 
|  | path.moveTo(0, 0); | 
|  | path.lineTo(SK_Scalar1, 0); | 
|  | path.lineTo(SK_Scalar1, SK_Scalar1); | 
|  | path.lineTo(0, SK_Scalar1); | 
|  |  | 
|  | SkPathMeasure   meas(path, true); | 
|  | SkScalar        length = meas.getLength(); | 
|  | SkASSERT(length == SK_Scalar1*4); | 
|  |  | 
|  | path.reset(); | 
|  | path.moveTo(0, 0); | 
|  | path.lineTo(SK_Scalar1*3, SK_Scalar1*4); | 
|  | meas.setPath(&path, false); | 
|  | length = meas.getLength(); | 
|  | REPORTER_ASSERT(reporter, length == SK_Scalar1*5); | 
|  |  | 
|  | path.reset(); | 
|  | path.addCircle(0, 0, SK_Scalar1); | 
|  | meas.setPath(&path, true); | 
|  | length = meas.getLength(); | 
|  | //    SkDebugf("circle arc-length = %g\n", length); | 
|  |  | 
|  | // Test the behavior following a close not followed by a move. | 
|  | path.reset(); | 
|  | path.lineTo(SK_Scalar1, 0); | 
|  | path.lineTo(SK_Scalar1, SK_Scalar1); | 
|  | path.lineTo(0, SK_Scalar1); | 
|  | path.close(); | 
|  | path.lineTo(-SK_Scalar1, 0); | 
|  | meas.setPath(&path, false); | 
|  | length = meas.getLength(); | 
|  | REPORTER_ASSERT(reporter, length == SK_Scalar1 * 4); | 
|  | meas.nextContour(); | 
|  | length = meas.getLength(); | 
|  | REPORTER_ASSERT(reporter, length == SK_Scalar1); | 
|  | SkPoint position; | 
|  | SkVector tangent; | 
|  | REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fX, | 
|  | -SK_ScalarHalf, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, position.fY == 0); | 
|  | REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, tangent.fY == 0); | 
|  |  | 
|  | // Test degenerate paths | 
|  | path.reset(); | 
|  | path.moveTo(0, 0); | 
|  | path.lineTo(0, 0); | 
|  | path.lineTo(SK_Scalar1, 0); | 
|  | path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0); | 
|  | path.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1 * 2); | 
|  | path.cubicTo(SK_Scalar1, SK_Scalar1 * 2, | 
|  | SK_Scalar1, SK_Scalar1 * 2, | 
|  | SK_Scalar1, SK_Scalar1 * 2); | 
|  | path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2, | 
|  | SK_Scalar1*3, SK_Scalar1 * 2, | 
|  | SK_Scalar1*4, SK_Scalar1 * 2); | 
|  | meas.setPath(&path, false); | 
|  | length = meas.getLength(); | 
|  | REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6); | 
|  | REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fX, | 
|  | SK_ScalarHalf, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, position.fY == 0); | 
|  | REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, tangent.fY == 0); | 
|  | REPORTER_ASSERT(reporter, meas.getPosTan(2.5f, &position, &tangent)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fX, SK_Scalar1, 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fY, 1.5f)); | 
|  | REPORTER_ASSERT(reporter, tangent.fX == 0); | 
|  | REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, meas.getPosTan(4.5f, &position, &tangent)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fX, | 
|  | 2.5f, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fY, | 
|  | 2.0f, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, tangent.fY == 0); | 
|  |  | 
|  | path.reset(); | 
|  | path.moveTo(0, 0); | 
|  | path.lineTo(SK_Scalar1, 0); | 
|  | path.moveTo(SK_Scalar1, SK_Scalar1); | 
|  | path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2); | 
|  | path.lineTo(SK_Scalar1, SK_Scalar1 * 2); | 
|  | meas.setPath(&path, false); | 
|  | length = meas.getLength(); | 
|  | REPORTER_ASSERT(reporter, length == SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fX, | 
|  | SK_ScalarHalf, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, position.fY == 0); | 
|  | REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, tangent.fY == 0); | 
|  | meas.nextContour(); | 
|  | length = meas.getLength(); | 
|  | REPORTER_ASSERT(reporter, length == SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fX, | 
|  | 1.5f, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, | 
|  | SkScalarNearlyEqual(position.fY, | 
|  | 2.0f, | 
|  | 0.0001f)); | 
|  | REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1); | 
|  | REPORTER_ASSERT(reporter, tangent.fY == 0); | 
|  |  | 
|  | test_small_segment(); | 
|  | test_small_segment2(); | 
|  | test_small_segment3(); | 
|  | } | 
|  |  | 
|  | DEF_TEST(PathMeasureConic, reporter) { | 
|  | SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}}; | 
|  | SkPath p; | 
|  | p.moveTo(0, 0); | 
|  | p.conicTo(pts[1], pts[2], 1); | 
|  | SkPathMeasure stdm(p, false); | 
|  | REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr)); | 
|  | p.reset(); | 
|  | p.moveTo(0, 0); | 
|  | p.conicTo(pts[1], pts[2], 10); | 
|  | stdm.setPath(&p, false); | 
|  | REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr)); | 
|  | REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f); | 
|  | REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f); | 
|  | } |