• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkMatrix.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkString.h"
14 #include "include/private/SkFloatingPoint.h"
15 #include "src/gpu/tessellate/Tessellation.h"
16 
17 #define AI SK_MAYBE_UNUSED SK_ALWAYS_INLINE
18 
19 // Wang's formula gives the minimum number of evenly spaced (in the parametric sense) line segments
20 // that a bezier curve must be chopped into in order to guarantee all lines stay within a distance
21 // of "1/precision" pixels from the true curve. Its definition for a bezier curve of degree "n" is
22 // as follows:
23 //
24 //     maxLength = max([length(p[i+2] - 2p[i+1] + p[i]) for (0 <= i <= n-2)])
25 //     numParametricSegments = sqrt(maxLength * precision * n*(n - 1)/8)
26 //
27 // (Goldman, Ron. (2003). 5.6.3 Wang's Formula. "Pyramid Algorithms: A Dynamic Programming Approach
28 // to Curves and Surfaces for Geometric Modeling". Morgan Kaufmann Publishers.)
29 namespace skgpu::wangs_formula {
30 
31 // Returns the value by which to multiply length in Wang's formula. (See above.)
length_term(float precision)32 template<int Degree> constexpr float length_term(float precision) {
33     return (Degree * (Degree - 1) / 8.f) * precision;
34 }
length_term_pow2(float precision)35 template<int Degree> constexpr float length_term_pow2(float precision) {
36     return ((Degree * Degree) * ((Degree - 1) * (Degree - 1)) / 64.f) * (precision * precision);
37 }
38 
root4(float x)39 AI float root4(float x) {
40     return sqrtf(sqrtf(x));
41 }
42 
43 // Returns nextlog2(sqrt(x)):
44 //
45 //   log2(sqrt(x)) == log2(x^(1/2)) == log2(x)/2 == log2(x)/log2(4) == log4(x)
46 //
nextlog4(float x)47 AI int nextlog4(float x) {
48     return (sk_float_nextlog2(x) + 1) >> 1;
49 }
50 
51 // Returns nextlog2(sqrt(sqrt(x))):
52 //
53 //   log2(sqrt(sqrt(x))) == log2(x^(1/4)) == log2(x)/4 == log2(x)/log2(16) == log16(x)
54 //
nextlog16(float x)55 AI int nextlog16(float x) {
56     return (sk_float_nextlog2(x) + 3) >> 2;
57 }
58 
59 // Represents the upper-left 2x2 matrix of an affine transform for applying to vectors:
60 //
61 //     VectorXform(p1 - p0) == M * float3(p1, 1) - M * float3(p0, 1)
62 //
63 class VectorXform {
64 public:
65     using float2 = skvx::Vec<2, float>;
66     using float4 = skvx::Vec<4, float>;
VectorXform()67     AI explicit VectorXform() : fType(Type::kIdentity) {}
VectorXform(const SkMatrix & m)68     AI explicit VectorXform(const SkMatrix& m) { *this = m; }
69     AI VectorXform& operator=(const SkMatrix& m) {
70         SkASSERT(!m.hasPerspective());
71         if (m.getType() & SkMatrix::kAffine_Mask) {
72             fType = Type::kAffine;
73             fScaleXSkewY = {m.getScaleX(), m.getSkewY()};
74             fSkewXScaleY = {m.getSkewX(), m.getScaleY()};
75             fScaleXYXY = {m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY()};
76             fSkewXYXY = {m.getSkewX(), m.getSkewY(), m.getSkewX(), m.getSkewY()};
77         } else if (m.getType() & SkMatrix::kScale_Mask) {
78             fType = Type::kScale;
79             fScaleXY = {m.getScaleX(), m.getScaleY()};
80             fScaleXYXY = {m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY()};
81         } else {
82             SkASSERT(!(m.getType() & ~SkMatrix::kTranslate_Mask));
83             fType = Type::kIdentity;
84         }
85         return *this;
86     }
operator()87     AI float2 operator()(float2 vector) const {
88         switch (fType) {
89             case Type::kIdentity:
90                 return vector;
91             case Type::kScale:
92                 return fScaleXY * vector;
93             case Type::kAffine:
94                 return fScaleXSkewY * float2(vector[0]) + fSkewXScaleY * vector[1];
95         }
96         SkUNREACHABLE;
97     }
operator()98     AI float4 operator()(float4 vectors) const {
99         switch (fType) {
100             case Type::kIdentity:
101                 return vectors;
102             case Type::kScale:
103                 return vectors * fScaleXYXY;
104             case Type::kAffine:
105                 return fScaleXYXY * vectors + fSkewXYXY * vectors.yxwz();
106         }
107         SkUNREACHABLE;
108     }
109 private:
110     enum class Type { kIdentity, kScale, kAffine } fType;
111     union { float2 fScaleXY, fScaleXSkewY; };
112     float2 fSkewXScaleY;
113     float4 fScaleXYXY;
114     float4 fSkewXYXY;
115 };
116 
117 // Returns Wang's formula, raised to the 4th power, specialized for a quadratic curve.
118 AI float quadratic_pow4(float precision,
119                         float2 p0, float2 p1, float2 p2,
120                         const VectorXform& vectorXform = VectorXform()) {
121     float2 v = -2*p1 + p0 + p2;
122     v = vectorXform(v);
123     float2 vv = v*v;
124     return (vv[0] + vv[1]) * length_term_pow2<2>(precision);
125 }
126 AI float quadratic_pow4(float precision,
127                         const SkPoint pts[],
128                         const VectorXform& vectorXform = VectorXform()) {
129     return quadratic_pow4(precision,
130                           skvx::bit_pun<float2>(pts[0]),
131                           skvx::bit_pun<float2>(pts[1]),
132                           skvx::bit_pun<float2>(pts[2]),
133                           vectorXform);
134 }
135 
136 // Returns Wang's formula specialized for a quadratic curve.
137 AI float quadratic(float precision,
138                    const SkPoint pts[],
139                    const VectorXform& vectorXform = VectorXform()) {
140     return root4(quadratic_pow4(precision, pts, vectorXform));
141 }
142 
143 // Returns the log2 value of Wang's formula specialized for a quadratic curve, rounded up to the
144 // next int.
145 AI int quadratic_log2(float precision,
146                       const SkPoint pts[],
147                       const VectorXform& vectorXform = VectorXform()) {
148     // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
149     return nextlog16(quadratic_pow4(precision, pts, vectorXform));
150 }
151 
152 // Returns Wang's formula, raised to the 4th power, specialized for a cubic curve.
153 AI float cubic_pow4(float precision,
154                     float2 p0, float2 p1, float2 p2, float2 p3,
155                     const VectorXform& vectorXform = VectorXform()) {
156     float4 p01{p0, p1};
157     float4 p12{p1, p2};
158     float4 p23{p2, p3};
159     float4 v = -2*p12 + p01 + p23;
160     v = vectorXform(v);
161     float4 vv = v*v;
162     return std::max(vv[0] + vv[1], vv[2] + vv[3]) * length_term_pow2<3>(precision);
163 }
164 AI float cubic_pow4(float precision,
165                     const SkPoint pts[],
166                     const VectorXform& vectorXform = VectorXform()) {
167     return cubic_pow4(precision,
168                       skvx::bit_pun<float2>(pts[0]),
169                       skvx::bit_pun<float2>(pts[1]),
170                       skvx::bit_pun<float2>(pts[2]),
171                       skvx::bit_pun<float2>(pts[3]),
172                       vectorXform);
173 }
174 
175 // Returns Wang's formula specialized for a cubic curve.
176 AI float cubic(float precision,
177                const SkPoint pts[],
178                const VectorXform& vectorXform = VectorXform()) {
179     return root4(cubic_pow4(precision, pts, vectorXform));
180 }
181 
182 // Returns the log2 value of Wang's formula specialized for a cubic curve, rounded up to the next
183 // int.
184 AI int cubic_log2(float precision,
185                   const SkPoint pts[],
186                   const VectorXform& vectorXform = VectorXform()) {
187     // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
188     return nextlog16(cubic_pow4(precision, pts, vectorXform));
189 }
190 
191 // Returns the maximum number of line segments a cubic with the given device-space bounding box size
192 // would ever need to be divided into, raised to the 4th power. This is simply a special case of the
193 // cubic formula where we maximize its value by placing control points on specific corners of the
194 // bounding box.
worst_case_cubic_pow4(float precision,float devWidth,float devHeight)195 AI float worst_case_cubic_pow4(float precision, float devWidth, float devHeight) {
196     float kk = length_term_pow2<3>(precision);
197     return 4*kk * (devWidth * devWidth + devHeight * devHeight);
198 }
199 
200 // Returns the maximum number of line segments a cubic with the given device-space bounding box size
201 // would ever need to be divided into.
worst_case_cubic(float precision,float devWidth,float devHeight)202 AI float worst_case_cubic(float precision, float devWidth, float devHeight) {
203     return root4(worst_case_cubic_pow4(precision, devWidth, devHeight));
204 }
205 
206 // Returns the maximum log2 number of line segments a cubic with the given device-space bounding box
207 // size would ever need to be divided into.
worst_case_cubic_log2(float precision,float devWidth,float devHeight)208 AI int worst_case_cubic_log2(float precision, float devWidth, float devHeight) {
209     // nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
210     return nextlog16(worst_case_cubic_pow4(precision, devWidth, devHeight));
211 }
212 
213 // Returns Wang's formula specialized for a conic curve, raised to the second power.
214 // Input points should be in projected space.
215 //
216 // This is not actually due to Wang, but is an analogue from (Theorem 3, corollary 1):
217 //   J. Zheng, T. Sederberg. "Estimating Tessellation Parameter Intervals for
218 //   Rational Curves and Surfaces." ACM Transactions on Graphics 19(1). 2000.
219 AI float conic_pow2(float precision,
220                     float2 p0, float2 p1, float2 p2,
221                     float w,
222                     const VectorXform& vectorXform = VectorXform()) {
223     p0 = vectorXform(p0);
224     p1 = vectorXform(p1);
225     p2 = vectorXform(p2);
226 
227     // Compute center of bounding box in projected space
228     const float2 C = 0.5f * (skvx::min(skvx::min(p0, p1), p2) + skvx::max(skvx::max(p0, p1), p2));
229 
230     // Translate by -C. This improves translation-invariance of the formula,
231     // see Sec. 3.3 of cited paper
232     p0 -= C;
233     p1 -= C;
234     p2 -= C;
235 
236     // Compute max length
237     const float max_len = sqrtf(std::max(dot(p0, p0), std::max(dot(p1, p1), dot(p2, p2))));
238 
239     // Compute forward differences
240     const float2 dp = -2*w*p1 + p0 + p2;
241     const float dw = fabsf(-2 * w + 2);
242 
243     // Compute numerator and denominator for parametric step size of linearization. Here, the
244     // epsilon referenced from the cited paper is 1/precision.
245     const float rp_minus_1 = std::max(0.f, max_len * precision - 1);
246     const float numer = sqrtf(dot(dp, dp)) * precision + rp_minus_1 * dw;
247     const float denom = 4 * std::min(w, 1.f);
248 
249     // Number of segments = sqrt(numer / denom).
250     // This assumes parametric interval of curve being linearized is [t0,t1] = [0, 1].
251     // If not, the number of segments is (tmax - tmin) / sqrt(denom / numer).
252     return numer / denom;
253 }
254 AI float conic_pow2(float precision,
255                     const SkPoint pts[],
256                     float w,
257                     const VectorXform& vectorXform = VectorXform()) {
258     return conic_pow2(precision,
259                       skvx::bit_pun<float2>(pts[0]),
260                       skvx::bit_pun<float2>(pts[1]),
261                       skvx::bit_pun<float2>(pts[2]),
262                       w,
263                       vectorXform);
264 }
265 
266 // Returns the value of Wang's formula specialized for a conic curve.
267 AI float conic(float tolerance,
268                const SkPoint pts[],
269                float w,
270                const VectorXform& vectorXform = VectorXform()) {
271     return sqrtf(conic_pow2(tolerance, pts, w, vectorXform));
272 }
273 
274 // Returns the log2 value of Wang's formula specialized for a conic curve, rounded up to the next
275 // int.
276 AI int conic_log2(float tolerance,
277                   const SkPoint pts[],
278                   float w,
279                   const VectorXform& vectorXform = VectorXform()) {
280     // nextlog4(x) == ceil(log2(sqrt(x)))
281     return nextlog4(conic_pow2(tolerance, pts, w, vectorXform));
282 }
283 
284 // Emits an SKSL function that calculates Wang's formula for the given set of 4 points. The points
285 // represent a cubic if w < 0, or if w >= 0, a conic defined by the first 3 points.
as_sksl()286 SK_MAYBE_UNUSED inline static SkString as_sksl() {
287     SkString code;
288     code.appendf(R"(
289     // Returns the length squared of the largest forward difference from Wang's cubic formula.
290     float wangs_formula_max_fdiff_pow2(float2 p0, float2 p1, float2 p2, float2 p3,
291                                        float2x2 matrix) {
292         float2 d0 = matrix * (fma(float2(-2), p1, p2) + p0);
293         float2 d1 = matrix * (fma(float2(-2), p2, p3) + p1);
294         return max(dot(d0,d0), dot(d1,d1));
295     }
296     float wangs_formula_cubic(float _precision_, float2 p0, float2 p1, float2 p2, float2 p3,
297                               float2x2 matrix) {
298         float m = wangs_formula_max_fdiff_pow2(p0, p1, p2, p3, matrix);
299         return max(ceil(sqrt(%f * _precision_ * sqrt(m))), 1.0);
300     }
301     float wangs_formula_cubic_log2(float _precision_, float2 p0, float2 p1, float2 p2, float2 p3,
302                                    float2x2 matrix) {
303         float m = wangs_formula_max_fdiff_pow2(p0, p1, p2, p3, matrix);
304         return ceil(log2(max(%f * _precision_ * _precision_ * m, 1.0)) * .25);
305     })", length_term<3>(1), length_term_pow2<3>(1));
306 
307     code.appendf(R"(
308     float wangs_formula_conic_pow2(float _precision_, float2 p0, float2 p1, float2 p2, float w) {
309         // Translate the bounding box center to the origin.
310         float2 C = (min(min(p0, p1), p2) + max(max(p0, p1), p2)) * 0.5;
311         p0 -= C;
312         p1 -= C;
313         p2 -= C;
314 
315         // Compute max length.
316         float m = sqrt(max(max(dot(p0,p0), dot(p1,p1)), dot(p2,p2)));
317 
318         // Compute forward differences.
319         float2 dp = fma(float2(-2.0 * w), p1, p0) + p2;
320         float dw = abs(fma(-2.0, w, 2.0));
321 
322         // Compute numerator and denominator for parametric step size of linearization. Here, the
323         // epsilon referenced from the cited paper is 1/precision.
324         float rp_minus_1 = max(0.0, fma(m, _precision_, -1.0));
325         float numer = length(dp) * _precision_ + rp_minus_1 * dw;
326         float denom = 4 * min(w, 1.0);
327 
328         return numer/denom;
329     }
330     float wangs_formula_conic(float _precision_, float2 p0, float2 p1, float2 p2, float w) {
331         float n2 = wangs_formula_conic_pow2(_precision_, p0, p1, p2, w);
332         return max(ceil(sqrt(n2)), 1.0);
333     }
334     float wangs_formula_conic_log2(float _precision_, float2 p0, float2 p1, float2 p2, float w) {
335         float n2 = wangs_formula_conic_pow2(_precision_, p0, p1, p2, w);
336         return ceil(log2(max(n2, 1.0)) * .5);
337     })");
338 
339     return code;
340 }
341 
342 }  // namespace skgpu::wangs_formula
343 
344 #undef AI
345 
346 #endif  // skgpu_tessellate_WangsFormula_DEFINED
347