blob: 7c5903322ce53f399b882f9fa7428b756f33c9bb [file] [log] [blame]
tomhudson@google.comddab2272011-06-08 14:46:28 +00001#include "SkPoint.h"
2#include "SkScalar.h"
3#include "Test.h"
4
5/*
6 Duplicates lots of code from gpu/src/GrPathUtils.cpp
tomhudson@google.comfc1539a2011-06-24 15:43:24 +00007 It'd be nice not to do so, but that code's set up currently to only have
8 a single implementation.
tomhudson@google.comddab2272011-06-08 14:46:28 +00009*/
10
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000011// Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily.
tomhudson@google.comddab2272011-06-08 14:46:28 +000012#define MAX_COEFF_SHIFT 6
13static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT;
14
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000015// max + 0.5 min has error [0.0, 0.12]
16// max + 0.375 min has error [-.03, 0.07]
17// 0.96043387 max + 0.397824735 min has error [-.06, +.05]
18// For determining the maximum possible number of points to use in
19// drawing a quadratic, we want to err on the high side.
tomhudson@google.comddab2272011-06-08 14:46:28 +000020static inline int cheap_distance(SkScalar dx, SkScalar dy) {
21 int idx = SkAbs32(SkScalarRound(dx));
22 int idy = SkAbs32(SkScalarRound(dy));
23 if (idx > idy) {
24 idx += idy >> 1;
25 } else {
26 idx = idy + (idx >> 1);
27 }
28 return idx;
29}
30
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000031static inline int estimate_distance(const SkPoint points[]) {
32 return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX,
33 points[1].fY * 2 - points[2].fY - points[0].fY);
tomhudson@google.comddab2272011-06-08 14:46:28 +000034}
35
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000036static inline SkScalar compute_distance(const SkPoint points[]) {
37 return points[1].distanceToLineSegmentBetween(points[0], points[2]);
38}
tomhudson@google.comddab2272011-06-08 14:46:28 +000039
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000040static inline uint32_t estimate_pointCount(int distance) {
41 // Includes -2 bias because this estimator runs 4x high?
42 int shift = 30 - SkCLZ(distance);
43 // Clamp to zero if above subtraction went negative.
44 shift &= ~(shift>>31);
tomhudson@google.comddab2272011-06-08 14:46:28 +000045 if (shift > MAX_COEFF_SHIFT) {
46 shift = MAX_COEFF_SHIFT;
47 }
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000048 return 1 << shift;
tomhudson@google.comddab2272011-06-08 14:46:28 +000049}
50
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000051static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) {
tomhudson@google.comddab2272011-06-08 14:46:28 +000052 if (d < tol) {
53 return 1;
54 } else {
55 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
56 uint32_t count = SkMinScalar(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
57 return count;
58 }
59}
60
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000061uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) {
62 int distance = estimate_distance(points);
63 return estimate_pointCount(distance);
64}
65
66uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
67 int distance = estimate_distance(points);
68 return compute_pointCount(SkIntToScalar(distance), tol);
69}
70
71uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) {
72 SkScalar distance = compute_distance(points);
73 return estimate_pointCount(SkScalarRound(distance));
74}
75
76uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
77 SkScalar distance = compute_distance(points);
78 return compute_pointCount(distance, tol);
79}
80
tomhudson@google.comddab2272011-06-08 14:46:28 +000081// Curve from samplecode/SampleSlides.cpp
82static const int gXY[] = {
83 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
84};
85
86static const int gSawtooth[] = {
87 0, 0, 10, 10, 20, 20, 30, 10, 40, 0, 50, -10, 60, -20, 70, -10, 80, 0
88};
89
90static const int gOvalish[] = {
91 0, 0, 5, 15, 20, 20, 35, 15, 40, 0
92};
93
94static const int gSharpSawtooth[] = {
95 0, 0, 1, 10, 2, 0, 3, -10, 4, 0
96};
97
98// Curve crosses back over itself around 0,10
99static const int gRibbon[] = {
100 -4, 0, 4, 20, 0, 25, -4, 20, 4, 0
101};
102
103static bool one_d_pe(const int* array, const unsigned int count,
104 skiatest::Reporter* reporter) {
105 SkPoint path [3];
106 path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1]));
107 path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3]));
108 int numErrors = 0;
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000109 for (unsigned i = 4; i < count; i += 2) {
tomhudson@google.comddab2272011-06-08 14:46:28 +0000110 path[0] = path[1];
111 path[1] = path[2];
112 path[2] = SkPoint::Make(SkIntToScalar(array[i]),
113 SkIntToScalar(array[i+1]));
114 uint32_t computedCount =
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000115 quadraticPointCount_CC(path, SkIntToScalar(1));
tomhudson@google.comddab2272011-06-08 14:46:28 +0000116 uint32_t estimatedCount =
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000117 quadraticPointCount_EE(path, SkIntToScalar(1));
118 // Allow estimated to be high by a factor of two, but no less than
119 // the computed value.
120 bool isAccurate = (estimatedCount >= computedCount) &&
121 (estimatedCount <= 2 * computedCount);
122
123 if (!isAccurate) {
tomhudson@google.comddab2272011-06-08 14:46:28 +0000124 SkString errorDescription;
125 errorDescription.printf(
126 "Curve from %.2f %.2f through %.2f %.2f to %.2f %.2f "
127 "computes %d, estimates %d\n",
128 path[0].fX, path[0].fY, path[1].fX, path[1].fY,
129 path[2].fX, path[2].fY, computedCount, estimatedCount);
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000130 printf(errorDescription.c_str());
tomhudson@google.comddab2272011-06-08 14:46:28 +0000131 numErrors++;
132 reporter->reportFailed(errorDescription);
133 }
134 }
135
136 if (numErrors > 0)
137 printf("%d curve segments differ\n", numErrors);
138 return (numErrors == 0);
139}
140
141
142
143static void TestQuadPointCount(skiatest::Reporter* reporter) {
144 one_d_pe(gXY, SK_ARRAY_COUNT(gXY), reporter);
145 one_d_pe(gSawtooth, SK_ARRAY_COUNT(gSawtooth), reporter);
146 one_d_pe(gOvalish, SK_ARRAY_COUNT(gOvalish), reporter);
147 one_d_pe(gSharpSawtooth, SK_ARRAY_COUNT(gSharpSawtooth), reporter);
148 one_d_pe(gRibbon, SK_ARRAY_COUNT(gRibbon), reporter);
149}
150
151static void TestPathCoverage(skiatest::Reporter* reporter) {
152 TestQuadPointCount(reporter);
153
154}
155
156#include "TestClassDef.h"
157DEFINE_TESTCLASS("PathCoverage", PathCoverageTestClass, TestPathCoverage)