Flutter Engine
The Flutter Engine
WangsFormula.h
Go to the documentation of this file.
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"
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.)
37
38// Returns the value by which to multiply length in Wang's formula. (See above.)
39template<int Degree> constexpr float length_term(float precision) {
40 return (Degree * (Degree - 1) / 8.f) * precision;
41}
42template<int Degree> constexpr float length_term_p2(float precision) {
43 return ((Degree * Degree) * ((Degree - 1) * (Degree - 1)) / 64.f) * (precision * precision);
44}
45
46AI float root4(float x) {
47 return sqrtf(sqrtf(x));
48}
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// ...
60AI int nextlog2(float x) {
61 if (x <= 1) {
62 return 0;
63 }
64
65 uint32_t bits = SkFloat2Bits(x);
66 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 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 const int exp = ((bits >> kDigitsAfterBinaryPoint) & 0b1111'1111) - 127;
80
81 // Return 0 for x <= 1.
82 return exp > 0 ? exp : 0;
83}
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//
89AI int nextlog4(float x) {
90 return (nextlog2(x) + 1) >> 1;
91}
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//
97AI int nextlog16(float x) {
98 return (nextlog2(x) + 3) >> 2;
99}
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//
106public:
107 AI VectorXform() : fC0{1.0f, 0.f}, fC1{0.f, 1.f} {}
108 AI explicit VectorXform(const SkMatrix& m) { *this = m; }
109 AI explicit VectorXform(const SkM44& m) { *this = m; }
110
112 SkASSERT(!m.hasPerspective());
113 fC0 = {m.rc(0,0), m.rc(1,0)};
114 fC1 = {m.rc(0,1), m.rc(1,1)};
115 return *this;
116 }
118 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 fC0 = {m.rc(0,0), m.rc(1,0)};
120 fC1 = {m.rc(0,1), m.rc(1,1)};
121 return *this;
122 }
124 return fC0 * vector.x() + fC1 * vector.y();
125 }
127 return join(fC0 * vectors.x() + fC1 * vectors.y(),
128 fC0 * vectors.z() + fC1 * vectors.w());
129 }
130private:
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.
137AI float quadratic_p4(float precision,
139 const VectorXform& vectorXform = VectorXform()) {
140 skvx::float2 v = -2*p1 + p0 + p2;
141 v = vectorXform(v);
142 skvx::float2 vv = v*v;
143 return (vv[0] + vv[1]) * length_term_p2<2>(precision);
144}
145AI float quadratic_p4(float precision,
146 const SkPoint pts[],
147 const VectorXform& vectorXform = VectorXform()) {
148 return quadratic_p4(precision,
149 sk_bit_cast<skvx::float2>(pts[0]),
150 sk_bit_cast<skvx::float2>(pts[1]),
151 sk_bit_cast<skvx::float2>(pts[2]),
152 vectorXform);
153}
154
155// Returns Wang's formula specialized for a quadratic curve.
156AI float quadratic(float precision,
157 const SkPoint pts[],
158 const VectorXform& vectorXform = VectorXform()) {
159 return root4(quadratic_p4(precision, pts, vectorXform));
160}
161
162// Returns the log2 value of Wang's formula specialized for a quadratic curve, rounded up to the
163// next int.
164AI int quadratic_log2(float precision,
165 const SkPoint pts[],
166 const VectorXform& vectorXform = VectorXform()) {
167 // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
168 return nextlog16(quadratic_p4(precision, pts, vectorXform));
169}
170
171// Returns Wang's formula, raised to the 4th power, specialized for a cubic curve.
172AI float cubic_p4(float precision,
174 const VectorXform& vectorXform = VectorXform()) {
175 skvx::float4 p01{p0, p1};
176 skvx::float4 p12{p1, p2};
177 skvx::float4 p23{p2, p3};
178 skvx::float4 v = -2*p12 + p01 + p23;
179 v = vectorXform(v);
180 skvx::float4 vv = v*v;
181 return std::max(vv[0] + vv[1], vv[2] + vv[3]) * length_term_p2<3>(precision);
182}
183AI float cubic_p4(float precision,
184 const SkPoint pts[],
185 const VectorXform& vectorXform = VectorXform()) {
186 return cubic_p4(precision,
187 sk_bit_cast<skvx::float2>(pts[0]),
188 sk_bit_cast<skvx::float2>(pts[1]),
189 sk_bit_cast<skvx::float2>(pts[2]),
190 sk_bit_cast<skvx::float2>(pts[3]),
191 vectorXform);
192}
193
194// Returns Wang's formula specialized for a cubic curve.
195AI float cubic(float precision,
196 const SkPoint pts[],
197 const VectorXform& vectorXform = VectorXform()) {
198 return root4(cubic_p4(precision, pts, vectorXform));
199}
200
201// Returns the log2 value of Wang's formula specialized for a cubic curve, rounded up to the next
202// int.
203AI int cubic_log2(float precision,
204 const SkPoint pts[],
205 const VectorXform& vectorXform = VectorXform()) {
206 // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
207 return nextlog16(cubic_p4(precision, pts, vectorXform));
208}
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.
214AI float worst_case_cubic_p4(float precision, float devWidth, float devHeight) {
215 float kk = length_term_p2<3>(precision);
216 return 4*kk * (devWidth * devWidth + devHeight * devHeight);
217}
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.
221AI float worst_case_cubic(float precision, float devWidth, float devHeight) {
222 return root4(worst_case_cubic_p4(precision, devWidth, devHeight));
223}
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.
227AI int worst_case_cubic_log2(float precision, float devWidth, float devHeight) {
228 // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
229 return nextlog16(worst_case_cubic_p4(precision, devWidth, devHeight));
230}
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.
238AI float conic_p2(float precision,
240 float w,
241 const VectorXform& vectorXform = VectorXform()) {
242 p0 = vectorXform(p0);
243 p1 = vectorXform(p1);
244 p2 = vectorXform(p2);
245
246 // Compute center of bounding box in projected space
247 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 p0 -= C;
252 p1 -= C;
253 p2 -= C;
254
255 // Compute max length
256 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 const skvx::float2 dp = -2*w*p1 + p0 + p2;
261 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 const float rp_minus_1 = std::max(0.f, max_len * precision - 1);
266 const float numer = sqrtf(dot(dp, dp)) * precision + rp_minus_1 * dw;
267 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 return numer / denom;
273}
274AI float conic_p2(float precision,
275 const SkPoint pts[],
276 float w,
277 const VectorXform& vectorXform = VectorXform()) {
278 return conic_p2(precision,
279 sk_bit_cast<skvx::float2>(pts[0]),
280 sk_bit_cast<skvx::float2>(pts[1]),
281 sk_bit_cast<skvx::float2>(pts[2]),
282 w,
283 vectorXform);
284}
285
286// Returns the value of Wang's formula specialized for a conic curve.
287AI float conic(float tolerance,
288 const SkPoint pts[],
289 float w,
290 const VectorXform& vectorXform = VectorXform()) {
291 return sqrtf(conic_p2(tolerance, pts, w, vectorXform));
292}
293
294// Returns the log2 value of Wang's formula specialized for a conic curve, rounded up to the next
295// int.
296AI int conic_log2(float tolerance,
297 const SkPoint pts[],
298 float w,
299 const VectorXform& vectorXform = VectorXform()) {
300 // nextlog4(x) == ceil(log2(sqrt(x)))
301 return nextlog4(conic_p2(tolerance, pts, w, vectorXform));
302}
303
304} // namespace skgpu::wangs_formula
305
306#undef AI
307
308#endif // skgpu_tessellate_WangsFormula_DEFINED
#define SkASSERT(cond)
Definition: SkAssert.h:116
static uint32_t SkFloat2Bits(float value)
Definition: SkFloatBits.h:41
#define AI
Definition: WangsFormula.h:24
Definition: SkM44.h:150
AI skvx::float4 operator()(skvx::float4 vectors) const
Definition: WangsFormula.h:126
AI skvx::float2 operator()(skvx::float2 vector) const
Definition: WangsFormula.h:123
AI VectorXform & operator=(const SkM44 &m)
Definition: WangsFormula.h:117
AI VectorXform & operator=(const SkMatrix &m)
Definition: WangsFormula.h:111
AI VectorXform(const SkMatrix &m)
Definition: WangsFormula.h:108
#define C(TEST_CATEGORY)
Definition: colrv1.cpp:248
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double x
AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:287
AI float conic_p2(float precision, skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:238
AI float quadratic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:156
AI int worst_case_cubic_log2(float precision, float devWidth, float devHeight)
Definition: WangsFormula.h:227
AI float worst_case_cubic(float precision, float devWidth, float devHeight)
Definition: WangsFormula.h:221
constexpr float length_term(float precision)
Definition: WangsFormula.h:39
AI float quadratic_p4(float precision, skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:137
AI int conic_log2(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:296
AI float cubic_p4(float precision, skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, skvx::float2 p3, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:172
AI int nextlog2(float x)
Definition: WangsFormula.h:60
AI int nextlog16(float x)
Definition: WangsFormula.h:97
AI int cubic_log2(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:203
AI int quadratic_log2(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:164
constexpr float length_term_p2(float precision)
Definition: WangsFormula.h:42
AI float root4(float x)
Definition: WangsFormula.h:46
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:195
AI float worst_case_cubic_p4(float precision, float devWidth, float devHeight)
Definition: WangsFormula.h:214
AI int nextlog4(float x)
Definition: WangsFormula.h:89
SINT T dot(const Vec< N, T > &a, const Vec< N, T > &b)
Definition: SkVx.h:964
SkScalar w
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
Definition: SkVx.h:83