xref: /aosp_15_r20/external/skia/src/sksl/sksl_graphite_vert.sksl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker// Graphite-specific vertex shader code
2*c8dee2aaSAndroid Build Coastguard Worker
3*c8dee2aaSAndroid Build Coastguard Workerconst float $PI = 3.141592653589793238;
4*c8dee2aaSAndroid Build Coastguard Worker
5*c8dee2aaSAndroid Build Coastguard Worker///////////////////////////////////////////////////////////////////////////////////////////////////
6*c8dee2aaSAndroid Build Coastguard Worker// Support functions for tessellating path renderers
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Workerconst float $kCubicCurveType = 0;            // skgpu::tess::kCubicCurveType
9*c8dee2aaSAndroid Build Coastguard Workerconst float $kConicCurveType = 1;            // skgpu::tess::kConicCurveType
10*c8dee2aaSAndroid Build Coastguard Workerconst float $kTriangularConicCurveType = 2;  // skgpu::tess::kTriangularConicCurveType
11*c8dee2aaSAndroid Build Coastguard Worker
12*c8dee2aaSAndroid Build Coastguard Worker// This function can be used on GPUs with infinity support to infer the curve type from the specific
13*c8dee2aaSAndroid Build Coastguard Worker// path control-point encoding used by tessellating path renderers. Calling this function on a
14*c8dee2aaSAndroid Build Coastguard Worker// platform that lacks infinity support may result in a shader compilation error.
15*c8dee2aaSAndroid Build Coastguard Worker$pure float curve_type_using_inf_support(float4 p23) {
16*c8dee2aaSAndroid Build Coastguard Worker    return isinf(p23.z) ? $kTriangularConicCurveType :
17*c8dee2aaSAndroid Build Coastguard Worker           isinf(p23.w) ? $kConicCurveType :
18*c8dee2aaSAndroid Build Coastguard Worker                          $kCubicCurveType;
19*c8dee2aaSAndroid Build Coastguard Worker}
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Worker$pure bool $is_conic_curve(float curveType) {
22*c8dee2aaSAndroid Build Coastguard Worker    return curveType != $kCubicCurveType;
23*c8dee2aaSAndroid Build Coastguard Worker}
24*c8dee2aaSAndroid Build Coastguard Worker
25*c8dee2aaSAndroid Build Coastguard Worker$pure bool $is_triangular_conic_curve(float curveType) {
26*c8dee2aaSAndroid Build Coastguard Worker    return curveType == $kTriangularConicCurveType;
27*c8dee2aaSAndroid Build Coastguard Worker}
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker// Wang's formula gives the minimum number of evenly spaced (in the parametric sense) line segments
30*c8dee2aaSAndroid Build Coastguard Worker// that a bezier curve must be chopped into in order to guarantee all lines stay within a distance
31*c8dee2aaSAndroid Build Coastguard Worker// of "1/precision" pixels from the true curve. Its definition for a bezier curve of degree "n" is
32*c8dee2aaSAndroid Build Coastguard Worker// as follows:
33*c8dee2aaSAndroid Build Coastguard Worker//
34*c8dee2aaSAndroid Build Coastguard Worker//     maxLength = max([length(p[i+2] - 2p[i+1] + p[i]) for (0 <= i <= n-2)])
35*c8dee2aaSAndroid Build Coastguard Worker//     numParametricSegments = sqrt(maxLength * precision * n*(n - 1)/8)
36*c8dee2aaSAndroid Build Coastguard Worker//
37*c8dee2aaSAndroid Build Coastguard Worker// (Goldman, Ron. (2003). 5.6.3 Wang's Formula. "Pyramid Algorithms: A Dynamic Programming Approach
38*c8dee2aaSAndroid Build Coastguard Worker// to Curves and Surfaces for Geometric Modeling". Morgan Kaufmann Publishers.)
39*c8dee2aaSAndroid Build Coastguard Worker
40*c8dee2aaSAndroid Build Coastguard Workerconst float $kDegree = 3;
41*c8dee2aaSAndroid Build Coastguard Workerconst float $kPrecision = 4; // Must match skgpu::tess::kPrecision
42*c8dee2aaSAndroid Build Coastguard Workerconst float $kLengthTerm     = ($kDegree * ($kDegree - 1) / 8.0) * $kPrecision;
43*c8dee2aaSAndroid Build Coastguard Workerconst float $kLengthTermPow2 = (($kDegree * $kDegree) * (($kDegree - 1) * ($kDegree - 1)) / 64.0) *
44*c8dee2aaSAndroid Build Coastguard Worker                               ($kPrecision * $kPrecision);
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker// Returns the length squared of the largest forward difference from Wang's cubic formula.
47*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_max_fdiff_p2(float2 p0, float2 p1, float2 p2, float2 p3,
48*c8dee2aaSAndroid Build Coastguard Worker                                        float2x2 matrix) {
49*c8dee2aaSAndroid Build Coastguard Worker    float2 d0 = matrix * (fma(float2(-2), p1, p2) + p0);
50*c8dee2aaSAndroid Build Coastguard Worker    float2 d1 = matrix * (fma(float2(-2), p2, p3) + p1);
51*c8dee2aaSAndroid Build Coastguard Worker    return max(dot(d0,d0), dot(d1,d1));
52*c8dee2aaSAndroid Build Coastguard Worker}
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_cubic(float2 p0, float2 p1, float2 p2, float2 p3,
55*c8dee2aaSAndroid Build Coastguard Worker                                 float2x2 matrix) {
56*c8dee2aaSAndroid Build Coastguard Worker    float m = $wangs_formula_max_fdiff_p2(p0, p1, p2, p3, matrix);
57*c8dee2aaSAndroid Build Coastguard Worker    return max(ceil(sqrt($kLengthTerm * sqrt(m))), 1.0);
58*c8dee2aaSAndroid Build Coastguard Worker}
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_cubic_log2(float2 p0, float2 p1, float2 p2, float2 p3,
61*c8dee2aaSAndroid Build Coastguard Worker                                      float2x2 matrix) {
62*c8dee2aaSAndroid Build Coastguard Worker    float m = $wangs_formula_max_fdiff_p2(p0, p1, p2, p3, matrix);
63*c8dee2aaSAndroid Build Coastguard Worker    return ceil(log2(max($kLengthTermPow2 * m, 1.0)) * .25);
64*c8dee2aaSAndroid Build Coastguard Worker}
65*c8dee2aaSAndroid Build Coastguard Worker
66*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_conic_p2(float2 p0, float2 p1, float2 p2, float w) {
67*c8dee2aaSAndroid Build Coastguard Worker    // Translate the bounding box center to the origin.
68*c8dee2aaSAndroid Build Coastguard Worker    float2 C = (min(min(p0, p1), p2) + max(max(p0, p1), p2)) * 0.5;
69*c8dee2aaSAndroid Build Coastguard Worker    p0 -= C;
70*c8dee2aaSAndroid Build Coastguard Worker    p1 -= C;
71*c8dee2aaSAndroid Build Coastguard Worker    p2 -= C;
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker    // Compute max length.
74*c8dee2aaSAndroid Build Coastguard Worker    float m = sqrt(max(max(dot(p0,p0), dot(p1,p1)), dot(p2,p2)));
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker    // Compute forward differences.
77*c8dee2aaSAndroid Build Coastguard Worker    float2 dp = fma(float2(-2.0 * w), p1, p0) + p2;
78*c8dee2aaSAndroid Build Coastguard Worker    float dw = abs(fma(-2.0, w, 2.0));
79*c8dee2aaSAndroid Build Coastguard Worker
80*c8dee2aaSAndroid Build Coastguard Worker    // Compute numerator and denominator for parametric step size of linearization. Here, the
81*c8dee2aaSAndroid Build Coastguard Worker    // epsilon referenced from the cited paper is 1/precision.
82*c8dee2aaSAndroid Build Coastguard Worker    float rp_minus_1 = max(0.0, fma(m, $kPrecision, -1.0));
83*c8dee2aaSAndroid Build Coastguard Worker    float numer = length(dp) * $kPrecision + rp_minus_1 * dw;
84*c8dee2aaSAndroid Build Coastguard Worker    float denom = 4 * min(w, 1.0);
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker    return numer/denom;
87*c8dee2aaSAndroid Build Coastguard Worker}
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_conic(float2 p0, float2 p1, float2 p2, float w) {
90*c8dee2aaSAndroid Build Coastguard Worker    float n2 = $wangs_formula_conic_p2(p0, p1, p2, w);
91*c8dee2aaSAndroid Build Coastguard Worker    return max(ceil(sqrt(n2)), 1.0);
92*c8dee2aaSAndroid Build Coastguard Worker}
93*c8dee2aaSAndroid Build Coastguard Worker
94*c8dee2aaSAndroid Build Coastguard Worker$pure float $wangs_formula_conic_log2(float2 p0, float2 p1, float2 p2, float w) {
95*c8dee2aaSAndroid Build Coastguard Worker    float n2 = $wangs_formula_conic_p2(p0, p1, p2, w);
96*c8dee2aaSAndroid Build Coastguard Worker    return ceil(log2(max(n2, 1.0)) * .5);
97*c8dee2aaSAndroid Build Coastguard Worker}
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker// Returns the normalized difference between a and b, i.e. normalize(a - b), with care taken for
100*c8dee2aaSAndroid Build Coastguard Worker// if 'a' and/or 'b' have large coordinates.
101*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $robust_normalize_diff(float2 a, float2 b) {
102*c8dee2aaSAndroid Build Coastguard Worker    float2 diff = a - b;
103*c8dee2aaSAndroid Build Coastguard Worker    if (diff == float2(0.0)) {
104*c8dee2aaSAndroid Build Coastguard Worker        return float2(0.0);
105*c8dee2aaSAndroid Build Coastguard Worker    } else {
106*c8dee2aaSAndroid Build Coastguard Worker        float invMag = 1.0 / max(abs(diff.x), abs(diff.y));
107*c8dee2aaSAndroid Build Coastguard Worker        return normalize(invMag * diff);
108*c8dee2aaSAndroid Build Coastguard Worker    }
109*c8dee2aaSAndroid Build Coastguard Worker}
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker// Returns the cosine of the angle between a and b, assuming a and b are unit vectors already.
112*c8dee2aaSAndroid Build Coastguard Worker// Guaranteed to be between [-1, 1].
113*c8dee2aaSAndroid Build Coastguard Worker$pure float $cosine_between_unit_vectors(float2 a, float2 b) {
114*c8dee2aaSAndroid Build Coastguard Worker    // Since a and b are assumed to be normalized, the cosine is equal to the dot product, although
115*c8dee2aaSAndroid Build Coastguard Worker    // we clamp that to ensure it falls within the expected range of [-1, 1].
116*c8dee2aaSAndroid Build Coastguard Worker    return clamp(dot(a, b), -1.0, 1.0);
117*c8dee2aaSAndroid Build Coastguard Worker}
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker// Extends the middle radius to either the miter point, or the bevel edge if we surpassed the
120*c8dee2aaSAndroid Build Coastguard Worker// miter limit and need to revert to a bevel join.
121*c8dee2aaSAndroid Build Coastguard Worker$pure float $miter_extent(float cosTheta, float miterLimit) {
122*c8dee2aaSAndroid Build Coastguard Worker    float x = fma(cosTheta, .5, .5);
123*c8dee2aaSAndroid Build Coastguard Worker    return (x * miterLimit * miterLimit >= 1.0) ? inversesqrt(x) : sqrt(x);
124*c8dee2aaSAndroid Build Coastguard Worker}
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker// Returns the number of radial segments required for each radian of rotation, in order for the
127*c8dee2aaSAndroid Build Coastguard Worker// curve to appear "smooth" as defined by the approximate device-space stroke radius.
128*c8dee2aaSAndroid Build Coastguard Worker$pure float $num_radial_segments_per_radian(float approxDevStrokeRadius) {
129*c8dee2aaSAndroid Build Coastguard Worker    return .5 / acos(max(1.0 - (1.0 / $kPrecision) / approxDevStrokeRadius, -1.0));
130*c8dee2aaSAndroid Build Coastguard Worker}
131*c8dee2aaSAndroid Build Coastguard Worker
132*c8dee2aaSAndroid Build Coastguard Worker// Unlike mix(), this does not return b when t==1. But it otherwise seems to get better
133*c8dee2aaSAndroid Build Coastguard Worker// precision than "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points.
134*c8dee2aaSAndroid Build Coastguard Worker// We override this result anyway when t==1 so it shouldn't be a problem.
135*c8dee2aaSAndroid Build Coastguard Worker$pure float $unchecked_mix(float a, float b, float T) {
136*c8dee2aaSAndroid Build Coastguard Worker    return fma(b - a, T, a);
137*c8dee2aaSAndroid Build Coastguard Worker}
138*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $unchecked_mix(float2 a, float2 b, float T) {
139*c8dee2aaSAndroid Build Coastguard Worker    return fma(b - a, float2(T), a);
140*c8dee2aaSAndroid Build Coastguard Worker}
141*c8dee2aaSAndroid Build Coastguard Worker$pure float4 $unchecked_mix(float4 a, float4 b, float4 T) {
142*c8dee2aaSAndroid Build Coastguard Worker    return fma(b - a, T, a);
143*c8dee2aaSAndroid Build Coastguard Worker}
144*c8dee2aaSAndroid Build Coastguard Worker
145*c8dee2aaSAndroid Build Coastguard Worker// Compute a vertex position for the curve described by p01 and p23 packed control points,
146*c8dee2aaSAndroid Build Coastguard Worker// tessellated to the given resolve level, and assuming it will be drawn as a filled curve.
147*c8dee2aaSAndroid Build Coastguard Worker$pure float2 tessellate_filled_curve(float2x2 vectorXform,
148*c8dee2aaSAndroid Build Coastguard Worker                                     float resolveLevel, float idxInResolveLevel,
149*c8dee2aaSAndroid Build Coastguard Worker                                     float4 p01, float4 p23,
150*c8dee2aaSAndroid Build Coastguard Worker                                     float curveType) {
151*c8dee2aaSAndroid Build Coastguard Worker    float2 localcoord;
152*c8dee2aaSAndroid Build Coastguard Worker    if ($is_triangular_conic_curve(curveType)) {
153*c8dee2aaSAndroid Build Coastguard Worker        // This patch is an exact triangle.
154*c8dee2aaSAndroid Build Coastguard Worker        localcoord = (resolveLevel != 0)      ? p01.zw
155*c8dee2aaSAndroid Build Coastguard Worker                   : (idxInResolveLevel != 0) ? p23.xy
156*c8dee2aaSAndroid Build Coastguard Worker                                              : p01.xy;
157*c8dee2aaSAndroid Build Coastguard Worker    } else {
158*c8dee2aaSAndroid Build Coastguard Worker        float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
159*c8dee2aaSAndroid Build Coastguard Worker        float w = -1;  // w < 0 tells us to treat the instance as an integral cubic.
160*c8dee2aaSAndroid Build Coastguard Worker        float maxResolveLevel;
161*c8dee2aaSAndroid Build Coastguard Worker        if ($is_conic_curve(curveType)) {
162*c8dee2aaSAndroid Build Coastguard Worker            // Conics are 3 points, with the weight in p3.
163*c8dee2aaSAndroid Build Coastguard Worker            w = p3.x;
164*c8dee2aaSAndroid Build Coastguard Worker            maxResolveLevel = $wangs_formula_conic_log2(vectorXform*p0,
165*c8dee2aaSAndroid Build Coastguard Worker                                                        vectorXform*p1,
166*c8dee2aaSAndroid Build Coastguard Worker                                                        vectorXform*p2, w);
167*c8dee2aaSAndroid Build Coastguard Worker            p1 *= w;  // Unproject p1.
168*c8dee2aaSAndroid Build Coastguard Worker            p3 = p2;  // Duplicate the endpoint for shared code that also runs on cubics.
169*c8dee2aaSAndroid Build Coastguard Worker        } else {
170*c8dee2aaSAndroid Build Coastguard Worker            // The patch is an integral cubic.
171*c8dee2aaSAndroid Build Coastguard Worker            maxResolveLevel = $wangs_formula_cubic_log2(p0, p1, p2, p3, vectorXform);
172*c8dee2aaSAndroid Build Coastguard Worker        }
173*c8dee2aaSAndroid Build Coastguard Worker        if (resolveLevel > maxResolveLevel) {
174*c8dee2aaSAndroid Build Coastguard Worker            // This vertex is at a higher resolve level than we need. Demote to a lower
175*c8dee2aaSAndroid Build Coastguard Worker            // resolveLevel, which will produce a degenerate triangle.
176*c8dee2aaSAndroid Build Coastguard Worker            idxInResolveLevel = floor(ldexp(idxInResolveLevel,
177*c8dee2aaSAndroid Build Coastguard Worker                                            int(maxResolveLevel - resolveLevel)));
178*c8dee2aaSAndroid Build Coastguard Worker            resolveLevel = maxResolveLevel;
179*c8dee2aaSAndroid Build Coastguard Worker        }
180*c8dee2aaSAndroid Build Coastguard Worker        // Promote our location to a discrete position in the maximum fixed resolve level.
181*c8dee2aaSAndroid Build Coastguard Worker        // This is extra paranoia to ensure we get the exact same fp32 coordinates for
182*c8dee2aaSAndroid Build Coastguard Worker        // colocated points from different resolve levels (e.g., the vertices T=3/4 and
183*c8dee2aaSAndroid Build Coastguard Worker        // T=6/8 should be exactly colocated).
184*c8dee2aaSAndroid Build Coastguard Worker        float fixedVertexID = floor(.5 + ldexp(idxInResolveLevel, int(5 - resolveLevel)));
185*c8dee2aaSAndroid Build Coastguard Worker        if (0 < fixedVertexID && fixedVertexID < 32) {
186*c8dee2aaSAndroid Build Coastguard Worker            float T = fixedVertexID * (1 / 32.0);
187*c8dee2aaSAndroid Build Coastguard Worker
188*c8dee2aaSAndroid Build Coastguard Worker            // Evaluate at T. Use De Casteljau's for its accuracy and stability.
189*c8dee2aaSAndroid Build Coastguard Worker            float2 ab = mix(p0, p1, T);
190*c8dee2aaSAndroid Build Coastguard Worker            float2 bc = mix(p1, p2, T);
191*c8dee2aaSAndroid Build Coastguard Worker            float2 cd = mix(p2, p3, T);
192*c8dee2aaSAndroid Build Coastguard Worker            float2 abc = mix(ab, bc, T);
193*c8dee2aaSAndroid Build Coastguard Worker            float2 bcd = mix(bc, cd, T);
194*c8dee2aaSAndroid Build Coastguard Worker            float2 abcd = mix(abc, bcd, T);
195*c8dee2aaSAndroid Build Coastguard Worker
196*c8dee2aaSAndroid Build Coastguard Worker            // Evaluate the conic weight at T.
197*c8dee2aaSAndroid Build Coastguard Worker            float u = mix(1.0, w, T);
198*c8dee2aaSAndroid Build Coastguard Worker            float v = w + 1 - u;  // == mix(w, 1, T)
199*c8dee2aaSAndroid Build Coastguard Worker            float uv = mix(u, v, T);
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker            localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv;
202*c8dee2aaSAndroid Build Coastguard Worker        } else {
203*c8dee2aaSAndroid Build Coastguard Worker            localcoord = (fixedVertexID == 0) ? p0.xy : p3.xy;
204*c8dee2aaSAndroid Build Coastguard Worker        }
205*c8dee2aaSAndroid Build Coastguard Worker    }
206*c8dee2aaSAndroid Build Coastguard Worker    return localcoord;
207*c8dee2aaSAndroid Build Coastguard Worker}
208*c8dee2aaSAndroid Build Coastguard Worker
209*c8dee2aaSAndroid Build Coastguard Worker// Device coords are in xy, local coords are in zw, since for now perspective isn't supported.
210*c8dee2aaSAndroid Build Coastguard Worker$pure float4 tessellate_stroked_curve(float edgeID, float maxEdges,
211*c8dee2aaSAndroid Build Coastguard Worker                                      float2x2 affineMatrix,
212*c8dee2aaSAndroid Build Coastguard Worker                                      float2 translate,
213*c8dee2aaSAndroid Build Coastguard Worker                                      float maxScale /* derived from affineMatrix */,
214*c8dee2aaSAndroid Build Coastguard Worker                                      float4 p01, float4 p23,
215*c8dee2aaSAndroid Build Coastguard Worker                                      float2 lastControlPoint,
216*c8dee2aaSAndroid Build Coastguard Worker                                      float2 strokeParams,
217*c8dee2aaSAndroid Build Coastguard Worker                                      float curveType) {
218*c8dee2aaSAndroid Build Coastguard Worker    float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
219*c8dee2aaSAndroid Build Coastguard Worker    float w = -1;  // w<0 means the curve is an integral cubic.
220*c8dee2aaSAndroid Build Coastguard Worker    if ($is_conic_curve(curveType)) {
221*c8dee2aaSAndroid Build Coastguard Worker        // Conics are 3 points, with the weight in p3.
222*c8dee2aaSAndroid Build Coastguard Worker        w = p3.x;
223*c8dee2aaSAndroid Build Coastguard Worker        p3 = p2;  // Setting p3 equal to p2 works for the remaining rotational logic.
224*c8dee2aaSAndroid Build Coastguard Worker    }
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker    // Call Wang's formula to determine parametric segments before transform points for hairlines
227*c8dee2aaSAndroid Build Coastguard Worker    // so that it is consistent with how the CPU tested the control points for chopping.
228*c8dee2aaSAndroid Build Coastguard Worker    float numParametricSegments;
229*c8dee2aaSAndroid Build Coastguard Worker    if (w < 0) {
230*c8dee2aaSAndroid Build Coastguard Worker        if (p0 == p1 && p2 == p3) {
231*c8dee2aaSAndroid Build Coastguard Worker            numParametricSegments = 1; // a line
232*c8dee2aaSAndroid Build Coastguard Worker        } else {
233*c8dee2aaSAndroid Build Coastguard Worker            numParametricSegments = $wangs_formula_cubic(p0, p1, p2, p3, affineMatrix);
234*c8dee2aaSAndroid Build Coastguard Worker        }
235*c8dee2aaSAndroid Build Coastguard Worker    } else {
236*c8dee2aaSAndroid Build Coastguard Worker        numParametricSegments = $wangs_formula_conic(affineMatrix * p0,
237*c8dee2aaSAndroid Build Coastguard Worker                                                     affineMatrix * p1,
238*c8dee2aaSAndroid Build Coastguard Worker                                                     affineMatrix * p2, w);
239*c8dee2aaSAndroid Build Coastguard Worker    }
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker    // Matches skgpu::tess::StrokeParams
242*c8dee2aaSAndroid Build Coastguard Worker    float strokeRadius = strokeParams.x;
243*c8dee2aaSAndroid Build Coastguard Worker    float joinType = strokeParams.y; // <0 = round join, ==0 = bevel join, >0 encodes miter limit
244*c8dee2aaSAndroid Build Coastguard Worker    bool isHairline = strokeParams.x == 0.0;
245*c8dee2aaSAndroid Build Coastguard Worker    float numRadialSegmentsPerRadian;
246*c8dee2aaSAndroid Build Coastguard Worker    if (isHairline) {
247*c8dee2aaSAndroid Build Coastguard Worker        numRadialSegmentsPerRadian = $num_radial_segments_per_radian(1.0);
248*c8dee2aaSAndroid Build Coastguard Worker        strokeRadius = 0.5;
249*c8dee2aaSAndroid Build Coastguard Worker    } else {
250*c8dee2aaSAndroid Build Coastguard Worker        numRadialSegmentsPerRadian = $num_radial_segments_per_radian(maxScale * strokeParams.x);
251*c8dee2aaSAndroid Build Coastguard Worker    }
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker    if (isHairline) {
254*c8dee2aaSAndroid Build Coastguard Worker        // Hairline case. Transform the points before tessellation. We can still hold off on the
255*c8dee2aaSAndroid Build Coastguard Worker        // translate until the end; we just need to perform the scale and skew right now.
256*c8dee2aaSAndroid Build Coastguard Worker        p0 = affineMatrix * p0;
257*c8dee2aaSAndroid Build Coastguard Worker        p1 = affineMatrix * p1;
258*c8dee2aaSAndroid Build Coastguard Worker        p2 = affineMatrix * p2;
259*c8dee2aaSAndroid Build Coastguard Worker        p3 = affineMatrix * p3;
260*c8dee2aaSAndroid Build Coastguard Worker        lastControlPoint = affineMatrix * lastControlPoint;
261*c8dee2aaSAndroid Build Coastguard Worker    }
262*c8dee2aaSAndroid Build Coastguard Worker
263*c8dee2aaSAndroid Build Coastguard Worker    // Find the starting and ending tangents.
264*c8dee2aaSAndroid Build Coastguard Worker    float2 tan0 = $robust_normalize_diff((p0 == p1) ? ((p1 == p2) ? p3 : p2) : p1, p0);
265*c8dee2aaSAndroid Build Coastguard Worker    float2 tan1 = $robust_normalize_diff(p3, (p3 == p2) ? ((p2 == p1) ? p0 : p1) : p2);
266*c8dee2aaSAndroid Build Coastguard Worker    if (tan0 == float2(0)) {
267*c8dee2aaSAndroid Build Coastguard Worker        // The stroke is a point. This special case tells us to draw a stroke-width circle as a
268*c8dee2aaSAndroid Build Coastguard Worker        // 180 degree point stroke instead.
269*c8dee2aaSAndroid Build Coastguard Worker        tan0 = float2(1,0);
270*c8dee2aaSAndroid Build Coastguard Worker        tan1 = float2(-1,0);
271*c8dee2aaSAndroid Build Coastguard Worker    }
272*c8dee2aaSAndroid Build Coastguard Worker
273*c8dee2aaSAndroid Build Coastguard Worker    // Determine how many edges to give to the join. We emit the first and final edges
274*c8dee2aaSAndroid Build Coastguard Worker    // of the join twice: once full width and once restricted to half width. This guarantees
275*c8dee2aaSAndroid Build Coastguard Worker    // perfect seaming by matching the vertices from the join as well as from the strokes on
276*c8dee2aaSAndroid Build Coastguard Worker    // either side.
277*c8dee2aaSAndroid Build Coastguard Worker    float numEdgesInJoin;
278*c8dee2aaSAndroid Build Coastguard Worker    if (joinType >= 0 /*Is the join not a round type?*/) {
279*c8dee2aaSAndroid Build Coastguard Worker        // Bevel(0) and miter(+) joins get 1 and 2 segments respectively.
280*c8dee2aaSAndroid Build Coastguard Worker        // +2 because we emit the beginning and ending edges twice (see above comments).
281*c8dee2aaSAndroid Build Coastguard Worker        numEdgesInJoin = sign(joinType) + (1 + 2);
282*c8dee2aaSAndroid Build Coastguard Worker    } else {
283*c8dee2aaSAndroid Build Coastguard Worker        float2 prevTan = $robust_normalize_diff(p0, lastControlPoint);
284*c8dee2aaSAndroid Build Coastguard Worker        float joinRads = acos($cosine_between_unit_vectors(prevTan, tan0));
285*c8dee2aaSAndroid Build Coastguard Worker        float numRadialSegmentsInJoin = max(ceil(joinRads * numRadialSegmentsPerRadian), 1);
286*c8dee2aaSAndroid Build Coastguard Worker        // +2 because we emit the beginning and ending edges twice (see above comment).
287*c8dee2aaSAndroid Build Coastguard Worker        numEdgesInJoin = numRadialSegmentsInJoin + 2;
288*c8dee2aaSAndroid Build Coastguard Worker        // The stroke section needs at least two edges. Don't assign more to the join than
289*c8dee2aaSAndroid Build Coastguard Worker        // "maxEdges - 2". (This is only relevant when the ideal max edge count calculated
290*c8dee2aaSAndroid Build Coastguard Worker        // on the CPU had to be limited to maxEdges in the draw call).
291*c8dee2aaSAndroid Build Coastguard Worker        numEdgesInJoin = min(numEdgesInJoin, maxEdges - 2);
292*c8dee2aaSAndroid Build Coastguard Worker    }
293*c8dee2aaSAndroid Build Coastguard Worker
294*c8dee2aaSAndroid Build Coastguard Worker    // Find which direction the curve turns.
295*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5).
296*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1)
297*c8dee2aaSAndroid Build Coastguard Worker    float turn = cross_length_2d(p2 - p0, p3 - p1);
298*c8dee2aaSAndroid Build Coastguard Worker    float combinedEdgeID = abs(edgeID) - numEdgesInJoin;
299*c8dee2aaSAndroid Build Coastguard Worker    if (combinedEdgeID < 0) {
300*c8dee2aaSAndroid Build Coastguard Worker        tan1 = tan0;
301*c8dee2aaSAndroid Build Coastguard Worker        // Don't let tan0 become zero. The code as-is isn't built to handle that case. tan0=0
302*c8dee2aaSAndroid Build Coastguard Worker        // means the join is disabled, and to disable it with the existing code we can leave
303*c8dee2aaSAndroid Build Coastguard Worker        // tan0 equal to tan1.
304*c8dee2aaSAndroid Build Coastguard Worker        if (lastControlPoint != p0) {
305*c8dee2aaSAndroid Build Coastguard Worker            tan0 = $robust_normalize_diff(p0, lastControlPoint);
306*c8dee2aaSAndroid Build Coastguard Worker        }
307*c8dee2aaSAndroid Build Coastguard Worker        turn = cross_length_2d(tan0, tan1);
308*c8dee2aaSAndroid Build Coastguard Worker    }
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker    // Calculate the curve's starting angle and rotation.
311*c8dee2aaSAndroid Build Coastguard Worker    float cosTheta = $cosine_between_unit_vectors(tan0, tan1);
312*c8dee2aaSAndroid Build Coastguard Worker    float rotation = acos(cosTheta);
313*c8dee2aaSAndroid Build Coastguard Worker    if (turn < 0) {
314*c8dee2aaSAndroid Build Coastguard Worker        // Adjust sign of rotation to match the direction the curve turns.
315*c8dee2aaSAndroid Build Coastguard Worker        rotation = -rotation;
316*c8dee2aaSAndroid Build Coastguard Worker    }
317*c8dee2aaSAndroid Build Coastguard Worker
318*c8dee2aaSAndroid Build Coastguard Worker    float numRadialSegments;
319*c8dee2aaSAndroid Build Coastguard Worker    float strokeOutset = sign(edgeID);
320*c8dee2aaSAndroid Build Coastguard Worker    if (combinedEdgeID < 0) {
321*c8dee2aaSAndroid Build Coastguard Worker        // We belong to the preceding join. The first and final edges get duplicated, so we only
322*c8dee2aaSAndroid Build Coastguard Worker        // have "numEdgesInJoin - 2" segments.
323*c8dee2aaSAndroid Build Coastguard Worker        numRadialSegments = numEdgesInJoin - 2;
324*c8dee2aaSAndroid Build Coastguard Worker        numParametricSegments = 1;  // Joins don't have parametric segments.
325*c8dee2aaSAndroid Build Coastguard Worker        p3 = p2 = p1 = p0;  // Colocate all points on the junction point.
326*c8dee2aaSAndroid Build Coastguard Worker        // Shift combinedEdgeID to the range [-1, numRadialSegments]. This duplicates the first
327*c8dee2aaSAndroid Build Coastguard Worker        // edge and lands one edge at the very end of the join. (The duplicated final edge will
328*c8dee2aaSAndroid Build Coastguard Worker        // actually come from the section of our strip that belongs to the stroke.)
329*c8dee2aaSAndroid Build Coastguard Worker        combinedEdgeID += numRadialSegments + 1;
330*c8dee2aaSAndroid Build Coastguard Worker        if (combinedEdgeID < 0) {
331*c8dee2aaSAndroid Build Coastguard Worker            combinedEdgeID = 0;
332*c8dee2aaSAndroid Build Coastguard Worker        } else {
333*c8dee2aaSAndroid Build Coastguard Worker            // We normally restrict the join on one side of the junction, but if the tangents are
334*c8dee2aaSAndroid Build Coastguard Worker            // nearly equivalent this could theoretically result in bad seaming and/or cracks on the
335*c8dee2aaSAndroid Build Coastguard Worker            // side we don't put it on. If the tangents are nearly equivalent then we leave the join
336*c8dee2aaSAndroid Build Coastguard Worker            // double-sided.
337*c8dee2aaSAndroid Build Coastguard Worker            const float sinEpsilon = 1e-2;  // ~= sin(180deg / 3000)
338*c8dee2aaSAndroid Build Coastguard Worker            bool tangentsNearlyParallel =
339*c8dee2aaSAndroid Build Coastguard Worker                    (abs(turn) * inversesqrt(dot(tan0, tan0) * dot(tan1, tan1))) < sinEpsilon;
340*c8dee2aaSAndroid Build Coastguard Worker            if (!tangentsNearlyParallel || dot(tan0, tan1) < 0) {
341*c8dee2aaSAndroid Build Coastguard Worker                // There are two edges colocated at the beginning. Leave the first one double sided
342*c8dee2aaSAndroid Build Coastguard Worker                // for seaming with the previous stroke. (The double sided edge at the end will
343*c8dee2aaSAndroid Build Coastguard Worker                // actually come from the section of our strip that belongs to the stroke.)
344*c8dee2aaSAndroid Build Coastguard Worker                strokeOutset = (turn < 0) ? min(strokeOutset, 0) : max(strokeOutset, 0);
345*c8dee2aaSAndroid Build Coastguard Worker            }
346*c8dee2aaSAndroid Build Coastguard Worker        }
347*c8dee2aaSAndroid Build Coastguard Worker    } else {
348*c8dee2aaSAndroid Build Coastguard Worker        // We belong to the stroke. Unless numRadialSegmentsPerRadian is incredibly high,
349*c8dee2aaSAndroid Build Coastguard Worker        // clamping to maxCombinedSegments will be a no-op because the draw call was invoked with
350*c8dee2aaSAndroid Build Coastguard Worker        // sufficient vertices to cover the worst case scenario of 180 degree rotation.
351*c8dee2aaSAndroid Build Coastguard Worker        float maxCombinedSegments = maxEdges - numEdgesInJoin - 1;
352*c8dee2aaSAndroid Build Coastguard Worker        numRadialSegments = max(ceil(abs(rotation) * numRadialSegmentsPerRadian), 1);
353*c8dee2aaSAndroid Build Coastguard Worker        numRadialSegments = min(numRadialSegments, maxCombinedSegments);
354*c8dee2aaSAndroid Build Coastguard Worker        numParametricSegments = min(numParametricSegments,
355*c8dee2aaSAndroid Build Coastguard Worker                                    maxCombinedSegments - numRadialSegments + 1);
356*c8dee2aaSAndroid Build Coastguard Worker    }
357*c8dee2aaSAndroid Build Coastguard Worker
358*c8dee2aaSAndroid Build Coastguard Worker    // Additional parameters for final tessellation evaluation.
359*c8dee2aaSAndroid Build Coastguard Worker    float radsPerSegment = rotation / numRadialSegments;
360*c8dee2aaSAndroid Build Coastguard Worker    float numCombinedSegments = numParametricSegments + numRadialSegments - 1;
361*c8dee2aaSAndroid Build Coastguard Worker    bool isFinalEdge = (combinedEdgeID >= numCombinedSegments);
362*c8dee2aaSAndroid Build Coastguard Worker    if (combinedEdgeID > numCombinedSegments) {
363*c8dee2aaSAndroid Build Coastguard Worker        strokeOutset = 0;  // The strip has more edges than we need. Drop this one.
364*c8dee2aaSAndroid Build Coastguard Worker    }
365*c8dee2aaSAndroid Build Coastguard Worker    // Edge #2 extends to the miter point.
366*c8dee2aaSAndroid Build Coastguard Worker    if (abs(edgeID) == 2 && joinType > 0/*Is the join a miter type?*/) {
367*c8dee2aaSAndroid Build Coastguard Worker        strokeOutset *= $miter_extent(cosTheta, joinType/*miterLimit*/);
368*c8dee2aaSAndroid Build Coastguard Worker    }
369*c8dee2aaSAndroid Build Coastguard Worker
370*c8dee2aaSAndroid Build Coastguard Worker    float2 tangent, strokeCoord;
371*c8dee2aaSAndroid Build Coastguard Worker    if (combinedEdgeID != 0 && !isFinalEdge) {
372*c8dee2aaSAndroid Build Coastguard Worker        // Compute the location and tangent direction of the stroke edge with the integral id
373*c8dee2aaSAndroid Build Coastguard Worker        // "combinedEdgeID", where combinedEdgeID is the sorted-order index of parametric and radial
374*c8dee2aaSAndroid Build Coastguard Worker        // edges. Start by finding the tangent function's power basis coefficients. These define a
375*c8dee2aaSAndroid Build Coastguard Worker        // tangent direction (scaled by some uniform value) as:
376*c8dee2aaSAndroid Build Coastguard Worker        //                                                 |T^2|
377*c8dee2aaSAndroid Build Coastguard Worker        //     Tangent_Direction(T) = dx,dy = |A  2B  C| * |T  |
378*c8dee2aaSAndroid Build Coastguard Worker        //                                    |.   .  .|   |1  |
379*c8dee2aaSAndroid Build Coastguard Worker        float2 A, B, C = p1 - p0;
380*c8dee2aaSAndroid Build Coastguard Worker        float2 D = p3 - p0;
381*c8dee2aaSAndroid Build Coastguard Worker        if (w >= 0.0) {
382*c8dee2aaSAndroid Build Coastguard Worker            // P0..P2 represent a conic and P3==P2. The derivative of a conic has a cumbersome
383*c8dee2aaSAndroid Build Coastguard Worker            // order-4 denominator. However, this isn't necessary if we are only interested in a
384*c8dee2aaSAndroid Build Coastguard Worker            // vector in the same *direction* as a given tangent line. Since the denominator scales
385*c8dee2aaSAndroid Build Coastguard Worker            // dx and dy uniformly, we can throw it out completely after evaluating the derivative
386*c8dee2aaSAndroid Build Coastguard Worker            // with the standard quotient rule. This leaves us with a simpler quadratic function
387*c8dee2aaSAndroid Build Coastguard Worker            // that we use to find a tangent.
388*c8dee2aaSAndroid Build Coastguard Worker            C *= w;
389*c8dee2aaSAndroid Build Coastguard Worker            B = .5*D - C;
390*c8dee2aaSAndroid Build Coastguard Worker            A = (w - 1.0) * D;
391*c8dee2aaSAndroid Build Coastguard Worker            p1 *= w;
392*c8dee2aaSAndroid Build Coastguard Worker        } else {
393*c8dee2aaSAndroid Build Coastguard Worker            float2 E = p2 - p1;
394*c8dee2aaSAndroid Build Coastguard Worker            B = E - C;
395*c8dee2aaSAndroid Build Coastguard Worker            A = fma(float2(-3), E, D);
396*c8dee2aaSAndroid Build Coastguard Worker        }
397*c8dee2aaSAndroid Build Coastguard Worker        // FIXME(crbug.com/800804,skbug.com/11268): Consider normalizing the exponents in A,B,C at
398*c8dee2aaSAndroid Build Coastguard Worker        // this point in order to prevent fp32 overflow.
399*c8dee2aaSAndroid Build Coastguard Worker
400*c8dee2aaSAndroid Build Coastguard Worker        // Now find the coefficients that give a tangent direction from a parametric edge ID:
401*c8dee2aaSAndroid Build Coastguard Worker        //
402*c8dee2aaSAndroid Build Coastguard Worker        //                                                                 |parametricEdgeID^2|
403*c8dee2aaSAndroid Build Coastguard Worker        //     Tangent_Direction(parametricEdgeID) = dx,dy = |A  B_  C_| * |parametricEdgeID  |
404*c8dee2aaSAndroid Build Coastguard Worker        //                                                   |.   .   .|   |1                 |
405*c8dee2aaSAndroid Build Coastguard Worker        //
406*c8dee2aaSAndroid Build Coastguard Worker        float2 B_ = B * (numParametricSegments * 2.0);
407*c8dee2aaSAndroid Build Coastguard Worker        float2 C_ = C * (numParametricSegments * numParametricSegments);
408*c8dee2aaSAndroid Build Coastguard Worker
409*c8dee2aaSAndroid Build Coastguard Worker        // Run a binary search to determine the highest parametric edge that is located on or before
410*c8dee2aaSAndroid Build Coastguard Worker        // the combinedEdgeID. A combined ID is determined by the sum of complete parametric and
411*c8dee2aaSAndroid Build Coastguard Worker        // radial segments behind it. i.e., find the highest parametric edge where:
412*c8dee2aaSAndroid Build Coastguard Worker        //
413*c8dee2aaSAndroid Build Coastguard Worker        //    parametricEdgeID + floor(numRadialSegmentsAtParametricT) <= combinedEdgeID
414*c8dee2aaSAndroid Build Coastguard Worker        //
415*c8dee2aaSAndroid Build Coastguard Worker        float lastParametricEdgeID = 0.0;
416*c8dee2aaSAndroid Build Coastguard Worker        float maxParametricEdgeID = min(numParametricSegments - 1.0, combinedEdgeID);
417*c8dee2aaSAndroid Build Coastguard Worker        float negAbsRadsPerSegment = -abs(radsPerSegment);
418*c8dee2aaSAndroid Build Coastguard Worker        float maxRotation0 = (1.0 + combinedEdgeID) * abs(radsPerSegment);
419*c8dee2aaSAndroid Build Coastguard Worker        for (float exp = 32.0; exp >= 1.0; exp *= 0.5) {
420*c8dee2aaSAndroid Build Coastguard Worker            // Test the parametric edge at lastParametricEdgeID + (32, 16, 8, 4, 2, 1).
421*c8dee2aaSAndroid Build Coastguard Worker            float testParametricID = lastParametricEdgeID + exp;
422*c8dee2aaSAndroid Build Coastguard Worker            if (testParametricID <= maxParametricEdgeID) {
423*c8dee2aaSAndroid Build Coastguard Worker                float2 testTan = fma(float2(testParametricID), A, B_);
424*c8dee2aaSAndroid Build Coastguard Worker                testTan = fma(float2(testParametricID), testTan, C_);
425*c8dee2aaSAndroid Build Coastguard Worker                float cosRotation = dot(normalize(testTan), tan0);
426*c8dee2aaSAndroid Build Coastguard Worker                float maxRotation = fma(testParametricID, negAbsRadsPerSegment, maxRotation0);
427*c8dee2aaSAndroid Build Coastguard Worker                maxRotation = min(maxRotation, $PI);
428*c8dee2aaSAndroid Build Coastguard Worker                // Is rotation <= maxRotation? (i.e., is the number of complete radial segments
429*c8dee2aaSAndroid Build Coastguard Worker                // behind testT, + testParametricID <= combinedEdgeID?)
430*c8dee2aaSAndroid Build Coastguard Worker                if (cosRotation >= cos(maxRotation)) {
431*c8dee2aaSAndroid Build Coastguard Worker                    // testParametricID is on or before the combinedEdgeID. Keep it!
432*c8dee2aaSAndroid Build Coastguard Worker                    lastParametricEdgeID = testParametricID;
433*c8dee2aaSAndroid Build Coastguard Worker                }
434*c8dee2aaSAndroid Build Coastguard Worker            }
435*c8dee2aaSAndroid Build Coastguard Worker        }
436*c8dee2aaSAndroid Build Coastguard Worker
437*c8dee2aaSAndroid Build Coastguard Worker        // Find the T value of the parametric edge at lastParametricEdgeID.
438*c8dee2aaSAndroid Build Coastguard Worker        float parametricT = lastParametricEdgeID / numParametricSegments;
439*c8dee2aaSAndroid Build Coastguard Worker
440*c8dee2aaSAndroid Build Coastguard Worker        // Now that we've identified the highest parametric edge on or before the
441*c8dee2aaSAndroid Build Coastguard Worker        // combinedEdgeID, the highest radial edge is easy:
442*c8dee2aaSAndroid Build Coastguard Worker        float lastRadialEdgeID = combinedEdgeID - lastParametricEdgeID;
443*c8dee2aaSAndroid Build Coastguard Worker
444*c8dee2aaSAndroid Build Coastguard Worker        // Find the angle of tan0, i.e. the angle between tan0 and the positive x axis.
445*c8dee2aaSAndroid Build Coastguard Worker        float angle0 = acos(clamp(tan0.x, -1.0, 1.0));
446*c8dee2aaSAndroid Build Coastguard Worker        angle0 = tan0.y >= 0.0 ? angle0 : -angle0;
447*c8dee2aaSAndroid Build Coastguard Worker
448*c8dee2aaSAndroid Build Coastguard Worker        // Find the tangent vector on the edge at lastRadialEdgeID. By construction it is already
449*c8dee2aaSAndroid Build Coastguard Worker        // normalized.
450*c8dee2aaSAndroid Build Coastguard Worker        float radialAngle = fma(lastRadialEdgeID, radsPerSegment, angle0);
451*c8dee2aaSAndroid Build Coastguard Worker        tangent = float2(cos(radialAngle), sin(radialAngle));
452*c8dee2aaSAndroid Build Coastguard Worker        float2 norm = float2(-tangent.y, tangent.x);
453*c8dee2aaSAndroid Build Coastguard Worker
454*c8dee2aaSAndroid Build Coastguard Worker        // Find the T value where the tangent is orthogonal to norm. This is a quadratic:
455*c8dee2aaSAndroid Build Coastguard Worker        //
456*c8dee2aaSAndroid Build Coastguard Worker        //     dot(norm, Tangent_Direction(T)) == 0
457*c8dee2aaSAndroid Build Coastguard Worker        //
458*c8dee2aaSAndroid Build Coastguard Worker        //                         |T^2|
459*c8dee2aaSAndroid Build Coastguard Worker        //     norm * |A  2B  C| * |T  | == 0
460*c8dee2aaSAndroid Build Coastguard Worker        //            |.   .  .|   |1  |
461*c8dee2aaSAndroid Build Coastguard Worker        //
462*c8dee2aaSAndroid Build Coastguard Worker        float a=dot(norm,A), b_over_2=dot(norm,B), c=dot(norm,C);
463*c8dee2aaSAndroid Build Coastguard Worker        float discr_over_4 = max(b_over_2*b_over_2 - a*c, 0.0);
464*c8dee2aaSAndroid Build Coastguard Worker        float q = sqrt(discr_over_4);
465*c8dee2aaSAndroid Build Coastguard Worker        if (b_over_2 > 0.0) {
466*c8dee2aaSAndroid Build Coastguard Worker            q = -q;
467*c8dee2aaSAndroid Build Coastguard Worker        }
468*c8dee2aaSAndroid Build Coastguard Worker        q -= b_over_2;
469*c8dee2aaSAndroid Build Coastguard Worker
470*c8dee2aaSAndroid Build Coastguard Worker        // Roots are q/a and c/q. Since each curve section does not inflect or rotate more than 180
471*c8dee2aaSAndroid Build Coastguard Worker        // degrees, there can only be one tangent orthogonal to "norm" inside 0..1. Pick the root
472*c8dee2aaSAndroid Build Coastguard Worker        // nearest .5.
473*c8dee2aaSAndroid Build Coastguard Worker        float _5qa = -.5*q*a;
474*c8dee2aaSAndroid Build Coastguard Worker        float2 root = (abs(fma(q,q,_5qa)) < abs(fma(a,c,_5qa))) ? float2(q,a) : float2(c,q);
475*c8dee2aaSAndroid Build Coastguard Worker
476*c8dee2aaSAndroid Build Coastguard Worker        // The root finder above can become unstable when lastRadialEdgeID == 0 (e.g., if there are
477*c8dee2aaSAndroid Build Coastguard Worker        // roots at exatly 0 and 1 both). radialT should always equal 0 in this case.
478*c8dee2aaSAndroid Build Coastguard Worker        float radialT = (lastRadialEdgeID != 0.0 && root.t != 0.0)
479*c8dee2aaSAndroid Build Coastguard Worker                            ? saturate(root.s / root.t)
480*c8dee2aaSAndroid Build Coastguard Worker                            : 0.0;
481*c8dee2aaSAndroid Build Coastguard Worker
482*c8dee2aaSAndroid Build Coastguard Worker        // Now that we've identified the T values of the last parametric and radial edges, our final
483*c8dee2aaSAndroid Build Coastguard Worker        // T value for combinedEdgeID is whichever is larger.
484*c8dee2aaSAndroid Build Coastguard Worker        float T = max(parametricT, radialT);
485*c8dee2aaSAndroid Build Coastguard Worker
486*c8dee2aaSAndroid Build Coastguard Worker        // Evaluate the cubic at T. Use De Casteljau's for its accuracy and stability.
487*c8dee2aaSAndroid Build Coastguard Worker        float2 ab = $unchecked_mix(p0, p1, T);
488*c8dee2aaSAndroid Build Coastguard Worker        float2 bc = $unchecked_mix(p1, p2, T);
489*c8dee2aaSAndroid Build Coastguard Worker        float2 cd = $unchecked_mix(p2, p3, T);
490*c8dee2aaSAndroid Build Coastguard Worker        float2 abc = $unchecked_mix(ab, bc, T);
491*c8dee2aaSAndroid Build Coastguard Worker        float2 bcd = $unchecked_mix(bc, cd, T);
492*c8dee2aaSAndroid Build Coastguard Worker        float2 abcd = $unchecked_mix(abc, bcd, T);
493*c8dee2aaSAndroid Build Coastguard Worker
494*c8dee2aaSAndroid Build Coastguard Worker        // Evaluate the conic weight at T.
495*c8dee2aaSAndroid Build Coastguard Worker        float u = $unchecked_mix(1.0, w, T);
496*c8dee2aaSAndroid Build Coastguard Worker        float v = w + 1 - u;  // == mix(w, 1, T)
497*c8dee2aaSAndroid Build Coastguard Worker        float uv = $unchecked_mix(u, v, T);
498*c8dee2aaSAndroid Build Coastguard Worker
499*c8dee2aaSAndroid Build Coastguard Worker        // If we went with T=parametricT, then update the tangent. Otherwise leave it at the radial
500*c8dee2aaSAndroid Build Coastguard Worker        // tangent found previously. (In the event that parametricT == radialT, we keep the radial
501*c8dee2aaSAndroid Build Coastguard Worker        // tangent.)
502*c8dee2aaSAndroid Build Coastguard Worker        if (T != radialT) {
503*c8dee2aaSAndroid Build Coastguard Worker            // We must re-normalize here because the tangent is determined by the curve coefficients
504*c8dee2aaSAndroid Build Coastguard Worker            tangent = w >= 0.0 ? $robust_normalize_diff(bc*u, ab*v)
505*c8dee2aaSAndroid Build Coastguard Worker                               : $robust_normalize_diff(bcd, abc);
506*c8dee2aaSAndroid Build Coastguard Worker        }
507*c8dee2aaSAndroid Build Coastguard Worker
508*c8dee2aaSAndroid Build Coastguard Worker        strokeCoord = (w >= 0.0) ? abc/uv : abcd;
509*c8dee2aaSAndroid Build Coastguard Worker    } else {
510*c8dee2aaSAndroid Build Coastguard Worker        // Edges at the beginning and end of the strip use exact endpoints and tangents. This
511*c8dee2aaSAndroid Build Coastguard Worker        // ensures crack-free seaming between instances.
512*c8dee2aaSAndroid Build Coastguard Worker        tangent = (combinedEdgeID == 0) ? tan0 : tan1;
513*c8dee2aaSAndroid Build Coastguard Worker        strokeCoord = (combinedEdgeID == 0) ? p0 : p3;
514*c8dee2aaSAndroid Build Coastguard Worker    }
515*c8dee2aaSAndroid Build Coastguard Worker
516*c8dee2aaSAndroid Build Coastguard Worker    // At this point 'tangent' is normalized, so the orthogonal vector is also normalized.
517*c8dee2aaSAndroid Build Coastguard Worker    float2 ortho = float2(tangent.y, -tangent.x);
518*c8dee2aaSAndroid Build Coastguard Worker    strokeCoord += ortho * (strokeRadius * strokeOutset);
519*c8dee2aaSAndroid Build Coastguard Worker
520*c8dee2aaSAndroid Build Coastguard Worker    if (isHairline) {
521*c8dee2aaSAndroid Build Coastguard Worker        // Hairline case. The scale and skew already happened before tessellation.
522*c8dee2aaSAndroid Build Coastguard Worker        // TODO: There's probably a more efficient way to tessellate the hairline that lets us
523*c8dee2aaSAndroid Build Coastguard Worker        // avoid inverting the affine matrix to get back to local coords, but it's just a 2x2 so
524*c8dee2aaSAndroid Build Coastguard Worker        // this works for now.
525*c8dee2aaSAndroid Build Coastguard Worker        return float4(strokeCoord + translate, inverse(affineMatrix) * strokeCoord);
526*c8dee2aaSAndroid Build Coastguard Worker    } else {
527*c8dee2aaSAndroid Build Coastguard Worker        // Normal case. Do the transform after tessellation.
528*c8dee2aaSAndroid Build Coastguard Worker        return float4(affineMatrix * strokeCoord + translate, strokeCoord);
529*c8dee2aaSAndroid Build Coastguard Worker    }
530*c8dee2aaSAndroid Build Coastguard Worker}
531*c8dee2aaSAndroid Build Coastguard Worker
532*c8dee2aaSAndroid Build Coastguard Workerfloat4 analytic_rrect_vertex_fn(// Vertex Attributes
533*c8dee2aaSAndroid Build Coastguard Worker                                float2 position,
534*c8dee2aaSAndroid Build Coastguard Worker                                float2 normal,
535*c8dee2aaSAndroid Build Coastguard Worker                                float normalScale,
536*c8dee2aaSAndroid Build Coastguard Worker                                float centerWeight,
537*c8dee2aaSAndroid Build Coastguard Worker                                // Instance Attributes
538*c8dee2aaSAndroid Build Coastguard Worker                                float4 xRadiiOrFlags,
539*c8dee2aaSAndroid Build Coastguard Worker                                float4 radiiOrQuadXs,
540*c8dee2aaSAndroid Build Coastguard Worker                                float4 ltrbOrQuadYs,
541*c8dee2aaSAndroid Build Coastguard Worker                                float4 center,
542*c8dee2aaSAndroid Build Coastguard Worker                                float depth,
543*c8dee2aaSAndroid Build Coastguard Worker                                float3x3 localToDevice,
544*c8dee2aaSAndroid Build Coastguard Worker                                // Varyings
545*c8dee2aaSAndroid Build Coastguard Worker                                out float4 jacobian,
546*c8dee2aaSAndroid Build Coastguard Worker                                out float4 edgeDistances,
547*c8dee2aaSAndroid Build Coastguard Worker                                out float4 xRadii,
548*c8dee2aaSAndroid Build Coastguard Worker                                out float4 yRadii,
549*c8dee2aaSAndroid Build Coastguard Worker                                out float2 strokeParams,
550*c8dee2aaSAndroid Build Coastguard Worker                                out float2 perPixelControl,
551*c8dee2aaSAndroid Build Coastguard Worker                                // Render Step
552*c8dee2aaSAndroid Build Coastguard Worker                                out float2 stepLocalCoords) {
553*c8dee2aaSAndroid Build Coastguard Worker    const uint kCornerVertexCount = 9; // KEEP IN SYNC WITH C++'s
554*c8dee2aaSAndroid Build Coastguard Worker                                       // AnalyticRRectRenderStep::kCornerVertexCount
555*c8dee2aaSAndroid Build Coastguard Worker    const float kMiterScale = 1.0;
556*c8dee2aaSAndroid Build Coastguard Worker    const float kBevelScale = 0.0;
557*c8dee2aaSAndroid Build Coastguard Worker    const float kRoundScale = 0.41421356237; // sqrt(2)-1
558*c8dee2aaSAndroid Build Coastguard Worker
559*c8dee2aaSAndroid Build Coastguard Worker    const float kEpsilon = 0.00024; // SK_ScalarNearlyZero
560*c8dee2aaSAndroid Build Coastguard Worker
561*c8dee2aaSAndroid Build Coastguard Worker    // Default to miter'ed vertex positioning. Corners with sufficiently large corner radii, or
562*c8dee2aaSAndroid Build Coastguard Worker    // bevel'ed strokes will adjust vertex placement on a per corner basis. This will not affect
563*c8dee2aaSAndroid Build Coastguard Worker    // the final coverage calculations in the fragment shader.
564*c8dee2aaSAndroid Build Coastguard Worker    float joinScale = kMiterScale;
565*c8dee2aaSAndroid Build Coastguard Worker
566*c8dee2aaSAndroid Build Coastguard Worker    // Unpack instance-level state that determines the vertex placement and style of shape.
567*c8dee2aaSAndroid Build Coastguard Worker    bool bidirectionalCoverage = center.z <= 0.0;
568*c8dee2aaSAndroid Build Coastguard Worker    bool deviceSpaceDistances = false;
569*c8dee2aaSAndroid Build Coastguard Worker    float4 xs, ys; // ordered TL, TR, BR, BL
570*c8dee2aaSAndroid Build Coastguard Worker    float4 edgeAA = float4(1.0); // ordered L,T,R,B. 1 = AA, 0 = no AA
571*c8dee2aaSAndroid Build Coastguard Worker    bool strokedLine = false;
572*c8dee2aaSAndroid Build Coastguard Worker    if (xRadiiOrFlags.x < -1.0) {
573*c8dee2aaSAndroid Build Coastguard Worker        // Stroked [round] rect or line
574*c8dee2aaSAndroid Build Coastguard Worker        // If y > 0, unpack the line end points, otherwise unpack the rect edges
575*c8dee2aaSAndroid Build Coastguard Worker        strokedLine = xRadiiOrFlags.y > 0.0;
576*c8dee2aaSAndroid Build Coastguard Worker        xs = strokedLine ? ltrbOrQuadYs.LLRR : ltrbOrQuadYs.LRRL;
577*c8dee2aaSAndroid Build Coastguard Worker        ys = ltrbOrQuadYs.TTBB;
578*c8dee2aaSAndroid Build Coastguard Worker
579*c8dee2aaSAndroid Build Coastguard Worker        if (xRadiiOrFlags.y < 0.0) {
580*c8dee2aaSAndroid Build Coastguard Worker            // A hairline [r]rect so the X radii are encoded as negative values in this field,
581*c8dee2aaSAndroid Build Coastguard Worker            // and Y radii are stored directly in the subsequent float4.
582*c8dee2aaSAndroid Build Coastguard Worker            xRadii = -xRadiiOrFlags - 2.0;
583*c8dee2aaSAndroid Build Coastguard Worker            yRadii = radiiOrQuadXs;
584*c8dee2aaSAndroid Build Coastguard Worker
585*c8dee2aaSAndroid Build Coastguard Worker            // All hairlines use miter joins (join style > 0)
586*c8dee2aaSAndroid Build Coastguard Worker            strokeParams = float2(0.0, 1.0);
587*c8dee2aaSAndroid Build Coastguard Worker        } else {
588*c8dee2aaSAndroid Build Coastguard Worker            xRadii = radiiOrQuadXs;
589*c8dee2aaSAndroid Build Coastguard Worker            yRadii = xRadii; // regular strokes are circular
590*c8dee2aaSAndroid Build Coastguard Worker            strokeParams = xRadiiOrFlags.zw;
591*c8dee2aaSAndroid Build Coastguard Worker
592*c8dee2aaSAndroid Build Coastguard Worker            // `sign(strokeParams.y)` evaluates to kMiterScale (1.0) when the
593*c8dee2aaSAndroid Build Coastguard Worker            // input is positive, and kBevelScale (0.0) when it is zero.
594*c8dee2aaSAndroid Build Coastguard Worker            // kRoundScale uses the stroke radius to round rectangular corners.
595*c8dee2aaSAndroid Build Coastguard Worker            joinScale = (strokeParams.y < 0.0) ? kRoundScale
596*c8dee2aaSAndroid Build Coastguard Worker                                               : sign(strokeParams.y);
597*c8dee2aaSAndroid Build Coastguard Worker        }
598*c8dee2aaSAndroid Build Coastguard Worker    } else if (any(greaterThan(xRadiiOrFlags, float4(0.0)))) {
599*c8dee2aaSAndroid Build Coastguard Worker        // Filled round rect
600*c8dee2aaSAndroid Build Coastguard Worker        xs = ltrbOrQuadYs.LRRL;
601*c8dee2aaSAndroid Build Coastguard Worker        ys = ltrbOrQuadYs.TTBB;
602*c8dee2aaSAndroid Build Coastguard Worker
603*c8dee2aaSAndroid Build Coastguard Worker        xRadii = xRadiiOrFlags;
604*c8dee2aaSAndroid Build Coastguard Worker        yRadii = radiiOrQuadXs;
605*c8dee2aaSAndroid Build Coastguard Worker
606*c8dee2aaSAndroid Build Coastguard Worker        strokeParams = float2(0.0, -1.0); // A negative join style is "round"
607*c8dee2aaSAndroid Build Coastguard Worker    } else {
608*c8dee2aaSAndroid Build Coastguard Worker        // Per-edge quadrilateral, so we have to calculate the corner's basis from the
609*c8dee2aaSAndroid Build Coastguard Worker        // quad's edges.
610*c8dee2aaSAndroid Build Coastguard Worker        xs = radiiOrQuadXs;
611*c8dee2aaSAndroid Build Coastguard Worker        ys = ltrbOrQuadYs;
612*c8dee2aaSAndroid Build Coastguard Worker        edgeAA = -xRadiiOrFlags; // AA flags needed to be < 0 on upload, so flip the sign.
613*c8dee2aaSAndroid Build Coastguard Worker
614*c8dee2aaSAndroid Build Coastguard Worker        xRadii = float4(0.0);
615*c8dee2aaSAndroid Build Coastguard Worker        yRadii = float4(0.0);
616*c8dee2aaSAndroid Build Coastguard Worker
617*c8dee2aaSAndroid Build Coastguard Worker        strokeParams = float2(0.0, 1.0); // Will be ignored, but set to a "miter"
618*c8dee2aaSAndroid Build Coastguard Worker        deviceSpaceDistances = true;
619*c8dee2aaSAndroid Build Coastguard Worker    }
620*c8dee2aaSAndroid Build Coastguard Worker
621*c8dee2aaSAndroid Build Coastguard Worker    // Adjust state on a per-corner basis
622*c8dee2aaSAndroid Build Coastguard Worker    uint cornerID = uint(sk_VertexID) / kCornerVertexCount;
623*c8dee2aaSAndroid Build Coastguard Worker    float2 cornerRadii = float2(xRadii[cornerID], yRadii[cornerID]);
624*c8dee2aaSAndroid Build Coastguard Worker    if (cornerID % 2 != 0) {
625*c8dee2aaSAndroid Build Coastguard Worker        // Corner radii are uploaded in the local coordinate frame, but vertex placement happens
626*c8dee2aaSAndroid Build Coastguard Worker        // in a consistent winding before transforming to final local coords, so swap the
627*c8dee2aaSAndroid Build Coastguard Worker        // radii for odd corners.
628*c8dee2aaSAndroid Build Coastguard Worker        cornerRadii = cornerRadii.yx;
629*c8dee2aaSAndroid Build Coastguard Worker    }
630*c8dee2aaSAndroid Build Coastguard Worker
631*c8dee2aaSAndroid Build Coastguard Worker    float2 cornerAspectRatio = float2(1.0);
632*c8dee2aaSAndroid Build Coastguard Worker    if (all(greaterThan(cornerRadii, float2(0.0)))) {
633*c8dee2aaSAndroid Build Coastguard Worker        // Position vertices for an elliptical corner; overriding any previous join style since
634*c8dee2aaSAndroid Build Coastguard Worker        // that only applies when radii are 0.
635*c8dee2aaSAndroid Build Coastguard Worker        joinScale = kRoundScale;
636*c8dee2aaSAndroid Build Coastguard Worker        cornerAspectRatio = cornerRadii.yx;
637*c8dee2aaSAndroid Build Coastguard Worker    }
638*c8dee2aaSAndroid Build Coastguard Worker
639*c8dee2aaSAndroid Build Coastguard Worker    // Calculate the local edge vectors, ordered L, T, R, B starting from the bottom left point.
640*c8dee2aaSAndroid Build Coastguard Worker    // For quadrilaterals these are not necessarily axis-aligned, but in all cases they orient
641*c8dee2aaSAndroid Build Coastguard Worker    // the +X/+Y normalized vertex template for each corner.
642*c8dee2aaSAndroid Build Coastguard Worker    float4 dx = xs - xs.wxyz;
643*c8dee2aaSAndroid Build Coastguard Worker    float4 dy = ys - ys.wxyz;
644*c8dee2aaSAndroid Build Coastguard Worker    float4 edgeSquaredLen = dx*dx + dy*dy;
645*c8dee2aaSAndroid Build Coastguard Worker
646*c8dee2aaSAndroid Build Coastguard Worker    float4 edgeMask = sign(edgeSquaredLen); // 0 for zero-length edge, 1 for non-zero edge.
647*c8dee2aaSAndroid Build Coastguard Worker    float4 edgeBias = float4(0.0); // adjustment to edge distance for butt cap correction
648*c8dee2aaSAndroid Build Coastguard Worker    float2 strokeRadius = float2(strokeParams.x);
649*c8dee2aaSAndroid Build Coastguard Worker    if (any(equal(edgeMask, float4(0.0)))) {
650*c8dee2aaSAndroid Build Coastguard Worker        // Must clean up (dx,dy) depending on the empty edge configuration
651*c8dee2aaSAndroid Build Coastguard Worker        if (all(equal(edgeMask, float4(0.0)))) {
652*c8dee2aaSAndroid Build Coastguard Worker            // A point so use the canonical basis
653*c8dee2aaSAndroid Build Coastguard Worker            dx = float4( 0.0, 1.0, 0.0, -1.0);
654*c8dee2aaSAndroid Build Coastguard Worker            dy = float4(-1.0, 0.0, 1.0,  0.0);
655*c8dee2aaSAndroid Build Coastguard Worker            edgeSquaredLen = float4(1.0);
656*c8dee2aaSAndroid Build Coastguard Worker        } else {
657*c8dee2aaSAndroid Build Coastguard Worker            // Triangles (3 non-zero edges) copy the adjacent edge. Otherwise it's a line so
658*c8dee2aaSAndroid Build Coastguard Worker            // replace empty edges with the left-hand normal vector of the adjacent edge.
659*c8dee2aaSAndroid Build Coastguard Worker            bool triangle = (edgeMask[0] + edgeMask[1] + edgeMask[2] + edgeMask[3]) > 2.5;
660*c8dee2aaSAndroid Build Coastguard Worker            float4 edgeX = triangle ? dx.yzwx :  dy.yzwx;
661*c8dee2aaSAndroid Build Coastguard Worker            float4 edgeY = triangle ? dy.yzwx : -dx.yzwx;
662*c8dee2aaSAndroid Build Coastguard Worker
663*c8dee2aaSAndroid Build Coastguard Worker            dx = mix(edgeX, dx, edgeMask);
664*c8dee2aaSAndroid Build Coastguard Worker            dy = mix(edgeY, dy, edgeMask);
665*c8dee2aaSAndroid Build Coastguard Worker            edgeSquaredLen = mix(edgeSquaredLen.yzwx, edgeSquaredLen, edgeMask);
666*c8dee2aaSAndroid Build Coastguard Worker            edgeAA = mix(edgeAA.yzwx, edgeAA, edgeMask);
667*c8dee2aaSAndroid Build Coastguard Worker
668*c8dee2aaSAndroid Build Coastguard Worker            if (!triangle && joinScale == kBevelScale) {
669*c8dee2aaSAndroid Build Coastguard Worker                // Don't outset by stroke radius for butt caps on the zero-length edge, but
670*c8dee2aaSAndroid Build Coastguard Worker                // adjust edgeBias and strokeParams to calculate an AA miter'ed shape with the
671*c8dee2aaSAndroid Build Coastguard Worker                // non-uniform stroke outset.
672*c8dee2aaSAndroid Build Coastguard Worker                strokeRadius *= float2(edgeMask[cornerID], edgeMask.yzwx[cornerID]);
673*c8dee2aaSAndroid Build Coastguard Worker                edgeBias = (edgeMask - 1.0) * strokeParams.x;
674*c8dee2aaSAndroid Build Coastguard Worker                strokeParams.y = 1.0;
675*c8dee2aaSAndroid Build Coastguard Worker                joinScale = kMiterScale;
676*c8dee2aaSAndroid Build Coastguard Worker            }
677*c8dee2aaSAndroid Build Coastguard Worker        }
678*c8dee2aaSAndroid Build Coastguard Worker    }
679*c8dee2aaSAndroid Build Coastguard Worker
680*c8dee2aaSAndroid Build Coastguard Worker    float4 inverseEdgeLen = inversesqrt(edgeSquaredLen);
681*c8dee2aaSAndroid Build Coastguard Worker    dx *= inverseEdgeLen;
682*c8dee2aaSAndroid Build Coastguard Worker    dy *= inverseEdgeLen;
683*c8dee2aaSAndroid Build Coastguard Worker
684*c8dee2aaSAndroid Build Coastguard Worker    // Calculate local coordinate for the vertex (relative to xAxis and yAxis at first).
685*c8dee2aaSAndroid Build Coastguard Worker    float2 xAxis = -float2(dx.yzwx[cornerID], dy.yzwx[cornerID]);
686*c8dee2aaSAndroid Build Coastguard Worker    float2 yAxis =  float2(dx.xyzw[cornerID], dy.xyzw[cornerID]);
687*c8dee2aaSAndroid Build Coastguard Worker    float2 localPos;
688*c8dee2aaSAndroid Build Coastguard Worker    bool snapToCenter = false;
689*c8dee2aaSAndroid Build Coastguard Worker    if (normalScale < 0.0) {
690*c8dee2aaSAndroid Build Coastguard Worker        // Vertex is inset from the base shape, so we scale by (cornerRadii - strokeRadius)
691*c8dee2aaSAndroid Build Coastguard Worker        // and have to check for the possibility of an inner miter. It is always inset by an
692*c8dee2aaSAndroid Build Coastguard Worker        // additional conservative AA amount.
693*c8dee2aaSAndroid Build Coastguard Worker        if (center.w < 0.0 || centerWeight * center.z != 0.0) {
694*c8dee2aaSAndroid Build Coastguard Worker            snapToCenter = true;
695*c8dee2aaSAndroid Build Coastguard Worker        } else {
696*c8dee2aaSAndroid Build Coastguard Worker            float localAARadius = center.w;
697*c8dee2aaSAndroid Build Coastguard Worker            float2 insetRadii =
698*c8dee2aaSAndroid Build Coastguard Worker                    cornerRadii + (bidirectionalCoverage ? -strokeRadius : strokeRadius);
699*c8dee2aaSAndroid Build Coastguard Worker            if (joinScale == kMiterScale ||
700*c8dee2aaSAndroid Build Coastguard Worker                any(lessThanEqual(insetRadii, float2(localAARadius)))) {
701*c8dee2aaSAndroid Build Coastguard Worker                // Miter the inset position
702*c8dee2aaSAndroid Build Coastguard Worker                localPos = (insetRadii - localAARadius);
703*c8dee2aaSAndroid Build Coastguard Worker            } else {
704*c8dee2aaSAndroid Build Coastguard Worker                localPos = insetRadii*position - localAARadius*normal;
705*c8dee2aaSAndroid Build Coastguard Worker            }
706*c8dee2aaSAndroid Build Coastguard Worker        }
707*c8dee2aaSAndroid Build Coastguard Worker    } else {
708*c8dee2aaSAndroid Build Coastguard Worker        // Vertex is outset from the base shape (and possibly with an additional AA outset later
709*c8dee2aaSAndroid Build Coastguard Worker        // in device space).
710*c8dee2aaSAndroid Build Coastguard Worker        localPos = (cornerRadii + strokeRadius) * (position + joinScale*position.yx);
711*c8dee2aaSAndroid Build Coastguard Worker    }
712*c8dee2aaSAndroid Build Coastguard Worker
713*c8dee2aaSAndroid Build Coastguard Worker    if (snapToCenter) {
714*c8dee2aaSAndroid Build Coastguard Worker        // Center is already relative to true local coords, not the corner basis.
715*c8dee2aaSAndroid Build Coastguard Worker        localPos = center.xy;
716*c8dee2aaSAndroid Build Coastguard Worker    } else {
717*c8dee2aaSAndroid Build Coastguard Worker        // Transform from corner basis to true local coords.
718*c8dee2aaSAndroid Build Coastguard Worker        localPos -= cornerRadii;
719*c8dee2aaSAndroid Build Coastguard Worker        localPos = float2(xs[cornerID], ys[cornerID]) + xAxis*localPos.x + yAxis*localPos.y;
720*c8dee2aaSAndroid Build Coastguard Worker    }
721*c8dee2aaSAndroid Build Coastguard Worker
722*c8dee2aaSAndroid Build Coastguard Worker    // Calculate edge distances and device space coordinate for the vertex
723*c8dee2aaSAndroid Build Coastguard Worker    edgeDistances = dy*(xs - localPos.x) - dx*(ys - localPos.y) + edgeBias;
724*c8dee2aaSAndroid Build Coastguard Worker
725*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: This 3x3 inverse is different than just taking the 1st two columns of the 4x4
726*c8dee2aaSAndroid Build Coastguard Worker    // inverse of the original SkM44 local-to-device matrix. We could calculate the 3x3 inverse
727*c8dee2aaSAndroid Build Coastguard Worker    // and upload it, but it does not seem to be a bottleneck and saves on bandwidth to
728*c8dee2aaSAndroid Build Coastguard Worker    // calculate it here instead.
729*c8dee2aaSAndroid Build Coastguard Worker    float3x3 deviceToLocal = inverse(localToDevice);
730*c8dee2aaSAndroid Build Coastguard Worker    float3 devPos = localToDevice * localPos.xy1;
731*c8dee2aaSAndroid Build Coastguard Worker    jacobian = float4(deviceToLocal[0].xy - deviceToLocal[0].z*localPos,
732*c8dee2aaSAndroid Build Coastguard Worker                      deviceToLocal[1].xy - deviceToLocal[1].z*localPos);
733*c8dee2aaSAndroid Build Coastguard Worker
734*c8dee2aaSAndroid Build Coastguard Worker    if (deviceSpaceDistances) {
735*c8dee2aaSAndroid Build Coastguard Worker        // Apply the Jacobian in the vertex shader so any quadrilateral normals do not have to
736*c8dee2aaSAndroid Build Coastguard Worker        // be passed to the fragment shader. However, it's important to use the Jacobian at a
737*c8dee2aaSAndroid Build Coastguard Worker        // vertex on the edge, not the current vertex's Jacobian.
738*c8dee2aaSAndroid Build Coastguard Worker        float4 gx = -dy*(deviceToLocal[0].x - deviceToLocal[0].z*xs) +
739*c8dee2aaSAndroid Build Coastguard Worker                     dx*(deviceToLocal[0].y - deviceToLocal[0].z*ys);
740*c8dee2aaSAndroid Build Coastguard Worker        float4 gy = -dy*(deviceToLocal[1].x - deviceToLocal[1].z*xs) +
741*c8dee2aaSAndroid Build Coastguard Worker                     dx*(deviceToLocal[1].y - deviceToLocal[1].z*ys);
742*c8dee2aaSAndroid Build Coastguard Worker        // NOTE: The gradient is missing a W term so edgeDistances must still be multiplied by
743*c8dee2aaSAndroid Build Coastguard Worker        // 1/w in the fragment shader. The same goes for the encoded coverage scale.
744*c8dee2aaSAndroid Build Coastguard Worker        edgeDistances *= inversesqrt(gx*gx + gy*gy);
745*c8dee2aaSAndroid Build Coastguard Worker
746*c8dee2aaSAndroid Build Coastguard Worker        // Bias non-AA edge distances by device W so its coverage contribution is >= 1.0
747*c8dee2aaSAndroid Build Coastguard Worker        edgeDistances += (1 - edgeAA)*abs(devPos.z);
748*c8dee2aaSAndroid Build Coastguard Worker
749*c8dee2aaSAndroid Build Coastguard Worker        // Mixed edge AA shapes do not use subpixel scale+bias for coverage, since they tile
750*c8dee2aaSAndroid Build Coastguard Worker        // to a large shape of unknown--but likely not subpixel--size. Triangles and quads do
751*c8dee2aaSAndroid Build Coastguard Worker        // not use subpixel coverage since the scale+bias is not constant over the shape, but
752*c8dee2aaSAndroid Build Coastguard Worker        // we can't evaluate per-fragment since we aren't passing down their arbitrary normals.
753*c8dee2aaSAndroid Build Coastguard Worker        bool subpixelCoverage = edgeAA == float4(1.0) &&
754*c8dee2aaSAndroid Build Coastguard Worker                                dot(abs(dx*dx.yzwx + dy*dy.yzwx), float4(1.0)) < kEpsilon;
755*c8dee2aaSAndroid Build Coastguard Worker        if (subpixelCoverage) {
756*c8dee2aaSAndroid Build Coastguard Worker            // Reconstructs the actual device-space width and height for all rectangle vertices.
757*c8dee2aaSAndroid Build Coastguard Worker            float2 dim = edgeDistances.xy + edgeDistances.zw;
758*c8dee2aaSAndroid Build Coastguard Worker            perPixelControl.y = 1.0 + min(min(dim.x, dim.y), abs(devPos.z));
759*c8dee2aaSAndroid Build Coastguard Worker        } else {
760*c8dee2aaSAndroid Build Coastguard Worker            perPixelControl.y = 1.0 + abs(devPos.z); // standard 1px width pre W division.
761*c8dee2aaSAndroid Build Coastguard Worker        }
762*c8dee2aaSAndroid Build Coastguard Worker    }
763*c8dee2aaSAndroid Build Coastguard Worker
764*c8dee2aaSAndroid Build Coastguard Worker    // Only outset for a vertex that is in front of the w=0 plane to avoid dealing with outset
765*c8dee2aaSAndroid Build Coastguard Worker    // triangles rasterizing differently from the main triangles as w crosses 0.
766*c8dee2aaSAndroid Build Coastguard Worker    if (normalScale > 0.0 && devPos.z > 0.0) {
767*c8dee2aaSAndroid Build Coastguard Worker        // Note that when there's no perspective, the jacobian is equivalent to the normal
768*c8dee2aaSAndroid Build Coastguard Worker        // matrix (inverse transpose), but produces correct results when there's perspective
769*c8dee2aaSAndroid Build Coastguard Worker        // because it accounts for the position's influence on a line's projected direction.
770*c8dee2aaSAndroid Build Coastguard Worker        float2x2 J = float2x2(jacobian);
771*c8dee2aaSAndroid Build Coastguard Worker
772*c8dee2aaSAndroid Build Coastguard Worker        float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA.yzwx[cornerID]) * normal;
773*c8dee2aaSAndroid Build Coastguard Worker        float2 nx = cornerAspectRatio.x * edgeAANormal.x * perp(-yAxis) * J;
774*c8dee2aaSAndroid Build Coastguard Worker        float2 ny = cornerAspectRatio.y * edgeAANormal.y * perp( xAxis) * J;
775*c8dee2aaSAndroid Build Coastguard Worker
776*c8dee2aaSAndroid Build Coastguard Worker        bool isMidVertex = all(notEqual(edgeAANormal, float2(0)));
777*c8dee2aaSAndroid Build Coastguard Worker        if (joinScale == kMiterScale && isMidVertex) {
778*c8dee2aaSAndroid Build Coastguard Worker            // Produce a bisecting vector in device space.
779*c8dee2aaSAndroid Build Coastguard Worker            nx = normalize(nx);
780*c8dee2aaSAndroid Build Coastguard Worker            ny = normalize(ny);
781*c8dee2aaSAndroid Build Coastguard Worker            if (dot(nx, ny) < -0.8) {
782*c8dee2aaSAndroid Build Coastguard Worker                // Normals are in nearly opposite directions, so adjust to avoid float error.
783*c8dee2aaSAndroid Build Coastguard Worker                float s = sign(cross_length_2d(nx, ny));
784*c8dee2aaSAndroid Build Coastguard Worker                nx =  s*perp(nx);
785*c8dee2aaSAndroid Build Coastguard Worker                ny = -s*perp(ny);
786*c8dee2aaSAndroid Build Coastguard Worker            }
787*c8dee2aaSAndroid Build Coastguard Worker        }
788*c8dee2aaSAndroid Build Coastguard Worker        // Adding the normal components together directly results in what we'd have
789*c8dee2aaSAndroid Build Coastguard Worker        // calculated if we'd just transformed 'normal' in one go, assuming they weren't
790*c8dee2aaSAndroid Build Coastguard Worker        // normalized in the if-block above. If they were normalized, the sum equals the
791*c8dee2aaSAndroid Build Coastguard Worker        // bisector between the original nx and ny.
792*c8dee2aaSAndroid Build Coastguard Worker        //
793*c8dee2aaSAndroid Build Coastguard Worker        // We multiply by W so that after perspective division the new point is offset by the
794*c8dee2aaSAndroid Build Coastguard Worker        // now-unit normal.
795*c8dee2aaSAndroid Build Coastguard Worker        // NOTE: (nx + ny) can become the zero vector if the device outset is for an edge
796*c8dee2aaSAndroid Build Coastguard Worker        // marked as non-AA. In this case normalize() could produce the zero vector or NaN.
797*c8dee2aaSAndroid Build Coastguard Worker        // Until a counter-example is found, GPUs seem to discard triangles with NaN vertices,
798*c8dee2aaSAndroid Build Coastguard Worker        // which has the same effect as outsetting by the zero vector with this mesh, so we
799*c8dee2aaSAndroid Build Coastguard Worker        // don't bother guarding the normalize() (yet).
800*c8dee2aaSAndroid Build Coastguard Worker        devPos.xy += devPos.z * normalize(nx + ny);
801*c8dee2aaSAndroid Build Coastguard Worker
802*c8dee2aaSAndroid Build Coastguard Worker        // By construction these points are 1px away from the outer edge in device space.
803*c8dee2aaSAndroid Build Coastguard Worker        if (deviceSpaceDistances) {
804*c8dee2aaSAndroid Build Coastguard Worker            // Apply directly to edgeDistances to save work per pixel later on.
805*c8dee2aaSAndroid Build Coastguard Worker            edgeDistances -= devPos.z;
806*c8dee2aaSAndroid Build Coastguard Worker        } else {
807*c8dee2aaSAndroid Build Coastguard Worker            // Otherwise store separately so edgeDistances can be used to reconstruct corner pos
808*c8dee2aaSAndroid Build Coastguard Worker            perPixelControl.y = -devPos.z;
809*c8dee2aaSAndroid Build Coastguard Worker        }
810*c8dee2aaSAndroid Build Coastguard Worker    } else if (!deviceSpaceDistances) {
811*c8dee2aaSAndroid Build Coastguard Worker        // Triangles are within the original shape so there's no additional outsetting to
812*c8dee2aaSAndroid Build Coastguard Worker        // take into account for coverage calculations.
813*c8dee2aaSAndroid Build Coastguard Worker        perPixelControl.y = 0.0;
814*c8dee2aaSAndroid Build Coastguard Worker    }
815*c8dee2aaSAndroid Build Coastguard Worker
816*c8dee2aaSAndroid Build Coastguard Worker    perPixelControl.x = (centerWeight != 0.0)
817*c8dee2aaSAndroid Build Coastguard Worker            // A positive value signals that a pixel is trivially full coverage.
818*c8dee2aaSAndroid Build Coastguard Worker            ? 1.0
819*c8dee2aaSAndroid Build Coastguard Worker            // A negative value signals bidirectional coverage, and a zero value signals a solid
820*c8dee2aaSAndroid Build Coastguard Worker            // interior with per-pixel coverage.
821*c8dee2aaSAndroid Build Coastguard Worker            : bidirectionalCoverage ? -1.0 : 0.0;
822*c8dee2aaSAndroid Build Coastguard Worker
823*c8dee2aaSAndroid Build Coastguard Worker    // The fragment shader operates in a canonical basis (x-axis = (1,0), y-axis = (0,1)). For
824*c8dee2aaSAndroid Build Coastguard Worker    // stroked lines, incorporate their local orientation into the Jacobian to preserve this.
825*c8dee2aaSAndroid Build Coastguard Worker    if (strokedLine) {
826*c8dee2aaSAndroid Build Coastguard Worker        // The updated Jacobian is J' = B^-1 * J, where B is float2x2(xAxis, yAxis) for the
827*c8dee2aaSAndroid Build Coastguard Worker        // top-left corner (so that B^-1 is constant over the whole shape). Since it's a line
828*c8dee2aaSAndroid Build Coastguard Worker        // the basis was constructed to be orthonormal, det(B) = 1 and B^-1 is trivial.
829*c8dee2aaSAndroid Build Coastguard Worker        // NOTE: float2x2 is column-major.
830*c8dee2aaSAndroid Build Coastguard Worker        jacobian = float4(float2x2(dy[0], -dy[1], -dx[0], dx[1]) * float2x2(jacobian));
831*c8dee2aaSAndroid Build Coastguard Worker    }
832*c8dee2aaSAndroid Build Coastguard Worker
833*c8dee2aaSAndroid Build Coastguard Worker    // Write out final results
834*c8dee2aaSAndroid Build Coastguard Worker    stepLocalCoords = localPos;
835*c8dee2aaSAndroid Build Coastguard Worker    return float4(devPos.xy, devPos.z*depth, devPos.z);
836*c8dee2aaSAndroid Build Coastguard Worker}
837*c8dee2aaSAndroid Build Coastguard Worker
838*c8dee2aaSAndroid Build Coastguard Workerfloat4 per_edge_aa_quad_vertex_fn(// Vertex Attributes
839*c8dee2aaSAndroid Build Coastguard Worker                                  float2 normal,
840*c8dee2aaSAndroid Build Coastguard Worker                                  // Instance Attributes
841*c8dee2aaSAndroid Build Coastguard Worker                                  float4 edgeAA,
842*c8dee2aaSAndroid Build Coastguard Worker                                  float4 xs, // ordered TL, TR, BR, BL
843*c8dee2aaSAndroid Build Coastguard Worker                                  float4 ys,
844*c8dee2aaSAndroid Build Coastguard Worker                                  float depth,
845*c8dee2aaSAndroid Build Coastguard Worker                                  float3x3 localToDevice,
846*c8dee2aaSAndroid Build Coastguard Worker                                  // Varyings
847*c8dee2aaSAndroid Build Coastguard Worker                                  out float4 edgeDistances,
848*c8dee2aaSAndroid Build Coastguard Worker                                  // Render Step
849*c8dee2aaSAndroid Build Coastguard Worker                                  out float2 stepLocalCoords) {
850*c8dee2aaSAndroid Build Coastguard Worker    const uint kCornerVertexCount = 4; // KEEP IN SYNC WITH C++'s
851*c8dee2aaSAndroid Build Coastguard Worker                                       // PerEdgeAAQuadRenderStep::kCornerVertexCount
852*c8dee2aaSAndroid Build Coastguard Worker
853*c8dee2aaSAndroid Build Coastguard Worker    const float kEpsilon = 0.00024; // SK_ScalarNearlyZero
854*c8dee2aaSAndroid Build Coastguard Worker
855*c8dee2aaSAndroid Build Coastguard Worker    // Calculate the local edge vectors, ordered L, T, R, B starting from the bottom left point.
856*c8dee2aaSAndroid Build Coastguard Worker    // For quadrilaterals these are not necessarily axis-aligned, but in all cases they orient
857*c8dee2aaSAndroid Build Coastguard Worker    // the +X/+Y normalized vertex template for each corner.
858*c8dee2aaSAndroid Build Coastguard Worker    float4 dx = xs - xs.wxyz;
859*c8dee2aaSAndroid Build Coastguard Worker    float4 dy = ys - ys.wxyz;
860*c8dee2aaSAndroid Build Coastguard Worker    float4 edgeSquaredLen = dx*dx + dy*dy;
861*c8dee2aaSAndroid Build Coastguard Worker
862*c8dee2aaSAndroid Build Coastguard Worker    float4 edgeMask = sign(edgeSquaredLen); // 0 for zero-length edge, 1 for non-zero edge.
863*c8dee2aaSAndroid Build Coastguard Worker    if (any(equal(edgeMask, float4(0.0)))) {
864*c8dee2aaSAndroid Build Coastguard Worker        // Must clean up (dx,dy) depending on the empty edge configuration
865*c8dee2aaSAndroid Build Coastguard Worker        if (all(equal(edgeMask, float4(0.0)))) {
866*c8dee2aaSAndroid Build Coastguard Worker            // A point so use the canonical basis
867*c8dee2aaSAndroid Build Coastguard Worker            dx = float4( 0.0, 1.0, 0.0, -1.0);
868*c8dee2aaSAndroid Build Coastguard Worker            dy = float4(-1.0, 0.0, 1.0,  0.0);
869*c8dee2aaSAndroid Build Coastguard Worker            edgeSquaredLen = float4(1.0);
870*c8dee2aaSAndroid Build Coastguard Worker        } else {
871*c8dee2aaSAndroid Build Coastguard Worker            // Triangles (3 non-zero edges) copy the adjacent edge. Otherwise it's a line so
872*c8dee2aaSAndroid Build Coastguard Worker            // replace empty edges with the left-hand normal vector of the adjacent edge.
873*c8dee2aaSAndroid Build Coastguard Worker            bool triangle = (edgeMask[0] + edgeMask[1] + edgeMask[2] + edgeMask[3]) > 2.5;
874*c8dee2aaSAndroid Build Coastguard Worker            float4 edgeX = triangle ? dx.yzwx :  dy.yzwx;
875*c8dee2aaSAndroid Build Coastguard Worker            float4 edgeY = triangle ? dy.yzwx : -dx.yzwx;
876*c8dee2aaSAndroid Build Coastguard Worker
877*c8dee2aaSAndroid Build Coastguard Worker            dx = mix(edgeX, dx, edgeMask);
878*c8dee2aaSAndroid Build Coastguard Worker            dy = mix(edgeY, dy, edgeMask);
879*c8dee2aaSAndroid Build Coastguard Worker            edgeSquaredLen = mix(edgeSquaredLen.yzwx, edgeSquaredLen, edgeMask);
880*c8dee2aaSAndroid Build Coastguard Worker            edgeAA = mix(edgeAA.yzwx, edgeAA, edgeMask);
881*c8dee2aaSAndroid Build Coastguard Worker        }
882*c8dee2aaSAndroid Build Coastguard Worker    }
883*c8dee2aaSAndroid Build Coastguard Worker
884*c8dee2aaSAndroid Build Coastguard Worker    float4 inverseEdgeLen = inversesqrt(edgeSquaredLen);
885*c8dee2aaSAndroid Build Coastguard Worker    dx *= inverseEdgeLen;
886*c8dee2aaSAndroid Build Coastguard Worker    dy *= inverseEdgeLen;
887*c8dee2aaSAndroid Build Coastguard Worker
888*c8dee2aaSAndroid Build Coastguard Worker    // Calculate local coordinate for the vertex (relative to xAxis and yAxis at first).
889*c8dee2aaSAndroid Build Coastguard Worker    uint cornerID = uint(sk_VertexID) / kCornerVertexCount;
890*c8dee2aaSAndroid Build Coastguard Worker    float2 xAxis = -float2(dx.yzwx[cornerID], dy.yzwx[cornerID]);
891*c8dee2aaSAndroid Build Coastguard Worker    float2 yAxis =  float2(dx.xyzw[cornerID], dy.xyzw[cornerID]);
892*c8dee2aaSAndroid Build Coastguard Worker
893*c8dee2aaSAndroid Build Coastguard Worker    // Vertex is outset from the base shape (and possibly with an additional AA outset later
894*c8dee2aaSAndroid Build Coastguard Worker    // in device space).
895*c8dee2aaSAndroid Build Coastguard Worker    float2 localPos = float2(xs[cornerID], ys[cornerID]);
896*c8dee2aaSAndroid Build Coastguard Worker
897*c8dee2aaSAndroid Build Coastguard Worker    // Calculate edge distances and device space coordinate for the vertex
898*c8dee2aaSAndroid Build Coastguard Worker    edgeDistances = dy*(xs - localPos.x) - dx*(ys - localPos.y);
899*c8dee2aaSAndroid Build Coastguard Worker
900*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: This 3x3 inverse is different than just taking the 1st two columns of the 4x4
901*c8dee2aaSAndroid Build Coastguard Worker    // inverse of the original SkM44 local-to-device matrix. We could calculate the 3x3 inverse
902*c8dee2aaSAndroid Build Coastguard Worker    // and upload it, but it does not seem to be a bottleneck and saves on bandwidth to
903*c8dee2aaSAndroid Build Coastguard Worker    // calculate it here instead.
904*c8dee2aaSAndroid Build Coastguard Worker    float3x3 deviceToLocal = inverse(localToDevice);
905*c8dee2aaSAndroid Build Coastguard Worker    float3 devPos = localToDevice * localPos.xy1;
906*c8dee2aaSAndroid Build Coastguard Worker
907*c8dee2aaSAndroid Build Coastguard Worker    // Apply the Jacobian in the vertex shader so any quadrilateral normals do not have to
908*c8dee2aaSAndroid Build Coastguard Worker    // be passed to the fragment shader. However, it's important to use the Jacobian at a
909*c8dee2aaSAndroid Build Coastguard Worker    // vertex on the edge, not the current vertex's Jacobian.
910*c8dee2aaSAndroid Build Coastguard Worker    float4 gx = -dy*(deviceToLocal[0].x - deviceToLocal[0].z*xs) +
911*c8dee2aaSAndroid Build Coastguard Worker                 dx*(deviceToLocal[0].y - deviceToLocal[0].z*ys);
912*c8dee2aaSAndroid Build Coastguard Worker    float4 gy = -dy*(deviceToLocal[1].x - deviceToLocal[1].z*xs) +
913*c8dee2aaSAndroid Build Coastguard Worker                 dx*(deviceToLocal[1].y - deviceToLocal[1].z*ys);
914*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: The gradient is missing a W term so edgeDistances must still be multiplied by
915*c8dee2aaSAndroid Build Coastguard Worker    // 1/w in the fragment shader. The same goes for the encoded coverage scale.
916*c8dee2aaSAndroid Build Coastguard Worker    edgeDistances *= inversesqrt(gx*gx + gy*gy);
917*c8dee2aaSAndroid Build Coastguard Worker
918*c8dee2aaSAndroid Build Coastguard Worker    // Bias non-AA edge distances by device W so its coverage contribution is >= 1.0
919*c8dee2aaSAndroid Build Coastguard Worker    // Add additional 1/2 bias here so we don't have to do so in the fragment shader.
920*c8dee2aaSAndroid Build Coastguard Worker    edgeDistances += (1.5 - edgeAA)*abs(devPos.z);
921*c8dee2aaSAndroid Build Coastguard Worker
922*c8dee2aaSAndroid Build Coastguard Worker    // Only outset for a vertex that is in front of the w=0 plane to avoid dealing with outset
923*c8dee2aaSAndroid Build Coastguard Worker    // triangles rasterizing differently from the main triangles as w crosses 0.
924*c8dee2aaSAndroid Build Coastguard Worker    if (any(notEqual(normal, float2(0.0))) && devPos.z > 0.0) {
925*c8dee2aaSAndroid Build Coastguard Worker        // Note that when there's no perspective, the jacobian is equivalent to the normal
926*c8dee2aaSAndroid Build Coastguard Worker        // matrix (inverse transpose), but produces correct results when there's perspective
927*c8dee2aaSAndroid Build Coastguard Worker        // because it accounts for the position's influence on a line's projected direction.
928*c8dee2aaSAndroid Build Coastguard Worker        float2x2 J = float2x2(deviceToLocal[0].xy - deviceToLocal[0].z*localPos,
929*c8dee2aaSAndroid Build Coastguard Worker                              deviceToLocal[1].xy - deviceToLocal[1].z*localPos);
930*c8dee2aaSAndroid Build Coastguard Worker
931*c8dee2aaSAndroid Build Coastguard Worker        float2 edgeAANormal = float2(edgeAA[cornerID], edgeAA.yzwx[cornerID]) * normal;
932*c8dee2aaSAndroid Build Coastguard Worker        float2 nx = edgeAANormal.x * perp(-yAxis) * J;
933*c8dee2aaSAndroid Build Coastguard Worker        float2 ny = edgeAANormal.y * perp( xAxis) * J;
934*c8dee2aaSAndroid Build Coastguard Worker
935*c8dee2aaSAndroid Build Coastguard Worker        bool isMidVertex = all(notEqual(edgeAANormal, float2(0)));
936*c8dee2aaSAndroid Build Coastguard Worker        if (isMidVertex) {
937*c8dee2aaSAndroid Build Coastguard Worker            // Produce a bisecting vector in device space.
938*c8dee2aaSAndroid Build Coastguard Worker            nx = normalize(nx);
939*c8dee2aaSAndroid Build Coastguard Worker            ny = normalize(ny);
940*c8dee2aaSAndroid Build Coastguard Worker            if (dot(nx, ny) < -0.8) {
941*c8dee2aaSAndroid Build Coastguard Worker                // Normals are in nearly opposite directions, so adjust to avoid float error.
942*c8dee2aaSAndroid Build Coastguard Worker                float s = sign(cross_length_2d(nx, ny));
943*c8dee2aaSAndroid Build Coastguard Worker                nx =  s*perp(nx);
944*c8dee2aaSAndroid Build Coastguard Worker                ny = -s*perp(ny);
945*c8dee2aaSAndroid Build Coastguard Worker            }
946*c8dee2aaSAndroid Build Coastguard Worker        }
947*c8dee2aaSAndroid Build Coastguard Worker        // Adding the normal components together directly results in what we'd have
948*c8dee2aaSAndroid Build Coastguard Worker        // calculated if we'd just transformed 'normal' in one go, assuming they weren't
949*c8dee2aaSAndroid Build Coastguard Worker        // normalized in the if-block above. If they were normalized, the sum equals the
950*c8dee2aaSAndroid Build Coastguard Worker        // bisector between the original nx and ny.
951*c8dee2aaSAndroid Build Coastguard Worker        //
952*c8dee2aaSAndroid Build Coastguard Worker        // We multiply by W so that after perspective division the new point is offset by the
953*c8dee2aaSAndroid Build Coastguard Worker        // now-unit normal.
954*c8dee2aaSAndroid Build Coastguard Worker        // NOTE: (nx + ny) can become the zero vector if the device outset is for an edge
955*c8dee2aaSAndroid Build Coastguard Worker        // marked as non-AA. In this case normalize() could produce the zero vector or NaN.
956*c8dee2aaSAndroid Build Coastguard Worker        // Until a counter-example is found, GPUs seem to discard triangles with NaN vertices,
957*c8dee2aaSAndroid Build Coastguard Worker        // which has the same effect as outsetting by the zero vector with this mesh, so we
958*c8dee2aaSAndroid Build Coastguard Worker        // don't bother guarding the normalize() (yet).
959*c8dee2aaSAndroid Build Coastguard Worker        devPos.xy += devPos.z * normalize(nx + ny);
960*c8dee2aaSAndroid Build Coastguard Worker
961*c8dee2aaSAndroid Build Coastguard Worker        // By construction these points are 1px away from the outer edge in device space.
962*c8dee2aaSAndroid Build Coastguard Worker        // Apply directly to edgeDistances to save work per pixel later on.
963*c8dee2aaSAndroid Build Coastguard Worker        edgeDistances -= devPos.z;
964*c8dee2aaSAndroid Build Coastguard Worker    }
965*c8dee2aaSAndroid Build Coastguard Worker
966*c8dee2aaSAndroid Build Coastguard Worker    // Write out final results
967*c8dee2aaSAndroid Build Coastguard Worker    stepLocalCoords = localPos;
968*c8dee2aaSAndroid Build Coastguard Worker    return float4(devPos.xy, devPos.z*depth, devPos.z);
969*c8dee2aaSAndroid Build Coastguard Worker}
970*c8dee2aaSAndroid Build Coastguard Worker
971*c8dee2aaSAndroid Build Coastguard Workerfloat4 circular_arc_vertex_fn(float3 position,
972*c8dee2aaSAndroid Build Coastguard Worker                              // Instance Attributes
973*c8dee2aaSAndroid Build Coastguard Worker                              float4 centerScales,
974*c8dee2aaSAndroid Build Coastguard Worker                              float3 radiiAndFlags,
975*c8dee2aaSAndroid Build Coastguard Worker                              float3 geoClipPlane,
976*c8dee2aaSAndroid Build Coastguard Worker                              float3 fragClipPlane0,
977*c8dee2aaSAndroid Build Coastguard Worker                              float3 fragClipPlane1,
978*c8dee2aaSAndroid Build Coastguard Worker                              float4 inRoundCapPos,
979*c8dee2aaSAndroid Build Coastguard Worker                              float depth,
980*c8dee2aaSAndroid Build Coastguard Worker                              float3x3 localToDevice,
981*c8dee2aaSAndroid Build Coastguard Worker                              // Varyings
982*c8dee2aaSAndroid Build Coastguard Worker                              out float4 circleEdge,
983*c8dee2aaSAndroid Build Coastguard Worker                              out float3 clipPlane,
984*c8dee2aaSAndroid Build Coastguard Worker                              out float3 isectPlane,
985*c8dee2aaSAndroid Build Coastguard Worker                              out float3 unionPlane,
986*c8dee2aaSAndroid Build Coastguard Worker                              out float  roundCapRadius,
987*c8dee2aaSAndroid Build Coastguard Worker                              out float4 roundCapPos,
988*c8dee2aaSAndroid Build Coastguard Worker                              // Render Step
989*c8dee2aaSAndroid Build Coastguard Worker                              out float2 stepLocalCoords) {
990*c8dee2aaSAndroid Build Coastguard Worker    // TODO: clip offset against clip planes
991*c8dee2aaSAndroid Build Coastguard Worker    float2 localCenter = centerScales.xy;
992*c8dee2aaSAndroid Build Coastguard Worker    float2 localPos = localCenter;
993*c8dee2aaSAndroid Build Coastguard Worker    // do geometric clip in normalized space
994*c8dee2aaSAndroid Build Coastguard Worker    float dist = min(dot(position.xy, geoClipPlane.xy) + geoClipPlane.z, 0);
995*c8dee2aaSAndroid Build Coastguard Worker    position.xy -= geoClipPlane.xy * dist;
996*c8dee2aaSAndroid Build Coastguard Worker    // Get the new length to use below for scaling the offset
997*c8dee2aaSAndroid Build Coastguard Worker    // (origLength is the initial length of position.xy).
998*c8dee2aaSAndroid Build Coastguard Worker    float offsetScale = length(position.xy);
999*c8dee2aaSAndroid Build Coastguard Worker
1000*c8dee2aaSAndroid Build Coastguard Worker    // scale and translate to local space
1001*c8dee2aaSAndroid Build Coastguard Worker    if (position.z > 0) {
1002*c8dee2aaSAndroid Build Coastguard Worker        localPos += position.xy * centerScales.z;
1003*c8dee2aaSAndroid Build Coastguard Worker    } else {
1004*c8dee2aaSAndroid Build Coastguard Worker        localPos += position.xy * centerScales.w;
1005*c8dee2aaSAndroid Build Coastguard Worker    }
1006*c8dee2aaSAndroid Build Coastguard Worker
1007*c8dee2aaSAndroid Build Coastguard Worker    float3 devPos = localToDevice * localPos.xy1;
1008*c8dee2aaSAndroid Build Coastguard Worker    float3 devCenter = localToDevice * localCenter.xy1;
1009*c8dee2aaSAndroid Build Coastguard Worker    float2 offset = devPos.xy - devCenter.xy;
1010*c8dee2aaSAndroid Build Coastguard Worker    // offset for AA and correct length of offset
1011*c8dee2aaSAndroid Build Coastguard Worker    if (offset != float2(0)) {
1012*c8dee2aaSAndroid Build Coastguard Worker        offset = normalize(offset);
1013*c8dee2aaSAndroid Build Coastguard Worker        devPos.xy += position.z*offset;
1014*c8dee2aaSAndroid Build Coastguard Worker        if (position.z > 0) {
1015*c8dee2aaSAndroid Build Coastguard Worker            // Scale using distance from center of unit octagon to the vertex
1016*c8dee2aaSAndroid Build Coastguard Worker            // Because of geometry clipping we need to scale by 1.0823922*newLength/origLength
1017*c8dee2aaSAndroid Build Coastguard Worker            // But the original length is 1.0823922 so the offsetScale is just newLength
1018*c8dee2aaSAndroid Build Coastguard Worker            offset *= offsetScale;
1019*c8dee2aaSAndroid Build Coastguard Worker        } else {
1020*c8dee2aaSAndroid Build Coastguard Worker            // Because of geometry clipping we need to scale by innerRadius*newLength/origLength
1021*c8dee2aaSAndroid Build Coastguard Worker            // But the original length is 1 so this is just innerRadius*newLength
1022*c8dee2aaSAndroid Build Coastguard Worker            offset *= offsetScale*radiiAndFlags.y;
1023*c8dee2aaSAndroid Build Coastguard Worker        }
1024*c8dee2aaSAndroid Build Coastguard Worker    }
1025*c8dee2aaSAndroid Build Coastguard Worker
1026*c8dee2aaSAndroid Build Coastguard Worker    circleEdge = float4(offset, radiiAndFlags.xy);
1027*c8dee2aaSAndroid Build Coastguard Worker    if (radiiAndFlags.z > 0) {
1028*c8dee2aaSAndroid Build Coastguard Worker        clipPlane = fragClipPlane0;
1029*c8dee2aaSAndroid Build Coastguard Worker        isectPlane = fragClipPlane1;
1030*c8dee2aaSAndroid Build Coastguard Worker        unionPlane = float3(0, 0, 0);
1031*c8dee2aaSAndroid Build Coastguard Worker    } else {
1032*c8dee2aaSAndroid Build Coastguard Worker        clipPlane = fragClipPlane0;
1033*c8dee2aaSAndroid Build Coastguard Worker        isectPlane = float3(0, 0, 1);
1034*c8dee2aaSAndroid Build Coastguard Worker        unionPlane = fragClipPlane1;
1035*c8dee2aaSAndroid Build Coastguard Worker    }
1036*c8dee2aaSAndroid Build Coastguard Worker    if (abs(radiiAndFlags.z) > 1) {
1037*c8dee2aaSAndroid Build Coastguard Worker        // This is the cap radius in normalized space where the outer radius is 1 and
1038*c8dee2aaSAndroid Build Coastguard Worker        // radii.y is the normalized inner radius.
1039*c8dee2aaSAndroid Build Coastguard Worker        roundCapRadius = (1.0 - radiiAndFlags.y) / 2.0;
1040*c8dee2aaSAndroid Build Coastguard Worker    } else {
1041*c8dee2aaSAndroid Build Coastguard Worker        roundCapRadius = 0;
1042*c8dee2aaSAndroid Build Coastguard Worker    }
1043*c8dee2aaSAndroid Build Coastguard Worker    roundCapPos = inRoundCapPos;
1044*c8dee2aaSAndroid Build Coastguard Worker    stepLocalCoords = localPos;
1045*c8dee2aaSAndroid Build Coastguard Worker
1046*c8dee2aaSAndroid Build Coastguard Worker    // We assume no perspective
1047*c8dee2aaSAndroid Build Coastguard Worker    return float4(devPos.xy, depth, 1);
1048*c8dee2aaSAndroid Build Coastguard Worker}
1049*c8dee2aaSAndroid Build Coastguard Worker
1050*c8dee2aaSAndroid Build Coastguard Workerfloat4 text_vertex_fn(float2 baseCoords,
1051*c8dee2aaSAndroid Build Coastguard Worker                      // Uniforms
1052*c8dee2aaSAndroid Build Coastguard Worker                      float4x4 subRunDeviceMatrix,
1053*c8dee2aaSAndroid Build Coastguard Worker                      float4x4 deviceToLocal,
1054*c8dee2aaSAndroid Build Coastguard Worker                      float2 atlasSizeInv,
1055*c8dee2aaSAndroid Build Coastguard Worker                      // Instance Attributes
1056*c8dee2aaSAndroid Build Coastguard Worker                      float2 size,
1057*c8dee2aaSAndroid Build Coastguard Worker                      float2 uvPos,
1058*c8dee2aaSAndroid Build Coastguard Worker                      float2 xyPos,
1059*c8dee2aaSAndroid Build Coastguard Worker                      float strikeToSourceScale,
1060*c8dee2aaSAndroid Build Coastguard Worker                      float depth,
1061*c8dee2aaSAndroid Build Coastguard Worker                      // Varyings
1062*c8dee2aaSAndroid Build Coastguard Worker                      out float2 textureCoords,
1063*c8dee2aaSAndroid Build Coastguard Worker                      out float2 unormTexCoords,  // used as varying in SDFText
1064*c8dee2aaSAndroid Build Coastguard Worker                      // Render Step
1065*c8dee2aaSAndroid Build Coastguard Worker                      out float2 stepLocalCoords) {
1066*c8dee2aaSAndroid Build Coastguard Worker    baseCoords.xy *= float2(size);
1067*c8dee2aaSAndroid Build Coastguard Worker
1068*c8dee2aaSAndroid Build Coastguard Worker    // Sub runs have a decomposed transform and are sometimes already transformed into device
1069*c8dee2aaSAndroid Build Coastguard Worker    // space, in which `subRunCoords` represents the bounds projected to device space without
1070*c8dee2aaSAndroid Build Coastguard Worker    // the local-to-device translation and `subRunDeviceMatrix` contains the translation.
1071*c8dee2aaSAndroid Build Coastguard Worker    float2 subRunCoords = strikeToSourceScale * baseCoords + xyPos;
1072*c8dee2aaSAndroid Build Coastguard Worker    float4 position = subRunDeviceMatrix * subRunCoords.xy01;
1073*c8dee2aaSAndroid Build Coastguard Worker
1074*c8dee2aaSAndroid Build Coastguard Worker    // Calculate the local coords used for shading.
1075*c8dee2aaSAndroid Build Coastguard Worker    // TODO(b/246963258): This is incorrect if the transform has perspective, which would
1076*c8dee2aaSAndroid Build Coastguard Worker    // require a division + a valid z coordinate (which is currently set to 0).
1077*c8dee2aaSAndroid Build Coastguard Worker    stepLocalCoords = (deviceToLocal * position).xy;
1078*c8dee2aaSAndroid Build Coastguard Worker
1079*c8dee2aaSAndroid Build Coastguard Worker    unormTexCoords = baseCoords + uvPos;
1080*c8dee2aaSAndroid Build Coastguard Worker    textureCoords = unormTexCoords * atlasSizeInv;
1081*c8dee2aaSAndroid Build Coastguard Worker
1082*c8dee2aaSAndroid Build Coastguard Worker    return float4(position.xy, depth*position.w, position.w);
1083*c8dee2aaSAndroid Build Coastguard Worker}
1084*c8dee2aaSAndroid Build Coastguard Worker
1085*c8dee2aaSAndroid Build Coastguard Workerfloat4 coverage_mask_vertex_fn(float2 quadCoords,
1086*c8dee2aaSAndroid Build Coastguard Worker                               // Uniforms
1087*c8dee2aaSAndroid Build Coastguard Worker                               float3x3 maskToDeviceRemainder,
1088*c8dee2aaSAndroid Build Coastguard Worker                               // Instance Attributes
1089*c8dee2aaSAndroid Build Coastguard Worker                               float4 drawBounds,
1090*c8dee2aaSAndroid Build Coastguard Worker                               float4 maskBoundsIn,
1091*c8dee2aaSAndroid Build Coastguard Worker                               float2 deviceOrigin,
1092*c8dee2aaSAndroid Build Coastguard Worker                               float depth,
1093*c8dee2aaSAndroid Build Coastguard Worker                               float3x3 deviceToLocal,
1094*c8dee2aaSAndroid Build Coastguard Worker                               // Varyings
1095*c8dee2aaSAndroid Build Coastguard Worker                               out float4 maskBounds,
1096*c8dee2aaSAndroid Build Coastguard Worker                               out float2 textureCoords,
1097*c8dee2aaSAndroid Build Coastguard Worker                               out half invert,
1098*c8dee2aaSAndroid Build Coastguard Worker                               // Render Step
1099*c8dee2aaSAndroid Build Coastguard Worker                               out float2 stepLocalCoords) {
1100*c8dee2aaSAndroid Build Coastguard Worker    // An atlas shape is an axis-aligned rectangle tessellated as a triangle strip.
1101*c8dee2aaSAndroid Build Coastguard Worker    //
1102*c8dee2aaSAndroid Build Coastguard Worker    // The bounds coordinates are in an intermediate space, pixel-aligned with the mask texture
1103*c8dee2aaSAndroid Build Coastguard Worker    // that's sampled in the fragment shader. The coords must be transformed by both
1104*c8dee2aaSAndroid Build Coastguard Worker    // maskToDeviceRemainder and translated by deviceOrigin to get device coords.
1105*c8dee2aaSAndroid Build Coastguard Worker    textureCoords = mix(drawBounds.xy, drawBounds.zw, quadCoords);
1106*c8dee2aaSAndroid Build Coastguard Worker    float3 drawCoords = maskToDeviceRemainder*((textureCoords + deviceOrigin).xy1);
1107*c8dee2aaSAndroid Build Coastguard Worker
1108*c8dee2aaSAndroid Build Coastguard Worker    // Local coordinates used for shading are derived from the final device coords and the inverse
1109*c8dee2aaSAndroid Build Coastguard Worker    // of the original local-to-device matrix.
1110*c8dee2aaSAndroid Build Coastguard Worker    float3 localCoords = deviceToLocal * drawCoords;
1111*c8dee2aaSAndroid Build Coastguard Worker    // TODO: Support float3 local coordinates if the matrix has perspective so that W is
1112*c8dee2aaSAndroid Build Coastguard Worker    // interpolated correctly to the fragment shader.
1113*c8dee2aaSAndroid Build Coastguard Worker    stepLocalCoords = localCoords.xy / localCoords.z;
1114*c8dee2aaSAndroid Build Coastguard Worker
1115*c8dee2aaSAndroid Build Coastguard Worker    // For an inverse fill, `textureCoords` will get clamped to `maskBounds` and the edge pixels
1116*c8dee2aaSAndroid Build Coastguard Worker    // will always land on a 0-coverage border pixel assuming the atlas was prepared with 1px
1117*c8dee2aaSAndroid Build Coastguard Worker    // padding around each mask entry. This includes inverse fills where the mask was fully clipped
1118*c8dee2aaSAndroid Build Coastguard Worker    // out, since then maskBounds.RBLT == (0,0,-1,-1) and we sample the top-left-most pixel of the
1119*c8dee2aaSAndroid Build Coastguard Worker    // atlas, which is guaranteed to be transparent.
1120*c8dee2aaSAndroid Build Coastguard Worker    if (all(lessThanEqual(maskBoundsIn.LT, maskBoundsIn.RB))) {
1121*c8dee2aaSAndroid Build Coastguard Worker        // Regular fill
1122*c8dee2aaSAndroid Build Coastguard Worker        maskBounds = maskBoundsIn;
1123*c8dee2aaSAndroid Build Coastguard Worker        invert = 0;
1124*c8dee2aaSAndroid Build Coastguard Worker    } else {
1125*c8dee2aaSAndroid Build Coastguard Worker        // Re-arrange the mask bounds to sorted order for texture clamping in the fragment shader
1126*c8dee2aaSAndroid Build Coastguard Worker        maskBounds = maskBoundsIn.RBLT;
1127*c8dee2aaSAndroid Build Coastguard Worker        invert = 1;
1128*c8dee2aaSAndroid Build Coastguard Worker    }
1129*c8dee2aaSAndroid Build Coastguard Worker
1130*c8dee2aaSAndroid Build Coastguard Worker    return float4(drawCoords.xy, depth*drawCoords.z, drawCoords.z);
1131*c8dee2aaSAndroid Build Coastguard Worker}
1132*c8dee2aaSAndroid Build Coastguard Worker
1133*c8dee2aaSAndroid Build Coastguard Workerfloat4 cover_bounds_vertex_fn(float2 corner,
1134*c8dee2aaSAndroid Build Coastguard Worker                              float4 bounds,
1135*c8dee2aaSAndroid Build Coastguard Worker                              float depth,
1136*c8dee2aaSAndroid Build Coastguard Worker                              float3x3 matrix,
1137*c8dee2aaSAndroid Build Coastguard Worker                              out float2 stepLocalCoords) {
1138*c8dee2aaSAndroid Build Coastguard Worker    if (all(lessThanEqual(bounds.LT, bounds.RB))) {
1139*c8dee2aaSAndroid Build Coastguard Worker        // A regular fill
1140*c8dee2aaSAndroid Build Coastguard Worker        corner = mix(bounds.LT, bounds.RB, corner);
1141*c8dee2aaSAndroid Build Coastguard Worker        float3 devCorner = matrix * corner.xy1;
1142*c8dee2aaSAndroid Build Coastguard Worker        stepLocalCoords = corner;
1143*c8dee2aaSAndroid Build Coastguard Worker        return float4(devCorner.xy, depth*devCorner.z, devCorner.z);
1144*c8dee2aaSAndroid Build Coastguard Worker    } else {
1145*c8dee2aaSAndroid Build Coastguard Worker        // An inverse fill
1146*c8dee2aaSAndroid Build Coastguard Worker        corner = mix(bounds.RB, bounds.LT, corner);
1147*c8dee2aaSAndroid Build Coastguard Worker        // TODO(b/351923375): Get the 3x3 inverse  of the local-to-device transform from the CPU
1148*c8dee2aaSAndroid Build Coastguard Worker        // if it can be computed fast enough on the CPU from the cached 4x4 inverse.
1149*c8dee2aaSAndroid Build Coastguard Worker        float3 localCoords = inverse(matrix) * corner.xy1;
1150*c8dee2aaSAndroid Build Coastguard Worker        // Dividing the inverse mapped local coords by its homogenous coordinate reconstructs the
1151*c8dee2aaSAndroid Build Coastguard Worker        // original local coords.
1152*c8dee2aaSAndroid Build Coastguard Worker        float invW = 1.0 / localCoords.z;
1153*c8dee2aaSAndroid Build Coastguard Worker        stepLocalCoords = localCoords.xy * invW;
1154*c8dee2aaSAndroid Build Coastguard Worker
1155*c8dee2aaSAndroid Build Coastguard Worker        // 1/W also happens to be equal to (matrix*stepLocalCoords.xy1).z, which is the device-space
1156*c8dee2aaSAndroid Build Coastguard Worker        // homogenous coordinate we want perspective interpolation to respect. We multiply the
1157*c8dee2aaSAndroid Build Coastguard Worker        // output position by 1/W and set the output position's homogenous coord to that same 1/W
1158*c8dee2aaSAndroid Build Coastguard Worker        // which ensures the projected vertices are still the device-space corners, but
1159*c8dee2aaSAndroid Build Coastguard Worker        // stepLocalCoords will be correctly perspective interpolated by HW.
1160*c8dee2aaSAndroid Build Coastguard Worker        return float4(corner*invW, depth*invW, invW);
1161*c8dee2aaSAndroid Build Coastguard Worker    }
1162*c8dee2aaSAndroid Build Coastguard Worker}
1163