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