blob: c133e9ccccc291cfb84781463dfb8adff63b6017 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
8#include "Test.h"
9#include "TestClassDef.h"
tomhudson@google.com889bd8b2011-09-27 17:38:17 +000010#include "SkMath.h"
tomhudson@google.comddab2272011-06-08 14:46:28 +000011#include "SkPoint.h"
12#include "SkScalar.h"
tomhudson@google.comddab2272011-06-08 14:46:28 +000013
14/*
15 Duplicates lots of code from gpu/src/GrPathUtils.cpp
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000016 It'd be nice not to do so, but that code's set up currently to only have
17 a single implementation.
tomhudson@google.comddab2272011-06-08 14:46:28 +000018*/
19
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000020// Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily.
tomhudson@google.comddab2272011-06-08 14:46:28 +000021#define MAX_COEFF_SHIFT 6
22static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT;
23
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000024// max + 0.5 min has error [0.0, 0.12]
25// max + 0.375 min has error [-.03, 0.07]
26// 0.96043387 max + 0.397824735 min has error [-.06, +.05]
27// For determining the maximum possible number of points to use in
28// drawing a quadratic, we want to err on the high side.
tomhudson@google.comddab2272011-06-08 14:46:28 +000029static inline int cheap_distance(SkScalar dx, SkScalar dy) {
reed@google.come1ca7052013-12-17 19:22:07 +000030 int idx = SkAbs32(SkScalarRoundToInt(dx));
31 int idy = SkAbs32(SkScalarRoundToInt(dy));
tomhudson@google.comddab2272011-06-08 14:46:28 +000032 if (idx > idy) {
33 idx += idy >> 1;
34 } else {
35 idx = idy + (idx >> 1);
36 }
37 return idx;
38}
39
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000040static inline int estimate_distance(const SkPoint points[]) {
41 return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX,
42 points[1].fY * 2 - points[2].fY - points[0].fY);
tomhudson@google.comddab2272011-06-08 14:46:28 +000043}
44
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000045static inline SkScalar compute_distance(const SkPoint points[]) {
46 return points[1].distanceToLineSegmentBetween(points[0], points[2]);
47}
tomhudson@google.comddab2272011-06-08 14:46:28 +000048
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000049static inline uint32_t estimate_pointCount(int distance) {
50 // Includes -2 bias because this estimator runs 4x high?
51 int shift = 30 - SkCLZ(distance);
52 // Clamp to zero if above subtraction went negative.
53 shift &= ~(shift>>31);
tomhudson@google.comddab2272011-06-08 14:46:28 +000054 if (shift > MAX_COEFF_SHIFT) {
55 shift = MAX_COEFF_SHIFT;
56 }
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000057 return 1 << shift;
tomhudson@google.comddab2272011-06-08 14:46:28 +000058}
59
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000060static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) {
tomhudson@google.comddab2272011-06-08 14:46:28 +000061 if (d < tol) {
62 return 1;
63 } else {
robertphillips@google.com6853e802012-04-16 15:50:18 +000064 int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
65 uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
tomhudson@google.comddab2272011-06-08 14:46:28 +000066 return count;
67 }
68}
69
sugoi@google.com54f0d1b2013-02-27 19:17:41 +000070static uint32_t quadraticPointCount_EE(const SkPoint points[]) {
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000071 int distance = estimate_distance(points);
72 return estimate_pointCount(distance);
73}
74
caryclark@google.com42639cd2012-06-06 12:03:39 +000075static uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000076 int distance = estimate_distance(points);
77 return compute_pointCount(SkIntToScalar(distance), tol);
78}
79
sugoi@google.com54f0d1b2013-02-27 19:17:41 +000080static uint32_t quadraticPointCount_CE(const SkPoint points[]) {
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000081 SkScalar distance = compute_distance(points);
reed@google.come1ca7052013-12-17 19:22:07 +000082 return estimate_pointCount(SkScalarRoundToInt(distance));
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000083}
84
caryclark@google.com42639cd2012-06-06 12:03:39 +000085static uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
tomhudson@google.comfc1539a2011-06-24 15:43:24 +000086 SkScalar distance = compute_distance(points);
87 return compute_pointCount(distance, tol);
88}
89
tomhudson@google.comddab2272011-06-08 14:46:28 +000090// Curve from samplecode/SampleSlides.cpp
91static const int gXY[] = {
92 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
93};
94
95static const int gSawtooth[] = {
96 0, 0, 10, 10, 20, 20, 30, 10, 40, 0, 50, -10, 60, -20, 70, -10, 80, 0
97};
98
99static const int gOvalish[] = {
100 0, 0, 5, 15, 20, 20, 35, 15, 40, 0
101};
102
103static const int gSharpSawtooth[] = {
104 0, 0, 1, 10, 2, 0, 3, -10, 4, 0
105};
106
107// Curve crosses back over itself around 0,10
108static const int gRibbon[] = {
109 -4, 0, 4, 20, 0, 25, -4, 20, 4, 0
110};
111
112static bool one_d_pe(const int* array, const unsigned int count,
113 skiatest::Reporter* reporter) {
114 SkPoint path [3];
115 path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1]));
116 path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3]));
117 int numErrors = 0;
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000118 for (unsigned i = 4; i < count; i += 2) {
tomhudson@google.comddab2272011-06-08 14:46:28 +0000119 path[0] = path[1];
120 path[1] = path[2];
121 path[2] = SkPoint::Make(SkIntToScalar(array[i]),
122 SkIntToScalar(array[i+1]));
123 uint32_t computedCount =
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000124 quadraticPointCount_CC(path, SkIntToScalar(1));
tomhudson@google.comddab2272011-06-08 14:46:28 +0000125 uint32_t estimatedCount =
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000126 quadraticPointCount_EE(path);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000127
caryclark@google.com42639cd2012-06-06 12:03:39 +0000128 if (false) { // avoid bit rot, suppress warning
129 computedCount =
130 quadraticPointCount_EC(path, SkIntToScalar(1));
131 estimatedCount =
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000132 quadraticPointCount_CE(path);
caryclark@google.com42639cd2012-06-06 12:03:39 +0000133 }
tomhudson@google.comfc1539a2011-06-24 15:43:24 +0000134 // Allow estimated to be high by a factor of two, but no less than
135 // the computed value.
136 bool isAccurate = (estimatedCount >= computedCount) &&
137 (estimatedCount <= 2 * computedCount);
138
139 if (!isAccurate) {
halcanary@google.coma9325fa2014-01-10 14:58:10 +0000140 ERRORF(reporter, "Curve from %.2f %.2f through %.2f %.2f to "
141 "%.2f %.2f computes %d, estimates %d\n",
142 path[0].fX, path[0].fY, path[1].fX, path[1].fY,
143 path[2].fX, path[2].fY, computedCount, estimatedCount);
tomhudson@google.comddab2272011-06-08 14:46:28 +0000144 numErrors++;
tomhudson@google.comddab2272011-06-08 14:46:28 +0000145 }
146 }
147
tomhudson@google.comddab2272011-06-08 14:46:28 +0000148 return (numErrors == 0);
149}
150
151
152
153static void TestQuadPointCount(skiatest::Reporter* reporter) {
154 one_d_pe(gXY, SK_ARRAY_COUNT(gXY), reporter);
155 one_d_pe(gSawtooth, SK_ARRAY_COUNT(gSawtooth), reporter);
156 one_d_pe(gOvalish, SK_ARRAY_COUNT(gOvalish), reporter);
157 one_d_pe(gSharpSawtooth, SK_ARRAY_COUNT(gSharpSawtooth), reporter);
158 one_d_pe(gRibbon, SK_ARRAY_COUNT(gRibbon), reporter);
159}
160
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000161DEF_TEST(PathCoverage, reporter) {
tomhudson@google.comddab2272011-06-08 14:46:28 +0000162 TestQuadPointCount(reporter);
163
164}