Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/gpu/tessellate/WangsFormula.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2020 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
 */
7
8
#ifndef skgpu_tessellate_WangsFormula_DEFINED
9
#define skgpu_tessellate_WangsFormula_DEFINED
10
11
#include "include/core/SkM44.h"
12
#include "include/core/SkMatrix.h"
13
#include "include/core/SkPoint.h"
14
#include "include/core/SkTypes.h"
15
#include "src/base/SkFloatBits.h"
16
#include "src/base/SkUtils.h"
17
#include "src/base/SkVx.h"
18
19
#include <math.h>
20
#include <algorithm>
21
#include <cstdint>
22
#include <limits>
23
24
#define AI [[maybe_unused]] SK_ALWAYS_INLINE
25
26
// Wang's formula gives the minimum number of evenly spaced (in the parametric sense) line segments
27
// that a bezier curve must be chopped into in order to guarantee all lines stay within a distance
28
// of "1/precision" pixels from the true curve. Its definition for a bezier curve of degree "n" is
29
// as follows:
30
//
31
//     maxLength = max([length(p[i+2] - 2p[i+1] + p[i]) for (0 <= i <= n-2)])
32
//     numParametricSegments = sqrt(maxLength * precision * n*(n - 1)/8)
33
//
34
// (Goldman, Ron. (2003). 5.6.3 Wang's Formula. "Pyramid Algorithms: A Dynamic Programming Approach
35
// to Curves and Surfaces for Geometric Modeling". Morgan Kaufmann Publishers.)
36
namespace skgpu::wangs_formula {
37
38
// Returns the value by which to multiply length in Wang's formula. (See above.)
39
0
template<int Degree> constexpr float length_term(float precision) {
40
0
    return (Degree * (Degree - 1) / 8.f) * precision;
41
0
}
42
1.90M
template<int Degree> constexpr float length_term_p2(float precision) {
43
1.90M
    return ((Degree * Degree) * ((Degree - 1) * (Degree - 1)) / 64.f) * (precision * precision);
44
1.90M
}
float skgpu::wangs_formula::length_term_p2<3>(float)
Line
Count
Source
42
96.3k
template<int Degree> constexpr float length_term_p2(float precision) {
43
96.3k
    return ((Degree * Degree) * ((Degree - 1) * (Degree - 1)) / 64.f) * (precision * precision);
44
96.3k
}
float skgpu::wangs_formula::length_term_p2<2>(float)
Line
Count
Source
42
1.81M
template<int Degree> constexpr float length_term_p2(float precision) {
43
1.81M
    return ((Degree * Degree) * ((Degree - 1) * (Degree - 1)) / 64.f) * (precision * precision);
44
1.81M
}
45
46
0
AI float root4(float x) {
47
0
    return sqrtf(sqrtf(x));
48
0
}
49
50
// For finite positive x > 1, return ceil(log2(x)) otherwise, return 0.
51
// For +/- NaN return 0.
52
// For +infinity return 128.
53
// For -infinity return 0.
54
//
55
//     nextlog2((-inf..1]) -> 0
56
//     nextlog2((1..2]) -> 1
57
//     nextlog2((2..4]) -> 2
58
//     nextlog2((4..8]) -> 3
59
//     ...
60
1.90M
AI int nextlog2(float x) {
61
1.90M
    if (x <= 1) {
62
206k
        return 0;
63
206k
    }
64
65
1.70M
    uint32_t bits = SkFloat2Bits(x);
66
1.70M
    static constexpr uint32_t kDigitsAfterBinaryPoint = std::numeric_limits<float>::digits - 1;
67
68
    // The constant is a significand of all 1s -- 0b0'00000000'111'1111111111'111111111. So, if
69
    // the significand of x is all 0s (and therefore an integer power of two) this will not
70
    // increment the exponent, but if it is just one ULP above the power of two the carry will
71
    // ripple into the exponent incrementing the exponent by 1.
72
1.70M
    bits += (1u << kDigitsAfterBinaryPoint) - 1u;
73
74
    // Shift the exponent down, and adjust it by the exponent offset so that 2^0 is really 0 instead
75
    // of 127. Remember that 1 was added to the exponent, if x is NaN, then the exponent will
76
    // carry a 1 into the sign bit during the addition to bits. Be sure to strip off the sign bit.
77
    // In addition, infinity is an exponent of all 1's, and a significand of all 0, so
78
    // the exponent is not affected during the addition to bits, and the exponent remains all 1's.
79
1.70M
    const int exp = ((bits >> kDigitsAfterBinaryPoint) & 0b1111'1111) - 127;
80
81
    // Return 0 for x <= 1.
82
1.70M
    return exp > 0 ? exp : 0;
83
1.90M
}
84
85
// Returns nextlog2(sqrt(x)):
86
//
87
//   log2(sqrt(x)) == log2(x^(1/2)) == log2(x)/2 == log2(x)/log2(4) == log4(x)
88
//
89
0
AI int nextlog4(float x) {
90
0
    return (nextlog2(x) + 1) >> 1;
91
0
}
92
93
// Returns nextlog2(sqrt(sqrt(x))):
94
//
95
//   log2(sqrt(sqrt(x))) == log2(x^(1/4)) == log2(x)/4 == log2(x)/log2(16) == log16(x)
96
//
97
1.90M
AI int nextlog16(float x) {
98
1.90M
    return (nextlog2(x) + 3) >> 2;
99
1.90M
}
100
101
// Represents the upper-left 2x2 matrix of an affine transform for applying to vectors:
102
//
103
//     VectorXform(p1 - p0) == M * float3(p1, 1) - M * float3(p0, 1)
104
//
105
class VectorXform {
106
public:
107
1.90M
    AI VectorXform() : fC0{1.0f, 0.f}, fC1{0.f, 1.f} {}
108
0
    AI explicit VectorXform(const SkMatrix& m) { *this = m; }
109
0
    AI explicit VectorXform(const SkM44& m) { *this = m; }
110
111
0
    AI VectorXform& operator=(const SkMatrix& m) {
112
0
        SkASSERT(!m.hasPerspective());
113
0
        fC0 = {m.rc(0,0), m.rc(1,0)};
114
0
        fC1 = {m.rc(0,1), m.rc(1,1)};
115
0
        return *this;
116
0
    }
Unexecuted instantiation: skgpu::wangs_formula::VectorXform::operator=(SkMatrix const&)
Unexecuted instantiation: skgpu::wangs_formula::VectorXform::operator=(SkMatrix const&)
117
0
    AI VectorXform& operator=(const SkM44& m) {
118
0
        SkASSERT(m.rc(3,0) == 0.f && m.rc(3,1) == 0.f && m.rc(3,2) == 0.f && m.rc(3,3) == 1.f);
119
0
        fC0 = {m.rc(0,0), m.rc(1,0)};
120
0
        fC1 = {m.rc(0,1), m.rc(1,1)};
121
0
        return *this;
122
0
    }
Unexecuted instantiation: skgpu::wangs_formula::VectorXform::operator=(SkM44 const&)
Unexecuted instantiation: skgpu::wangs_formula::VectorXform::operator=(SkM44 const&)
123
1.81M
    AI skvx::float2 operator()(skvx::float2 vector) const {
124
1.81M
        return fC0 * vector.x() + fC1 * vector.y();
125
1.81M
    }
126
96.3k
    AI skvx::float4 operator()(skvx::float4 vectors) const {
127
96.3k
        return join(fC0 * vectors.x() + fC1 * vectors.y(),
128
96.3k
                    fC0 * vectors.z() + fC1 * vectors.w());
129
96.3k
    }
130
private:
131
    // First and second columns of 2x2 matrix
132
    skvx::float2 fC0;
133
    skvx::float2 fC1;
134
};
135
136
// Returns Wang's formula, raised to the 4th power, specialized for a quadratic curve.
137
AI float quadratic_p4(float precision,
138
                      skvx::float2 p0, skvx::float2 p1, skvx::float2 p2,
139
1.81M
                      const VectorXform& vectorXform = VectorXform()) {
140
1.81M
    skvx::float2 v = -2*p1 + p0 + p2;
141
1.81M
    v = vectorXform(v);
142
1.81M
    skvx::float2 vv = v*v;
143
1.81M
    return (vv[0] + vv[1]) * length_term_p2<2>(precision);
144
1.81M
}
145
AI float quadratic_p4(float precision,
146
                      const SkPoint pts[],
147
1.81M
                      const VectorXform& vectorXform = VectorXform()) {
148
1.81M
    return quadratic_p4(precision,
149
1.81M
                        sk_bit_cast<skvx::float2>(pts[0]),
150
1.81M
                        sk_bit_cast<skvx::float2>(pts[1]),
151
1.81M
                        sk_bit_cast<skvx::float2>(pts[2]),
152
1.81M
                        vectorXform);
153
1.81M
}
154
155
// Returns Wang's formula specialized for a quadratic curve.
156
AI float quadratic(float precision,
157
                   const SkPoint pts[],
158
0
                   const VectorXform& vectorXform = VectorXform()) {
159
0
    return root4(quadratic_p4(precision, pts, vectorXform));
160
0
}
161
162
// Returns the log2 value of Wang's formula specialized for a quadratic curve, rounded up to the
163
// next int.
164
AI int quadratic_log2(float precision,
165
                      const SkPoint pts[],
166
1.81M
                      const VectorXform& vectorXform = VectorXform()) {
167
    // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
168
1.81M
    return nextlog16(quadratic_p4(precision, pts, vectorXform));
169
1.81M
}
170
171
// Returns Wang's formula, raised to the 4th power, specialized for a cubic curve.
172
AI float cubic_p4(float precision,
173
                  skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, skvx::float2 p3,
174
96.3k
                  const VectorXform& vectorXform = VectorXform()) {
175
96.3k
    skvx::float4 p01{p0, p1};
176
96.3k
    skvx::float4 p12{p1, p2};
177
96.3k
    skvx::float4 p23{p2, p3};
178
96.3k
    skvx::float4 v = -2*p12 + p01 + p23;
179
96.3k
    v = vectorXform(v);
180
96.3k
    skvx::float4 vv = v*v;
181
96.3k
    return std::max(vv[0] + vv[1], vv[2] + vv[3]) * length_term_p2<3>(precision);
182
96.3k
}
183
AI float cubic_p4(float precision,
184
                  const SkPoint pts[],
185
96.3k
                  const VectorXform& vectorXform = VectorXform()) {
186
96.3k
    return cubic_p4(precision,
187
96.3k
                    sk_bit_cast<skvx::float2>(pts[0]),
188
96.3k
                    sk_bit_cast<skvx::float2>(pts[1]),
189
96.3k
                    sk_bit_cast<skvx::float2>(pts[2]),
190
96.3k
                    sk_bit_cast<skvx::float2>(pts[3]),
191
96.3k
                    vectorXform);
192
96.3k
}
193
194
// Returns Wang's formula specialized for a cubic curve.
195
AI float cubic(float precision,
196
               const SkPoint pts[],
197
0
               const VectorXform& vectorXform = VectorXform()) {
198
0
    return root4(cubic_p4(precision, pts, vectorXform));
199
0
}
200
201
// Returns the log2 value of Wang's formula specialized for a cubic curve, rounded up to the next
202
// int.
203
AI int cubic_log2(float precision,
204
                  const SkPoint pts[],
205
96.3k
                  const VectorXform& vectorXform = VectorXform()) {
206
    // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
207
96.3k
    return nextlog16(cubic_p4(precision, pts, vectorXform));
208
96.3k
}
209
210
// Returns the maximum number of line segments a cubic with the given device-space bounding box size
211
// would ever need to be divided into, raised to the 4th power. This is simply a special case of the
212
// cubic formula where we maximize its value by placing control points on specific corners of the
213
// bounding box.
214
0
AI float worst_case_cubic_p4(float precision, float devWidth, float devHeight) {
215
0
    float kk = length_term_p2<3>(precision);
216
0
    return 4*kk * (devWidth * devWidth + devHeight * devHeight);
217
0
}
218
219
// Returns the maximum number of line segments a cubic with the given device-space bounding box size
220
// would ever need to be divided into.
221
0
AI float worst_case_cubic(float precision, float devWidth, float devHeight) {
222
0
    return root4(worst_case_cubic_p4(precision, devWidth, devHeight));
223
0
}
224
225
// Returns the maximum log2 number of line segments a cubic with the given device-space bounding box
226
// size would ever need to be divided into.
227
0
AI int worst_case_cubic_log2(float precision, float devWidth, float devHeight) {
228
0
    // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
229
0
    return nextlog16(worst_case_cubic_p4(precision, devWidth, devHeight));
230
0
}
231
232
// Returns Wang's formula specialized for a conic curve, raised to the second power.
233
// Input points should be in projected space.
234
//
235
// This is not actually due to Wang, but is an analogue from (Theorem 3, corollary 1):
236
//   J. Zheng, T. Sederberg. "Estimating Tessellation Parameter Intervals for
237
//   Rational Curves and Surfaces." ACM Transactions on Graphics 19(1). 2000.
238
AI float conic_p2(float precision,
239
                  skvx::float2 p0, skvx::float2 p1, skvx::float2 p2,
240
                  float w,
241
0
                  const VectorXform& vectorXform = VectorXform()) {
242
0
    p0 = vectorXform(p0);
243
0
    p1 = vectorXform(p1);
244
0
    p2 = vectorXform(p2);
245
246
    // Compute center of bounding box in projected space
247
0
    const skvx::float2 C = 0.5f * (min(min(p0, p1), p2) + max(max(p0, p1), p2));
248
249
    // Translate by -C. This improves translation-invariance of the formula,
250
    // see Sec. 3.3 of cited paper
251
0
    p0 -= C;
252
0
    p1 -= C;
253
0
    p2 -= C;
254
255
    // Compute max length
256
0
    const float max_len = sqrtf(std::max(dot(p0, p0), std::max(dot(p1, p1), dot(p2, p2))));
257
258
259
    // Compute forward differences
260
0
    const skvx::float2 dp = -2*w*p1 + p0 + p2;
261
0
    const float dw = fabsf(-2 * w + 2);
262
263
    // Compute numerator and denominator for parametric step size of linearization. Here, the
264
    // epsilon referenced from the cited paper is 1/precision.
265
0
    const float rp_minus_1 = std::max(0.f, max_len * precision - 1);
266
0
    const float numer = sqrtf(dot(dp, dp)) * precision + rp_minus_1 * dw;
267
0
    const float denom = 4 * std::min(w, 1.f);
268
269
    // Number of segments = sqrt(numer / denom).
270
    // This assumes parametric interval of curve being linearized is [t0,t1] = [0, 1].
271
    // If not, the number of segments is (tmax - tmin) / sqrt(denom / numer).
272
0
    return numer / denom;
273
0
}
274
AI float conic_p2(float precision,
275
                  const SkPoint pts[],
276
                  float w,
277
0
                  const VectorXform& vectorXform = VectorXform()) {
278
0
    return conic_p2(precision,
279
0
                    sk_bit_cast<skvx::float2>(pts[0]),
280
0
                    sk_bit_cast<skvx::float2>(pts[1]),
281
0
                    sk_bit_cast<skvx::float2>(pts[2]),
282
0
                    w,
283
0
                    vectorXform);
284
0
}
285
286
// Returns the value of Wang's formula specialized for a conic curve.
287
AI float conic(float tolerance,
288
               const SkPoint pts[],
289
               float w,
290
0
               const VectorXform& vectorXform = VectorXform()) {
291
0
    return sqrtf(conic_p2(tolerance, pts, w, vectorXform));
292
0
}
293
294
// Returns the log2 value of Wang's formula specialized for a conic curve, rounded up to the next
295
// int.
296
AI int conic_log2(float tolerance,
297
                  const SkPoint pts[],
298
                  float w,
299
0
                  const VectorXform& vectorXform = VectorXform()) {
300
0
    // nextlog4(x) == ceil(log2(sqrt(x)))
301
0
    return nextlog4(conic_p2(tolerance, pts, w, vectorXform));
302
0
}
303
304
}  // namespace skgpu::wangs_formula
305
306
#undef AI
307
308
#endif  // skgpu_tessellate_WangsFormula_DEFINED