blob: e5c16226b99e5ed63069b8bd4d5db5a07790e253 [file] [log] [blame]
// 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);
}