xref: /aosp_15_r20/external/skia/src/sksl/sksl_graphite_frag.sksl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker// Graphite-specific fragment shader code
2*c8dee2aaSAndroid Build Coastguard Worker
3*c8dee2aaSAndroid Build Coastguard Workerconst int $kTileModeClamp  = 0;
4*c8dee2aaSAndroid Build Coastguard Workerconst int $kTileModeRepeat = 1;
5*c8dee2aaSAndroid Build Coastguard Workerconst int $kTileModeMirror = 2;
6*c8dee2aaSAndroid Build Coastguard Workerconst int $kTileModeDecal  = 3;
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Workerconst int $kFilterModeNearest = 0;
9*c8dee2aaSAndroid Build Coastguard Workerconst int $kFilterModeLinear  = 1;
10*c8dee2aaSAndroid Build Coastguard Worker
11*c8dee2aaSAndroid Build Coastguard Workerconst int $kTFTypeSRGB = 1;
12*c8dee2aaSAndroid Build Coastguard Workerconst int $kTFTypePQ = 2;
13*c8dee2aaSAndroid Build Coastguard Workerconst int $kTFTypeHLG = 3;
14*c8dee2aaSAndroid Build Coastguard Workerconst int $kTFTypeHLGinv = 4;
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Workerconst int $kColorSpaceXformFlagUnpremul = 0x1;
17*c8dee2aaSAndroid Build Coastguard Workerconst int $kColorSpaceXformFlagLinearize = 0x2;
18*c8dee2aaSAndroid Build Coastguard Workerconst int $kColorSpaceXformFlagGamutTransform = 0x4;
19*c8dee2aaSAndroid Build Coastguard Workerconst int $kColorSpaceXformFlagEncode = 0x8;
20*c8dee2aaSAndroid Build Coastguard Workerconst int $kColorSpaceXformFlagPremul = 0x10;
21*c8dee2aaSAndroid Build Coastguard Workerconst int $kColorSpaceXformFlagAlphaSwizzle = 0x20;
22*c8dee2aaSAndroid Build Coastguard Worker
23*c8dee2aaSAndroid Build Coastguard Workerconst int $kMaskFormatA8   = 0;
24*c8dee2aaSAndroid Build Coastguard Workerconst int $kMaskFormatA565 = 1;
25*c8dee2aaSAndroid Build Coastguard Workerconst int $kMaskFormatARGB = 2;
26*c8dee2aaSAndroid Build Coastguard Worker
27*c8dee2aaSAndroid Build Coastguard Workerconst int $kShapeTypeRect = 0;
28*c8dee2aaSAndroid Build Coastguard Workerconst int $kShapeTypeRRect = 1;
29*c8dee2aaSAndroid Build Coastguard Workerconst int $kShapeTypeCircle = 2;
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker// Matches GrTextureEffect::kLinearInset, to make sure we don't touch an outer
32*c8dee2aaSAndroid Build Coastguard Worker// row or column with a weight of 0 when linear filtering.
33*c8dee2aaSAndroid Build Coastguard Workerconst float $kLinearInset = 0.5 + 0.00001;
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_error() {
36*c8dee2aaSAndroid Build Coastguard Worker    return half4(1.0, 0.0, 0.0, 1.0);
37*c8dee2aaSAndroid Build Coastguard Worker}
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_passthrough(half4 color) {
40*c8dee2aaSAndroid Build Coastguard Worker    return color;
41*c8dee2aaSAndroid Build Coastguard Worker}
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_solid_shader(float4 colorParam) {
44*c8dee2aaSAndroid Build Coastguard Worker    return half4(colorParam);
45*c8dee2aaSAndroid Build Coastguard Worker}
46*c8dee2aaSAndroid Build Coastguard Worker
47*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_rgb_opaque(float4 colorParam) {
48*c8dee2aaSAndroid Build Coastguard Worker    return half4(colorParam.rgb, 1.0);
49*c8dee2aaSAndroid Build Coastguard Worker}
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_alpha_only(float4 colorParam) {
52*c8dee2aaSAndroid Build Coastguard Worker    return half4(0.0, 0.0, 0.0, colorParam.a);
53*c8dee2aaSAndroid Build Coastguard Worker}
54*c8dee2aaSAndroid Build Coastguard Worker
55*c8dee2aaSAndroid Build Coastguard Worker$pure float $apply_xfer_fn(int kind, float x, half4 cs[2]) {
56*c8dee2aaSAndroid Build Coastguard Worker    float G = cs[0][0], A = cs[0][1], B = cs[0][2], C = cs[0][3],
57*c8dee2aaSAndroid Build Coastguard Worker          D = cs[1][0], E = cs[1][1], F = cs[1][2];
58*c8dee2aaSAndroid Build Coastguard Worker    float s = sign(x);
59*c8dee2aaSAndroid Build Coastguard Worker    x = abs(x);
60*c8dee2aaSAndroid Build Coastguard Worker    switch (kind) {
61*c8dee2aaSAndroid Build Coastguard Worker        case $kTFTypeSRGB:
62*c8dee2aaSAndroid Build Coastguard Worker            x = (x < D) ? (C * x) + F
63*c8dee2aaSAndroid Build Coastguard Worker                        : pow(A * x + B, G) + E;
64*c8dee2aaSAndroid Build Coastguard Worker            break;
65*c8dee2aaSAndroid Build Coastguard Worker        case $kTFTypePQ:
66*c8dee2aaSAndroid Build Coastguard Worker            float x_C = pow(x, C);
67*c8dee2aaSAndroid Build Coastguard Worker            x = pow(max(A + B * x_C, 0) / (D + E * x_C), F);
68*c8dee2aaSAndroid Build Coastguard Worker            break;
69*c8dee2aaSAndroid Build Coastguard Worker        case $kTFTypeHLG:
70*c8dee2aaSAndroid Build Coastguard Worker            x = (x * A <= 1) ? pow(x * A, B)
71*c8dee2aaSAndroid Build Coastguard Worker                             : exp((x - E) * C) + D;
72*c8dee2aaSAndroid Build Coastguard Worker            x *= (F + 1);
73*c8dee2aaSAndroid Build Coastguard Worker            break;
74*c8dee2aaSAndroid Build Coastguard Worker        case $kTFTypeHLGinv:
75*c8dee2aaSAndroid Build Coastguard Worker            x /= (F + 1);
76*c8dee2aaSAndroid Build Coastguard Worker            x = (x <= 1) ? A * pow(x, B)
77*c8dee2aaSAndroid Build Coastguard Worker                         : C * log(x - D) + E;
78*c8dee2aaSAndroid Build Coastguard Worker            break;
79*c8dee2aaSAndroid Build Coastguard Worker    }
80*c8dee2aaSAndroid Build Coastguard Worker    return s * x;
81*c8dee2aaSAndroid Build Coastguard Worker}
82*c8dee2aaSAndroid Build Coastguard Worker
83*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_premul_alpha(float4 color) {
84*c8dee2aaSAndroid Build Coastguard Worker    return half4(color.rgb*color.a, color.a);
85*c8dee2aaSAndroid Build Coastguard Worker}
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_color_space_transform(half4 halfColor,
88*c8dee2aaSAndroid Build Coastguard Worker                                     int flags,
89*c8dee2aaSAndroid Build Coastguard Worker                                     int srcKind,
90*c8dee2aaSAndroid Build Coastguard Worker                                     half3x3 gamutTransform,
91*c8dee2aaSAndroid Build Coastguard Worker                                     int dstKind,
92*c8dee2aaSAndroid Build Coastguard Worker                                     half4x4 coeffs) {
93*c8dee2aaSAndroid Build Coastguard Worker    if (flags != 0) {
94*c8dee2aaSAndroid Build Coastguard Worker        float4 color = float4(halfColor);
95*c8dee2aaSAndroid Build Coastguard Worker
96*c8dee2aaSAndroid Build Coastguard Worker        if (bool(flags & $kColorSpaceXformFlagAlphaSwizzle)) {
97*c8dee2aaSAndroid Build Coastguard Worker            color.a = dot(color.r1, float2(coeffs[1][3], coeffs[3][3]));
98*c8dee2aaSAndroid Build Coastguard Worker        }
99*c8dee2aaSAndroid Build Coastguard Worker        if (bool(flags & $kColorSpaceXformFlagUnpremul)) {
100*c8dee2aaSAndroid Build Coastguard Worker            color = unpremul(color);
101*c8dee2aaSAndroid Build Coastguard Worker        }
102*c8dee2aaSAndroid Build Coastguard Worker        if (bool(flags & $kColorSpaceXformFlagLinearize)) {
103*c8dee2aaSAndroid Build Coastguard Worker            half4 srcCoeffs[2];
104*c8dee2aaSAndroid Build Coastguard Worker            srcCoeffs[0] = coeffs[0];
105*c8dee2aaSAndroid Build Coastguard Worker            srcCoeffs[1] = coeffs[1];
106*c8dee2aaSAndroid Build Coastguard Worker            color.r = $apply_xfer_fn(srcKind, color.r, srcCoeffs);
107*c8dee2aaSAndroid Build Coastguard Worker            color.g = $apply_xfer_fn(srcKind, color.g, srcCoeffs);
108*c8dee2aaSAndroid Build Coastguard Worker            color.b = $apply_xfer_fn(srcKind, color.b, srcCoeffs);
109*c8dee2aaSAndroid Build Coastguard Worker        }
110*c8dee2aaSAndroid Build Coastguard Worker        if (bool(flags & $kColorSpaceXformFlagGamutTransform)) {
111*c8dee2aaSAndroid Build Coastguard Worker            color.rgb = gamutTransform * color.rgb;
112*c8dee2aaSAndroid Build Coastguard Worker        }
113*c8dee2aaSAndroid Build Coastguard Worker        if (bool(flags & $kColorSpaceXformFlagEncode)) {
114*c8dee2aaSAndroid Build Coastguard Worker            half4 dstCoeffs[2];
115*c8dee2aaSAndroid Build Coastguard Worker            dstCoeffs[0] = coeffs[2];
116*c8dee2aaSAndroid Build Coastguard Worker            dstCoeffs[1] = coeffs[3];
117*c8dee2aaSAndroid Build Coastguard Worker            color.r = $apply_xfer_fn(dstKind, color.r, dstCoeffs);
118*c8dee2aaSAndroid Build Coastguard Worker            color.g = $apply_xfer_fn(dstKind, color.g, dstCoeffs);
119*c8dee2aaSAndroid Build Coastguard Worker            color.b = $apply_xfer_fn(dstKind, color.b, dstCoeffs);
120*c8dee2aaSAndroid Build Coastguard Worker        }
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker        halfColor = bool(flags & $kColorSpaceXformFlagPremul) ? sk_premul_alpha(color)
123*c8dee2aaSAndroid Build Coastguard Worker                                                              : half4(color);
124*c8dee2aaSAndroid Build Coastguard Worker    }
125*c8dee2aaSAndroid Build Coastguard Worker    return halfColor;
126*c8dee2aaSAndroid Build Coastguard Worker}
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_circular_rrect_clip(float4 rect,
129*c8dee2aaSAndroid Build Coastguard Worker                                   float2 radiusPlusHalf,
130*c8dee2aaSAndroid Build Coastguard Worker                                   half4 edgeSelect) {
131*c8dee2aaSAndroid Build Coastguard Worker    float2 fragCoord = sk_FragCoord.xy;
132*c8dee2aaSAndroid Build Coastguard Worker
133*c8dee2aaSAndroid Build Coastguard Worker    // Negative x indicates inverse fill
134*c8dee2aaSAndroid Build Coastguard Worker    float2 radius = float2(abs(radiusPlusHalf.x));
135*c8dee2aaSAndroid Build Coastguard Worker    float2 dxy0 = edgeSelect.LT*((rect.LT + radius) - fragCoord);
136*c8dee2aaSAndroid Build Coastguard Worker    float2 dxy1 = edgeSelect.RB*(fragCoord - (rect.RB - radius));
137*c8dee2aaSAndroid Build Coastguard Worker    float2 dxy = max(max(dxy0, dxy1), 0.0);
138*c8dee2aaSAndroid Build Coastguard Worker    // Use more complex length calculation to manage precision issues
139*c8dee2aaSAndroid Build Coastguard Worker    half circleCornerAlpha = half(saturate(radius.x*(1.0 - length(dxy*radiusPlusHalf.y))));
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker    half4 rectEdgeAlphas = saturate(half4(fragCoord - rect.LT, rect.RB - fragCoord));
142*c8dee2aaSAndroid Build Coastguard Worker    rectEdgeAlphas = mix(rectEdgeAlphas, half4(1), edgeSelect);
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker    half alpha = circleCornerAlpha * rectEdgeAlphas.x * rectEdgeAlphas.y *
145*c8dee2aaSAndroid Build Coastguard Worker                 rectEdgeAlphas.z * rectEdgeAlphas.w;
146*c8dee2aaSAndroid Build Coastguard Worker    // Check for inverse fill
147*c8dee2aaSAndroid Build Coastguard Worker    alpha = radiusPlusHalf.x < 0 ? (1-alpha) : alpha;
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker    return half4(alpha);
150*c8dee2aaSAndroid Build Coastguard Worker}
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker$pure float $tile(int tileMode, float f, float low, float high) {
153*c8dee2aaSAndroid Build Coastguard Worker    switch (tileMode) {
154*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeClamp:
155*c8dee2aaSAndroid Build Coastguard Worker            return clamp(f, low, high);
156*c8dee2aaSAndroid Build Coastguard Worker
157*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeRepeat: {
158*c8dee2aaSAndroid Build Coastguard Worker            float length = high - low;
159*c8dee2aaSAndroid Build Coastguard Worker            return (mod(f - low, length) + low);
160*c8dee2aaSAndroid Build Coastguard Worker        }
161*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeMirror: {
162*c8dee2aaSAndroid Build Coastguard Worker            float length = high - low;
163*c8dee2aaSAndroid Build Coastguard Worker            float length2 = 2 * length;
164*c8dee2aaSAndroid Build Coastguard Worker            float tmp = mod(f - low, length2);
165*c8dee2aaSAndroid Build Coastguard Worker            return (mix(tmp, length2 - tmp, step(length, tmp)) + low);
166*c8dee2aaSAndroid Build Coastguard Worker        }
167*c8dee2aaSAndroid Build Coastguard Worker        default:  // $kTileModeDecal
168*c8dee2aaSAndroid Build Coastguard Worker            // Decal is handled later.
169*c8dee2aaSAndroid Build Coastguard Worker            return f;
170*c8dee2aaSAndroid Build Coastguard Worker    }
171*c8dee2aaSAndroid Build Coastguard Worker}
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $sample_image(float2 pos, float2 invImgSize, sampler2D s) {
174*c8dee2aaSAndroid Build Coastguard Worker    return sample(s, pos * invImgSize);
175*c8dee2aaSAndroid Build Coastguard Worker}
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $sample_image_subset(float2 pos,
178*c8dee2aaSAndroid Build Coastguard Worker                                 float2 invImgSize,
179*c8dee2aaSAndroid Build Coastguard Worker                                 float4 subset,
180*c8dee2aaSAndroid Build Coastguard Worker                                 int tileModeX,
181*c8dee2aaSAndroid Build Coastguard Worker                                 int tileModeY,
182*c8dee2aaSAndroid Build Coastguard Worker                                 int filterMode,
183*c8dee2aaSAndroid Build Coastguard Worker                                 float2 linearFilterInset,
184*c8dee2aaSAndroid Build Coastguard Worker                                 sampler2D s) {
185*c8dee2aaSAndroid Build Coastguard Worker    // Do hard-edge shader transitions to the border color for nearest-neighbor decal tiling at the
186*c8dee2aaSAndroid Build Coastguard Worker    // subset boundaries. Snap the input coordinates to nearest neighbor before comparing to the
187*c8dee2aaSAndroid Build Coastguard Worker    // subset rect, to avoid GPU interpolation errors. See https://crbug.com/skia/10403.
188*c8dee2aaSAndroid Build Coastguard Worker    if (tileModeX == $kTileModeDecal && filterMode == $kFilterModeNearest) {
189*c8dee2aaSAndroid Build Coastguard Worker        float snappedX = floor(pos.x) + 0.5;
190*c8dee2aaSAndroid Build Coastguard Worker        if (snappedX < subset.x || snappedX > subset.z) {
191*c8dee2aaSAndroid Build Coastguard Worker            return half4(0);
192*c8dee2aaSAndroid Build Coastguard Worker        }
193*c8dee2aaSAndroid Build Coastguard Worker    }
194*c8dee2aaSAndroid Build Coastguard Worker    if (tileModeY == $kTileModeDecal && filterMode == $kFilterModeNearest) {
195*c8dee2aaSAndroid Build Coastguard Worker        float snappedY = floor(pos.y) + 0.5;
196*c8dee2aaSAndroid Build Coastguard Worker        if (snappedY < subset.y || snappedY > subset.w) {
197*c8dee2aaSAndroid Build Coastguard Worker            return half4(0);
198*c8dee2aaSAndroid Build Coastguard Worker        }
199*c8dee2aaSAndroid Build Coastguard Worker    }
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker    pos.x = $tile(tileModeX, pos.x, subset.x, subset.z);
202*c8dee2aaSAndroid Build Coastguard Worker    pos.y = $tile(tileModeY, pos.y, subset.y, subset.w);
203*c8dee2aaSAndroid Build Coastguard Worker
204*c8dee2aaSAndroid Build Coastguard Worker    // Clamp to an inset subset to prevent sampling neighboring texels when coords fall exactly at
205*c8dee2aaSAndroid Build Coastguard Worker    // texel boundaries.
206*c8dee2aaSAndroid Build Coastguard Worker    float4 insetClamp;
207*c8dee2aaSAndroid Build Coastguard Worker    if (filterMode == $kFilterModeNearest) {
208*c8dee2aaSAndroid Build Coastguard Worker        insetClamp = float4(floor(subset.xy) + $kLinearInset, ceil(subset.zw) - $kLinearInset);
209*c8dee2aaSAndroid Build Coastguard Worker    } else {
210*c8dee2aaSAndroid Build Coastguard Worker        insetClamp = float4(subset.xy + linearFilterInset.x, subset.zw - linearFilterInset.y);
211*c8dee2aaSAndroid Build Coastguard Worker    }
212*c8dee2aaSAndroid Build Coastguard Worker    float2 clampedPos = clamp(pos, insetClamp.xy, insetClamp.zw);
213*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $sample_image(clampedPos, invImgSize, s);
214*c8dee2aaSAndroid Build Coastguard Worker
215*c8dee2aaSAndroid Build Coastguard Worker    if (filterMode == $kFilterModeLinear) {
216*c8dee2aaSAndroid Build Coastguard Worker        // Remember the amount the coord moved for clamping. This is used to implement shader-based
217*c8dee2aaSAndroid Build Coastguard Worker        // filtering for repeat and decal tiling.
218*c8dee2aaSAndroid Build Coastguard Worker        half2 error = half2(pos - clampedPos);
219*c8dee2aaSAndroid Build Coastguard Worker        half2 absError = abs(error);
220*c8dee2aaSAndroid Build Coastguard Worker
221*c8dee2aaSAndroid Build Coastguard Worker        // Do 1 or 3 more texture reads depending on whether both x and y tiling modes are repeat
222*c8dee2aaSAndroid Build Coastguard Worker        // and whether we're near a single subset edge or a corner. Then blend the multiple reads
223*c8dee2aaSAndroid Build Coastguard Worker        // using the error values calculated above.
224*c8dee2aaSAndroid Build Coastguard Worker        bool sampleExtraX = tileModeX == $kTileModeRepeat;
225*c8dee2aaSAndroid Build Coastguard Worker        bool sampleExtraY = tileModeY == $kTileModeRepeat;
226*c8dee2aaSAndroid Build Coastguard Worker        if (sampleExtraX || sampleExtraY) {
227*c8dee2aaSAndroid Build Coastguard Worker            float extraCoordX;
228*c8dee2aaSAndroid Build Coastguard Worker            float extraCoordY;
229*c8dee2aaSAndroid Build Coastguard Worker            half4 extraColorX;
230*c8dee2aaSAndroid Build Coastguard Worker            half4 extraColorY;
231*c8dee2aaSAndroid Build Coastguard Worker            if (sampleExtraX) {
232*c8dee2aaSAndroid Build Coastguard Worker                extraCoordX = error.x > 0 ? insetClamp.x : insetClamp.z;
233*c8dee2aaSAndroid Build Coastguard Worker                extraColorX = $sample_image(float2(extraCoordX, clampedPos.y),
234*c8dee2aaSAndroid Build Coastguard Worker                                            invImgSize, s);
235*c8dee2aaSAndroid Build Coastguard Worker            }
236*c8dee2aaSAndroid Build Coastguard Worker            if (sampleExtraY) {
237*c8dee2aaSAndroid Build Coastguard Worker                extraCoordY = error.y > 0 ? insetClamp.y : insetClamp.w;
238*c8dee2aaSAndroid Build Coastguard Worker                extraColorY = $sample_image(float2(clampedPos.x, extraCoordY),
239*c8dee2aaSAndroid Build Coastguard Worker                                            invImgSize, s);
240*c8dee2aaSAndroid Build Coastguard Worker            }
241*c8dee2aaSAndroid Build Coastguard Worker            if (sampleExtraX && sampleExtraY) {
242*c8dee2aaSAndroid Build Coastguard Worker                half4 extraColorXY = $sample_image(float2(extraCoordX, extraCoordY),
243*c8dee2aaSAndroid Build Coastguard Worker                                                   invImgSize, s);
244*c8dee2aaSAndroid Build Coastguard Worker                color = mix(mix(color, extraColorX, absError.x),
245*c8dee2aaSAndroid Build Coastguard Worker                            mix(extraColorY, extraColorXY, absError.x),
246*c8dee2aaSAndroid Build Coastguard Worker                            absError.y);
247*c8dee2aaSAndroid Build Coastguard Worker            } else if (sampleExtraX) {
248*c8dee2aaSAndroid Build Coastguard Worker                color = mix(color, extraColorX, absError.x);
249*c8dee2aaSAndroid Build Coastguard Worker            } else if (sampleExtraY) {
250*c8dee2aaSAndroid Build Coastguard Worker                color = mix(color, extraColorY, absError.y);
251*c8dee2aaSAndroid Build Coastguard Worker            }
252*c8dee2aaSAndroid Build Coastguard Worker        }
253*c8dee2aaSAndroid Build Coastguard Worker
254*c8dee2aaSAndroid Build Coastguard Worker        // Do soft edge shader filtering for decal tiling and linear filtering using the error
255*c8dee2aaSAndroid Build Coastguard Worker        // values calculated above.
256*c8dee2aaSAndroid Build Coastguard Worker        if (tileModeX == $kTileModeDecal) {
257*c8dee2aaSAndroid Build Coastguard Worker            color *= max(1 - absError.x, 0);
258*c8dee2aaSAndroid Build Coastguard Worker        }
259*c8dee2aaSAndroid Build Coastguard Worker        if (tileModeY == $kTileModeDecal) {
260*c8dee2aaSAndroid Build Coastguard Worker            color *= max(1 - absError.y, 0);
261*c8dee2aaSAndroid Build Coastguard Worker        }
262*c8dee2aaSAndroid Build Coastguard Worker    }
263*c8dee2aaSAndroid Build Coastguard Worker
264*c8dee2aaSAndroid Build Coastguard Worker    return color;
265*c8dee2aaSAndroid Build Coastguard Worker}
266*c8dee2aaSAndroid Build Coastguard Worker
267*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $cubic_filter_image(float2 pos,
268*c8dee2aaSAndroid Build Coastguard Worker                                float2 invImgSize,
269*c8dee2aaSAndroid Build Coastguard Worker                                float4 subset,
270*c8dee2aaSAndroid Build Coastguard Worker                                int tileModeX,
271*c8dee2aaSAndroid Build Coastguard Worker                                int tileModeY,
272*c8dee2aaSAndroid Build Coastguard Worker                                half4x4 coeffs,
273*c8dee2aaSAndroid Build Coastguard Worker                                sampler2D s) {
274*c8dee2aaSAndroid Build Coastguard Worker    // Determine pos's fractional offset f between texel centers.
275*c8dee2aaSAndroid Build Coastguard Worker    float2 f = fract(pos - 0.5);
276*c8dee2aaSAndroid Build Coastguard Worker    // Sample 16 points at 1-pixel intervals from [p - 1.5 ... p + 1.5].
277*c8dee2aaSAndroid Build Coastguard Worker    pos -= 1.5;
278*c8dee2aaSAndroid Build Coastguard Worker    // Snap to texel centers to prevent sampling neighboring texels.
279*c8dee2aaSAndroid Build Coastguard Worker    pos = floor(pos) + 0.5;
280*c8dee2aaSAndroid Build Coastguard Worker
281*c8dee2aaSAndroid Build Coastguard Worker    half4 wx = coeffs * half4(1.0, f.x, f.x * f.x, f.x * f.x * f.x);
282*c8dee2aaSAndroid Build Coastguard Worker    half4 wy = coeffs * half4(1.0, f.y, f.y * f.y, f.y * f.y * f.y);
283*c8dee2aaSAndroid Build Coastguard Worker    half4 color = half4(0);
284*c8dee2aaSAndroid Build Coastguard Worker    for (int y = 0; y < 4; ++y) {
285*c8dee2aaSAndroid Build Coastguard Worker        half4 rowColor = half4(0);
286*c8dee2aaSAndroid Build Coastguard Worker        for (int x = 0; x < 4; ++x) {
287*c8dee2aaSAndroid Build Coastguard Worker            rowColor += wx[x] * $sample_image_subset(pos + float2(x, y), invImgSize, subset,
288*c8dee2aaSAndroid Build Coastguard Worker                                                     tileModeX, tileModeY, $kFilterModeNearest,
289*c8dee2aaSAndroid Build Coastguard Worker                                                     float2($kLinearInset), s);
290*c8dee2aaSAndroid Build Coastguard Worker        }
291*c8dee2aaSAndroid Build Coastguard Worker        color += wy[y] * rowColor;
292*c8dee2aaSAndroid Build Coastguard Worker    }
293*c8dee2aaSAndroid Build Coastguard Worker    // Bicubic can send colors out of range, so clamp to get them back in gamut, assuming premul.
294*c8dee2aaSAndroid Build Coastguard Worker    color.a = saturate(color.a);
295*c8dee2aaSAndroid Build Coastguard Worker    color.rgb = clamp(color.rgb, half3(0.0), color.aaa);
296*c8dee2aaSAndroid Build Coastguard Worker    return color;
297*c8dee2aaSAndroid Build Coastguard Worker}
298*c8dee2aaSAndroid Build Coastguard Worker
299*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_image_shader(float2 coords,
300*c8dee2aaSAndroid Build Coastguard Worker                            float2 invImgSize,
301*c8dee2aaSAndroid Build Coastguard Worker                            float4 subset,
302*c8dee2aaSAndroid Build Coastguard Worker                            int tileModeX,
303*c8dee2aaSAndroid Build Coastguard Worker                            int tileModeY,
304*c8dee2aaSAndroid Build Coastguard Worker                            int filterMode,
305*c8dee2aaSAndroid Build Coastguard Worker                            sampler2D s) {
306*c8dee2aaSAndroid Build Coastguard Worker    return $sample_image_subset(coords, invImgSize, subset, tileModeX, tileModeY,
307*c8dee2aaSAndroid Build Coastguard Worker                                filterMode, float2($kLinearInset), s);
308*c8dee2aaSAndroid Build Coastguard Worker}
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_cubic_image_shader(float2 coords,
311*c8dee2aaSAndroid Build Coastguard Worker                                  float2 invImgSize,
312*c8dee2aaSAndroid Build Coastguard Worker                                  float4 subset,
313*c8dee2aaSAndroid Build Coastguard Worker                                  int tileModeX,
314*c8dee2aaSAndroid Build Coastguard Worker                                  int tileModeY,
315*c8dee2aaSAndroid Build Coastguard Worker                                  half4x4 cubicCoeffs,
316*c8dee2aaSAndroid Build Coastguard Worker                                  sampler2D s) {
317*c8dee2aaSAndroid Build Coastguard Worker    return $cubic_filter_image(coords, invImgSize, subset, tileModeX, tileModeY, cubicCoeffs, s);
318*c8dee2aaSAndroid Build Coastguard Worker}
319*c8dee2aaSAndroid Build Coastguard Worker
320*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_hw_image_shader(float2 coords,
321*c8dee2aaSAndroid Build Coastguard Worker                               float2 invImgSize,
322*c8dee2aaSAndroid Build Coastguard Worker                               sampler2D s) {
323*c8dee2aaSAndroid Build Coastguard Worker    return $sample_image(coords, invImgSize, s);
324*c8dee2aaSAndroid Build Coastguard Worker}
325*c8dee2aaSAndroid Build Coastguard Worker
326*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $yuv_to_rgb_no_swizzle(half Y,
327*c8dee2aaSAndroid Build Coastguard Worker                                   half U,
328*c8dee2aaSAndroid Build Coastguard Worker                                   half V,
329*c8dee2aaSAndroid Build Coastguard Worker                                   half alpha,
330*c8dee2aaSAndroid Build Coastguard Worker                                   half3x3 yuvToRGBMatrix,
331*c8dee2aaSAndroid Build Coastguard Worker                                   half3 yuvToRGBTranslate) {
332*c8dee2aaSAndroid Build Coastguard Worker    half3 preColor = half3(Y, U, V);
333*c8dee2aaSAndroid Build Coastguard Worker    half4 sampleColor;
334*c8dee2aaSAndroid Build Coastguard Worker    sampleColor.rgb = saturate(yuvToRGBMatrix * preColor.rgb + yuvToRGBTranslate);
335*c8dee2aaSAndroid Build Coastguard Worker    sampleColor.a = alpha;
336*c8dee2aaSAndroid Build Coastguard Worker
337*c8dee2aaSAndroid Build Coastguard Worker    // We always return an unpremul color here to skip work when transforming colorspaces
338*c8dee2aaSAndroid Build Coastguard Worker    return sampleColor;
339*c8dee2aaSAndroid Build Coastguard Worker}
340*c8dee2aaSAndroid Build Coastguard Worker
341*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $yuv_to_rgb(half4 sampleColorY,
342*c8dee2aaSAndroid Build Coastguard Worker                        half4 sampleColorU,
343*c8dee2aaSAndroid Build Coastguard Worker                        half4 sampleColorV,
344*c8dee2aaSAndroid Build Coastguard Worker                        half alpha,
345*c8dee2aaSAndroid Build Coastguard Worker                        half4 channelSelectY,
346*c8dee2aaSAndroid Build Coastguard Worker                        half4 channelSelectU,
347*c8dee2aaSAndroid Build Coastguard Worker                        half4 channelSelectV,
348*c8dee2aaSAndroid Build Coastguard Worker                        half3x3 yuvToRGBMatrix,
349*c8dee2aaSAndroid Build Coastguard Worker                        half3 yuvToRGBTranslate) {
350*c8dee2aaSAndroid Build Coastguard Worker    half Y = dot(channelSelectY, sampleColorY);
351*c8dee2aaSAndroid Build Coastguard Worker    half U = dot(channelSelectU, sampleColorU);
352*c8dee2aaSAndroid Build Coastguard Worker    half V = dot(channelSelectV, sampleColorV);
353*c8dee2aaSAndroid Build Coastguard Worker    return $yuv_to_rgb_no_swizzle(Y, U, V, alpha, yuvToRGBMatrix, yuvToRGBTranslate);
354*c8dee2aaSAndroid Build Coastguard Worker}
355*c8dee2aaSAndroid Build Coastguard Worker
356*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_yuv_image_shader(float2 coords,
357*c8dee2aaSAndroid Build Coastguard Worker                                float2 invImgSizeY,
358*c8dee2aaSAndroid Build Coastguard Worker                                float2 invImgSizeUV,  // Relative to Y's coordinate space
359*c8dee2aaSAndroid Build Coastguard Worker                                float4 subset,
360*c8dee2aaSAndroid Build Coastguard Worker                                float2 linearFilterUVInset,
361*c8dee2aaSAndroid Build Coastguard Worker                                int tileModeX,
362*c8dee2aaSAndroid Build Coastguard Worker                                int tileModeY,
363*c8dee2aaSAndroid Build Coastguard Worker                                int filterModeY,
364*c8dee2aaSAndroid Build Coastguard Worker                                int filterModeUV,
365*c8dee2aaSAndroid Build Coastguard Worker                                half4 channelSelectY,
366*c8dee2aaSAndroid Build Coastguard Worker                                half4 channelSelectU,
367*c8dee2aaSAndroid Build Coastguard Worker                                half4 channelSelectV,
368*c8dee2aaSAndroid Build Coastguard Worker                                half4 channelSelectA,
369*c8dee2aaSAndroid Build Coastguard Worker                                half3x3 yuvToRGBMatrix,
370*c8dee2aaSAndroid Build Coastguard Worker                                half3 yuvToRGBTranslate,
371*c8dee2aaSAndroid Build Coastguard Worker                                sampler2D sY,
372*c8dee2aaSAndroid Build Coastguard Worker                                sampler2D sU,
373*c8dee2aaSAndroid Build Coastguard Worker                                sampler2D sV,
374*c8dee2aaSAndroid Build Coastguard Worker                                sampler2D sA) {
375*c8dee2aaSAndroid Build Coastguard Worker    // If the filter modes are different between Y and UV, this means that
376*c8dee2aaSAndroid Build Coastguard Worker    // the base filtermode is nearest and we have to snap the coords to Y's
377*c8dee2aaSAndroid Build Coastguard Worker    // texel centers to get the correct positions for UV.
378*c8dee2aaSAndroid Build Coastguard Worker    if (filterModeY != filterModeUV) {
379*c8dee2aaSAndroid Build Coastguard Worker        coords = floor(coords) + 0.5;
380*c8dee2aaSAndroid Build Coastguard Worker    }
381*c8dee2aaSAndroid Build Coastguard Worker
382*c8dee2aaSAndroid Build Coastguard Worker    int tileModeX_UV = tileModeX == $kTileModeDecal ? $kTileModeClamp : tileModeX;
383*c8dee2aaSAndroid Build Coastguard Worker    int tileModeY_UV = tileModeY == $kTileModeDecal ? $kTileModeClamp : tileModeY;
384*c8dee2aaSAndroid Build Coastguard Worker
385*c8dee2aaSAndroid Build Coastguard Worker    half4 sampleColorY, sampleColorU, sampleColorV;
386*c8dee2aaSAndroid Build Coastguard Worker    sampleColorY = $sample_image_subset(coords, invImgSizeY, subset, tileModeX, tileModeY,
387*c8dee2aaSAndroid Build Coastguard Worker                                        filterModeY, float2($kLinearInset), sY);
388*c8dee2aaSAndroid Build Coastguard Worker    sampleColorU = $sample_image_subset(coords, invImgSizeUV, subset, tileModeX_UV, tileModeY_UV,
389*c8dee2aaSAndroid Build Coastguard Worker                                        filterModeUV, linearFilterUVInset, sU);
390*c8dee2aaSAndroid Build Coastguard Worker    sampleColorV = $sample_image_subset(coords, invImgSizeUV, subset, tileModeX_UV, tileModeY_UV,
391*c8dee2aaSAndroid Build Coastguard Worker                                        filterModeUV, linearFilterUVInset, sV);
392*c8dee2aaSAndroid Build Coastguard Worker    half alpha;
393*c8dee2aaSAndroid Build Coastguard Worker    if (channelSelectA == half4(1)) {
394*c8dee2aaSAndroid Build Coastguard Worker        alpha = 1;
395*c8dee2aaSAndroid Build Coastguard Worker    } else {
396*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleColorA = $sample_image_subset(coords, invImgSizeY, subset, tileModeX, tileModeY,
397*c8dee2aaSAndroid Build Coastguard Worker                                                  filterModeY, float2($kLinearInset), sA);
398*c8dee2aaSAndroid Build Coastguard Worker        alpha = dot(channelSelectA, sampleColorA);
399*c8dee2aaSAndroid Build Coastguard Worker    }
400*c8dee2aaSAndroid Build Coastguard Worker
401*c8dee2aaSAndroid Build Coastguard Worker    return $yuv_to_rgb(sampleColorY, sampleColorU, sampleColorV, alpha,
402*c8dee2aaSAndroid Build Coastguard Worker                       channelSelectY, channelSelectU, channelSelectV,
403*c8dee2aaSAndroid Build Coastguard Worker                       yuvToRGBMatrix, yuvToRGBTranslate);
404*c8dee2aaSAndroid Build Coastguard Worker}
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_cubic_yuv_image_shader(float2 coords,
407*c8dee2aaSAndroid Build Coastguard Worker                                      float2 invImgSizeY,
408*c8dee2aaSAndroid Build Coastguard Worker                                      float2 invImgSizeUV,  // Relative to Y's coordinate space
409*c8dee2aaSAndroid Build Coastguard Worker                                      float4 subset,
410*c8dee2aaSAndroid Build Coastguard Worker                                      int tileModeX,
411*c8dee2aaSAndroid Build Coastguard Worker                                      int tileModeY,
412*c8dee2aaSAndroid Build Coastguard Worker                                      half4x4 cubicCoeffs,
413*c8dee2aaSAndroid Build Coastguard Worker                                      half4 channelSelectY,
414*c8dee2aaSAndroid Build Coastguard Worker                                      half4 channelSelectU,
415*c8dee2aaSAndroid Build Coastguard Worker                                      half4 channelSelectV,
416*c8dee2aaSAndroid Build Coastguard Worker                                      half4 channelSelectA,
417*c8dee2aaSAndroid Build Coastguard Worker                                      half3x3 yuvToRGBMatrix,
418*c8dee2aaSAndroid Build Coastguard Worker                                      half3 yuvToRGBTranslate,
419*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D sY,
420*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D sU,
421*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D sV,
422*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D sA) {
423*c8dee2aaSAndroid Build Coastguard Worker    int tileModeX_UV = tileModeX == $kTileModeDecal ? $kTileModeClamp : tileModeX;
424*c8dee2aaSAndroid Build Coastguard Worker    int tileModeY_UV = tileModeY == $kTileModeDecal ? $kTileModeClamp : tileModeY;
425*c8dee2aaSAndroid Build Coastguard Worker
426*c8dee2aaSAndroid Build Coastguard Worker    half4 sampleColorY, sampleColorU, sampleColorV;
427*c8dee2aaSAndroid Build Coastguard Worker    sampleColorY = $cubic_filter_image(coords, invImgSizeY, subset, tileModeX, tileModeY,
428*c8dee2aaSAndroid Build Coastguard Worker                                       cubicCoeffs, sY);
429*c8dee2aaSAndroid Build Coastguard Worker    sampleColorU = $cubic_filter_image(coords, invImgSizeUV, subset, tileModeX_UV, tileModeY_UV,
430*c8dee2aaSAndroid Build Coastguard Worker                                       cubicCoeffs, sU);
431*c8dee2aaSAndroid Build Coastguard Worker    sampleColorV = $cubic_filter_image(coords, invImgSizeUV, subset, tileModeX_UV, tileModeY_UV,
432*c8dee2aaSAndroid Build Coastguard Worker                                       cubicCoeffs, sV);
433*c8dee2aaSAndroid Build Coastguard Worker    half alpha;
434*c8dee2aaSAndroid Build Coastguard Worker    if (channelSelectA == half4(1)) {
435*c8dee2aaSAndroid Build Coastguard Worker        alpha = 1;
436*c8dee2aaSAndroid Build Coastguard Worker    } else {
437*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleColorA = $cubic_filter_image(coords, invImgSizeY, subset, tileModeX, tileModeY,
438*c8dee2aaSAndroid Build Coastguard Worker                                                 cubicCoeffs, sA);
439*c8dee2aaSAndroid Build Coastguard Worker        alpha = dot(channelSelectA, sampleColorA);
440*c8dee2aaSAndroid Build Coastguard Worker    }
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker    return $yuv_to_rgb(sampleColorY, sampleColorU, sampleColorV, alpha,
443*c8dee2aaSAndroid Build Coastguard Worker                       channelSelectY, channelSelectU, channelSelectV,
444*c8dee2aaSAndroid Build Coastguard Worker                       yuvToRGBMatrix, yuvToRGBTranslate);
445*c8dee2aaSAndroid Build Coastguard Worker}
446*c8dee2aaSAndroid Build Coastguard Worker
447*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_hw_yuv_image_shader(float2 coords,
448*c8dee2aaSAndroid Build Coastguard Worker                                   float2 invImgSizeY,
449*c8dee2aaSAndroid Build Coastguard Worker                                   float2 invImgSizeUV,  // Relative to Y's coordinate space
450*c8dee2aaSAndroid Build Coastguard Worker                                   half4 channelSelectY,
451*c8dee2aaSAndroid Build Coastguard Worker                                   half4 channelSelectU,
452*c8dee2aaSAndroid Build Coastguard Worker                                   half4 channelSelectV,
453*c8dee2aaSAndroid Build Coastguard Worker                                   half4 channelSelectA,
454*c8dee2aaSAndroid Build Coastguard Worker                                   half3x3 yuvToRGBMatrix,
455*c8dee2aaSAndroid Build Coastguard Worker                                   half3 yuvToRGBTranslate,
456*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D sY,
457*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D sU,
458*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D sV,
459*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D sA) {
460*c8dee2aaSAndroid Build Coastguard Worker    half4 sampleColorY, sampleColorU, sampleColorV, sampleColorA;
461*c8dee2aaSAndroid Build Coastguard Worker    sampleColorY = $sample_image(coords, invImgSizeY, sY);
462*c8dee2aaSAndroid Build Coastguard Worker    sampleColorU = $sample_image(coords, invImgSizeUV, sU);
463*c8dee2aaSAndroid Build Coastguard Worker    sampleColorV = $sample_image(coords, invImgSizeUV, sV);
464*c8dee2aaSAndroid Build Coastguard Worker    half alpha;
465*c8dee2aaSAndroid Build Coastguard Worker    if (channelSelectA == half4(1)) {
466*c8dee2aaSAndroid Build Coastguard Worker        alpha = 1;
467*c8dee2aaSAndroid Build Coastguard Worker    } else {
468*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleColorA = $sample_image(coords, invImgSizeY, sA);
469*c8dee2aaSAndroid Build Coastguard Worker        alpha = dot(channelSelectA, sampleColorA);
470*c8dee2aaSAndroid Build Coastguard Worker    }
471*c8dee2aaSAndroid Build Coastguard Worker
472*c8dee2aaSAndroid Build Coastguard Worker    return $yuv_to_rgb(sampleColorY, sampleColorU, sampleColorV, alpha,
473*c8dee2aaSAndroid Build Coastguard Worker                       channelSelectY, channelSelectU, channelSelectV,
474*c8dee2aaSAndroid Build Coastguard Worker                       yuvToRGBMatrix, yuvToRGBTranslate);
475*c8dee2aaSAndroid Build Coastguard Worker}
476*c8dee2aaSAndroid Build Coastguard Worker
477*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_hw_yuv_no_swizzle_image_shader(float2 coords,
478*c8dee2aaSAndroid Build Coastguard Worker                                              float2 invImgSizeY,
479*c8dee2aaSAndroid Build Coastguard Worker                                              float2 invImgSizeUV,  // Relative to Y's coord space
480*c8dee2aaSAndroid Build Coastguard Worker                                              half3x3 yuvToRGBMatrix,
481*c8dee2aaSAndroid Build Coastguard Worker                                              half4 yuvToRGBXlateAlphaParam,
482*c8dee2aaSAndroid Build Coastguard Worker                                              sampler2D sY,
483*c8dee2aaSAndroid Build Coastguard Worker                                              sampler2D sU,
484*c8dee2aaSAndroid Build Coastguard Worker                                              sampler2D sV,
485*c8dee2aaSAndroid Build Coastguard Worker                                              sampler2D sA) {
486*c8dee2aaSAndroid Build Coastguard Worker    half4 sampleColorY, sampleColorU, sampleColorV, sampleColorA;
487*c8dee2aaSAndroid Build Coastguard Worker    half Y = $sample_image(coords, invImgSizeY, sY).r;
488*c8dee2aaSAndroid Build Coastguard Worker    half U = $sample_image(coords, invImgSizeUV, sU).r;
489*c8dee2aaSAndroid Build Coastguard Worker    half V = $sample_image(coords, invImgSizeUV, sV).r;
490*c8dee2aaSAndroid Build Coastguard Worker    // When it's a Y_U_V_A texture, yuvToRGBXlateAlphaParam.w is 0 and we have a real A sampler so
491*c8dee2aaSAndroid Build Coastguard Worker    // alpha is just that saturated value (should be a no-op). For Y_U_V, we set
492*c8dee2aaSAndroid Build Coastguard Worker    // yuvToRGBXlateAlphaParam.w to 1 and sample from Y, which after the saturate is always 1.
493*c8dee2aaSAndroid Build Coastguard Worker    half alpha = saturate($sample_image(coords, invImgSizeY, sA).r + yuvToRGBXlateAlphaParam.w);
494*c8dee2aaSAndroid Build Coastguard Worker
495*c8dee2aaSAndroid Build Coastguard Worker    return $yuv_to_rgb_no_swizzle(Y, U, V, alpha,
496*c8dee2aaSAndroid Build Coastguard Worker                                  yuvToRGBMatrix, yuvToRGBXlateAlphaParam.xyz);
497*c8dee2aaSAndroid Build Coastguard Worker}
498*c8dee2aaSAndroid Build Coastguard Worker
499*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_dither(half4 colorIn, half range, sampler2D lut) {
500*c8dee2aaSAndroid Build Coastguard Worker    const float kImgSize = 8;
501*c8dee2aaSAndroid Build Coastguard Worker
502*c8dee2aaSAndroid Build Coastguard Worker    half value = sample(lut, sk_FragCoord.xy / kImgSize).r - 0.5; // undo the bias in the table
503*c8dee2aaSAndroid Build Coastguard Worker    // For each color channel, add the random offset to the channel value and then clamp
504*c8dee2aaSAndroid Build Coastguard Worker    // between 0 and alpha to keep the color premultiplied.
505*c8dee2aaSAndroid Build Coastguard Worker    return half4(clamp(colorIn.rgb + value * range, 0.0, colorIn.a), colorIn.a);
506*c8dee2aaSAndroid Build Coastguard Worker}
507*c8dee2aaSAndroid Build Coastguard Worker
508*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $tile_grad(int tileMode, float2 t) {
509*c8dee2aaSAndroid Build Coastguard Worker    switch (tileMode) {
510*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeClamp:
511*c8dee2aaSAndroid Build Coastguard Worker            t.x = saturate(t.x);
512*c8dee2aaSAndroid Build Coastguard Worker            break;
513*c8dee2aaSAndroid Build Coastguard Worker
514*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeRepeat:
515*c8dee2aaSAndroid Build Coastguard Worker            t.x = fract(t.x);
516*c8dee2aaSAndroid Build Coastguard Worker            break;
517*c8dee2aaSAndroid Build Coastguard Worker
518*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeMirror: {
519*c8dee2aaSAndroid Build Coastguard Worker            float t_1 = t.x - 1;
520*c8dee2aaSAndroid Build Coastguard Worker            t.x = t_1 - 2 * floor(t_1 * 0.5) - 1;
521*c8dee2aaSAndroid Build Coastguard Worker            if (sk_Caps.mustDoOpBetweenFloorAndAbs) {
522*c8dee2aaSAndroid Build Coastguard Worker                // At this point the expected value of tiled_t should between -1 and 1, so this
523*c8dee2aaSAndroid Build Coastguard Worker                // clamp has no effect other than to break up the floor and abs calls and make sure
524*c8dee2aaSAndroid Build Coastguard Worker                // the compiler doesn't merge them back together.
525*c8dee2aaSAndroid Build Coastguard Worker                t.x = clamp(t.x, -1, 1);
526*c8dee2aaSAndroid Build Coastguard Worker            }
527*c8dee2aaSAndroid Build Coastguard Worker            t.x = abs(t.x);
528*c8dee2aaSAndroid Build Coastguard Worker            break;
529*c8dee2aaSAndroid Build Coastguard Worker        }
530*c8dee2aaSAndroid Build Coastguard Worker
531*c8dee2aaSAndroid Build Coastguard Worker        case $kTileModeDecal:
532*c8dee2aaSAndroid Build Coastguard Worker            if (t.x < 0 || t.x > 1) {
533*c8dee2aaSAndroid Build Coastguard Worker                return float2(0, -1);
534*c8dee2aaSAndroid Build Coastguard Worker            }
535*c8dee2aaSAndroid Build Coastguard Worker            break;
536*c8dee2aaSAndroid Build Coastguard Worker    }
537*c8dee2aaSAndroid Build Coastguard Worker
538*c8dee2aaSAndroid Build Coastguard Worker    return t;
539*c8dee2aaSAndroid Build Coastguard Worker}
540*c8dee2aaSAndroid Build Coastguard Worker
541*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $colorize_grad_4(float4 colorsParam[4], float4 offsetsParam, float2 t) {
542*c8dee2aaSAndroid Build Coastguard Worker    if (t.y < 0) {
543*c8dee2aaSAndroid Build Coastguard Worker        return half4(0);
544*c8dee2aaSAndroid Build Coastguard Worker
545*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x <= offsetsParam[0]) {
546*c8dee2aaSAndroid Build Coastguard Worker        return half4(colorsParam[0]);
547*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x < offsetsParam[1]) {
548*c8dee2aaSAndroid Build Coastguard Worker        return half4(mix(colorsParam[0], colorsParam[1], (t.x             - offsetsParam[0]) /
549*c8dee2aaSAndroid Build Coastguard Worker                                                         (offsetsParam[1] - offsetsParam[0])));
550*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x < offsetsParam[2]) {
551*c8dee2aaSAndroid Build Coastguard Worker        return half4(mix(colorsParam[1], colorsParam[2], (t.x             - offsetsParam[1]) /
552*c8dee2aaSAndroid Build Coastguard Worker                                                         (offsetsParam[2] - offsetsParam[1])));
553*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x < offsetsParam[3]) {
554*c8dee2aaSAndroid Build Coastguard Worker        return half4(mix(colorsParam[2], colorsParam[3], (t.x             - offsetsParam[2]) /
555*c8dee2aaSAndroid Build Coastguard Worker                                                         (offsetsParam[3] - offsetsParam[2])));
556*c8dee2aaSAndroid Build Coastguard Worker    } else {
557*c8dee2aaSAndroid Build Coastguard Worker        return half4(colorsParam[3]);
558*c8dee2aaSAndroid Build Coastguard Worker    }
559*c8dee2aaSAndroid Build Coastguard Worker}
560*c8dee2aaSAndroid Build Coastguard Worker
561*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $colorize_grad_8(float4 colorsParam[8], float4 offsetsParam[2], float2 t) {
562*c8dee2aaSAndroid Build Coastguard Worker    if (t.y < 0) {
563*c8dee2aaSAndroid Build Coastguard Worker        return half4(0);
564*c8dee2aaSAndroid Build Coastguard Worker
565*c8dee2aaSAndroid Build Coastguard Worker    // Unrolled binary search through intervals
566*c8dee2aaSAndroid Build Coastguard Worker    // ( .. 0), (0 .. 1), (1 .. 2), (2 .. 3), (3 .. 4), (4 .. 5), (5 .. 6), (6 .. 7), (7 .. ).
567*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x < offsetsParam[1][0]) {
568*c8dee2aaSAndroid Build Coastguard Worker        if (t.x < offsetsParam[0][2]) {
569*c8dee2aaSAndroid Build Coastguard Worker            if (t.x <= offsetsParam[0][0]) {
570*c8dee2aaSAndroid Build Coastguard Worker                return half4(colorsParam[0]);
571*c8dee2aaSAndroid Build Coastguard Worker            } else if (t.x < offsetsParam[0][1]) {
572*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[0], colorsParam[1],
573*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[0][0]) /
574*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[0][1] - offsetsParam[0][0])));
575*c8dee2aaSAndroid Build Coastguard Worker            } else {
576*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[1], colorsParam[2],
577*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[0][1]) /
578*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[0][2] - offsetsParam[0][1])));
579*c8dee2aaSAndroid Build Coastguard Worker            }
580*c8dee2aaSAndroid Build Coastguard Worker        } else {
581*c8dee2aaSAndroid Build Coastguard Worker            if (t.x < offsetsParam[0][3]) {
582*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[2], colorsParam[3],
583*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[0][2]) /
584*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[0][3] - offsetsParam[0][2])));
585*c8dee2aaSAndroid Build Coastguard Worker            } else {
586*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[3], colorsParam[4],
587*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[0][3]) /
588*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[1][0] - offsetsParam[0][3])));
589*c8dee2aaSAndroid Build Coastguard Worker            }
590*c8dee2aaSAndroid Build Coastguard Worker        }
591*c8dee2aaSAndroid Build Coastguard Worker    } else {
592*c8dee2aaSAndroid Build Coastguard Worker        if (t.x < offsetsParam[1][2]) {
593*c8dee2aaSAndroid Build Coastguard Worker            if (t.x < offsetsParam[1][1]) {
594*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[4], colorsParam[5],
595*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[1][0]) /
596*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[1][1] - offsetsParam[1][0])));
597*c8dee2aaSAndroid Build Coastguard Worker            } else {
598*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[5], colorsParam[6],
599*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[1][1]) /
600*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[1][2] - offsetsParam[1][1])));
601*c8dee2aaSAndroid Build Coastguard Worker            }
602*c8dee2aaSAndroid Build Coastguard Worker        } else {
603*c8dee2aaSAndroid Build Coastguard Worker            if (t.x < offsetsParam[1][3]) {
604*c8dee2aaSAndroid Build Coastguard Worker                return half4(mix(colorsParam[6], colorsParam[7],
605*c8dee2aaSAndroid Build Coastguard Worker                                 (t.x                - offsetsParam[1][2]) /
606*c8dee2aaSAndroid Build Coastguard Worker                                 (offsetsParam[1][3] - offsetsParam[1][2])));
607*c8dee2aaSAndroid Build Coastguard Worker            } else {
608*c8dee2aaSAndroid Build Coastguard Worker                return half4(colorsParam[7]);
609*c8dee2aaSAndroid Build Coastguard Worker            }
610*c8dee2aaSAndroid Build Coastguard Worker        }
611*c8dee2aaSAndroid Build Coastguard Worker    }
612*c8dee2aaSAndroid Build Coastguard Worker}
613*c8dee2aaSAndroid Build Coastguard Worker
614*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $colorize_grad_tex(sampler2D colorsAndOffsetsSampler, int numStops, float2 t) {
615*c8dee2aaSAndroid Build Coastguard Worker    const float kColorCoord = 0.25;
616*c8dee2aaSAndroid Build Coastguard Worker    const float kOffsetCoord = 0.75;
617*c8dee2aaSAndroid Build Coastguard Worker
618*c8dee2aaSAndroid Build Coastguard Worker    if (t.y < 0) {
619*c8dee2aaSAndroid Build Coastguard Worker        return half4(0);
620*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x == 0) {
621*c8dee2aaSAndroid Build Coastguard Worker        return sampleLod(colorsAndOffsetsSampler, float2(0, kColorCoord), 0);
622*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x == 1) {
623*c8dee2aaSAndroid Build Coastguard Worker        return sampleLod(colorsAndOffsetsSampler, float2(1, kColorCoord), 0);
624*c8dee2aaSAndroid Build Coastguard Worker    } else {
625*c8dee2aaSAndroid Build Coastguard Worker        float low = 0;
626*c8dee2aaSAndroid Build Coastguard Worker        float high = float(numStops);
627*c8dee2aaSAndroid Build Coastguard Worker        float invNumStops = 1.0 / high;
628*c8dee2aaSAndroid Build Coastguard Worker        for (int loop = 1; loop < numStops; loop += loop) {
629*c8dee2aaSAndroid Build Coastguard Worker            float mid = floor((low + high) * 0.5);
630*c8dee2aaSAndroid Build Coastguard Worker            float samplePos = (mid + 0.5) * invNumStops;
631*c8dee2aaSAndroid Build Coastguard Worker
632*c8dee2aaSAndroid Build Coastguard Worker            float2 tmp = sampleLod(colorsAndOffsetsSampler, float2(samplePos, kOffsetCoord), 0).xy;
633*c8dee2aaSAndroid Build Coastguard Worker            float offset = ldexp(tmp.x, int(tmp.y));
634*c8dee2aaSAndroid Build Coastguard Worker
635*c8dee2aaSAndroid Build Coastguard Worker            if (t.x < offset) {
636*c8dee2aaSAndroid Build Coastguard Worker                high = mid;
637*c8dee2aaSAndroid Build Coastguard Worker            } else {
638*c8dee2aaSAndroid Build Coastguard Worker                low = mid;
639*c8dee2aaSAndroid Build Coastguard Worker            }
640*c8dee2aaSAndroid Build Coastguard Worker        }
641*c8dee2aaSAndroid Build Coastguard Worker
642*c8dee2aaSAndroid Build Coastguard Worker        high = (low + 1.5) * invNumStops;
643*c8dee2aaSAndroid Build Coastguard Worker        low = (low + 0.5) * invNumStops;
644*c8dee2aaSAndroid Build Coastguard Worker        half4 color0 = sampleLod(colorsAndOffsetsSampler, float2(low, kColorCoord), 0);
645*c8dee2aaSAndroid Build Coastguard Worker        half4 color1 = sampleLod(colorsAndOffsetsSampler, float2(high, kColorCoord), 0);
646*c8dee2aaSAndroid Build Coastguard Worker
647*c8dee2aaSAndroid Build Coastguard Worker        float2 tmp = sampleLod(colorsAndOffsetsSampler, float2(low, kOffsetCoord), 0).xy;
648*c8dee2aaSAndroid Build Coastguard Worker        float offset0 = ldexp(tmp.x, int(tmp.y));
649*c8dee2aaSAndroid Build Coastguard Worker
650*c8dee2aaSAndroid Build Coastguard Worker        tmp = sampleLod(colorsAndOffsetsSampler, float2(high, kOffsetCoord), 0).xy;
651*c8dee2aaSAndroid Build Coastguard Worker        float offset1 = ldexp(tmp.x, int(tmp.y));
652*c8dee2aaSAndroid Build Coastguard Worker
653*c8dee2aaSAndroid Build Coastguard Worker        return half4(mix(color0, color1,
654*c8dee2aaSAndroid Build Coastguard Worker                         (t.x     - offset0) /
655*c8dee2aaSAndroid Build Coastguard Worker                         (offset1 - offset0)));
656*c8dee2aaSAndroid Build Coastguard Worker    }
657*c8dee2aaSAndroid Build Coastguard Worker}
658*c8dee2aaSAndroid Build Coastguard Worker
659*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $half4_from_array(float[] arr, int offset) {
660*c8dee2aaSAndroid Build Coastguard Worker    return half4(arr[offset + 0],
661*c8dee2aaSAndroid Build Coastguard Worker                 arr[offset + 1],
662*c8dee2aaSAndroid Build Coastguard Worker                 arr[offset + 2],
663*c8dee2aaSAndroid Build Coastguard Worker                 arr[offset + 3]);
664*c8dee2aaSAndroid Build Coastguard Worker}
665*c8dee2aaSAndroid Build Coastguard Worker
666*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $colorize_grad_buf(float[] colorAndOffsetData,
667*c8dee2aaSAndroid Build Coastguard Worker                               int offsetsBaseIndex,
668*c8dee2aaSAndroid Build Coastguard Worker                               int numStops,
669*c8dee2aaSAndroid Build Coastguard Worker                               float2 t) {
670*c8dee2aaSAndroid Build Coastguard Worker    int colorsBaseIndex = offsetsBaseIndex + numStops;
671*c8dee2aaSAndroid Build Coastguard Worker    if (t.y < 0) {
672*c8dee2aaSAndroid Build Coastguard Worker        return half4(0);
673*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x == 0) {
674*c8dee2aaSAndroid Build Coastguard Worker        return $half4_from_array(colorAndOffsetData, colorsBaseIndex);
675*c8dee2aaSAndroid Build Coastguard Worker    } else if (t.x == 1) {
676*c8dee2aaSAndroid Build Coastguard Worker        int lastColorIndex = colorsBaseIndex + (numStops - 1) * 4;
677*c8dee2aaSAndroid Build Coastguard Worker        return $half4_from_array(colorAndOffsetData, lastColorIndex);
678*c8dee2aaSAndroid Build Coastguard Worker    } else {
679*c8dee2aaSAndroid Build Coastguard Worker        // Binary search for the matching adjacent stop offsets running log2(numStops).
680*c8dee2aaSAndroid Build Coastguard Worker        int lowOffsetIndex = offsetsBaseIndex;
681*c8dee2aaSAndroid Build Coastguard Worker        int highOffsetIndex = lowOffsetIndex + numStops - 1;
682*c8dee2aaSAndroid Build Coastguard Worker        for (int i = 1; i < numStops; i += i) {
683*c8dee2aaSAndroid Build Coastguard Worker            int middleOffsetIndex = (lowOffsetIndex + highOffsetIndex) / 2;
684*c8dee2aaSAndroid Build Coastguard Worker            if (t.x < colorAndOffsetData[middleOffsetIndex]) {
685*c8dee2aaSAndroid Build Coastguard Worker                highOffsetIndex = middleOffsetIndex;
686*c8dee2aaSAndroid Build Coastguard Worker            } else {
687*c8dee2aaSAndroid Build Coastguard Worker                lowOffsetIndex = middleOffsetIndex;
688*c8dee2aaSAndroid Build Coastguard Worker            }
689*c8dee2aaSAndroid Build Coastguard Worker        }
690*c8dee2aaSAndroid Build Coastguard Worker        int lowColorIndex = colorsBaseIndex + (lowOffsetIndex - offsetsBaseIndex) * 4;
691*c8dee2aaSAndroid Build Coastguard Worker        float lowOffset = colorAndOffsetData[lowOffsetIndex];
692*c8dee2aaSAndroid Build Coastguard Worker        half4 lowColor = $half4_from_array(colorAndOffsetData, lowColorIndex);
693*c8dee2aaSAndroid Build Coastguard Worker
694*c8dee2aaSAndroid Build Coastguard Worker        int highColorIndex = colorsBaseIndex + (highOffsetIndex - offsetsBaseIndex) * 4;
695*c8dee2aaSAndroid Build Coastguard Worker        float highOffset = colorAndOffsetData[highOffsetIndex];
696*c8dee2aaSAndroid Build Coastguard Worker        if (highOffset == lowOffset) {
697*c8dee2aaSAndroid Build Coastguard Worker            // If the t value falls exactly on a color stop, both lowOffset
698*c8dee2aaSAndroid Build Coastguard Worker            // and highOffset will be exactly the same so we avoid having
699*c8dee2aaSAndroid Build Coastguard Worker            // 0/0=NaN as our mix value.
700*c8dee2aaSAndroid Build Coastguard Worker            return lowColor;
701*c8dee2aaSAndroid Build Coastguard Worker        } else {
702*c8dee2aaSAndroid Build Coastguard Worker            half4 highColor = $half4_from_array(colorAndOffsetData, highColorIndex);
703*c8dee2aaSAndroid Build Coastguard Worker
704*c8dee2aaSAndroid Build Coastguard Worker            return half4(mix(lowColor, highColor, (t.x - lowOffset) / (highOffset - lowOffset)));
705*c8dee2aaSAndroid Build Coastguard Worker        }
706*c8dee2aaSAndroid Build Coastguard Worker    }
707*c8dee2aaSAndroid Build Coastguard Worker}
708*c8dee2aaSAndroid Build Coastguard Worker
709*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $linear_grad_layout(float2 pos) {
710*c8dee2aaSAndroid Build Coastguard Worker    // Add small epsilon since when the gradient is horizontally or vertically aligned,
711*c8dee2aaSAndroid Build Coastguard Worker    // pixels along the same column or row can have slightly different interpolated t values
712*c8dee2aaSAndroid Build Coastguard Worker    // causing pixels to choose the wrong offset when colorizing. This helps ensure pixels
713*c8dee2aaSAndroid Build Coastguard Worker    // along the same column or row choose the same gradient offsets.
714*c8dee2aaSAndroid Build Coastguard Worker    return float2(pos.x + 0.00001, 1);
715*c8dee2aaSAndroid Build Coastguard Worker}
716*c8dee2aaSAndroid Build Coastguard Worker
717*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $radial_grad_layout(float2 pos) {
718*c8dee2aaSAndroid Build Coastguard Worker    float t = length(pos);
719*c8dee2aaSAndroid Build Coastguard Worker    return float2(t, 1);
720*c8dee2aaSAndroid Build Coastguard Worker}
721*c8dee2aaSAndroid Build Coastguard Worker
722*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $sweep_grad_layout(float biasParam, float scaleParam, float2 pos) {
723*c8dee2aaSAndroid Build Coastguard Worker    // Some devices incorrectly implement atan2(y,x) as atan(y/x). In actuality it is
724*c8dee2aaSAndroid Build Coastguard Worker    // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). To work around this we pass in
725*c8dee2aaSAndroid Build Coastguard Worker    // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device
726*c8dee2aaSAndroid Build Coastguard Worker    // handle the undefined behavior if the second parameter is 0, instead of doing the divide
727*c8dee2aaSAndroid Build Coastguard Worker    // ourselves and calling atan with the quotient.
728*c8dee2aaSAndroid Build Coastguard Worker    float angle;
729*c8dee2aaSAndroid Build Coastguard Worker    if (sk_Caps.atan2ImplementedAsAtanYOverX) {
730*c8dee2aaSAndroid Build Coastguard Worker        angle = 2 * atan(-pos.y, length(pos) - pos.x);
731*c8dee2aaSAndroid Build Coastguard Worker    } else {
732*c8dee2aaSAndroid Build Coastguard Worker        // Hardcode pi/2 for the angle when x == 0, to avoid undefined behavior in this
733*c8dee2aaSAndroid Build Coastguard Worker        // case. This hasn't proven to be necessary in the atan workaround case.
734*c8dee2aaSAndroid Build Coastguard Worker        angle = pos.x != 0.0 ? atan(-pos.y, -pos.x) : sign(pos.y) * -1.5707963267949;
735*c8dee2aaSAndroid Build Coastguard Worker    }
736*c8dee2aaSAndroid Build Coastguard Worker
737*c8dee2aaSAndroid Build Coastguard Worker    // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
738*c8dee2aaSAndroid Build Coastguard Worker    float t = (angle * 0.1591549430918 + 0.5 + biasParam) * scaleParam;
739*c8dee2aaSAndroid Build Coastguard Worker    return float2(t, 1);
740*c8dee2aaSAndroid Build Coastguard Worker}
741*c8dee2aaSAndroid Build Coastguard Worker
742*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $conical_grad_layout(float radius0,
743*c8dee2aaSAndroid Build Coastguard Worker                                  float dRadius,
744*c8dee2aaSAndroid Build Coastguard Worker                                  float a,
745*c8dee2aaSAndroid Build Coastguard Worker                                  float invA,
746*c8dee2aaSAndroid Build Coastguard Worker                                  float2 pos) {
747*c8dee2aaSAndroid Build Coastguard Worker    // When writing uniform values, if the gradient is radial, we encode a == 0 and since
748*c8dee2aaSAndroid Build Coastguard Worker    // the linear edge case is when a == 0, we differentiate the radial case with invA == 1.
749*c8dee2aaSAndroid Build Coastguard Worker    if (a == 0 && invA == 1) {
750*c8dee2aaSAndroid Build Coastguard Worker        // Radial case
751*c8dee2aaSAndroid Build Coastguard Worker        float t = length(pos) * dRadius - radius0;
752*c8dee2aaSAndroid Build Coastguard Worker
753*c8dee2aaSAndroid Build Coastguard Worker        return float2(t, 1);
754*c8dee2aaSAndroid Build Coastguard Worker    } else {
755*c8dee2aaSAndroid Build Coastguard Worker        // Focal/strip case.
756*c8dee2aaSAndroid Build Coastguard Worker        float c = dot(pos, pos) - radius0 * radius0;
757*c8dee2aaSAndroid Build Coastguard Worker        float negB = 2 * (dRadius * radius0 + pos.x);
758*c8dee2aaSAndroid Build Coastguard Worker
759*c8dee2aaSAndroid Build Coastguard Worker        float t;
760*c8dee2aaSAndroid Build Coastguard Worker        if (a == 0) {
761*c8dee2aaSAndroid Build Coastguard Worker            // Linear case, both circles intersect as exactly one point
762*c8dee2aaSAndroid Build Coastguard Worker            // with the focal point sitting on that point.
763*c8dee2aaSAndroid Build Coastguard Worker
764*c8dee2aaSAndroid Build Coastguard Worker            // It is highly unlikely that b and c would be 0 resulting in NaN, if b == 0 due to
765*c8dee2aaSAndroid Build Coastguard Worker            // a specific translation, it would result is +/-Inf which propogates the sign to
766*c8dee2aaSAndroid Build Coastguard Worker            // isValid resulting in how the pixel is expected to look.
767*c8dee2aaSAndroid Build Coastguard Worker            t = c / negB;
768*c8dee2aaSAndroid Build Coastguard Worker        } else {
769*c8dee2aaSAndroid Build Coastguard Worker            // Quadratic case
770*c8dee2aaSAndroid Build Coastguard Worker            float d = negB*negB - 4*a*c;
771*c8dee2aaSAndroid Build Coastguard Worker            if (d < 0) {
772*c8dee2aaSAndroid Build Coastguard Worker                return float2(0, -1);
773*c8dee2aaSAndroid Build Coastguard Worker            }
774*c8dee2aaSAndroid Build Coastguard Worker
775*c8dee2aaSAndroid Build Coastguard Worker            // T should be as large as possible, so when one circle fully encloses the other,
776*c8dee2aaSAndroid Build Coastguard Worker            // the sign will be positive or negative depending on the sign of dRadius.
777*c8dee2aaSAndroid Build Coastguard Worker            // When this isn't the case and they form a cone, the sign will always be positive.
778*c8dee2aaSAndroid Build Coastguard Worker            float quadSign = sign(1 - dRadius);
779*c8dee2aaSAndroid Build Coastguard Worker            t = invA * (negB + quadSign * sqrt(d));
780*c8dee2aaSAndroid Build Coastguard Worker        }
781*c8dee2aaSAndroid Build Coastguard Worker
782*c8dee2aaSAndroid Build Coastguard Worker        // Interpolated radius must be positive.
783*c8dee2aaSAndroid Build Coastguard Worker        float isValid = sign(t * dRadius + radius0);
784*c8dee2aaSAndroid Build Coastguard Worker        return float2(t, isValid);
785*c8dee2aaSAndroid Build Coastguard Worker    }
786*c8dee2aaSAndroid Build Coastguard Worker}
787*c8dee2aaSAndroid Build Coastguard Worker
788*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_linear_grad_4_shader(float2 coords,
789*c8dee2aaSAndroid Build Coastguard Worker                                    float4 colorsParam[4],
790*c8dee2aaSAndroid Build Coastguard Worker                                    float4 offsetsParam,
791*c8dee2aaSAndroid Build Coastguard Worker                                    int tileMode,
792*c8dee2aaSAndroid Build Coastguard Worker                                    int colorSpace,
793*c8dee2aaSAndroid Build Coastguard Worker                                    int doUnpremul) {
794*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $linear_grad_layout(coords);
795*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
796*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_4(colorsParam, offsetsParam, t);
797*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
798*c8dee2aaSAndroid Build Coastguard Worker}
799*c8dee2aaSAndroid Build Coastguard Worker
800*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_linear_grad_8_shader(float2 coords,
801*c8dee2aaSAndroid Build Coastguard Worker                                    float4 colorsParam[8],
802*c8dee2aaSAndroid Build Coastguard Worker                                    float4 offsetsParam[2],
803*c8dee2aaSAndroid Build Coastguard Worker                                    int tileMode,
804*c8dee2aaSAndroid Build Coastguard Worker                                    int colorSpace,
805*c8dee2aaSAndroid Build Coastguard Worker                                    int doUnpremul) {
806*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $linear_grad_layout(coords);
807*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
808*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_8(colorsParam, offsetsParam, t);
809*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
810*c8dee2aaSAndroid Build Coastguard Worker}
811*c8dee2aaSAndroid Build Coastguard Worker
812*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_linear_grad_tex_shader(float2 coords,
813*c8dee2aaSAndroid Build Coastguard Worker                                      int numStops,
814*c8dee2aaSAndroid Build Coastguard Worker                                      int tileMode,
815*c8dee2aaSAndroid Build Coastguard Worker                                      int colorSpace,
816*c8dee2aaSAndroid Build Coastguard Worker                                      int doUnpremul,
817*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D colorAndOffsetSampler) {
818*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $linear_grad_layout(coords);
819*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
820*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_tex(colorAndOffsetSampler, numStops, t);
821*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
822*c8dee2aaSAndroid Build Coastguard Worker}
823*c8dee2aaSAndroid Build Coastguard Worker
824*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_linear_grad_buf_shader(float2 coords,
825*c8dee2aaSAndroid Build Coastguard Worker                                      int numStops,
826*c8dee2aaSAndroid Build Coastguard Worker                                      int bufferOffset,
827*c8dee2aaSAndroid Build Coastguard Worker                                      int tileMode,
828*c8dee2aaSAndroid Build Coastguard Worker                                      int colorSpace,
829*c8dee2aaSAndroid Build Coastguard Worker                                      int doUnpremul,
830*c8dee2aaSAndroid Build Coastguard Worker                                      float[] colorAndOffsetData) {
831*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $linear_grad_layout(coords);
832*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
833*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_buf(colorAndOffsetData, bufferOffset, numStops, t);
834*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
835*c8dee2aaSAndroid Build Coastguard Worker}
836*c8dee2aaSAndroid Build Coastguard Worker
837*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_radial_grad_4_shader(float2 coords,
838*c8dee2aaSAndroid Build Coastguard Worker                                    float4 colorsParam[4],
839*c8dee2aaSAndroid Build Coastguard Worker                                    float4 offsetsParam,
840*c8dee2aaSAndroid Build Coastguard Worker                                    int tileMode,
841*c8dee2aaSAndroid Build Coastguard Worker                                    int colorSpace,
842*c8dee2aaSAndroid Build Coastguard Worker                                    int doUnpremul) {
843*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $radial_grad_layout(coords);
844*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
845*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_4(colorsParam, offsetsParam, t);
846*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
847*c8dee2aaSAndroid Build Coastguard Worker}
848*c8dee2aaSAndroid Build Coastguard Worker
849*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_radial_grad_8_shader(float2 coords,
850*c8dee2aaSAndroid Build Coastguard Worker                                    float4 colorsParam[8],
851*c8dee2aaSAndroid Build Coastguard Worker                                    float4 offsetsParam[2],
852*c8dee2aaSAndroid Build Coastguard Worker                                    int tileMode,
853*c8dee2aaSAndroid Build Coastguard Worker                                    int colorSpace,
854*c8dee2aaSAndroid Build Coastguard Worker                                    int doUnpremul) {
855*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $radial_grad_layout(coords);
856*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
857*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_8(colorsParam, offsetsParam, t);
858*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
859*c8dee2aaSAndroid Build Coastguard Worker}
860*c8dee2aaSAndroid Build Coastguard Worker
861*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_radial_grad_tex_shader(float2 coords,
862*c8dee2aaSAndroid Build Coastguard Worker                                      int numStops,
863*c8dee2aaSAndroid Build Coastguard Worker                                      int tileMode,
864*c8dee2aaSAndroid Build Coastguard Worker                                      int colorSpace,
865*c8dee2aaSAndroid Build Coastguard Worker                                      int doUnpremul,
866*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D colorAndOffsetSampler) {
867*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $radial_grad_layout(coords);
868*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
869*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_tex(colorAndOffsetSampler, numStops, t);
870*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
871*c8dee2aaSAndroid Build Coastguard Worker}
872*c8dee2aaSAndroid Build Coastguard Worker
873*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_radial_grad_buf_shader(float2 coords,
874*c8dee2aaSAndroid Build Coastguard Worker                                      int numStops,
875*c8dee2aaSAndroid Build Coastguard Worker                                      int bufferOffset,
876*c8dee2aaSAndroid Build Coastguard Worker                                      int tileMode,
877*c8dee2aaSAndroid Build Coastguard Worker                                      int colorSpace,
878*c8dee2aaSAndroid Build Coastguard Worker                                      int doUnpremul,
879*c8dee2aaSAndroid Build Coastguard Worker                                      float[] colorAndOffsetData) {
880*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $radial_grad_layout(coords);
881*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
882*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_buf(colorAndOffsetData, bufferOffset, numStops, t);
883*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
884*c8dee2aaSAndroid Build Coastguard Worker}
885*c8dee2aaSAndroid Build Coastguard Worker
886*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_sweep_grad_4_shader(float2 coords,
887*c8dee2aaSAndroid Build Coastguard Worker                                   float4 colorsParam[4],
888*c8dee2aaSAndroid Build Coastguard Worker                                   float4 offsetsParam,
889*c8dee2aaSAndroid Build Coastguard Worker                                   float biasParam,
890*c8dee2aaSAndroid Build Coastguard Worker                                   float scaleParam,
891*c8dee2aaSAndroid Build Coastguard Worker                                   int tileMode,
892*c8dee2aaSAndroid Build Coastguard Worker                                   int colorSpace,
893*c8dee2aaSAndroid Build Coastguard Worker                                   int doUnpremul) {
894*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $sweep_grad_layout(biasParam, scaleParam, coords);
895*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
896*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_4(colorsParam, offsetsParam, t);
897*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
898*c8dee2aaSAndroid Build Coastguard Worker}
899*c8dee2aaSAndroid Build Coastguard Worker
900*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_sweep_grad_8_shader(float2 coords,
901*c8dee2aaSAndroid Build Coastguard Worker                                   float4 colorsParam[8],
902*c8dee2aaSAndroid Build Coastguard Worker                                   float4 offsetsParam[2],
903*c8dee2aaSAndroid Build Coastguard Worker                                   float biasParam,
904*c8dee2aaSAndroid Build Coastguard Worker                                   float scaleParam,
905*c8dee2aaSAndroid Build Coastguard Worker                                   int tileMode,
906*c8dee2aaSAndroid Build Coastguard Worker                                   int colorSpace,
907*c8dee2aaSAndroid Build Coastguard Worker                                   int doUnpremul) {
908*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $sweep_grad_layout(biasParam, scaleParam, coords);
909*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
910*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_8(colorsParam, offsetsParam, t);
911*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
912*c8dee2aaSAndroid Build Coastguard Worker}
913*c8dee2aaSAndroid Build Coastguard Worker
914*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_sweep_grad_tex_shader(float2 coords,
915*c8dee2aaSAndroid Build Coastguard Worker                                     float biasParam,
916*c8dee2aaSAndroid Build Coastguard Worker                                     float scaleParam,
917*c8dee2aaSAndroid Build Coastguard Worker                                     int numStops,
918*c8dee2aaSAndroid Build Coastguard Worker                                     int tileMode,
919*c8dee2aaSAndroid Build Coastguard Worker                                     int colorSpace,
920*c8dee2aaSAndroid Build Coastguard Worker                                     int doUnpremul,
921*c8dee2aaSAndroid Build Coastguard Worker                                     sampler2D colorAndOffsetSampler) {
922*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $sweep_grad_layout(biasParam, scaleParam, coords);
923*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
924*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_tex(colorAndOffsetSampler, numStops, t);
925*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
926*c8dee2aaSAndroid Build Coastguard Worker}
927*c8dee2aaSAndroid Build Coastguard Worker
928*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_sweep_grad_buf_shader(float2 coords,
929*c8dee2aaSAndroid Build Coastguard Worker                                     float biasParam,
930*c8dee2aaSAndroid Build Coastguard Worker                                     float scaleParam,
931*c8dee2aaSAndroid Build Coastguard Worker                                     int numStops,
932*c8dee2aaSAndroid Build Coastguard Worker                                     int bufferOffset,
933*c8dee2aaSAndroid Build Coastguard Worker                                     int tileMode,
934*c8dee2aaSAndroid Build Coastguard Worker                                     int colorSpace,
935*c8dee2aaSAndroid Build Coastguard Worker                                     int doUnpremul,
936*c8dee2aaSAndroid Build Coastguard Worker                                     float[] colorAndOffsetData) {
937*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $sweep_grad_layout(biasParam, scaleParam, coords);
938*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
939*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_buf(colorAndOffsetData, bufferOffset, numStops, t);
940*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
941*c8dee2aaSAndroid Build Coastguard Worker}
942*c8dee2aaSAndroid Build Coastguard Worker
943*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_conical_grad_4_shader(float2 coords,
944*c8dee2aaSAndroid Build Coastguard Worker                                     float4 colorsParam[4],
945*c8dee2aaSAndroid Build Coastguard Worker                                     float4 offsetsParam,
946*c8dee2aaSAndroid Build Coastguard Worker                                     float radius0Param,
947*c8dee2aaSAndroid Build Coastguard Worker                                     float dRadiusParam,
948*c8dee2aaSAndroid Build Coastguard Worker                                     float aParam,
949*c8dee2aaSAndroid Build Coastguard Worker                                     float invAParam,
950*c8dee2aaSAndroid Build Coastguard Worker                                     int tileMode,
951*c8dee2aaSAndroid Build Coastguard Worker                                     int colorSpace,
952*c8dee2aaSAndroid Build Coastguard Worker                                     int doUnpremul) {
953*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $conical_grad_layout(radius0Param, dRadiusParam, aParam, invAParam, coords);
954*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
955*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_4(colorsParam, offsetsParam, t);
956*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
957*c8dee2aaSAndroid Build Coastguard Worker}
958*c8dee2aaSAndroid Build Coastguard Worker
959*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_conical_grad_8_shader(float2 coords,
960*c8dee2aaSAndroid Build Coastguard Worker                                     float4 colorsParam[8],
961*c8dee2aaSAndroid Build Coastguard Worker                                     float4 offsetsParam[2],
962*c8dee2aaSAndroid Build Coastguard Worker                                     float radius0Param,
963*c8dee2aaSAndroid Build Coastguard Worker                                     float dRadiusParam,
964*c8dee2aaSAndroid Build Coastguard Worker                                     float aParam,
965*c8dee2aaSAndroid Build Coastguard Worker                                     float invAParam,
966*c8dee2aaSAndroid Build Coastguard Worker                                     int tileMode,
967*c8dee2aaSAndroid Build Coastguard Worker                                     int colorSpace,
968*c8dee2aaSAndroid Build Coastguard Worker                                     int doUnpremul) {
969*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $conical_grad_layout(radius0Param, dRadiusParam, aParam, invAParam, coords);
970*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
971*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_8(colorsParam, offsetsParam, t);
972*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
973*c8dee2aaSAndroid Build Coastguard Worker}
974*c8dee2aaSAndroid Build Coastguard Worker
975*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_conical_grad_tex_shader(float2 coords,
976*c8dee2aaSAndroid Build Coastguard Worker                                       float radius0Param,
977*c8dee2aaSAndroid Build Coastguard Worker                                       float dRadiusParam,
978*c8dee2aaSAndroid Build Coastguard Worker                                       float aParam,
979*c8dee2aaSAndroid Build Coastguard Worker                                       float invAParam,
980*c8dee2aaSAndroid Build Coastguard Worker                                       int numStops,
981*c8dee2aaSAndroid Build Coastguard Worker                                       int tileMode,
982*c8dee2aaSAndroid Build Coastguard Worker                                       int colorSpace,
983*c8dee2aaSAndroid Build Coastguard Worker                                       int doUnpremul,
984*c8dee2aaSAndroid Build Coastguard Worker                                       sampler2D colorAndOffsetSampler) {
985*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $conical_grad_layout(radius0Param, dRadiusParam, aParam, invAParam, coords);
986*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
987*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_tex(colorAndOffsetSampler, numStops, t);
988*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
989*c8dee2aaSAndroid Build Coastguard Worker}
990*c8dee2aaSAndroid Build Coastguard Worker
991*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_conical_grad_buf_shader(float2 coords,
992*c8dee2aaSAndroid Build Coastguard Worker                                       float radius0Param,
993*c8dee2aaSAndroid Build Coastguard Worker                                       float dRadiusParam,
994*c8dee2aaSAndroid Build Coastguard Worker                                       float aParam,
995*c8dee2aaSAndroid Build Coastguard Worker                                       float invAParam,
996*c8dee2aaSAndroid Build Coastguard Worker                                       int numStops,
997*c8dee2aaSAndroid Build Coastguard Worker                                       int bufferOffset,
998*c8dee2aaSAndroid Build Coastguard Worker                                       int tileMode,
999*c8dee2aaSAndroid Build Coastguard Worker                                       int colorSpace,
1000*c8dee2aaSAndroid Build Coastguard Worker                                       int doUnpremul,
1001*c8dee2aaSAndroid Build Coastguard Worker                                       float[] colorAndOffsetData) {
1002*c8dee2aaSAndroid Build Coastguard Worker    float2 t = $conical_grad_layout(radius0Param, dRadiusParam, aParam, invAParam, coords);
1003*c8dee2aaSAndroid Build Coastguard Worker    t = $tile_grad(tileMode, t);
1004*c8dee2aaSAndroid Build Coastguard Worker    half4 color = $colorize_grad_buf(colorAndOffsetData, bufferOffset, numStops, t);
1005*c8dee2aaSAndroid Build Coastguard Worker    return $interpolated_to_rgb_unpremul(color, colorSpace, doUnpremul);
1006*c8dee2aaSAndroid Build Coastguard Worker}
1007*c8dee2aaSAndroid Build Coastguard Worker
1008*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_matrix_colorfilter(half4 colorIn, float4x4 m, float4 v, int inHSLA, int clampRGB) {
1009*c8dee2aaSAndroid Build Coastguard Worker    if (bool(inHSLA)) {
1010*c8dee2aaSAndroid Build Coastguard Worker        colorIn = $rgb_to_hsl(colorIn.rgb, colorIn.a); // includes unpremul
1011*c8dee2aaSAndroid Build Coastguard Worker    } else {
1012*c8dee2aaSAndroid Build Coastguard Worker        colorIn = unpremul(colorIn);
1013*c8dee2aaSAndroid Build Coastguard Worker    }
1014*c8dee2aaSAndroid Build Coastguard Worker
1015*c8dee2aaSAndroid Build Coastguard Worker    half4 colorOut = half4((m * colorIn) + v);
1016*c8dee2aaSAndroid Build Coastguard Worker
1017*c8dee2aaSAndroid Build Coastguard Worker    if (bool(inHSLA)) {
1018*c8dee2aaSAndroid Build Coastguard Worker        colorOut = $hsl_to_rgb(colorOut.rgb, colorOut.a); // includes clamp and premul
1019*c8dee2aaSAndroid Build Coastguard Worker    } else {
1020*c8dee2aaSAndroid Build Coastguard Worker        if (bool(clampRGB)) {
1021*c8dee2aaSAndroid Build Coastguard Worker            colorOut = saturate(colorOut);
1022*c8dee2aaSAndroid Build Coastguard Worker        } else {
1023*c8dee2aaSAndroid Build Coastguard Worker            colorOut.a = saturate(colorOut.a);
1024*c8dee2aaSAndroid Build Coastguard Worker        }
1025*c8dee2aaSAndroid Build Coastguard Worker        colorOut.rgb *= colorOut.a;
1026*c8dee2aaSAndroid Build Coastguard Worker    }
1027*c8dee2aaSAndroid Build Coastguard Worker
1028*c8dee2aaSAndroid Build Coastguard Worker    return colorOut;
1029*c8dee2aaSAndroid Build Coastguard Worker}
1030*c8dee2aaSAndroid Build Coastguard Worker
1031*c8dee2aaSAndroid Build Coastguard Worker// This method computes the 4 x-coodinates ([0..1]) that should be used to look
1032*c8dee2aaSAndroid Build Coastguard Worker// up in the Perlin noise shader's noise table.
1033*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $noise_helper(half2 noiseVec,
1034*c8dee2aaSAndroid Build Coastguard Worker                          half2 stitchData,
1035*c8dee2aaSAndroid Build Coastguard Worker                          int stitching,
1036*c8dee2aaSAndroid Build Coastguard Worker                          sampler2D permutationSampler) {
1037*c8dee2aaSAndroid Build Coastguard Worker    const half kBlockSize = 256.0;
1038*c8dee2aaSAndroid Build Coastguard Worker
1039*c8dee2aaSAndroid Build Coastguard Worker    half4 floorVal;
1040*c8dee2aaSAndroid Build Coastguard Worker    floorVal.xy = floor(noiseVec);
1041*c8dee2aaSAndroid Build Coastguard Worker    floorVal.zw = floorVal.xy + half2(1);
1042*c8dee2aaSAndroid Build Coastguard Worker
1043*c8dee2aaSAndroid Build Coastguard Worker    // Adjust frequencies if we're stitching tiles
1044*c8dee2aaSAndroid Build Coastguard Worker    if (bool(stitching)) {
1045*c8dee2aaSAndroid Build Coastguard Worker        floorVal -= step(stitchData.xyxy, floorVal) * stitchData.xyxy;
1046*c8dee2aaSAndroid Build Coastguard Worker    }
1047*c8dee2aaSAndroid Build Coastguard Worker
1048*c8dee2aaSAndroid Build Coastguard Worker    half sampleX = sample(permutationSampler, half2((floorVal.x + 0.5) / kBlockSize, 0.5)).r;
1049*c8dee2aaSAndroid Build Coastguard Worker    half sampleY = sample(permutationSampler, half2((floorVal.z + 0.5) / kBlockSize, 0.5)).r;
1050*c8dee2aaSAndroid Build Coastguard Worker
1051*c8dee2aaSAndroid Build Coastguard Worker    half2 latticeIdx = half2(sampleX, sampleY);
1052*c8dee2aaSAndroid Build Coastguard Worker
1053*c8dee2aaSAndroid Build Coastguard Worker    if (sk_Caps.PerlinNoiseRoundingFix) {
1054*c8dee2aaSAndroid Build Coastguard Worker        // Aggressively round to the nearest exact (N / 255) floating point values.
1055*c8dee2aaSAndroid Build Coastguard Worker        // This prevents rounding errors on some platforms (e.g., Tegras)
1056*c8dee2aaSAndroid Build Coastguard Worker        const half kInv255 = 1.0 / 255.0;
1057*c8dee2aaSAndroid Build Coastguard Worker
1058*c8dee2aaSAndroid Build Coastguard Worker        latticeIdx = floor(latticeIdx * half2(255.0) + half2(0.5)) * half2(kInv255);
1059*c8dee2aaSAndroid Build Coastguard Worker    }
1060*c8dee2aaSAndroid Build Coastguard Worker
1061*c8dee2aaSAndroid Build Coastguard Worker    // Get (x,y) coordinates with the permuted x
1062*c8dee2aaSAndroid Build Coastguard Worker    half4 noiseXCoords = kBlockSize*latticeIdx.xyxy + floorVal.yyww;
1063*c8dee2aaSAndroid Build Coastguard Worker
1064*c8dee2aaSAndroid Build Coastguard Worker    noiseXCoords /= half4(kBlockSize);
1065*c8dee2aaSAndroid Build Coastguard Worker    return noiseXCoords;
1066*c8dee2aaSAndroid Build Coastguard Worker}
1067*c8dee2aaSAndroid Build Coastguard Worker
1068*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $noise_function(half2 noiseVec,
1069*c8dee2aaSAndroid Build Coastguard Worker                            half4 noiseXCoords,
1070*c8dee2aaSAndroid Build Coastguard Worker                            sampler2D noiseSampler) {
1071*c8dee2aaSAndroid Build Coastguard Worker    half2 fractVal = fract(noiseVec);
1072*c8dee2aaSAndroid Build Coastguard Worker
1073*c8dee2aaSAndroid Build Coastguard Worker    // Hermite interpolation : t^2*(3 - 2*t)
1074*c8dee2aaSAndroid Build Coastguard Worker    half2 noiseSmooth = smoothstep(0, 1, fractVal);
1075*c8dee2aaSAndroid Build Coastguard Worker
1076*c8dee2aaSAndroid Build Coastguard Worker    // This is used to convert the two 16bit integers packed into rgba 8 bit input into
1077*c8dee2aaSAndroid Build Coastguard Worker    // a [-1,1] vector
1078*c8dee2aaSAndroid Build Coastguard Worker    const half kInv256 = 0.00390625;  // 1.0 / 256.0
1079*c8dee2aaSAndroid Build Coastguard Worker
1080*c8dee2aaSAndroid Build Coastguard Worker    half4 result;
1081*c8dee2aaSAndroid Build Coastguard Worker
1082*c8dee2aaSAndroid Build Coastguard Worker    for (int channel = 0; channel < 4; channel++) {
1083*c8dee2aaSAndroid Build Coastguard Worker
1084*c8dee2aaSAndroid Build Coastguard Worker        // There are 4 lines in the noise texture, put y coords at center of each.
1085*c8dee2aaSAndroid Build Coastguard Worker        half chanCoord = (half(channel) + 0.5) / 4.0;
1086*c8dee2aaSAndroid Build Coastguard Worker
1087*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleA = sample(noiseSampler, float2(noiseXCoords.x, chanCoord));
1088*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleB = sample(noiseSampler, float2(noiseXCoords.y, chanCoord));
1089*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleC = sample(noiseSampler, float2(noiseXCoords.w, chanCoord));
1090*c8dee2aaSAndroid Build Coastguard Worker        half4 sampleD = sample(noiseSampler, float2(noiseXCoords.z, chanCoord));
1091*c8dee2aaSAndroid Build Coastguard Worker
1092*c8dee2aaSAndroid Build Coastguard Worker        half2 tmpFractVal = fractVal;
1093*c8dee2aaSAndroid Build Coastguard Worker
1094*c8dee2aaSAndroid Build Coastguard Worker        // Compute u, at offset (0,0)
1095*c8dee2aaSAndroid Build Coastguard Worker        half u = dot((sampleA.ga + sampleA.rb*kInv256)*2 - 1, tmpFractVal);
1096*c8dee2aaSAndroid Build Coastguard Worker
1097*c8dee2aaSAndroid Build Coastguard Worker        // Compute v, at offset (-1,0)
1098*c8dee2aaSAndroid Build Coastguard Worker        tmpFractVal.x -= 1.0;
1099*c8dee2aaSAndroid Build Coastguard Worker        half v = dot((sampleB.ga + sampleB.rb*kInv256)*2 - 1, tmpFractVal);
1100*c8dee2aaSAndroid Build Coastguard Worker
1101*c8dee2aaSAndroid Build Coastguard Worker        // Compute 'a' as a linear interpolation of 'u' and 'v'
1102*c8dee2aaSAndroid Build Coastguard Worker        half a = mix(u, v, noiseSmooth.x);
1103*c8dee2aaSAndroid Build Coastguard Worker
1104*c8dee2aaSAndroid Build Coastguard Worker        // Compute v, at offset (-1,-1)
1105*c8dee2aaSAndroid Build Coastguard Worker        tmpFractVal.y -= 1.0;
1106*c8dee2aaSAndroid Build Coastguard Worker        v = dot((sampleC.ga + sampleC.rb*kInv256)*2 - 1, tmpFractVal);
1107*c8dee2aaSAndroid Build Coastguard Worker
1108*c8dee2aaSAndroid Build Coastguard Worker        // Compute u, at offset (0,-1)
1109*c8dee2aaSAndroid Build Coastguard Worker        tmpFractVal.x += 1.0;
1110*c8dee2aaSAndroid Build Coastguard Worker        u = dot((sampleD.ga + sampleD.rb*kInv256)*2 - 1, tmpFractVal);
1111*c8dee2aaSAndroid Build Coastguard Worker
1112*c8dee2aaSAndroid Build Coastguard Worker        // Compute 'b' as a linear interpolation of 'u' and 'v'
1113*c8dee2aaSAndroid Build Coastguard Worker        half b = mix(u, v, noiseSmooth.x);
1114*c8dee2aaSAndroid Build Coastguard Worker
1115*c8dee2aaSAndroid Build Coastguard Worker        // Compute the noise as a linear interpolation of 'a' and 'b'
1116*c8dee2aaSAndroid Build Coastguard Worker        result[channel] = mix(a, b, noiseSmooth.y);
1117*c8dee2aaSAndroid Build Coastguard Worker    }
1118*c8dee2aaSAndroid Build Coastguard Worker
1119*c8dee2aaSAndroid Build Coastguard Worker    return result;
1120*c8dee2aaSAndroid Build Coastguard Worker}
1121*c8dee2aaSAndroid Build Coastguard Worker
1122*c8dee2aaSAndroid Build Coastguard Worker// permutationSampler is [kBlockSize x 1] A8
1123*c8dee2aaSAndroid Build Coastguard Worker// noiseSampler is [kBlockSize x 4] RGBA8 premul
1124*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_perlin_noise_shader(float2 coords,
1125*c8dee2aaSAndroid Build Coastguard Worker                                   float2 baseFrequency,
1126*c8dee2aaSAndroid Build Coastguard Worker                                   float2 stitchDataIn,
1127*c8dee2aaSAndroid Build Coastguard Worker                                   int noiseType,
1128*c8dee2aaSAndroid Build Coastguard Worker                                   int numOctaves,
1129*c8dee2aaSAndroid Build Coastguard Worker                                   int stitching,
1130*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D permutationSampler,
1131*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D noiseSampler) {
1132*c8dee2aaSAndroid Build Coastguard Worker    const int kFractalNoise = 0;
1133*c8dee2aaSAndroid Build Coastguard Worker    const int kTurbulence = 1;
1134*c8dee2aaSAndroid Build Coastguard Worker
1135*c8dee2aaSAndroid Build Coastguard Worker    // In the past, Perlin noise handled coordinates a bit differently than most shaders.
1136*c8dee2aaSAndroid Build Coastguard Worker    // It operated in device space, floored; it also had a one-pixel transform matrix applied to
1137*c8dee2aaSAndroid Build Coastguard Worker    // both the X and Y coordinates. This is roughly equivalent to adding 0.5 to the coordinates.
1138*c8dee2aaSAndroid Build Coastguard Worker    // This was originally done in order to better match preexisting golden images from WebKit.
1139*c8dee2aaSAndroid Build Coastguard Worker    // Perlin noise now operates in local space, which allows rotation to work correctly. To better
1140*c8dee2aaSAndroid Build Coastguard Worker    // approximate past behavior, we add 0.5 to the coordinates here. This is _not_ exactly the same
1141*c8dee2aaSAndroid Build Coastguard Worker    // because this adjustment is occurring in local space, not device space.
1142*c8dee2aaSAndroid Build Coastguard Worker    half2 noiseVec = half2((coords + 0.5) * baseFrequency);
1143*c8dee2aaSAndroid Build Coastguard Worker
1144*c8dee2aaSAndroid Build Coastguard Worker    // Clear the color accumulator
1145*c8dee2aaSAndroid Build Coastguard Worker    half4 color = half4(0);
1146*c8dee2aaSAndroid Build Coastguard Worker
1147*c8dee2aaSAndroid Build Coastguard Worker    half2 stitchData = half2(stitchDataIn);
1148*c8dee2aaSAndroid Build Coastguard Worker
1149*c8dee2aaSAndroid Build Coastguard Worker    half ratio = 1.0;
1150*c8dee2aaSAndroid Build Coastguard Worker
1151*c8dee2aaSAndroid Build Coastguard Worker    // Loop over all octaves
1152*c8dee2aaSAndroid Build Coastguard Worker    for (int octave = 0; octave < numOctaves; ++octave) {
1153*c8dee2aaSAndroid Build Coastguard Worker        half4 noiseXCoords = $noise_helper(noiseVec, stitchData, stitching, permutationSampler);
1154*c8dee2aaSAndroid Build Coastguard Worker
1155*c8dee2aaSAndroid Build Coastguard Worker        half4 tmp = $noise_function(noiseVec, noiseXCoords, noiseSampler);
1156*c8dee2aaSAndroid Build Coastguard Worker
1157*c8dee2aaSAndroid Build Coastguard Worker        if (noiseType != kFractalNoise) {
1158*c8dee2aaSAndroid Build Coastguard Worker            // For kTurbulence the result is: abs(noise[-1,1])
1159*c8dee2aaSAndroid Build Coastguard Worker            tmp = abs(tmp);
1160*c8dee2aaSAndroid Build Coastguard Worker        }
1161*c8dee2aaSAndroid Build Coastguard Worker
1162*c8dee2aaSAndroid Build Coastguard Worker        color += tmp * ratio;
1163*c8dee2aaSAndroid Build Coastguard Worker
1164*c8dee2aaSAndroid Build Coastguard Worker        noiseVec *= half2(2.0);
1165*c8dee2aaSAndroid Build Coastguard Worker        ratio *= 0.5;
1166*c8dee2aaSAndroid Build Coastguard Worker        stitchData *= half2(2.0);
1167*c8dee2aaSAndroid Build Coastguard Worker    }
1168*c8dee2aaSAndroid Build Coastguard Worker
1169*c8dee2aaSAndroid Build Coastguard Worker    if (noiseType == kFractalNoise) {
1170*c8dee2aaSAndroid Build Coastguard Worker        // For kFractalNoise the result is: noise[-1,1] * 0.5 + 0.5
1171*c8dee2aaSAndroid Build Coastguard Worker        color = color * half4(0.5) + half4(0.5);
1172*c8dee2aaSAndroid Build Coastguard Worker    }
1173*c8dee2aaSAndroid Build Coastguard Worker
1174*c8dee2aaSAndroid Build Coastguard Worker    // Clamp values
1175*c8dee2aaSAndroid Build Coastguard Worker    color = saturate(color);
1176*c8dee2aaSAndroid Build Coastguard Worker
1177*c8dee2aaSAndroid Build Coastguard Worker    // Pre-multiply the result
1178*c8dee2aaSAndroid Build Coastguard Worker    return sk_premul_alpha(color);
1179*c8dee2aaSAndroid Build Coastguard Worker}
1180*c8dee2aaSAndroid Build Coastguard Worker
1181*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_porter_duff_blend(half4 src, half4 dst, half4 coeffs) {
1182*c8dee2aaSAndroid Build Coastguard Worker    return blend_porter_duff(coeffs, src, dst);
1183*c8dee2aaSAndroid Build Coastguard Worker}
1184*c8dee2aaSAndroid Build Coastguard Worker
1185*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_hslc_blend(half4 src, half4 dst, half2 flipSat) {
1186*c8dee2aaSAndroid Build Coastguard Worker    return blend_hslc(flipSat, src, dst);
1187*c8dee2aaSAndroid Build Coastguard Worker}
1188*c8dee2aaSAndroid Build Coastguard Worker
1189*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_table_colorfilter(half4 inColor, sampler2D s) {
1190*c8dee2aaSAndroid Build Coastguard Worker    half4 coords = unpremul(inColor) * (255.0/256.0) + (0.5/256.0);
1191*c8dee2aaSAndroid Build Coastguard Worker    half4 color = half4(sample(s, half2(coords.r, 3.0/8.0)).r,
1192*c8dee2aaSAndroid Build Coastguard Worker                        sample(s, half2(coords.g, 5.0/8.0)).r,
1193*c8dee2aaSAndroid Build Coastguard Worker                        sample(s, half2(coords.b, 7.0/8.0)).r,
1194*c8dee2aaSAndroid Build Coastguard Worker                        1);
1195*c8dee2aaSAndroid Build Coastguard Worker    return color * sample(s, half2(coords.a, 1.0/8.0)).r;
1196*c8dee2aaSAndroid Build Coastguard Worker}
1197*c8dee2aaSAndroid Build Coastguard Worker
1198*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sk_gaussian_colorfilter(half4 inColor) {
1199*c8dee2aaSAndroid Build Coastguard Worker    half factor = 1 - inColor.a;
1200*c8dee2aaSAndroid Build Coastguard Worker    factor = exp(-factor * factor * 4) - 0.018;
1201*c8dee2aaSAndroid Build Coastguard Worker    return half4(factor);
1202*c8dee2aaSAndroid Build Coastguard Worker}
1203*c8dee2aaSAndroid Build Coastguard Worker
1204*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sample_indexed_atlas(float2 textureCoords,
1205*c8dee2aaSAndroid Build Coastguard Worker                                 int atlasIndex,
1206*c8dee2aaSAndroid Build Coastguard Worker                                 sampler2D atlas0,
1207*c8dee2aaSAndroid Build Coastguard Worker                                 sampler2D atlas1,
1208*c8dee2aaSAndroid Build Coastguard Worker                                 sampler2D atlas2,
1209*c8dee2aaSAndroid Build Coastguard Worker                                 sampler2D atlas3) {
1210*c8dee2aaSAndroid Build Coastguard Worker    switch (atlasIndex) {
1211*c8dee2aaSAndroid Build Coastguard Worker        case 1:
1212*c8dee2aaSAndroid Build Coastguard Worker            return sample(atlas1, textureCoords);
1213*c8dee2aaSAndroid Build Coastguard Worker        case 2:
1214*c8dee2aaSAndroid Build Coastguard Worker            return sample(atlas2, textureCoords);
1215*c8dee2aaSAndroid Build Coastguard Worker        case 3:
1216*c8dee2aaSAndroid Build Coastguard Worker            return sample(atlas3, textureCoords);
1217*c8dee2aaSAndroid Build Coastguard Worker        default:
1218*c8dee2aaSAndroid Build Coastguard Worker            return sample(atlas0, textureCoords);
1219*c8dee2aaSAndroid Build Coastguard Worker    }
1220*c8dee2aaSAndroid Build Coastguard Worker}
1221*c8dee2aaSAndroid Build Coastguard Worker
1222*c8dee2aaSAndroid Build Coastguard Worker$pure half3 $sample_indexed_atlas_lcd(float2 textureCoords,
1223*c8dee2aaSAndroid Build Coastguard Worker                                      int atlasIndex,
1224*c8dee2aaSAndroid Build Coastguard Worker                                      half2 offset,
1225*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D atlas0,
1226*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D atlas1,
1227*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D atlas2,
1228*c8dee2aaSAndroid Build Coastguard Worker                                      sampler2D atlas3) {
1229*c8dee2aaSAndroid Build Coastguard Worker    half3 distance = half3(1);
1230*c8dee2aaSAndroid Build Coastguard Worker    switch (atlasIndex) {
1231*c8dee2aaSAndroid Build Coastguard Worker        case 1:
1232*c8dee2aaSAndroid Build Coastguard Worker            distance.x = sample(atlas1, half2(textureCoords) - offset).r;
1233*c8dee2aaSAndroid Build Coastguard Worker            distance.y = sample(atlas1, textureCoords).r;
1234*c8dee2aaSAndroid Build Coastguard Worker            distance.z = sample(atlas1, half2(textureCoords) + offset).r;
1235*c8dee2aaSAndroid Build Coastguard Worker        case 2:
1236*c8dee2aaSAndroid Build Coastguard Worker            distance.x = sample(atlas2, half2(textureCoords) - offset).r;
1237*c8dee2aaSAndroid Build Coastguard Worker            distance.y = sample(atlas2, textureCoords).r;
1238*c8dee2aaSAndroid Build Coastguard Worker            distance.z = sample(atlas2, half2(textureCoords) + offset).r;
1239*c8dee2aaSAndroid Build Coastguard Worker        case 3:
1240*c8dee2aaSAndroid Build Coastguard Worker            distance.x = sample(atlas3, half2(textureCoords) - offset).r;
1241*c8dee2aaSAndroid Build Coastguard Worker            distance.y = sample(atlas3, textureCoords).r;
1242*c8dee2aaSAndroid Build Coastguard Worker            distance.z = sample(atlas3, half2(textureCoords) + offset).r;
1243*c8dee2aaSAndroid Build Coastguard Worker        default:
1244*c8dee2aaSAndroid Build Coastguard Worker            distance.x = sample(atlas0, half2(textureCoords) - offset).r;
1245*c8dee2aaSAndroid Build Coastguard Worker            distance.y = sample(atlas0, textureCoords).r;
1246*c8dee2aaSAndroid Build Coastguard Worker            distance.z = sample(atlas0, half2(textureCoords) + offset).r;
1247*c8dee2aaSAndroid Build Coastguard Worker    }
1248*c8dee2aaSAndroid Build Coastguard Worker    return distance;
1249*c8dee2aaSAndroid Build Coastguard Worker}
1250*c8dee2aaSAndroid Build Coastguard Worker
1251*c8dee2aaSAndroid Build Coastguard Worker$pure half4 bitmap_text_coverage_fn(half4 texColor, int maskFormat) {
1252*c8dee2aaSAndroid Build Coastguard Worker    return (maskFormat == $kMaskFormatA8) ? texColor.rrrr
1253*c8dee2aaSAndroid Build Coastguard Worker                                          : texColor;
1254*c8dee2aaSAndroid Build Coastguard Worker}
1255*c8dee2aaSAndroid Build Coastguard Worker
1256*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sdf_text_coverage_fn(half texColor,
1257*c8dee2aaSAndroid Build Coastguard Worker                                 half2 gammaParams,
1258*c8dee2aaSAndroid Build Coastguard Worker                                 float2 unormTexCoords) {
1259*c8dee2aaSAndroid Build Coastguard Worker    // TODO: To minimize the number of shaders generated this is the full affine shader.
1260*c8dee2aaSAndroid Build Coastguard Worker    // For best performance it may be worth creating the uniform scale shader as well,
1261*c8dee2aaSAndroid Build Coastguard Worker    // as that's the most common case.
1262*c8dee2aaSAndroid Build Coastguard Worker    // TODO: Need to add 565 support.
1263*c8dee2aaSAndroid Build Coastguard Worker
1264*c8dee2aaSAndroid Build Coastguard Worker    // The distance field is constructed as uchar8_t values, so that the zero value is at 128,
1265*c8dee2aaSAndroid Build Coastguard Worker    // and the supported range of distances is [-4 * 127/128, 4].
1266*c8dee2aaSAndroid Build Coastguard Worker    // Hence to convert to floats our multiplier (width of the range) is 4 * 255/128 = 7.96875
1267*c8dee2aaSAndroid Build Coastguard Worker    // and zero threshold is 128/255 = 0.50196078431.
1268*c8dee2aaSAndroid Build Coastguard Worker    half dist = 7.96875 * (texColor - 0.50196078431);
1269*c8dee2aaSAndroid Build Coastguard Worker
1270*c8dee2aaSAndroid Build Coastguard Worker    // We may further adjust the distance for gamma correction.
1271*c8dee2aaSAndroid Build Coastguard Worker    dist -= gammaParams.x;
1272*c8dee2aaSAndroid Build Coastguard Worker
1273*c8dee2aaSAndroid Build Coastguard Worker    // After the distance is unpacked, we need to correct it by a factor dependent on the
1274*c8dee2aaSAndroid Build Coastguard Worker    // current transformation. For general transforms, to determine the amount of correction
1275*c8dee2aaSAndroid Build Coastguard Worker    // we multiply a unit vector pointing along the SDF gradient direction by the Jacobian of
1276*c8dee2aaSAndroid Build Coastguard Worker    // unormTexCoords (which is the inverse transform for this fragment) and take the length of
1277*c8dee2aaSAndroid Build Coastguard Worker    // the result.
1278*c8dee2aaSAndroid Build Coastguard Worker    half2 dist_grad = half2(dFdx(dist), dFdy(dist));
1279*c8dee2aaSAndroid Build Coastguard Worker    half dg_len2 = dot(dist_grad, dist_grad);
1280*c8dee2aaSAndroid Build Coastguard Worker
1281*c8dee2aaSAndroid Build Coastguard Worker    // The length of the gradient may be near 0, so we need to check for that. This also
1282*c8dee2aaSAndroid Build Coastguard Worker    // compensates for the Adreno, which likes to drop tiles on division by 0.
1283*c8dee2aaSAndroid Build Coastguard Worker    dist_grad = (dg_len2 >= 0.0001) ? dist_grad * inversesqrt(dg_len2)
1284*c8dee2aaSAndroid Build Coastguard Worker                                    : half2(0.7071);
1285*c8dee2aaSAndroid Build Coastguard Worker
1286*c8dee2aaSAndroid Build Coastguard Worker    // Computing the Jacobian and multiplying by the gradient.
1287*c8dee2aaSAndroid Build Coastguard Worker    float2x2 jacobian = float2x2(dFdx(unormTexCoords), dFdy(unormTexCoords));
1288*c8dee2aaSAndroid Build Coastguard Worker    half2 grad = half2(jacobian * dist_grad);
1289*c8dee2aaSAndroid Build Coastguard Worker
1290*c8dee2aaSAndroid Build Coastguard Worker    // This gives us a smooth step across approximately one fragment.
1291*c8dee2aaSAndroid Build Coastguard Worker    half approxFragWidth = 0.65 * length(grad);
1292*c8dee2aaSAndroid Build Coastguard Worker
1293*c8dee2aaSAndroid Build Coastguard Worker    // TODO: handle aliased rendering
1294*c8dee2aaSAndroid Build Coastguard Worker    if (gammaParams.y > 0) {
1295*c8dee2aaSAndroid Build Coastguard Worker        // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
1296*c8dee2aaSAndroid Build Coastguard Worker        // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want
1297*c8dee2aaSAndroid Build Coastguard Worker        // distance mapped linearly to coverage, so use a linear step:
1298*c8dee2aaSAndroid Build Coastguard Worker        return half4(saturate((dist + approxFragWidth) / (2.0 * approxFragWidth)));
1299*c8dee2aaSAndroid Build Coastguard Worker    } else {
1300*c8dee2aaSAndroid Build Coastguard Worker        return half4(smoothstep(-approxFragWidth, approxFragWidth, dist));
1301*c8dee2aaSAndroid Build Coastguard Worker    }
1302*c8dee2aaSAndroid Build Coastguard Worker}
1303*c8dee2aaSAndroid Build Coastguard Worker
1304*c8dee2aaSAndroid Build Coastguard Worker$pure half4 sdf_text_lcd_coverage_fn(float2 textureCoords,
1305*c8dee2aaSAndroid Build Coastguard Worker                                     half2 pixelGeometryDelta,
1306*c8dee2aaSAndroid Build Coastguard Worker                                     half4 gammaParams,
1307*c8dee2aaSAndroid Build Coastguard Worker                                     float2 unormTexCoords,
1308*c8dee2aaSAndroid Build Coastguard Worker                                     float texIndex,
1309*c8dee2aaSAndroid Build Coastguard Worker                                     sampler2D atlas0,
1310*c8dee2aaSAndroid Build Coastguard Worker                                     sampler2D atlas1,
1311*c8dee2aaSAndroid Build Coastguard Worker                                     sampler2D atlas2,
1312*c8dee2aaSAndroid Build Coastguard Worker                                     sampler2D atlas3) {
1313*c8dee2aaSAndroid Build Coastguard Worker    // TODO: To minimize the number of shaders generated this is the full affine shader.
1314*c8dee2aaSAndroid Build Coastguard Worker    // For best performance it may be worth creating the uniform scale shader as well,
1315*c8dee2aaSAndroid Build Coastguard Worker    // as that's the most common case.
1316*c8dee2aaSAndroid Build Coastguard Worker
1317*c8dee2aaSAndroid Build Coastguard Worker    float2x2 jacobian = float2x2(dFdx(unormTexCoords), dFdy(unormTexCoords));
1318*c8dee2aaSAndroid Build Coastguard Worker    half2 offset = half2(jacobian * pixelGeometryDelta);
1319*c8dee2aaSAndroid Build Coastguard Worker
1320*c8dee2aaSAndroid Build Coastguard Worker    half3 distance = $sample_indexed_atlas_lcd(textureCoords,
1321*c8dee2aaSAndroid Build Coastguard Worker                                               int(texIndex),
1322*c8dee2aaSAndroid Build Coastguard Worker                                               offset,
1323*c8dee2aaSAndroid Build Coastguard Worker                                               atlas0,
1324*c8dee2aaSAndroid Build Coastguard Worker                                               atlas1,
1325*c8dee2aaSAndroid Build Coastguard Worker                                               atlas2,
1326*c8dee2aaSAndroid Build Coastguard Worker                                               atlas3);
1327*c8dee2aaSAndroid Build Coastguard Worker    // The distance field is constructed as uchar8_t values, so that the zero value is at 128,
1328*c8dee2aaSAndroid Build Coastguard Worker    // and the supported range of distances is [-4 * 127/128, 4].
1329*c8dee2aaSAndroid Build Coastguard Worker    // Hence to convert to floats our multiplier (width of the range) is 4 * 255/128 = 7.96875
1330*c8dee2aaSAndroid Build Coastguard Worker    // and zero threshold is 128/255 = 0.50196078431.
1331*c8dee2aaSAndroid Build Coastguard Worker    half3 dist = half3(7.96875) * (distance - half3(0.50196078431));
1332*c8dee2aaSAndroid Build Coastguard Worker
1333*c8dee2aaSAndroid Build Coastguard Worker    // We may further adjust the distance for gamma correction.
1334*c8dee2aaSAndroid Build Coastguard Worker    dist -= gammaParams.xyz;
1335*c8dee2aaSAndroid Build Coastguard Worker
1336*c8dee2aaSAndroid Build Coastguard Worker    // After the distance is unpacked, we need to correct it by a factor dependent on the
1337*c8dee2aaSAndroid Build Coastguard Worker    // current transformation. For general transforms, to determine the amount of correction
1338*c8dee2aaSAndroid Build Coastguard Worker    // we multiply a unit vector pointing along the SDF gradient direction by the Jacobian of
1339*c8dee2aaSAndroid Build Coastguard Worker    // unormTexCoords (which is the inverse transform for this fragment) and take the length of
1340*c8dee2aaSAndroid Build Coastguard Worker    // the result.
1341*c8dee2aaSAndroid Build Coastguard Worker    half2 dist_grad = half2(dFdx(dist.g), dFdy(dist.g));
1342*c8dee2aaSAndroid Build Coastguard Worker    half dg_len2 = dot(dist_grad, dist_grad);
1343*c8dee2aaSAndroid Build Coastguard Worker
1344*c8dee2aaSAndroid Build Coastguard Worker    // The length of the gradient may be near 0, so we need to check for that. This also
1345*c8dee2aaSAndroid Build Coastguard Worker    // compensates for the Adreno, which likes to drop tiles on division by 0.
1346*c8dee2aaSAndroid Build Coastguard Worker    dist_grad = (dg_len2 >= 0.0001) ? dist_grad * inversesqrt(dg_len2)
1347*c8dee2aaSAndroid Build Coastguard Worker                                    : half2(0.7071);
1348*c8dee2aaSAndroid Build Coastguard Worker
1349*c8dee2aaSAndroid Build Coastguard Worker    // Multiplying the Jacobian by the gradient.
1350*c8dee2aaSAndroid Build Coastguard Worker    half2 grad = half2(jacobian * dist_grad);
1351*c8dee2aaSAndroid Build Coastguard Worker
1352*c8dee2aaSAndroid Build Coastguard Worker    // This gives us a smooth step across approximately one fragment.
1353*c8dee2aaSAndroid Build Coastguard Worker    half3 approxFragWidth = half3(0.65 * length(grad));
1354*c8dee2aaSAndroid Build Coastguard Worker
1355*c8dee2aaSAndroid Build Coastguard Worker    // TODO: handle aliased rendering
1356*c8dee2aaSAndroid Build Coastguard Worker    if (gammaParams.w > 0) {
1357*c8dee2aaSAndroid Build Coastguard Worker        // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are
1358*c8dee2aaSAndroid Build Coastguard Worker        // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want
1359*c8dee2aaSAndroid Build Coastguard Worker        // distance mapped linearly to coverage, so use a linear step:
1360*c8dee2aaSAndroid Build Coastguard Worker        return half4(saturate((dist + approxFragWidth / (2.0 * approxFragWidth))), 1);
1361*c8dee2aaSAndroid Build Coastguard Worker    } else {
1362*c8dee2aaSAndroid Build Coastguard Worker        return half4(smoothstep(half3(-approxFragWidth), half3(approxFragWidth), dist), 1);
1363*c8dee2aaSAndroid Build Coastguard Worker    }
1364*c8dee2aaSAndroid Build Coastguard Worker}
1365*c8dee2aaSAndroid Build Coastguard Worker
1366*c8dee2aaSAndroid Build Coastguard Worker///////////////////////////////////////////////////////////////////////////////////////////////////
1367*c8dee2aaSAndroid Build Coastguard Worker// Support functions for analytic round rectangles
1368*c8dee2aaSAndroid Build Coastguard Worker
1369*c8dee2aaSAndroid Build Coastguard Worker// Calculates 1/|∇| in device space by applying the chain rule to a local gradient vector and the
1370*c8dee2aaSAndroid Build Coastguard Worker// 2x2 Jacobian describing the transform from local-to-device space. For non-perspective, this is
1371*c8dee2aaSAndroid Build Coastguard Worker// equivalent to the "normal matrix", or the inverse transpose. For perspective, J should be
1372*c8dee2aaSAndroid Build Coastguard Worker//    W(u,v) [m00' - m20'u  m01' - m21'u] derived from the first two columns of the 3x3 inverse.
1373*c8dee2aaSAndroid Build Coastguard Worker//           [m10' - m20'v  m11' - m21'v]
1374*c8dee2aaSAndroid Build Coastguard Worker$pure float $inverse_grad_len(float2 localGrad, float2x2 jacobian) {
1375*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: By chain rule, the local gradient is on the left side of the Jacobian matrix
1376*c8dee2aaSAndroid Build Coastguard Worker    float2 devGrad = localGrad * jacobian;
1377*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: This uses the L2 norm, which is more accurate than the L1 norm used by fwidth().
1378*c8dee2aaSAndroid Build Coastguard Worker    // TODO: Switch to L1 since it is a 2x perf improvement according to Xcode with little visual
1379*c8dee2aaSAndroid Build Coastguard Worker    // impact, but start with L2 to measure the change separately from the algorithmic update.
1380*c8dee2aaSAndroid Build Coastguard Worker    // return 1.0 / (abs(devGrad.x) + abs(devGrad.y));
1381*c8dee2aaSAndroid Build Coastguard Worker    return inversesqrt(dot(devGrad, devGrad));
1382*c8dee2aaSAndroid Build Coastguard Worker}
1383*c8dee2aaSAndroid Build Coastguard Worker
1384*c8dee2aaSAndroid Build Coastguard Worker// Returns distance from both sides of a stroked circle or ellipse. Elliptical coverage is
1385*c8dee2aaSAndroid Build Coastguard Worker// only accurate if strokeRadius = 0. A positive value represents the interior of the stroke.
1386*c8dee2aaSAndroid Build Coastguard Worker$pure float2 $elliptical_distance(float2 uv, float2 radii, float strokeRadius, float2x2 jacobian) {
1387*c8dee2aaSAndroid Build Coastguard Worker    // We do need to evaluate up to two circle equations: one with
1388*c8dee2aaSAndroid Build Coastguard Worker    //    R = cornerRadius(r)+strokeRadius(s), and another with R = r-s.
1389*c8dee2aaSAndroid Build Coastguard Worker    // This can be consolidated into a common evaluation against a circle of radius sqrt(r^2+s^2):
1390*c8dee2aaSAndroid Build Coastguard Worker    //    (x/(r+/-s))^2 + (y/(r+/-s))^2 = 1
1391*c8dee2aaSAndroid Build Coastguard Worker    //    x^2 + y^2 = (r+/-s)^2
1392*c8dee2aaSAndroid Build Coastguard Worker    //    x^2 + y^2 = r^2 + s^2 +/- 2rs
1393*c8dee2aaSAndroid Build Coastguard Worker    //    (x/sqrt(r^2+s^2))^2 + (y/sqrt(r^2+s^2)) = 1 +/- 2rs/(r^2+s^2)
1394*c8dee2aaSAndroid Build Coastguard Worker    // The 2rs/(r^2+s^2) is the "width" that adjusts the implicit function to the outer or inner
1395*c8dee2aaSAndroid Build Coastguard Worker    // edge of the stroke. For fills and hairlines, s = 0, which means these operations remain valid
1396*c8dee2aaSAndroid Build Coastguard Worker    // for elliptical corners where radii holds the different X and Y corner radii.
1397*c8dee2aaSAndroid Build Coastguard Worker    float2 invR2 = 1.0 / (radii * radii + strokeRadius*strokeRadius);
1398*c8dee2aaSAndroid Build Coastguard Worker    float2 normUV = invR2 * uv;
1399*c8dee2aaSAndroid Build Coastguard Worker    float invGradLength = $inverse_grad_len(normUV, jacobian);
1400*c8dee2aaSAndroid Build Coastguard Worker
1401*c8dee2aaSAndroid Build Coastguard Worker    // Since normUV already includes 1/r^2 in the denominator, dot with just 'uv' instead.
1402*c8dee2aaSAndroid Build Coastguard Worker    float f = 0.5 * invGradLength * (dot(uv, normUV) - 1.0);
1403*c8dee2aaSAndroid Build Coastguard Worker
1404*c8dee2aaSAndroid Build Coastguard Worker    // This is 0 for fills/hairlines, which are the only types that allow
1405*c8dee2aaSAndroid Build Coastguard Worker    // elliptical corners (strokeRadius == 0). For regular strokes just use X.
1406*c8dee2aaSAndroid Build Coastguard Worker    float width = radii.x * strokeRadius * invR2.x * invGradLength;
1407*c8dee2aaSAndroid Build Coastguard Worker    return float2(width - f, width + f);
1408*c8dee2aaSAndroid Build Coastguard Worker}
1409*c8dee2aaSAndroid Build Coastguard Worker
1410*c8dee2aaSAndroid Build Coastguard Worker// Accumulates the minimum (and negative maximum) of the outer and inner corner distances in 'dist'
1411*c8dee2aaSAndroid Build Coastguard Worker// for a possibly elliptical corner with 'radii' and relative pixel location specified by
1412*c8dee2aaSAndroid Build Coastguard Worker// 'cornerEdgeDist'. The corner's basis relative to the jacobian is defined in 'xyFlip'.
1413*c8dee2aaSAndroid Build Coastguard Workervoid $corner_distance(inout float2 dist,
1414*c8dee2aaSAndroid Build Coastguard Worker                      float2x2 jacobian,
1415*c8dee2aaSAndroid Build Coastguard Worker                      float2 strokeParams,
1416*c8dee2aaSAndroid Build Coastguard Worker                      float2 cornerEdgeDist,
1417*c8dee2aaSAndroid Build Coastguard Worker                      float2 xyFlip,
1418*c8dee2aaSAndroid Build Coastguard Worker                      float2 radii) {
1419*c8dee2aaSAndroid Build Coastguard Worker    float2 uv = radii - cornerEdgeDist;
1420*c8dee2aaSAndroid Build Coastguard Worker    // NOTE: For mitered corners uv > 0 only if it's stroked, and in that case the
1421*c8dee2aaSAndroid Build Coastguard Worker    // subsequent conditions skip calculating anything.
1422*c8dee2aaSAndroid Build Coastguard Worker    if (all(greaterThan(uv, float2(0.0)))) {
1423*c8dee2aaSAndroid Build Coastguard Worker        if (all(greaterThan(radii, float2(0.0))) ||
1424*c8dee2aaSAndroid Build Coastguard Worker            (strokeParams.x > 0.0 && strokeParams.y < 0.0 /* round-join */)) {
1425*c8dee2aaSAndroid Build Coastguard Worker            // A rounded corner so incorporate outer elliptical distance if we're within the
1426*c8dee2aaSAndroid Build Coastguard Worker            // quarter circle.
1427*c8dee2aaSAndroid Build Coastguard Worker            float2 d = $elliptical_distance(uv * xyFlip, radii, strokeParams.x, jacobian);
1428*c8dee2aaSAndroid Build Coastguard Worker            d.y = (radii.x - strokeParams.x <= 0.0)
1429*c8dee2aaSAndroid Build Coastguard Worker                      ? 1.0    // Disregard inner curve since it's collapsed into an inner miter.
1430*c8dee2aaSAndroid Build Coastguard Worker                      : -d.y;  // Negate so that "min" accumulates the maximum value instead.
1431*c8dee2aaSAndroid Build Coastguard Worker            dist = min(dist, d);
1432*c8dee2aaSAndroid Build Coastguard Worker        } else if (strokeParams.y == 0.0 /* bevel-join */) {
1433*c8dee2aaSAndroid Build Coastguard Worker            // Bevels are--by construction--interior mitered, so inner distance is based
1434*c8dee2aaSAndroid Build Coastguard Worker            // purely on the edge distance calculations, but the outer distance is to a 45-degree
1435*c8dee2aaSAndroid Build Coastguard Worker            // line and not the vertical/horizontal lines of the other edges.
1436*c8dee2aaSAndroid Build Coastguard Worker            float bevelDist = (strokeParams.x - uv.x - uv.y) * $inverse_grad_len(xyFlip, jacobian);
1437*c8dee2aaSAndroid Build Coastguard Worker            dist.x = min(dist.x, bevelDist);
1438*c8dee2aaSAndroid Build Coastguard Worker        } // Else it's a miter so both inner and outer distances are unmodified
1439*c8dee2aaSAndroid Build Coastguard Worker    } // Else we're not affected by the corner so leave distances unmodified
1440*c8dee2aaSAndroid Build Coastguard Worker}
1441*c8dee2aaSAndroid Build Coastguard Worker
1442*c8dee2aaSAndroid Build Coastguard Worker// Accumulates the minimum (and negative maximum) of the outer and inner corner distances into 'd',
1443*c8dee2aaSAndroid Build Coastguard Worker// for all four corners of a [round] rectangle. 'edgeDists' should be ordered LTRB with positive
1444*c8dee2aaSAndroid Build Coastguard Worker// distance representing the interior of the edge. 'xRadii' and 'yRadii' should hold the per-corner
1445*c8dee2aaSAndroid Build Coastguard Worker// elliptical radii, ordered TL, TR, BR, BL.
1446*c8dee2aaSAndroid Build Coastguard Workervoid $corner_distances(inout float2 d,
1447*c8dee2aaSAndroid Build Coastguard Worker                       float2x2 J,
1448*c8dee2aaSAndroid Build Coastguard Worker                       float2 stroke, // {radii, joinStyle}, see StrokeStyle struct definition
1449*c8dee2aaSAndroid Build Coastguard Worker                       float4 edgeDists,
1450*c8dee2aaSAndroid Build Coastguard Worker                       float4 xRadii,
1451*c8dee2aaSAndroid Build Coastguard Worker                       float4 yRadii) {
1452*c8dee2aaSAndroid Build Coastguard Worker    $corner_distance(d, J, stroke, edgeDists.xy, float2(-1.0, -1.0), float2(xRadii[0], yRadii[0]));
1453*c8dee2aaSAndroid Build Coastguard Worker    $corner_distance(d, J, stroke, edgeDists.zy, float2( 1.0, -1.0), float2(xRadii[1], yRadii[1]));
1454*c8dee2aaSAndroid Build Coastguard Worker    $corner_distance(d, J, stroke, edgeDists.zw, float2( 1.0,  1.0), float2(xRadii[2], yRadii[2]));
1455*c8dee2aaSAndroid Build Coastguard Worker    $corner_distance(d, J, stroke, edgeDists.xw, float2(-1.0,  1.0), float2(xRadii[3], yRadii[3]));
1456*c8dee2aaSAndroid Build Coastguard Worker}
1457*c8dee2aaSAndroid Build Coastguard Worker
1458*c8dee2aaSAndroid Build Coastguard Worker$pure half4 analytic_rrect_coverage_fn(float4 coords,
1459*c8dee2aaSAndroid Build Coastguard Worker                                       float4 jacobian,
1460*c8dee2aaSAndroid Build Coastguard Worker                                       float4 edgeDistances,
1461*c8dee2aaSAndroid Build Coastguard Worker                                       float4 xRadii,
1462*c8dee2aaSAndroid Build Coastguard Worker                                       float4 yRadii,
1463*c8dee2aaSAndroid Build Coastguard Worker                                       float2 strokeParams,
1464*c8dee2aaSAndroid Build Coastguard Worker                                       float2 perPixelControl) {
1465*c8dee2aaSAndroid Build Coastguard Worker    if (perPixelControl.x > 0.0) {
1466*c8dee2aaSAndroid Build Coastguard Worker        // A trivially solid interior pixel, either from a filled rect or round rect, or a
1467*c8dee2aaSAndroid Build Coastguard Worker        // stroke with sufficiently large width that the interior completely overlaps itself.
1468*c8dee2aaSAndroid Build Coastguard Worker        return half4(1.0);
1469*c8dee2aaSAndroid Build Coastguard Worker    } else if (perPixelControl.y > 1.0) {
1470*c8dee2aaSAndroid Build Coastguard Worker        // This represents a filled rectangle or quadrilateral, where the distances have already
1471*c8dee2aaSAndroid Build Coastguard Worker        // been converted to device space. Mitered strokes cannot use this optimization because
1472*c8dee2aaSAndroid Build Coastguard Worker        // their scale and bias is not uniform over the shape; Rounded shapes cannot use this
1473*c8dee2aaSAndroid Build Coastguard Worker        // because they rely on the edge distances being in local space to reconstruct the
1474*c8dee2aaSAndroid Build Coastguard Worker        // per-corner positions for the elliptical implicit functions.
1475*c8dee2aaSAndroid Build Coastguard Worker        float2 outerDist = min(edgeDistances.xy, edgeDistances.zw);
1476*c8dee2aaSAndroid Build Coastguard Worker        float c = min(outerDist.x, outerDist.y) * coords.w;
1477*c8dee2aaSAndroid Build Coastguard Worker        float scale = (perPixelControl.y - 1.0) * coords.w;
1478*c8dee2aaSAndroid Build Coastguard Worker        float bias = coverage_bias(scale);
1479*c8dee2aaSAndroid Build Coastguard Worker        return half4(saturate(scale * (c + bias)));
1480*c8dee2aaSAndroid Build Coastguard Worker    } else {
1481*c8dee2aaSAndroid Build Coastguard Worker        // Compute per-pixel coverage, mixing four outer edge distances, possibly four inner
1482*c8dee2aaSAndroid Build Coastguard Worker        // edge distances, and per-corner elliptical distances into a final coverage value.
1483*c8dee2aaSAndroid Build Coastguard Worker        // The Jacobian needs to be multiplied by W, but coords.w stores 1/w.
1484*c8dee2aaSAndroid Build Coastguard Worker        float2x2 J = float2x2(jacobian) / coords.w;
1485*c8dee2aaSAndroid Build Coastguard Worker
1486*c8dee2aaSAndroid Build Coastguard Worker        float2 invGradLen = float2($inverse_grad_len(float2(1.0, 0.0), J),
1487*c8dee2aaSAndroid Build Coastguard Worker                                   $inverse_grad_len(float2(0.0, 1.0), J));
1488*c8dee2aaSAndroid Build Coastguard Worker        float2 outerDist = invGradLen * (strokeParams.x + min(edgeDistances.xy,
1489*c8dee2aaSAndroid Build Coastguard Worker                                                              edgeDistances.zw));
1490*c8dee2aaSAndroid Build Coastguard Worker
1491*c8dee2aaSAndroid Build Coastguard Worker        // d.x tracks minimum outer distance (pre scale-and-biasing to a coverage value).
1492*c8dee2aaSAndroid Build Coastguard Worker        // d.y tracks negative maximum inner distance (so min() over c accumulates min and outer
1493*c8dee2aaSAndroid Build Coastguard Worker        // and max inner simultaneously).)
1494*c8dee2aaSAndroid Build Coastguard Worker        float2 d = float2(min(outerDist.x, outerDist.y), -1.0);
1495*c8dee2aaSAndroid Build Coastguard Worker        float scale, bias;
1496*c8dee2aaSAndroid Build Coastguard Worker
1497*c8dee2aaSAndroid Build Coastguard Worker        // Check for bidirectional coverage, which is is marked as a -1 from the vertex shader.
1498*c8dee2aaSAndroid Build Coastguard Worker        // We don't just check for < 0 since extrapolated fill triangle samples can have small
1499*c8dee2aaSAndroid Build Coastguard Worker        // negative values.
1500*c8dee2aaSAndroid Build Coastguard Worker        if (perPixelControl.x > -0.95) {
1501*c8dee2aaSAndroid Build Coastguard Worker            // A solid interior, so update scale and bias based on full width and height
1502*c8dee2aaSAndroid Build Coastguard Worker            float2 dim = invGradLen * (edgeDistances.xy + edgeDistances.zw + 2*strokeParams.xx);
1503*c8dee2aaSAndroid Build Coastguard Worker            scale = min(min(dim.x, dim.y), 1.0);
1504*c8dee2aaSAndroid Build Coastguard Worker            bias = coverage_bias(scale);
1505*c8dee2aaSAndroid Build Coastguard Worker            // Since we leave d.y = -1.0, no inner curve coverage will adjust it closer to 0,
1506*c8dee2aaSAndroid Build Coastguard Worker            // so 'finalCoverage' is based solely on outer edges and curves.
1507*c8dee2aaSAndroid Build Coastguard Worker        } else {
1508*c8dee2aaSAndroid Build Coastguard Worker            // Bidirectional coverage, so we modify c.y to hold the negative of the maximum
1509*c8dee2aaSAndroid Build Coastguard Worker            // interior coverage, and update scale and bias based on stroke width.
1510*c8dee2aaSAndroid Build Coastguard Worker            float2 strokeWidth = 2.0 * strokeParams.x * invGradLen;
1511*c8dee2aaSAndroid Build Coastguard Worker            float2 innerDist = strokeWidth - outerDist;
1512*c8dee2aaSAndroid Build Coastguard Worker
1513*c8dee2aaSAndroid Build Coastguard Worker            d.y = -max(innerDist.x, innerDist.y);
1514*c8dee2aaSAndroid Build Coastguard Worker            if (strokeParams.x > 0.0) {
1515*c8dee2aaSAndroid Build Coastguard Worker                float narrowStroke = min(strokeWidth.x, strokeWidth.y);
1516*c8dee2aaSAndroid Build Coastguard Worker                // On an axis where innerDist >= -0.5, allow strokeWidth.x/y to be preserved as-is.
1517*c8dee2aaSAndroid Build Coastguard Worker                // On an axis where innerDist < -0.5, use the smaller of strokeWidth.x/y.
1518*c8dee2aaSAndroid Build Coastguard Worker                float2 strokeDim = mix(float2(narrowStroke), strokeWidth,
1519*c8dee2aaSAndroid Build Coastguard Worker                                       greaterThanEqual(innerDist, float2(-0.5)));
1520*c8dee2aaSAndroid Build Coastguard Worker                // Preserve the wider axis from the above calculation.
1521*c8dee2aaSAndroid Build Coastguard Worker                scale = saturate(max(strokeDim.x, strokeDim.y));
1522*c8dee2aaSAndroid Build Coastguard Worker                bias = coverage_bias(scale);
1523*c8dee2aaSAndroid Build Coastguard Worker            } else {
1524*c8dee2aaSAndroid Build Coastguard Worker                // A hairline, so scale and bias should both be 1
1525*c8dee2aaSAndroid Build Coastguard Worker                 scale = bias = 1.0;
1526*c8dee2aaSAndroid Build Coastguard Worker            }
1527*c8dee2aaSAndroid Build Coastguard Worker        }
1528*c8dee2aaSAndroid Build Coastguard Worker
1529*c8dee2aaSAndroid Build Coastguard Worker        // Check all corners, although most pixels should only be influenced by 1.
1530*c8dee2aaSAndroid Build Coastguard Worker        $corner_distances(d, J, strokeParams, edgeDistances, xRadii, yRadii);
1531*c8dee2aaSAndroid Build Coastguard Worker
1532*c8dee2aaSAndroid Build Coastguard Worker        float outsetDist = min(perPixelControl.y, 0.0) * coords.w;
1533*c8dee2aaSAndroid Build Coastguard Worker        float finalCoverage = scale * (min(d.x + outsetDist, -d.y) + bias);
1534*c8dee2aaSAndroid Build Coastguard Worker
1535*c8dee2aaSAndroid Build Coastguard Worker        return half4(saturate(finalCoverage));
1536*c8dee2aaSAndroid Build Coastguard Worker    }
1537*c8dee2aaSAndroid Build Coastguard Worker}
1538*c8dee2aaSAndroid Build Coastguard Worker
1539*c8dee2aaSAndroid Build Coastguard Worker$pure half4 per_edge_aa_quad_coverage_fn(float4 coords,
1540*c8dee2aaSAndroid Build Coastguard Worker                                         float4 edgeDistances) {
1541*c8dee2aaSAndroid Build Coastguard Worker    // This represents a filled rectangle or quadrilateral, where the distances have already
1542*c8dee2aaSAndroid Build Coastguard Worker    // been converted to device space.
1543*c8dee2aaSAndroid Build Coastguard Worker    float2 outerDist = min(edgeDistances.xy, edgeDistances.zw);
1544*c8dee2aaSAndroid Build Coastguard Worker    float c = min(outerDist.x, outerDist.y) * coords.w;
1545*c8dee2aaSAndroid Build Coastguard Worker    return half4(saturate(c));
1546*c8dee2aaSAndroid Build Coastguard Worker}
1547*c8dee2aaSAndroid Build Coastguard Worker
1548*c8dee2aaSAndroid Build Coastguard Worker$pure half4 circular_arc_coverage_fn(float4 circleEdge,
1549*c8dee2aaSAndroid Build Coastguard Worker                                     float3 clipPlane,
1550*c8dee2aaSAndroid Build Coastguard Worker                                     float3 isectPlane,
1551*c8dee2aaSAndroid Build Coastguard Worker                                     float3 unionPlane,
1552*c8dee2aaSAndroid Build Coastguard Worker                                     float roundCapRadius,
1553*c8dee2aaSAndroid Build Coastguard Worker                                     float4 roundCapPos) {
1554*c8dee2aaSAndroid Build Coastguard Worker    float d = length(circleEdge.xy);
1555*c8dee2aaSAndroid Build Coastguard Worker    half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));
1556*c8dee2aaSAndroid Build Coastguard Worker    half edgeAlpha = saturate(distanceToOuterEdge);
1557*c8dee2aaSAndroid Build Coastguard Worker    half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));
1558*c8dee2aaSAndroid Build Coastguard Worker    half innerAlpha = saturate(distanceToInnerEdge);
1559*c8dee2aaSAndroid Build Coastguard Worker    edgeAlpha *= innerAlpha;
1560*c8dee2aaSAndroid Build Coastguard Worker
1561*c8dee2aaSAndroid Build Coastguard Worker    half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z));
1562*c8dee2aaSAndroid Build Coastguard Worker    clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z));
1563*c8dee2aaSAndroid Build Coastguard Worker    clip = clip + half(saturate(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z));
1564*c8dee2aaSAndroid Build Coastguard Worker
1565*c8dee2aaSAndroid Build Coastguard Worker    // We compute coverage of the round caps as circles at the butt caps produced
1566*c8dee2aaSAndroid Build Coastguard Worker    // by the clip planes, and union it with the current clip.
1567*c8dee2aaSAndroid Build Coastguard Worker    half dcap1 = half(circleEdge.z * (roundCapRadius - length(circleEdge.xy - roundCapPos.xy)));
1568*c8dee2aaSAndroid Build Coastguard Worker    half dcap2 = half(circleEdge.z * (roundCapRadius - length(circleEdge.xy - roundCapPos.zw)));
1569*c8dee2aaSAndroid Build Coastguard Worker    half capAlpha = max(dcap1, 0) + max(dcap2, 0);
1570*c8dee2aaSAndroid Build Coastguard Worker    clip = saturate(clip + capAlpha);
1571*c8dee2aaSAndroid Build Coastguard Worker
1572*c8dee2aaSAndroid Build Coastguard Worker    return half4(clip*edgeAlpha);
1573*c8dee2aaSAndroid Build Coastguard Worker}
1574*c8dee2aaSAndroid Build Coastguard Worker
1575*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $rect_blur_coverage_fn(float2 coords,
1576*c8dee2aaSAndroid Build Coastguard Worker                                   float4 rect,
1577*c8dee2aaSAndroid Build Coastguard Worker                                   half isFast,
1578*c8dee2aaSAndroid Build Coastguard Worker                                   half invSixSigma,
1579*c8dee2aaSAndroid Build Coastguard Worker                                   sampler2D integral) {
1580*c8dee2aaSAndroid Build Coastguard Worker    half xCoverage;
1581*c8dee2aaSAndroid Build Coastguard Worker    half yCoverage;
1582*c8dee2aaSAndroid Build Coastguard Worker
1583*c8dee2aaSAndroid Build Coastguard Worker    if (isFast != 0.0) {
1584*c8dee2aaSAndroid Build Coastguard Worker        // Get the smaller of the signed distance from the frag coord to the left and right
1585*c8dee2aaSAndroid Build Coastguard Worker        // edges and similar for y.
1586*c8dee2aaSAndroid Build Coastguard Worker        // The integral texture goes "backwards" (from 3*sigma to -3*sigma), So, the below
1587*c8dee2aaSAndroid Build Coastguard Worker        // computations align the left edge of the integral texture with the inset rect's
1588*c8dee2aaSAndroid Build Coastguard Worker        // edge extending outward 6 * sigma from the inset rect.
1589*c8dee2aaSAndroid Build Coastguard Worker        half2 pos = max(half2(rect.LT - coords), half2(coords - rect.RB));
1590*c8dee2aaSAndroid Build Coastguard Worker        xCoverage = sample(integral, float2(invSixSigma * pos.x, 0.5)).r;
1591*c8dee2aaSAndroid Build Coastguard Worker        yCoverage = sample(integral, float2(invSixSigma * pos.y, 0.5)).r;
1592*c8dee2aaSAndroid Build Coastguard Worker
1593*c8dee2aaSAndroid Build Coastguard Worker    } else {
1594*c8dee2aaSAndroid Build Coastguard Worker        // We just consider just the x direction here. In practice we compute x and y
1595*c8dee2aaSAndroid Build Coastguard Worker        // separately and multiply them together.
1596*c8dee2aaSAndroid Build Coastguard Worker        // We define our coord system so that the point at which we're evaluating a kernel
1597*c8dee2aaSAndroid Build Coastguard Worker        // defined by the normal distribution (K) at 0. In this coord system let L be left
1598*c8dee2aaSAndroid Build Coastguard Worker        // edge and R be the right edge of the rectangle.
1599*c8dee2aaSAndroid Build Coastguard Worker        // We can calculate C by integrating K with the half infinite ranges outside the
1600*c8dee2aaSAndroid Build Coastguard Worker        // L to R range and subtracting from 1:
1601*c8dee2aaSAndroid Build Coastguard Worker        //   C = 1 - <integral of K from from -inf to  L> - <integral of K from R to inf>
1602*c8dee2aaSAndroid Build Coastguard Worker        // K is symmetric about x=0 so:
1603*c8dee2aaSAndroid Build Coastguard Worker        //   C = 1 - <integral of K from from -inf to  L> - <integral of K from -inf to -R>
1604*c8dee2aaSAndroid Build Coastguard Worker
1605*c8dee2aaSAndroid Build Coastguard Worker        // The integral texture goes "backwards" (from 3*sigma to -3*sigma) which is
1606*c8dee2aaSAndroid Build Coastguard Worker        // factored in to the below calculations.
1607*c8dee2aaSAndroid Build Coastguard Worker        // Also, our rect uniform was pre-inset by 3 sigma from the actual rect being
1608*c8dee2aaSAndroid Build Coastguard Worker        // blurred, also factored in.
1609*c8dee2aaSAndroid Build Coastguard Worker        half4 rect = half4(half2(rect.LT - coords), half2(coords - rect.RB));
1610*c8dee2aaSAndroid Build Coastguard Worker        xCoverage = 1 - sample(integral, float2(invSixSigma * rect.L, 0.5)).r
1611*c8dee2aaSAndroid Build Coastguard Worker                      - sample(integral, float2(invSixSigma * rect.R, 0.5)).r;
1612*c8dee2aaSAndroid Build Coastguard Worker        yCoverage = 1 - sample(integral, float2(invSixSigma * rect.T, 0.5)).r
1613*c8dee2aaSAndroid Build Coastguard Worker                      - sample(integral, float2(invSixSigma * rect.B, 0.5)).r;
1614*c8dee2aaSAndroid Build Coastguard Worker    }
1615*c8dee2aaSAndroid Build Coastguard Worker
1616*c8dee2aaSAndroid Build Coastguard Worker    return half4(xCoverage * yCoverage);
1617*c8dee2aaSAndroid Build Coastguard Worker}
1618*c8dee2aaSAndroid Build Coastguard Worker
1619*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $circle_blur_coverage_fn(float2 coords, float4 circle, sampler2D blurProfile) {
1620*c8dee2aaSAndroid Build Coastguard Worker    // We just want to compute "(length(vec) - solidRadius + 0.5) / textureRadius" but need to
1621*c8dee2aaSAndroid Build Coastguard Worker    // rearrange to avoid passing large values to length() that would overflow. We've precalculated
1622*c8dee2aaSAndroid Build Coastguard Worker    // "1 / textureRadius" and "(solidRadius - 0.5) / textureRadius" on the CPU as circle.z and
1623*c8dee2aaSAndroid Build Coastguard Worker    // circle.w, respectively.
1624*c8dee2aaSAndroid Build Coastguard Worker    float invTextureRadius = circle.z;
1625*c8dee2aaSAndroid Build Coastguard Worker    float normSolidRadius = circle.w;
1626*c8dee2aaSAndroid Build Coastguard Worker
1627*c8dee2aaSAndroid Build Coastguard Worker    half2 vec = half2((coords - circle.xy) * invTextureRadius);
1628*c8dee2aaSAndroid Build Coastguard Worker    float dist = length(vec) - normSolidRadius;
1629*c8dee2aaSAndroid Build Coastguard Worker    return sample(blurProfile, float2(dist, 0.5)).rrrr;
1630*c8dee2aaSAndroid Build Coastguard Worker}
1631*c8dee2aaSAndroid Build Coastguard Worker
1632*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $rrect_blur_coverage_fn(float2 coords,
1633*c8dee2aaSAndroid Build Coastguard Worker                                    float4 proxyRect,
1634*c8dee2aaSAndroid Build Coastguard Worker                                    half edgeSize,
1635*c8dee2aaSAndroid Build Coastguard Worker                                    sampler2D ninePatch) {
1636*c8dee2aaSAndroid Build Coastguard Worker    // Warp the fragment position to the appropriate part of the 9-patch blur texture by
1637*c8dee2aaSAndroid Build Coastguard Worker    // snipping out the middle section of the proxy rect.
1638*c8dee2aaSAndroid Build Coastguard Worker    float2 translatedFragPosFloat = coords - proxyRect.LT;
1639*c8dee2aaSAndroid Build Coastguard Worker    float2 proxyCenter = (proxyRect.RB - proxyRect.LT) * 0.5;
1640*c8dee2aaSAndroid Build Coastguard Worker
1641*c8dee2aaSAndroid Build Coastguard Worker    // Position the fragment so that (0, 0) marks the center of the proxy rectangle.
1642*c8dee2aaSAndroid Build Coastguard Worker    // Negative coordinates are on the left/top side and positive numbers are on the
1643*c8dee2aaSAndroid Build Coastguard Worker    // right/bottom.
1644*c8dee2aaSAndroid Build Coastguard Worker    translatedFragPosFloat -= proxyCenter;
1645*c8dee2aaSAndroid Build Coastguard Worker
1646*c8dee2aaSAndroid Build Coastguard Worker    // Temporarily strip off the fragment's sign. x/y are now strictly increasing as we
1647*c8dee2aaSAndroid Build Coastguard Worker    // move away from the center.
1648*c8dee2aaSAndroid Build Coastguard Worker    half2 fragDirection = half2(sign(translatedFragPosFloat));
1649*c8dee2aaSAndroid Build Coastguard Worker    translatedFragPosFloat = abs(translatedFragPosFloat);
1650*c8dee2aaSAndroid Build Coastguard Worker
1651*c8dee2aaSAndroid Build Coastguard Worker    // Our goal is to snip out the "middle section" of the proxy rect (everything but the
1652*c8dee2aaSAndroid Build Coastguard Worker    // edge). We've repositioned our fragment position so that (0, 0) is the centerpoint
1653*c8dee2aaSAndroid Build Coastguard Worker    // and x/y are always positive, so we can subtract here and interpret negative results
1654*c8dee2aaSAndroid Build Coastguard Worker    // as being within the middle section.
1655*c8dee2aaSAndroid Build Coastguard Worker    half2 translatedFragPosHalf = half2(translatedFragPosFloat - (proxyCenter - edgeSize));
1656*c8dee2aaSAndroid Build Coastguard Worker
1657*c8dee2aaSAndroid Build Coastguard Worker    // Remove the middle section by clamping to zero.
1658*c8dee2aaSAndroid Build Coastguard Worker    translatedFragPosHalf = max(translatedFragPosHalf, 0);
1659*c8dee2aaSAndroid Build Coastguard Worker
1660*c8dee2aaSAndroid Build Coastguard Worker    // Reapply the fragment's sign, so that negative coordinates once again mean left/top
1661*c8dee2aaSAndroid Build Coastguard Worker    // side and positive means bottom/right side.
1662*c8dee2aaSAndroid Build Coastguard Worker    translatedFragPosHalf *= fragDirection;
1663*c8dee2aaSAndroid Build Coastguard Worker
1664*c8dee2aaSAndroid Build Coastguard Worker    // Offset the fragment so that (0, 0) marks the upper-left again, instead of the center
1665*c8dee2aaSAndroid Build Coastguard Worker    // point.
1666*c8dee2aaSAndroid Build Coastguard Worker    translatedFragPosHalf += half2(edgeSize);
1667*c8dee2aaSAndroid Build Coastguard Worker
1668*c8dee2aaSAndroid Build Coastguard Worker    half2 proxyDims = half2(2.0 * edgeSize);
1669*c8dee2aaSAndroid Build Coastguard Worker    half2 texCoord = translatedFragPosHalf / proxyDims;
1670*c8dee2aaSAndroid Build Coastguard Worker
1671*c8dee2aaSAndroid Build Coastguard Worker    return sample(ninePatch, texCoord).rrrr;
1672*c8dee2aaSAndroid Build Coastguard Worker}
1673*c8dee2aaSAndroid Build Coastguard Worker
1674*c8dee2aaSAndroid Build Coastguard Worker$pure half4 blur_coverage_fn(float2 coords,
1675*c8dee2aaSAndroid Build Coastguard Worker                             float4 shapeData,
1676*c8dee2aaSAndroid Build Coastguard Worker                             half2 blurData,
1677*c8dee2aaSAndroid Build Coastguard Worker                             int shapeType,
1678*c8dee2aaSAndroid Build Coastguard Worker                             sampler2D s) {
1679*c8dee2aaSAndroid Build Coastguard Worker    switch (shapeType) {
1680*c8dee2aaSAndroid Build Coastguard Worker        case $kShapeTypeRect: {
1681*c8dee2aaSAndroid Build Coastguard Worker            return $rect_blur_coverage_fn(coords, shapeData, blurData.x, blurData.y, s);
1682*c8dee2aaSAndroid Build Coastguard Worker        }
1683*c8dee2aaSAndroid Build Coastguard Worker        case $kShapeTypeCircle: {
1684*c8dee2aaSAndroid Build Coastguard Worker            return $circle_blur_coverage_fn(coords, shapeData, s);
1685*c8dee2aaSAndroid Build Coastguard Worker        }
1686*c8dee2aaSAndroid Build Coastguard Worker        case $kShapeTypeRRect: {
1687*c8dee2aaSAndroid Build Coastguard Worker            return $rrect_blur_coverage_fn(coords, shapeData, blurData.x, s);
1688*c8dee2aaSAndroid Build Coastguard Worker        }
1689*c8dee2aaSAndroid Build Coastguard Worker    }
1690*c8dee2aaSAndroid Build Coastguard Worker    return half4(0);
1691*c8dee2aaSAndroid Build Coastguard Worker}
1692