1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkSafe32.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkMathPriv.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPointPriv.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
17*c8dee2aaSAndroid Build Coastguard Worker #include <array>
18*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
19*c8dee2aaSAndroid Build Coastguard Worker
20*c8dee2aaSAndroid Build Coastguard Worker /*
21*c8dee2aaSAndroid Build Coastguard Worker Duplicates lots of code from gpu/src/GrPathUtils.cpp
22*c8dee2aaSAndroid Build Coastguard Worker It'd be nice not to do so, but that code's set up currently to only have
23*c8dee2aaSAndroid Build Coastguard Worker a single implementation.
24*c8dee2aaSAndroid Build Coastguard Worker */
25*c8dee2aaSAndroid Build Coastguard Worker
26*c8dee2aaSAndroid Build Coastguard Worker // Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily.
27*c8dee2aaSAndroid Build Coastguard Worker #define MAX_COEFF_SHIFT 6
28*c8dee2aaSAndroid Build Coastguard Worker static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT;
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker // max + 0.5 min has error [0.0, 0.12]
31*c8dee2aaSAndroid Build Coastguard Worker // max + 0.375 min has error [-.03, 0.07]
32*c8dee2aaSAndroid Build Coastguard Worker // 0.96043387 max + 0.397824735 min has error [-.06, +.05]
33*c8dee2aaSAndroid Build Coastguard Worker // For determining the maximum possible number of points to use in
34*c8dee2aaSAndroid Build Coastguard Worker // drawing a quadratic, we want to err on the high side.
cheap_distance(SkScalar dx,SkScalar dy)35*c8dee2aaSAndroid Build Coastguard Worker static inline int cheap_distance(SkScalar dx, SkScalar dy) {
36*c8dee2aaSAndroid Build Coastguard Worker int idx = SkAbs32(SkScalarRoundToInt(dx));
37*c8dee2aaSAndroid Build Coastguard Worker int idy = SkAbs32(SkScalarRoundToInt(dy));
38*c8dee2aaSAndroid Build Coastguard Worker if (idx > idy) {
39*c8dee2aaSAndroid Build Coastguard Worker idx += idy >> 1;
40*c8dee2aaSAndroid Build Coastguard Worker } else {
41*c8dee2aaSAndroid Build Coastguard Worker idx = idy + (idx >> 1);
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker return idx;
44*c8dee2aaSAndroid Build Coastguard Worker }
45*c8dee2aaSAndroid Build Coastguard Worker
estimate_distance(const SkPoint points[])46*c8dee2aaSAndroid Build Coastguard Worker static inline int estimate_distance(const SkPoint points[]) {
47*c8dee2aaSAndroid Build Coastguard Worker return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX,
48*c8dee2aaSAndroid Build Coastguard Worker points[1].fY * 2 - points[2].fY - points[0].fY);
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker
compute_distance(const SkPoint points[])51*c8dee2aaSAndroid Build Coastguard Worker static inline SkScalar compute_distance(const SkPoint points[]) {
52*c8dee2aaSAndroid Build Coastguard Worker return SkPointPriv::DistanceToLineSegmentBetween(points[1], points[0], points[2]);
53*c8dee2aaSAndroid Build Coastguard Worker }
54*c8dee2aaSAndroid Build Coastguard Worker
estimate_pointCount(int distance)55*c8dee2aaSAndroid Build Coastguard Worker static inline uint32_t estimate_pointCount(int distance) {
56*c8dee2aaSAndroid Build Coastguard Worker // Includes -2 bias because this estimator runs 4x high?
57*c8dee2aaSAndroid Build Coastguard Worker int shift = 30 - SkCLZ(distance);
58*c8dee2aaSAndroid Build Coastguard Worker // Clamp to zero if above subtraction went negative.
59*c8dee2aaSAndroid Build Coastguard Worker shift &= ~(shift>>31);
60*c8dee2aaSAndroid Build Coastguard Worker if (shift > MAX_COEFF_SHIFT) {
61*c8dee2aaSAndroid Build Coastguard Worker shift = MAX_COEFF_SHIFT;
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker return 1 << shift;
64*c8dee2aaSAndroid Build Coastguard Worker }
65*c8dee2aaSAndroid Build Coastguard Worker
compute_pointCount(SkScalar d,SkScalar tol)66*c8dee2aaSAndroid Build Coastguard Worker static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) {
67*c8dee2aaSAndroid Build Coastguard Worker if (d < tol) {
68*c8dee2aaSAndroid Build Coastguard Worker return 1;
69*c8dee2aaSAndroid Build Coastguard Worker } else {
70*c8dee2aaSAndroid Build Coastguard Worker int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
71*c8dee2aaSAndroid Build Coastguard Worker uint32_t count = std::min<uint32_t>(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
72*c8dee2aaSAndroid Build Coastguard Worker return count;
73*c8dee2aaSAndroid Build Coastguard Worker }
74*c8dee2aaSAndroid Build Coastguard Worker }
75*c8dee2aaSAndroid Build Coastguard Worker
quadraticPointCount_EE(const SkPoint points[])76*c8dee2aaSAndroid Build Coastguard Worker static uint32_t quadraticPointCount_EE(const SkPoint points[]) {
77*c8dee2aaSAndroid Build Coastguard Worker int distance = estimate_distance(points);
78*c8dee2aaSAndroid Build Coastguard Worker return estimate_pointCount(distance);
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker
quadraticPointCount_EC(const SkPoint points[],SkScalar tol)81*c8dee2aaSAndroid Build Coastguard Worker static uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
82*c8dee2aaSAndroid Build Coastguard Worker int distance = estimate_distance(points);
83*c8dee2aaSAndroid Build Coastguard Worker return compute_pointCount(SkIntToScalar(distance), tol);
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker
quadraticPointCount_CE(const SkPoint points[])86*c8dee2aaSAndroid Build Coastguard Worker static uint32_t quadraticPointCount_CE(const SkPoint points[]) {
87*c8dee2aaSAndroid Build Coastguard Worker SkScalar distance = compute_distance(points);
88*c8dee2aaSAndroid Build Coastguard Worker return estimate_pointCount(SkScalarRoundToInt(distance));
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker
quadraticPointCount_CC(const SkPoint points[],SkScalar tol)91*c8dee2aaSAndroid Build Coastguard Worker static uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
92*c8dee2aaSAndroid Build Coastguard Worker SkScalar distance = compute_distance(points);
93*c8dee2aaSAndroid Build Coastguard Worker return compute_pointCount(distance, tol);
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker
96*c8dee2aaSAndroid Build Coastguard Worker // Curve from samplecode/SampleSlides.cpp
97*c8dee2aaSAndroid Build Coastguard Worker static const int gXY[] = {
98*c8dee2aaSAndroid Build Coastguard Worker 4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
99*c8dee2aaSAndroid Build Coastguard Worker };
100*c8dee2aaSAndroid Build Coastguard Worker
101*c8dee2aaSAndroid Build Coastguard Worker static const int gSawtooth[] = {
102*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 10, 10, 20, 20, 30, 10, 40, 0, 50, -10, 60, -20, 70, -10, 80, 0
103*c8dee2aaSAndroid Build Coastguard Worker };
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker static const int gOvalish[] = {
106*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 5, 15, 20, 20, 35, 15, 40, 0
107*c8dee2aaSAndroid Build Coastguard Worker };
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker static const int gSharpSawtooth[] = {
110*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1, 10, 2, 0, 3, -10, 4, 0
111*c8dee2aaSAndroid Build Coastguard Worker };
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker // Curve crosses back over itself around 0,10
114*c8dee2aaSAndroid Build Coastguard Worker static const int gRibbon[] = {
115*c8dee2aaSAndroid Build Coastguard Worker -4, 0, 4, 20, 0, 25, -4, 20, 4, 0
116*c8dee2aaSAndroid Build Coastguard Worker };
117*c8dee2aaSAndroid Build Coastguard Worker
one_d_pe(const int * array,const unsigned int count,skiatest::Reporter * reporter)118*c8dee2aaSAndroid Build Coastguard Worker static bool one_d_pe(const int* array, const unsigned int count,
119*c8dee2aaSAndroid Build Coastguard Worker skiatest::Reporter* reporter) {
120*c8dee2aaSAndroid Build Coastguard Worker SkPoint path [3];
121*c8dee2aaSAndroid Build Coastguard Worker path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1]));
122*c8dee2aaSAndroid Build Coastguard Worker path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3]));
123*c8dee2aaSAndroid Build Coastguard Worker int numErrors = 0;
124*c8dee2aaSAndroid Build Coastguard Worker for (unsigned i = 4; i < count; i += 2) {
125*c8dee2aaSAndroid Build Coastguard Worker path[0] = path[1];
126*c8dee2aaSAndroid Build Coastguard Worker path[1] = path[2];
127*c8dee2aaSAndroid Build Coastguard Worker path[2] = SkPoint::Make(SkIntToScalar(array[i]),
128*c8dee2aaSAndroid Build Coastguard Worker SkIntToScalar(array[i+1]));
129*c8dee2aaSAndroid Build Coastguard Worker uint32_t computedCount =
130*c8dee2aaSAndroid Build Coastguard Worker quadraticPointCount_CC(path, SkIntToScalar(1));
131*c8dee2aaSAndroid Build Coastguard Worker uint32_t estimatedCount =
132*c8dee2aaSAndroid Build Coastguard Worker quadraticPointCount_EE(path);
133*c8dee2aaSAndroid Build Coastguard Worker
134*c8dee2aaSAndroid Build Coastguard Worker if ((false)) { // avoid bit rot, suppress warning
135*c8dee2aaSAndroid Build Coastguard Worker computedCount = quadraticPointCount_EC(path, SkIntToScalar(1));
136*c8dee2aaSAndroid Build Coastguard Worker estimatedCount = quadraticPointCount_CE(path);
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker // Allow estimated to be high by a factor of two, but no less than
139*c8dee2aaSAndroid Build Coastguard Worker // the computed value.
140*c8dee2aaSAndroid Build Coastguard Worker bool isAccurate = (estimatedCount >= computedCount) &&
141*c8dee2aaSAndroid Build Coastguard Worker (estimatedCount <= 2 * computedCount);
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker if (!isAccurate) {
144*c8dee2aaSAndroid Build Coastguard Worker ERRORF(reporter, "Curve from %.2f %.2f through %.2f %.2f to "
145*c8dee2aaSAndroid Build Coastguard Worker "%.2f %.2f computes %u, estimates %u\n",
146*c8dee2aaSAndroid Build Coastguard Worker path[0].fX, path[0].fY, path[1].fX, path[1].fY,
147*c8dee2aaSAndroid Build Coastguard Worker path[2].fX, path[2].fY, computedCount, estimatedCount);
148*c8dee2aaSAndroid Build Coastguard Worker numErrors++;
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker return (numErrors == 0);
153*c8dee2aaSAndroid Build Coastguard Worker }
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker
156*c8dee2aaSAndroid Build Coastguard Worker
TestQuadPointCount(skiatest::Reporter * reporter)157*c8dee2aaSAndroid Build Coastguard Worker static void TestQuadPointCount(skiatest::Reporter* reporter) {
158*c8dee2aaSAndroid Build Coastguard Worker one_d_pe(gXY, std::size(gXY), reporter);
159*c8dee2aaSAndroid Build Coastguard Worker one_d_pe(gSawtooth, std::size(gSawtooth), reporter);
160*c8dee2aaSAndroid Build Coastguard Worker one_d_pe(gOvalish, std::size(gOvalish), reporter);
161*c8dee2aaSAndroid Build Coastguard Worker one_d_pe(gSharpSawtooth, std::size(gSharpSawtooth), reporter);
162*c8dee2aaSAndroid Build Coastguard Worker one_d_pe(gRibbon, std::size(gRibbon), reporter);
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(PathCoverage,reporter)165*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(PathCoverage, reporter) {
166*c8dee2aaSAndroid Build Coastguard Worker TestQuadPointCount(reporter);
167*c8dee2aaSAndroid Build Coastguard Worker
168*c8dee2aaSAndroid Build Coastguard Worker }
169