xref: /aosp_15_r20/external/skia/src/sksl/sksl_rt_shader.sksl (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Workerlayout(builtin=15) float4 sk_FragCoord;
2*c8dee2aaSAndroid Build Coastguard Worker
3*c8dee2aaSAndroid Build Coastguard Worker//--- Luma ------------------------------------------------------------------------
4*c8dee2aaSAndroid Build Coastguard Worker
5*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_luma(half3 color) {
6*c8dee2aaSAndroid Build Coastguard Worker    return saturate(dot(half3(0.2126, 0.7152, 0.0722), color)).000r;
7*c8dee2aaSAndroid Build Coastguard Worker}
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker//--- Decal ------------------------------------------------------------------------
10*c8dee2aaSAndroid Build Coastguard Worker
11*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_decal(shader image, float2 coord, float4 decalBounds) {
12*c8dee2aaSAndroid Build Coastguard Worker    half4 d = half4(decalBounds - coord.xyxy) * half4(-1, -1, 1, 1);
13*c8dee2aaSAndroid Build Coastguard Worker    d = saturate(d + 0.5);
14*c8dee2aaSAndroid Build Coastguard Worker    return (d.x * d.y * d.z * d.w) * image.eval(coord);
15*c8dee2aaSAndroid Build Coastguard Worker}
16*c8dee2aaSAndroid Build Coastguard Worker
17*c8dee2aaSAndroid Build Coastguard Worker//--- Displacement -----------------------------------------------------------------
18*c8dee2aaSAndroid Build Coastguard Worker
19*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_displacement(shader displMap,
20*c8dee2aaSAndroid Build Coastguard Worker                      shader colorMap,
21*c8dee2aaSAndroid Build Coastguard Worker                      float2 coord,
22*c8dee2aaSAndroid Build Coastguard Worker                      half2 scale,
23*c8dee2aaSAndroid Build Coastguard Worker                      half4 xSelect,  // Only one of RGBA will be 1, the rest are 0
24*c8dee2aaSAndroid Build Coastguard Worker                      half4 ySelect) {
25*c8dee2aaSAndroid Build Coastguard Worker    half4 displColor = unpremul(displMap.eval(coord));
26*c8dee2aaSAndroid Build Coastguard Worker    half2 displ = half2(dot(displColor, xSelect), dot(displColor, ySelect));
27*c8dee2aaSAndroid Build Coastguard Worker    displ = scale * (displ - 0.5);
28*c8dee2aaSAndroid Build Coastguard Worker    return colorMap.eval(coord + displ);
29*c8dee2aaSAndroid Build Coastguard Worker}
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker//--- Magnifier --------------------------------------------------------------------
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_magnifier(shader src, float2 coord, float4 lensBounds, float4 zoomXform,
34*c8dee2aaSAndroid Build Coastguard Worker                   float2 invInset) {
35*c8dee2aaSAndroid Build Coastguard Worker    float2 zoomCoord = zoomXform.xy + zoomXform.zw*coord;
36*c8dee2aaSAndroid Build Coastguard Worker    // edgeInset is the smallest distance to the lens bounds edges, in units of "insets".
37*c8dee2aaSAndroid Build Coastguard Worker    float2 edgeInset = min(coord - lensBounds.xy, lensBounds.zw - coord) * invInset;
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker    // The equations for 'weight' ensure that it is 0 along the outside of
40*c8dee2aaSAndroid Build Coastguard Worker    // lensBounds so it seams with any un-zoomed, un-filtered content. The zoomed
41*c8dee2aaSAndroid Build Coastguard Worker    // content fills a rounded rectangle that is 1 "inset" in from lensBounds with
42*c8dee2aaSAndroid Build Coastguard Worker    // circular corners with radii equal to the inset distance. Outside of this
43*c8dee2aaSAndroid Build Coastguard Worker    // region, there is a non-linear weighting to compress the un-zoomed content
44*c8dee2aaSAndroid Build Coastguard Worker    // to the zoomed content. The critical zone about each corner is limited
45*c8dee2aaSAndroid Build Coastguard Worker    // to 2x"inset" square.
46*c8dee2aaSAndroid Build Coastguard Worker    float weight = all(lessThan(edgeInset, float2(2.0)))
47*c8dee2aaSAndroid Build Coastguard Worker        // Circular distortion weighted by distance to inset corner
48*c8dee2aaSAndroid Build Coastguard Worker        ? (2.0 - length(2.0 - edgeInset))
49*c8dee2aaSAndroid Build Coastguard Worker        // Linear zoom, or single-axis compression outside of the inset
50*c8dee2aaSAndroid Build Coastguard Worker        // area (if delta < 1)
51*c8dee2aaSAndroid Build Coastguard Worker        : min(edgeInset.x, edgeInset.y);
52*c8dee2aaSAndroid Build Coastguard Worker
53*c8dee2aaSAndroid Build Coastguard Worker    // Saturate before squaring so that negative weights are clamped to 0
54*c8dee2aaSAndroid Build Coastguard Worker    // before squaring
55*c8dee2aaSAndroid Build Coastguard Worker    weight = saturate(weight);
56*c8dee2aaSAndroid Build Coastguard Worker    return src.eval(mix(coord, zoomCoord, weight*weight));
57*c8dee2aaSAndroid Build Coastguard Worker}
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Worker//--- High Contrast ----------------------------------------------------------------
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker$pure half3 $high_contrast_rgb_to_hsl(half3 c) {
62*c8dee2aaSAndroid Build Coastguard Worker    half mx = max(max(c.r,c.g),c.b),
63*c8dee2aaSAndroid Build Coastguard Worker         mn = min(min(c.r,c.g),c.b),
64*c8dee2aaSAndroid Build Coastguard Worker          d = mx-mn,
65*c8dee2aaSAndroid Build Coastguard Worker       invd = 1.0 / d,
66*c8dee2aaSAndroid Build Coastguard Worker     g_lt_b = c.g < c.b ? 6.0 : 0.0;
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker    // We'd prefer to write these tests like `mx == c.r`, but on some GPUs, max(x,y) is
69*c8dee2aaSAndroid Build Coastguard Worker    // not always equal to either x or y. So we use long form, c.r >= c.g && c.r >= c.b.
70*c8dee2aaSAndroid Build Coastguard Worker    half h = (1/6.0) * (mx == mn                 ? 0.0 :
71*c8dee2aaSAndroid Build Coastguard Worker        /*mx==c.r*/     c.r >= c.g && c.r >= c.b ? invd * (c.g - c.b) + g_lt_b :
72*c8dee2aaSAndroid Build Coastguard Worker        /*mx==c.g*/     c.g >= c.b               ? invd * (c.b - c.r) + 2.0
73*c8dee2aaSAndroid Build Coastguard Worker        /*mx==c.b*/                              : invd * (c.r - c.g) + 4.0);
74*c8dee2aaSAndroid Build Coastguard Worker    half sum = mx+mn,
75*c8dee2aaSAndroid Build Coastguard Worker           l = sum * 0.5,
76*c8dee2aaSAndroid Build Coastguard Worker           s = mx == mn ? 0.0
77*c8dee2aaSAndroid Build Coastguard Worker                        : d / (l > 0.5 ? 2.0 - sum : sum);
78*c8dee2aaSAndroid Build Coastguard Worker    return half3(h,s,l);
79*c8dee2aaSAndroid Build Coastguard Worker}
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Workerhalf3 sk_high_contrast(half3 color, half grayscale, half invertStyle, half contrast) {
82*c8dee2aaSAndroid Build Coastguard Worker    if (grayscale == 1) {
83*c8dee2aaSAndroid Build Coastguard Worker        color = dot(half3(0.2126, 0.7152, 0.0722), color).rrr;
84*c8dee2aaSAndroid Build Coastguard Worker    }
85*c8dee2aaSAndroid Build Coastguard Worker    if (invertStyle == 1) {  // brightness
86*c8dee2aaSAndroid Build Coastguard Worker        color = 1.0 - color;
87*c8dee2aaSAndroid Build Coastguard Worker    } else if (invertStyle == 2) {  // lightness
88*c8dee2aaSAndroid Build Coastguard Worker        color = $high_contrast_rgb_to_hsl(color);
89*c8dee2aaSAndroid Build Coastguard Worker        color.b = 1 - color.b;
90*c8dee2aaSAndroid Build Coastguard Worker        color = $hsl_to_rgb(color);
91*c8dee2aaSAndroid Build Coastguard Worker    }
92*c8dee2aaSAndroid Build Coastguard Worker    return saturate(mix(half3(0.5), color, contrast));
93*c8dee2aaSAndroid Build Coastguard Worker}
94*c8dee2aaSAndroid Build Coastguard Worker
95*c8dee2aaSAndroid Build Coastguard Worker//--- Normal -----------------------------------------------------------------------
96*c8dee2aaSAndroid Build Coastguard Worker
97*c8dee2aaSAndroid Build Coastguard Worker$pure half3 $normal_filter(half3 alphaC0, half3 alphaC1, half3 alphaC2, half negSurfaceDepth) {
98*c8dee2aaSAndroid Build Coastguard Worker    // The right column (or bottom row) terms of the Sobel filter. The left/top is just the
99*c8dee2aaSAndroid Build Coastguard Worker    // negative, and the middle row/column is all 0s so those instructions are skipped.
100*c8dee2aaSAndroid Build Coastguard Worker    const half3 kSobel = 0.25 * half3(1,2,1);
101*c8dee2aaSAndroid Build Coastguard Worker    half3 alphaR0 = half3(alphaC0.x, alphaC1.x, alphaC2.x);
102*c8dee2aaSAndroid Build Coastguard Worker    half3 alphaR2 = half3(alphaC0.z, alphaC1.z, alphaC2.z);
103*c8dee2aaSAndroid Build Coastguard Worker    half nx = dot(kSobel, alphaC2) - dot(kSobel, alphaC0);
104*c8dee2aaSAndroid Build Coastguard Worker    half ny = dot(kSobel, alphaR2) - dot(kSobel, alphaR0);
105*c8dee2aaSAndroid Build Coastguard Worker    return normalize(half3(negSurfaceDepth * half2(nx, ny), 1));
106*c8dee2aaSAndroid Build Coastguard Worker}
107*c8dee2aaSAndroid Build Coastguard Worker
108*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_normal(shader alphaMap, float2 coord, float4 edgeBounds, half negSurfaceDepth) {
109*c8dee2aaSAndroid Build Coastguard Worker   half3 alphaC0 = half3(
110*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2(-1,-1), edgeBounds.LT, edgeBounds.RB)).a,
111*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2(-1, 0), edgeBounds.LT, edgeBounds.RB)).a,
112*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2(-1, 1), edgeBounds.LT, edgeBounds.RB)).a);
113*c8dee2aaSAndroid Build Coastguard Worker   half3 alphaC1 = half3(
114*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2( 0,-1), edgeBounds.LT, edgeBounds.RB)).a,
115*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2( 0, 0), edgeBounds.LT, edgeBounds.RB)).a,
116*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2( 0, 1), edgeBounds.LT, edgeBounds.RB)).a);
117*c8dee2aaSAndroid Build Coastguard Worker   half3 alphaC2 = half3(
118*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2( 1,-1), edgeBounds.LT, edgeBounds.RB)).a,
119*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2( 1, 0), edgeBounds.LT, edgeBounds.RB)).a,
120*c8dee2aaSAndroid Build Coastguard Worker        alphaMap.eval(clamp(coord + float2( 1, 1), edgeBounds.LT, edgeBounds.RB)).a);
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker   half mainAlpha = alphaC1.y; // offset = (0,0)
123*c8dee2aaSAndroid Build Coastguard Worker   return half4($normal_filter(alphaC0, alphaC1, alphaC2, negSurfaceDepth), mainAlpha);
124*c8dee2aaSAndroid Build Coastguard Worker}
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker//--- Lighting ---------------------------------------------------------------------
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker$pure half3 $surface_to_light(half lightType, half3 lightPos, half3 lightDir, half3 coord) {
129*c8dee2aaSAndroid Build Coastguard Worker    // Spot (> 0) and point (== 0) have the same equation
130*c8dee2aaSAndroid Build Coastguard Worker    return lightType >= 0 ? normalize(lightPos - coord)
131*c8dee2aaSAndroid Build Coastguard Worker                          : lightDir;
132*c8dee2aaSAndroid Build Coastguard Worker}
133*c8dee2aaSAndroid Build Coastguard Worker
134*c8dee2aaSAndroid Build Coastguard Worker$pure half $spotlight_scale(half3 lightDir, half3 surfaceToLight, half cosCutoffAngle,
135*c8dee2aaSAndroid Build Coastguard Worker                            half spotFalloff) {
136*c8dee2aaSAndroid Build Coastguard Worker    const half kConeAAThreshold = 0.016;
137*c8dee2aaSAndroid Build Coastguard Worker    const half kConeScale = 1.0 / kConeAAThreshold;
138*c8dee2aaSAndroid Build Coastguard Worker
139*c8dee2aaSAndroid Build Coastguard Worker    half cosAngle = -dot(surfaceToLight, lightDir);
140*c8dee2aaSAndroid Build Coastguard Worker    if (cosAngle < cosCutoffAngle) {
141*c8dee2aaSAndroid Build Coastguard Worker        return 0.0;
142*c8dee2aaSAndroid Build Coastguard Worker    } else {
143*c8dee2aaSAndroid Build Coastguard Worker        half scale = pow(cosAngle, spotFalloff);
144*c8dee2aaSAndroid Build Coastguard Worker        return (cosAngle < cosCutoffAngle + kConeAAThreshold)
145*c8dee2aaSAndroid Build Coastguard Worker                    ? scale * (cosAngle - cosCutoffAngle) * kConeScale
146*c8dee2aaSAndroid Build Coastguard Worker                    : scale;
147*c8dee2aaSAndroid Build Coastguard Worker    }
148*c8dee2aaSAndroid Build Coastguard Worker}
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker$pure half4 $compute_lighting(half3 color, half shininess, half materialType, half lightType,
151*c8dee2aaSAndroid Build Coastguard Worker                              half3 normal, half3 lightDir, half3 surfaceToLight,
152*c8dee2aaSAndroid Build Coastguard Worker                              half cosCutoffAngle, half spotFalloff) {
153*c8dee2aaSAndroid Build Coastguard Worker    // Point and distant light color contributions are constant, but
154*c8dee2aaSAndroid Build Coastguard Worker    // spotlights fade based on the angle away from its direction.
155*c8dee2aaSAndroid Build Coastguard Worker    if (lightType > 0) {
156*c8dee2aaSAndroid Build Coastguard Worker        color *= $spotlight_scale(lightDir, surfaceToLight, cosCutoffAngle, spotFalloff);
157*c8dee2aaSAndroid Build Coastguard Worker    }
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker    // Diffuse and specular reflections scale the light's color differently
160*c8dee2aaSAndroid Build Coastguard Worker    if (materialType == 0) {
161*c8dee2aaSAndroid Build Coastguard Worker        half coeff = dot(normal, surfaceToLight);
162*c8dee2aaSAndroid Build Coastguard Worker        color = saturate(coeff * color);
163*c8dee2aaSAndroid Build Coastguard Worker        return half4(color, 1.0);
164*c8dee2aaSAndroid Build Coastguard Worker    } else {
165*c8dee2aaSAndroid Build Coastguard Worker        half3 halfDir = normalize(surfaceToLight + half3(0, 0, 1));
166*c8dee2aaSAndroid Build Coastguard Worker        half coeff = pow(dot(normal, halfDir), shininess);
167*c8dee2aaSAndroid Build Coastguard Worker        color = saturate(coeff * color);
168*c8dee2aaSAndroid Build Coastguard Worker        return half4(color, max(max(color.r, color.g), color.b));
169*c8dee2aaSAndroid Build Coastguard Worker    }
170*c8dee2aaSAndroid Build Coastguard Worker}
171*c8dee2aaSAndroid Build Coastguard Worker
172*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_lighting(shader normalMap, float2 coord,
173*c8dee2aaSAndroid Build Coastguard Worker                  half depth, half shininess, half materialType, half lightType,
174*c8dee2aaSAndroid Build Coastguard Worker                  half3 lightPos, half spotFalloff,
175*c8dee2aaSAndroid Build Coastguard Worker                  half3 lightDir, half cosCutoffAngle,
176*c8dee2aaSAndroid Build Coastguard Worker                  half3 lightColor) {
177*c8dee2aaSAndroid Build Coastguard Worker    half4 normalAndA = normalMap.eval(coord);
178*c8dee2aaSAndroid Build Coastguard Worker    half3 surfaceToLight = $surface_to_light(lightType, lightPos, lightDir,
179*c8dee2aaSAndroid Build Coastguard Worker                                             half3(coord, depth * normalAndA.a));
180*c8dee2aaSAndroid Build Coastguard Worker    return $compute_lighting(lightColor, shininess, materialType, lightType, normalAndA.xyz,
181*c8dee2aaSAndroid Build Coastguard Worker                             lightDir, surfaceToLight, cosCutoffAngle, spotFalloff);
182*c8dee2aaSAndroid Build Coastguard Worker}
183*c8dee2aaSAndroid Build Coastguard Worker
184*c8dee2aaSAndroid Build Coastguard Worker//--- Arithmetic Blend -------------------------------------------------------------
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_arithmetic_blend(half4 src, half4 dst, half4 k, half pmClamp) {
187*c8dee2aaSAndroid Build Coastguard Worker    half4 color = saturate(k.x * src * dst + k.y * src + k.z * dst + k.w);
188*c8dee2aaSAndroid Build Coastguard Worker    color.rgb = min(color.rgb, max(color.a, pmClamp));
189*c8dee2aaSAndroid Build Coastguard Worker    return color;
190*c8dee2aaSAndroid Build Coastguard Worker}
191*c8dee2aaSAndroid Build Coastguard Worker
192*c8dee2aaSAndroid Build Coastguard Worker//--- Sparse Morphology ------------------------------------------------------------
193*c8dee2aaSAndroid Build Coastguard Worker
194*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_sparse_morphology(shader child, float2 coord, half2 offset, half flip) {
195*c8dee2aaSAndroid Build Coastguard Worker    half4 aggregate = max(flip * child.eval(coord + offset),
196*c8dee2aaSAndroid Build Coastguard Worker                          flip * child.eval(coord - offset));
197*c8dee2aaSAndroid Build Coastguard Worker    return flip * aggregate;
198*c8dee2aaSAndroid Build Coastguard Worker}
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Worker//--- Linear Morphology ------------------------------------------------------------
201*c8dee2aaSAndroid Build Coastguard Worker
202*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_linear_morphology(shader child, float2 coord, half2 offset, half flip, int radius) {
203*c8dee2aaSAndroid Build Coastguard Worker
204*c8dee2aaSAndroid Build Coastguard Worker    // KEEP IN SYNC WITH CONSTANT IN `SkMorphologyImageFilter.cpp`
205*c8dee2aaSAndroid Build Coastguard Worker    const int kMaxLinearRadius = 14;
206*c8dee2aaSAndroid Build Coastguard Worker
207*c8dee2aaSAndroid Build Coastguard Worker    half4 aggregate = flip * child.eval(coord); // case 0 only needs a single sample
208*c8dee2aaSAndroid Build Coastguard Worker    half2 delta = offset;
209*c8dee2aaSAndroid Build Coastguard Worker    for (int i = 1; i <= kMaxLinearRadius; ++i) {
210*c8dee2aaSAndroid Build Coastguard Worker        if (i > radius) break;
211*c8dee2aaSAndroid Build Coastguard Worker        aggregate = max(aggregate, max(flip * child.eval(coord + delta),
212*c8dee2aaSAndroid Build Coastguard Worker                                       flip * child.eval(coord - delta)));
213*c8dee2aaSAndroid Build Coastguard Worker        delta += offset;
214*c8dee2aaSAndroid Build Coastguard Worker    }
215*c8dee2aaSAndroid Build Coastguard Worker    return flip * aggregate;
216*c8dee2aaSAndroid Build Coastguard Worker}
217*c8dee2aaSAndroid Build Coastguard Worker
218*c8dee2aaSAndroid Build Coastguard Worker//--- Overdraw ---------------------------------------------------------------------
219*c8dee2aaSAndroid Build Coastguard Worker
220*c8dee2aaSAndroid Build Coastguard Workerhalf4 sk_overdraw(half  alpha,  half4 color0, half4 color1, half4 color2,
221*c8dee2aaSAndroid Build Coastguard Worker                  half4 color3, half4 color4, half4 color5) {
222*c8dee2aaSAndroid Build Coastguard Worker   return alpha < (0.5 / 255.) ? color0
223*c8dee2aaSAndroid Build Coastguard Worker        : alpha < (1.5 / 255.) ? color1
224*c8dee2aaSAndroid Build Coastguard Worker        : alpha < (2.5 / 255.) ? color2
225*c8dee2aaSAndroid Build Coastguard Worker        : alpha < (3.5 / 255.) ? color3
226*c8dee2aaSAndroid Build Coastguard Worker        : alpha < (4.5 / 255.) ? color4
227*c8dee2aaSAndroid Build Coastguard Worker                               : color5;
228*c8dee2aaSAndroid Build Coastguard Worker}
229