xref: /aosp_15_r20/external/skia/tests/PathCoverageTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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