blob: 4eaa73df6c2dc15f43635b079666daeb07af36da [file] [log] [blame]
R"SKSL(
// Definitions of functions implementing all of the SkBlendMode blends.
half4 blend_clear(half4 src, half4 dst) { return half4(0); }
half4 blend_src(half4 src, half4 dst) { return src; }
half4 blend_dst(half4 src, half4 dst) { return dst; }
half4 blend_src_over(half4 src, half4 dst) { return src + (1 - src.a)*dst; }
half4 blend_dst_over(half4 src, half4 dst) { return (1 - dst.a)*src + dst; }
half4 blend_src_in(half4 src, half4 dst) {
@if (sk_Caps.inBlendModesFailRandomlyForAllZeroVec) {
if (src == half4(0)) {
return half4(0);
}
}
return src*dst.a;
}
half4 blend_dst_in(half4 src, half4 dst) { return blend_src_in(dst, src); }
half4 blend_src_out(half4 src, half4 dst) { return (1 - dst.a)*src; }
half4 blend_dst_out(half4 src, half4 dst) { return (1 - src.a)*dst; }
half4 blend_src_atop(half4 src, half4 dst) { return dst.a*src + (1 - src.a)*dst; }
half4 blend_dst_atop(half4 src, half4 dst) { return (1 - dst.a) * src + src.a*dst; }
half4 blend_xor(half4 src, half4 dst) { return (1 - dst.a)*src + (1 - src.a)*dst; }
half4 blend_plus(half4 src, half4 dst) { return min(src + dst, 1); }
half4 blend_modulate(half4 src, half4 dst) { return src*dst; }
half4 blend_screen(half4 src, half4 dst) { return src + (1 - src)*dst; }
half _blend_overlay_component(half sc, half sa, half dc, half da) {
if (2*dc <= da) {
return 2*sc*dc;
}
return sa*da - 2*(da - dc)*(sa - sc);
}
half4 blend_overlay(half4 src, half4 dst) {
half4 result = half4(_blend_overlay_component(src.r, src.a, dst.r, dst.a),
_blend_overlay_component(src.g, src.a, dst.g, dst.a),
_blend_overlay_component(src.b, src.a, dst.b, dst.a),
src.a + (1 - src.a)*dst.a);
result.rgb += dst.rgb*(1 - src.a) + src.rgb*(1 - dst.a);
return result;
}
half4 blend_darken(half4 src, half4 dst) {
half4 result = blend_src_over(src, dst);
result.rgb = min(result.rgb, (1 - dst.a)*src.rgb + dst.rgb);
return result;
}
half4 blend_lighten(half4 src, half4 dst) {
half4 result = blend_src_over(src, dst);
result.rgb = max(result.rgb, (1 - dst.a)*src.rgb + dst.rgb);
return result;
}
half _guarded_divide(half n, half d) {
@if (sk_Caps.mustGuardDivisionEvenAfterExplicitZeroCheck) {
return n/(d + 0.00000001);
} else {
return n/d;
}
}
half _color_dodge_component(half sc, half sa, half dc, half da) {
if (dc == 0) {
return sc*(1 - da);
} else {
half d = sa - sc;
if (d == 0) {
return sa*da + sc*(1 - da) + dc*(1 - sa);
}
d = min(da, _guarded_divide(dc*sa, d));
return d*sa + sc*(1 - da) + dc*(1 - sa);
}
}
half4 blend_color_dodge(half4 src, half4 dst) {
return half4(_color_dodge_component(src.r, src.a, dst.r, dst.a),
_color_dodge_component(src.g, src.a, dst.g, dst.a),
_color_dodge_component(src.b, src.a, dst.b, dst.a),
src.a + (1 - src.a)*dst.a);
}
half _color_burn_component(half sc, half sa, half dc, half da) {
if (da == dc) {
return sa*da + sc*(1 - da) + dc*(1 - sa);
} else if (sc == 0) {
return dc*(1 - sa);
}
half d = max(0, da - _guarded_divide((da - dc)*sa, sc));
return d*sa + sc*(1 - da) + dc*(1 - sa);
}
half4 blend_color_burn(half4 src, half4 dst) {
return half4(_color_burn_component(src.r, src.a, dst.r, dst.a),
_color_burn_component(src.g, src.a, dst.g, dst.a),
_color_burn_component(src.b, src.a, dst.b, dst.a),
src.a + (1 - src.a)*dst.a);
}
half4 blend_hard_light(half4 src, half4 dst) { return blend_overlay(dst, src); }
half _soft_light_component(half sc, half sa, half dc, half da) {
if (2*sc <= sa) {
return _guarded_divide(dc*dc*(sa - 2*sc), da) + (1 - da)*sc + dc*(-sa + 2*sc + 1);
} else if (4.0 * dc <= da) {
half DSqd = dc*dc;
half DCub = DSqd*dc;
half DaSqd = da*da;
half DaCub = DaSqd*da;
return _guarded_divide(DaSqd*(sc - dc*(3*sa - 6*sc - 1)) + 12*da*DSqd*(sa - 2*sc)
- 16*DCub * (sa - 2*sc) - DaCub*sc, DaSqd);
}
return dc*(sa - 2*sc + 1) + sc - sqrt(da*dc)*(sa - 2*sc) - da*sc;
}
half4 blend_soft_light(half4 src, half4 dst) {
if (dst.a == 0) {
return src;
}
return half4(_soft_light_component(src.r, src.a, dst.r, dst.a),
_soft_light_component(src.g, src.a, dst.g, dst.a),
_soft_light_component(src.b, src.a, dst.b, dst.a),
src.a + (1 - src.a)*dst.a);
}
half4 blend_difference(half4 src, half4 dst) {
return half4(src.rgb + dst.rgb - 2*min(src.rgb*dst.a, dst.rgb*src.a),
src.a + (1 - src.a)*dst.a);
}
half4 blend_exclusion(half4 src, half4 dst) {
return half4(dst.rgb + src.rgb - 2*dst.rgb*src.rgb, src.a + (1 - src.a)*dst.a);
}
half4 blend_multiply(half4 src, half4 dst) {
return half4((1 - src.a)*dst.rgb + (1 - dst.a)*src.rgb + src.rgb*dst.rgb,
src.a + (1 - src.a)*dst.a);
}
half _blend_color_luminance(half3 color) { return dot(half3(0.3, 0.59, 0.11), color); }
half3 _blend_set_color_luminance(half3 hueSatColor, half alpha, half3 lumColor) {
half lum = _blend_color_luminance(lumColor);
half3 result = lum - _blend_color_luminance(hueSatColor) + hueSatColor;
half minComp = min(min(result.r, result.g), result.b);
half maxComp = max(max(result.r, result.g), result.b);
if (minComp < 0 && lum != minComp) {
result = lum + (result - lum) * lum/(lum - minComp);
}
if (maxComp > alpha && maxComp != lum) {
return lum + ((result - lum) * (alpha - lum))/(maxComp - lum);
}
return result;
}
half _blend_color_saturation(half3 color) {
return max(max(color.r, color.g), color.b) - min(min(color.r, color.g), color.b);
}
half3 _blend_set_color_saturation_helper(half3 minMidMax, half sat) {
if (minMidMax.r < minMidMax.b) {
return half3(0, sat*(minMidMax.g - minMidMax.r)/(minMidMax.b - minMidMax.r), sat);
}
return half3(0);
}
half3 _blend_set_color_saturation(half3 hueLumColor, half3 satColor) {
half sat = _blend_color_saturation(satColor);
if (hueLumColor.r <= hueLumColor.g) {
if (hueLumColor.g <= hueLumColor.b) {
hueLumColor.rgb = _blend_set_color_saturation_helper(hueLumColor.rgb, sat);
} else if (hueLumColor.r <= hueLumColor.b) {
hueLumColor.rbg = _blend_set_color_saturation_helper(hueLumColor.rbg, sat);
} else {
hueLumColor.brg = _blend_set_color_saturation_helper(hueLumColor.brg, sat);
}
} else if (hueLumColor.r <= hueLumColor.b) {
hueLumColor.grb = _blend_set_color_saturation_helper(hueLumColor.grb, sat);
} else if (hueLumColor.g <= hueLumColor.b) {
hueLumColor.gbr = _blend_set_color_saturation_helper(hueLumColor.gbr, sat);
} else {
hueLumColor.bgr = _blend_set_color_saturation_helper(hueLumColor.bgr, sat);
}
return hueLumColor;
}
half4 blend_hue(half4 src, half4 dst) {
half alpha = dst.a*src.a;
half3 sda = src.rgb*dst.a;
half3 dsa = dst.rgb*src.a;
return half4(_blend_set_color_luminance(_blend_set_color_saturation(sda, dsa), alpha, dsa) +
dst.rgb - dsa + src.rgb - sda,
src.a + dst.a - alpha);
}
half4 blend_saturation(half4 src, half4 dst) {
half alpha = dst.a*src.a;
half3 sda = src.rgb*dst.a;
half3 dsa = dst.rgb*src.a;
return half4(_blend_set_color_luminance(_blend_set_color_saturation(dsa, sda), alpha, dsa) +
dst.rgb - dsa + src.rgb - sda,
src.a + dst.a - alpha);
}
half4 blend_color(half4 src, half4 dst) {
half alpha = dst.a*src.a;
half3 sda = src.rgb*dst.a;
half3 dsa = dst.rgb*src.a;
return half4(_blend_set_color_luminance(sda, alpha, dsa) + dst.rgb - dsa + src.rgb - sda,
src.a + dst.a - alpha);
}
half4 blend_luminosity(half4 src, half4 dst) {
half alpha = dst.a*src.a;
half3 sda = src.rgb*dst.a;
half3 dsa = dst.rgb*src.a;
return half4(_blend_set_color_luminance(dsa, alpha, sda) + dst.rgb - dsa + src.rgb - sda,
src.a + dst.a - alpha);
}
enum class SkBlendMode {
kClear = 0,
kSrc = 1,
kDst = 2,
kSrcOver = 3,
kDstOver = 4,
kSrcIn = 5,
kDstIn = 6,
kSrcOut = 7,
kDstOut = 8,
kSrcATop = 9,
kDstATop = 10,
kXor = 11,
kPlus = 12,
kModulate = 13,
kScreen = 14,
kOverlay = 15,
kDarken = 16,
kLighten = 17,
kColorDodge = 18,
kColorBurn = 19,
kHardLight = 20,
kSoftLight = 21,
kDifference = 22,
kExclusion = 23,
kMultiply = 24,
kHue = 25,
kSaturation = 26,
kColor = 27,
kLuminosity = 28
};
half4 blend(SkBlendMode mode, half4 src, half4 dst) {
switch (mode) {
case SkBlendMode::kClear: return blend_clear(src, dst);
case SkBlendMode::kSrc: return blend_src(src, dst);
case SkBlendMode::kDst: return blend_dst(src, dst);
case SkBlendMode::kSrcOver: return blend_src_over(src, dst);
case SkBlendMode::kDstOver: return blend_dst_over(src, dst);
case SkBlendMode::kSrcIn: return blend_src_in(src, dst);
case SkBlendMode::kDstIn: return blend_dst_in(src, dst);
case SkBlendMode::kSrcOut: return blend_src_out(src, dst);
case SkBlendMode::kDstOut: return blend_dst_out(src, dst);
case SkBlendMode::kSrcATop: return blend_src_atop(src, dst);
case SkBlendMode::kDstATop: return blend_dst_atop(src, dst);
case SkBlendMode::kXor: return blend_xor(src, dst);
case SkBlendMode::kPlus: return blend_plus(src, dst);
case SkBlendMode::kModulate: return blend_modulate(src, dst);
case SkBlendMode::kScreen: return blend_screen(src, dst);
case SkBlendMode::kOverlay: return blend_overlay(src, dst);
case SkBlendMode::kDarken: return blend_darken(src, dst);
case SkBlendMode::kLighten: return blend_lighten(src, dst);
case SkBlendMode::kColorDodge: return blend_color_dodge(src, dst);
case SkBlendMode::kColorBurn: return blend_color_burn(src, dst);
case SkBlendMode::kHardLight: return blend_hard_light(src, dst);
case SkBlendMode::kSoftLight: return blend_soft_light(src, dst);
case SkBlendMode::kDifference: return blend_difference(src, dst);
case SkBlendMode::kExclusion: return blend_exclusion(src, dst);
case SkBlendMode::kMultiply: return blend_multiply(src, dst);
case SkBlendMode::kHue: return blend_hue(src, dst);
case SkBlendMode::kSaturation: return blend_saturation(src, dst);
case SkBlendMode::kColor: return blend_color(src, dst);
case SkBlendMode::kLuminosity: return blend_luminosity(src, dst);
}
return half4(0); // Avoids "'blend' can exit without returning a value."
}
// The max() guards against division by zero when the incoming color is transparent black
half4 unpremul(half4 color) { return half4(color.rgb / max(color.a, 0.0001), color.a); }
float4 unpremul_float(float4 color) { return float4(color.rgb / max(color.a, 0.0001), color.a); }
float2 proj(float3 p) { return p.xy / p.z; }
)SKSL"