blob: d9683c120f14485c55293fd72a506f754f0ae3dd [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
tomhudson@google.comddab2272011-06-08 14:46:28 +00008#include "SkPoint.h"
9#include "SkScalar.h"
10#include "Test.h"
11
12/*
13 Duplicates lots of code from gpu/src/GrPathUtils.cpp
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000014 It'd be nice not to do so, but that code's set up currently to only have
15 a single implementation.
tomhudson@google.comddab2272011-06-08 14:46:28 +000016*/
17
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000018// Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily.
tomhudson@google.comddab2272011-06-08 14:46:28 +000019#define MAX_COEFF_SHIFT 6
20static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT;
21
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000022// max + 0.5 min has error [0.0, 0.12]
23// max + 0.375 min has error [-.03, 0.07]
24// 0.96043387 max + 0.397824735 min has error [-.06, +.05]
25// For determining the maximum possible number of points to use in
26// drawing a quadratic, we want to err on the high side.
tomhudson@google.comddab2272011-06-08 14:46:28 +000027static inline int cheap_distance(SkScalar dx, SkScalar dy) {
28 int idx = SkAbs32(SkScalarRound(dx));
29 int idy = SkAbs32(SkScalarRound(dy));
30 if (idx > idy) {
31 idx += idy >> 1;
32 } else {
33 idx = idy + (idx >> 1);
34 }
35 return idx;
36}
37
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000038static inline int estimate_distance(const SkPoint points[]) {
39 return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX,
40 points[1].fY * 2 - points[2].fY - points[0].fY);
tomhudson@google.comddab2272011-06-08 14:46:28 +000041}
42
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000043static inline SkScalar compute_distance(const SkPoint points[]) {
44 return points[1].distanceToLineSegmentBetween(points[0], points[2]);
45}
tomhudson@google.comddab2272011-06-08 14:46:28 +000046
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000047static inline uint32_t estimate_pointCount(int distance) {
48 // Includes -2 bias because this estimator runs 4x high?
49 int shift = 30 - SkCLZ(distance);
50 // Clamp to zero if above subtraction went negative.
51 shift &= ~(shift>>31);
tomhudson@google.comddab2272011-06-08 14:46:28 +000052 if (shift > MAX_COEFF_SHIFT) {
53 shift = MAX_COEFF_SHIFT;
54 }
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000055 return 1 << shift;
tomhudson@google.comddab2272011-06-08 14:46:28 +000056}
57
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000058static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) {
tomhudson@google.comddab2272011-06-08 14:46:28 +000059 if (d < tol) {
60 return 1;
61 } else {
62 int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
63 uint32_t count = SkMinScalar(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
64 return count;
65 }
66}
67
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000068uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) {
69 int distance = estimate_distance(points);
70 return estimate_pointCount(distance);
71}
72
73uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
74 int distance = estimate_distance(points);
75 return compute_pointCount(SkIntToScalar(distance), tol);
76}
77
78uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) {
79 SkScalar distance = compute_distance(points);
80 return estimate_pointCount(SkScalarRound(distance));
81}
82
83uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
84 SkScalar distance = compute_distance(points);
85 return compute_pointCount(distance, tol);
86}
87
tomhudson@google.comddab2272011-06-08 14:46:28 +000088// Curve from samplecode/SampleSlides.cpp
89static const int gXY[] = {
90 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
91};
92
93static const int gSawtooth[] = {
94 0, 0, 10, 10, 20, 20, 30, 10, 40, 0, 50, -10, 60, -20, 70, -10, 80, 0
95};
96
97static const int gOvalish[] = {
98 0, 0, 5, 15, 20, 20, 35, 15, 40, 0
99};
100
101static const int gSharpSawtooth[] = {
102 0, 0, 1, 10, 2, 0, 3, -10, 4, 0
103};
104
105// Curve crosses back over itself around 0,10
106static const int gRibbon[] = {
107 -4, 0, 4, 20, 0, 25, -4, 20, 4, 0
108};
109
110static bool one_d_pe(const int* array, const unsigned int count,
111 skiatest::Reporter* reporter) {
112 SkPoint path [3];
113 path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1]));
114 path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3]));
115 int numErrors = 0;
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000116 for (unsigned i = 4; i < count; i += 2) {
tomhudson@google.comddab2272011-06-08 14:46:28 +0000117 path[0] = path[1];
118 path[1] = path[2];
119 path[2] = SkPoint::Make(SkIntToScalar(array[i]),
120 SkIntToScalar(array[i+1]));
121 uint32_t computedCount =
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000122 quadraticPointCount_CC(path, SkIntToScalar(1));
tomhudson@google.comddab2272011-06-08 14:46:28 +0000123 uint32_t estimatedCount =
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000124 quadraticPointCount_EE(path, SkIntToScalar(1));
125 // Allow estimated to be high by a factor of two, but no less than
126 // the computed value.
127 bool isAccurate = (estimatedCount >= computedCount) &&
128 (estimatedCount <= 2 * computedCount);
129
130 if (!isAccurate) {
tomhudson@google.comddab2272011-06-08 14:46:28 +0000131 SkString errorDescription;
132 errorDescription.printf(
133 "Curve from %.2f %.2f through %.2f %.2f to %.2f %.2f "
134 "computes %d, estimates %d\n",
135 path[0].fX, path[0].fY, path[1].fX, path[1].fY,
136 path[2].fX, path[2].fY, computedCount, estimatedCount);
tomhudson@google.comddab2272011-06-08 14:46:28 +0000137 numErrors++;
138 reporter->reportFailed(errorDescription);
139 }
140 }
141
tomhudson@google.comddab2272011-06-08 14:46:28 +0000142 return (numErrors == 0);
143}
144
145
146
147static void TestQuadPointCount(skiatest::Reporter* reporter) {
148 one_d_pe(gXY, SK_ARRAY_COUNT(gXY), reporter);
149 one_d_pe(gSawtooth, SK_ARRAY_COUNT(gSawtooth), reporter);
150 one_d_pe(gOvalish, SK_ARRAY_COUNT(gOvalish), reporter);
151 one_d_pe(gSharpSawtooth, SK_ARRAY_COUNT(gSharpSawtooth), reporter);
152 one_d_pe(gRibbon, SK_ARRAY_COUNT(gRibbon), reporter);
153}
154
155static void TestPathCoverage(skiatest::Reporter* reporter) {
156 TestQuadPointCount(reporter);
157
158}
159
160#include "TestClassDef.h"
161DEFINE_TESTCLASS("PathCoverage", PathCoverageTestClass, TestPathCoverage)