| // Graphite-specific fragment shader code |
| |
| half4 sk_error() { |
| return half4(1.0, 0.0, 1.0, 1.0); |
| } |
| |
| half4 sk_solid_shader(float4 colorParam) { |
| return half4(colorParam); |
| } |
| |
| // The localMatrix is passed to the child by the glue code. This snippet just needs to bubble the |
| // child's output back up. 'localMatrix' is passed in to be consistent w/ the default glue code. |
| half4 sk_local_matrix_shader(float4x4 localMatrix, half4 childResult) { |
| return childResult; |
| } |
| |
| float $tile(int tm, float f, float min, float max, float normalizer) { |
| const int kClamp = 0; |
| const int kRepeat = 1; |
| const int kMirrorRepeat = 2; |
| const int kClampToBorder = 3; |
| |
| if (tm == kClamp) { |
| return clamp(f, min, max) / normalizer; |
| } else if (tm == kRepeat) { |
| float length = max - min; |
| return (mod(f - min, length) + min) / normalizer; |
| } else if (tm == kMirrorRepeat) { |
| float length = max - min; |
| float length2 = 2 * length; |
| float tmp = mod(f - min, length2); |
| return (mix(tmp, length2 - tmp, step(length, tmp)) + min) / normalizer; |
| } else { // kClampToBorder |
| // For now, just clamp. |
| return clamp(f, min, max) / normalizer; |
| } |
| } |
| |
| float2 sk_compute_coords(float4x4 dev2Local, |
| float4 subset, |
| int tmX, |
| int tmY, |
| int imgWidth, |
| int imgHeight) { |
| float4 localCoords = dev2Local * sk_FragCoord; |
| float2 coords = float2($tile(tmX, localCoords.x, subset.x, subset.z, float(imgWidth)), |
| $tile(tmY, localCoords.y, subset.y, subset.w, float(imgHeight))); |
| return coords; |
| } |
| |
| half4 sk_clamp_grad_4(float4 colorsParam[4], float offsetsParam[4], float t) { |
| float4 result = colorsParam[0]; |
| result = mix(result, colorsParam[1], |
| clamp((t-offsetsParam[0])/(offsetsParam[1]-offsetsParam[0]), |
| 0, 1)); |
| result = mix(result, colorsParam[2], |
| clamp((t-offsetsParam[1])/(offsetsParam[2]-offsetsParam[1]), |
| 0, 1)); |
| result = mix(result, colorsParam[3], |
| clamp((t-offsetsParam[2])/(offsetsParam[3]-offsetsParam[2]), |
| 0, 1)); |
| return half4(result); |
| } |
| |
| half4 sk_linear_grad_4_shader(float4x4 dev2Local, |
| float4 colorsParam[4], |
| float offsetsParam[4], |
| float2 point0Param, |
| float2 point1Param, |
| float radius0Param, |
| float radius1Param, |
| float2 padding) { |
| float2 pos = (dev2Local * sk_FragCoord).xy; |
| float2 delta = point1Param - point0Param; |
| float2 pt = pos - point0Param; |
| float t = dot(pt, delta) / dot(delta, delta); |
| return sk_clamp_grad_4(colorsParam, offsetsParam, t); |
| } |
| |
| half4 sk_radial_grad_4_shader(float4x4 dev2Local, |
| float4 colorsParam[4], |
| float offsetsParam[4], |
| float2 centerParam, |
| float radiusParam, |
| float padding) { |
| float2 pos = (dev2Local * sk_FragCoord).xy; |
| float2 pt = pos - centerParam; |
| float t = length(pt) / radiusParam; |
| return sk_clamp_grad_4(colorsParam, offsetsParam, t); |
| } |
| |
| half4 sk_sweep_grad_4_shader(float4x4 dev2Local, |
| float4 colorsParam[4], |
| float offsetsParam[4], |
| float2 centerParam, |
| float biasParam, |
| float scaleParam) { |
| float2 pos = (dev2Local * sk_FragCoord).xy; |
| float2 pt = pos - centerParam; |
| |
| // Some devices incorrectly implement atan2(y,x) as atan(y/x). In actuality it is |
| // atan2(y,x) = 2 * atan(y / (sqrt(x^2 + y^2) + x)). To work around this we pass in |
| // (sqrt(x^2 + y^2) + x) as the second parameter to atan2 in these cases. We let the device |
| // handle the undefined behavior if the second parameter is 0, instead of doing the divide |
| // ourselves and calling atan with the quotient. |
| float angle = sk_Caps.atan2ImplementedAsAtanYOverX ? 2 * atan(-pt.y, length(pt) - pt.x) |
| : atan(-pt.y, -pt.x); |
| |
| // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi] |
| float t = (angle * 0.1591549430918 + 0.5 + biasParam) * scaleParam; |
| return sk_clamp_grad_4(colorsParam, offsetsParam, t); |
| } |
| |
| half4 sk_blend(int blendMode, half4 src, half4 dst) { |
| const int kClear = 0; |
| const int kSrc = 1; |
| const int kDst = 2; |
| const int kSrcOver = 3; |
| const int kDstOver = 4; |
| const int kSrcIn = 5; |
| const int kDstIn = 6; |
| const int kSrcOut = 7; |
| const int kDstOut = 8; |
| const int kSrcATop = 9; |
| const int kDstATop = 10; |
| const int kXor = 11; |
| const int kPlus = 12; |
| const int kModulate = 13; |
| const int kScreen = 14; |
| const int kOverlay = 15; |
| const int kDarken = 16; |
| const int kLighten = 17; |
| const int kColorDodge = 18; |
| const int kColorBurn = 19; |
| const int kHardLight = 20; |
| const int kSoftLight = 21; |
| const int kDifference = 22; |
| const int kExclusion = 23; |
| const int kMultiply = 24; |
| const int kHue = 25; |
| const int kSaturation = 26; |
| const int kColor = 27; |
| const int kLuminosity = 28; |
| |
| switch (blendMode) { |
| case kClear: { return blend_clear(src, dst); } |
| case kSrc: { return blend_src(src, dst); } |
| case kDst: { return blend_dst(src, dst); } |
| case kSrcOver: { return blend_porter_duff(src, dst, half4(1, 0, 0, -1)); } |
| case kDstOver: { return blend_porter_duff(src, dst, half4(0, 1, -1, 0)); } |
| case kSrcIn: { return blend_porter_duff(src, dst, half4(0, 0, 1, 0)); } |
| case kDstIn: { return blend_porter_duff(src, dst, half4(0, 0, 0, 1)); } |
| case kSrcOut: { return blend_porter_duff(src, dst, half4(0, 0, -1, 0)); } |
| case kDstOut: { return blend_porter_duff(src, dst, half4(0, 0, 0, -1)); } |
| case kSrcATop: { return blend_porter_duff(src, dst, half4(0, 0, 1, -1)); } |
| case kDstATop: { return blend_porter_duff(src, dst, half4(0, 0, -1, 1)); } |
| case kXor: { return blend_porter_duff(src, dst, half4(0, 0, -1, -1)); } |
| case kPlus: { return blend_porter_duff(src, dst, half4(1, 1, 0, 0)); } |
| case kModulate: { return blend_modulate(src, dst); } |
| case kScreen: { return blend_screen(src, dst); } |
| case kOverlay: { return blend_overlay(src, dst, /*flip=*/0); } |
| case kDarken: { return blend_darken(src, dst, /*mode=*/1); } |
| case kLighten: { return blend_darken(src, dst, /*mode=*/-1); } |
| case kColorDodge: { return blend_color_dodge(src, dst); } |
| case kColorBurn: { return blend_color_burn(src, dst); } |
| case kHardLight: { return blend_overlay(src, dst, /*flip=*/1); } |
| case kSoftLight: { return blend_soft_light(src, dst); } |
| case kDifference: { return blend_difference(src, dst); } |
| case kExclusion: { return blend_exclusion(src, dst); } |
| case kMultiply: { return blend_multiply(src, dst); } |
| case kHue: { return blend_hslc(src, dst, /*flipSat=*/half2(0, 1)); } |
| case kSaturation: { return blend_hslc(src, dst, /*flipSat=*/half2(1)); } |
| case kColor: { return blend_hslc(src, dst, /*flipSat=*/half2(0)); } |
| case kLuminosity: { return blend_hslc(src, dst, /*flipSat=*/half2(1, 0)); } |
| default: return half4(0); // Avoids 'blend can exit without returning a value' error |
| } |
| } |
| |
| half4 sk_blend_shader(int blendMode, int pad0, int pad1, int pad2, half4 child0, half4 child1) { |
| return sk_blend(blendMode, child1, child0); |
| } |