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