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