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