blob: 8a1ed83b7b8b741dee8c74ee1b5ef64f6b277491 [file] [log] [blame]
// Graphite-specific fragment shader code
const int $kTileModeClamp = 0;
const int $kTileModeRepeat = 1;
const int $kTileModeMirror = 2;
const int $kTileModeDecal = 3;
const int $kFilterModeNearest = 0;
const int $kFilterModeLinear = 1;
const int $kTFTypeSRGB = 1;
const int $kTFTypePQ = 2;
const int $kTFTypeHLG = 3;
const int $kTFTypeHLGinv = 4;
const int $kColorSpaceXformFlagUnpremul = 0x1;
const int $kColorSpaceXformFlagLinearize = 0x2;
const int $kColorSpaceXformFlagGamutTransform = 0x4;
const int $kColorSpaceXformFlagEncode = 0x8;
const int $kColorSpaceXformFlagPremul = 0x10;
$pure half4 sk_error() {
return half4(1.0, 0.0, 1.0, 1.0);
}
$pure half4 sk_passthrough(half4 color) {
return color;
}
$pure half4 sk_solid_shader(float4 colorParam) {
return half4(colorParam);
}
$pure half $apply_xfer_fn(int kind, half x, half cs[7]) {
half G = cs[0], A = cs[1], B = cs[2], C = cs[3], D = cs[4], E = cs[5], F = cs[6];
half s = sign(x);
x = abs(x);
switch (kind) {
case $kTFTypeSRGB:
x = (x < D) ? (C * x) + F
: pow(A * x + B, G) + E;
break;
case $kTFTypePQ:
x = pow(max(A + B * pow(x, C), 0) / (D + E * pow(x, C)), F);
break;
case $kTFTypeHLG:
x = (x * A <= 1) ? pow(x * A, B)
: exp((x - E) * C) + D;
x *= (F + 1);
break;
case $kTFTypeHLGinv:
x /= (F + 1);
x = (x <= 1) ? A * pow(x, B)
: C * log(x - D) + E;
break;
}
return s * x;
}
// TODO(b/239548614) need to plumb Graphite equivalent of fColorSpaceMathNeedsFloat.
// This would change 'color' from half4 to float4
$pure half4 sk_color_space_transform(half4 color,
int flags,
int srcKind,
int dstKind,
half srcCoeffs[7],
half dstCoeffs[7],
half3x3 gamutTransform) {
if (bool(flags & $kColorSpaceXformFlagUnpremul)) {
color = unpremul(color);
}
if (bool(flags & $kColorSpaceXformFlagLinearize)) {
color.r = $apply_xfer_fn(srcKind, color.r, srcCoeffs);
color.g = $apply_xfer_fn(srcKind, color.g, srcCoeffs);
color.b = $apply_xfer_fn(srcKind, color.b, srcCoeffs);
}
if (bool(flags & $kColorSpaceXformFlagGamutTransform)) {
color.rgb = gamutTransform * color.rgb;
}
if (bool(flags & $kColorSpaceXformFlagEncode)) {
color.r = $apply_xfer_fn(dstKind, color.r, dstCoeffs);
color.g = $apply_xfer_fn(dstKind, color.g, dstCoeffs);
color.b = $apply_xfer_fn(dstKind, color.b, dstCoeffs);
}
if (bool(flags & $kColorSpaceXformFlagPremul)) {
color.rgb *= color.a;
}
return color;
}
$pure float $tile(int tileMode, float f, float low, float high) {
switch (tileMode) {
case $kTileModeClamp:
return clamp(f, low, high);
case $kTileModeRepeat: {
float length = high - low;
return (mod(f - low, length) + low);
}
case $kTileModeMirror: {
float length = high - low;
float length2 = 2 * length;
float tmp = mod(f - low, length2);
return (mix(tmp, length2 - tmp, step(length, tmp)) + low);
}
default: // $kTileModeDecal
// Decal is handled later.
return f;
}
}
$pure half4 $sample_image(float2 pos,
float2 imgSize,
float4 subset,
int tileModeX,
int tileModeY,
int filterMode,
sampler2D s) {
// Do hard-edge shader transitions to the border color for nearest-neighbor decal tiling at the
// subset boundaries. Snap the input coordinates to nearest neighbor before comparing to the
// subset rect, to avoid GPU interpolation errors. See https://crbug.com/skia/10403.
if (tileModeX == $kTileModeDecal && filterMode == $kFilterModeNearest) {
float snappedX = floor(pos.x) + 0.5;
if (snappedX < subset.x || snappedX > subset.z) {
return half4(0);
}
}
if (tileModeY == $kTileModeDecal && filterMode == $kFilterModeNearest) {
float snappedY = floor(pos.y) + 0.5;
if (snappedY < subset.y || snappedY > subset.w) {
return half4(0);
}
}
pos.x = $tile(tileModeX, pos.x, subset.x, subset.z);
pos.y = $tile(tileModeY, pos.y, subset.y, subset.w);
// Clamp to an inset subset to prevent sampling neighboring texels when coords fall exactly at
// texel boundaries.
float4 insetClamp;
if (filterMode == $kFilterModeNearest) {
insetClamp = float4(floor(subset.xy) + 0.5, ceil(subset.zw) - 0.5);
} else {
insetClamp = float4(subset.xy + 0.5, subset.zw - 0.5);
}
float2 clampedPos = clamp(pos, insetClamp.xy, insetClamp.zw);
half4 color = sample(s, clampedPos / imgSize);
if (filterMode == $kFilterModeLinear) {
// Remember the amount the coord moved for clamping. This is used to implement shader-based
// filtering for repeat and decal tiling.
half2 error = half2(pos - clampedPos);
half2 absError = abs(error);
// Do 1 or 3 more texture reads depending on whether both x and y tiling modes are repeat
// and whether we're near a single subset edge or a corner. Then blend the multiple reads
// using the error values calculated above.
bool sampleExtraX = tileModeX == $kTileModeRepeat;
bool sampleExtraY = tileModeY == $kTileModeRepeat;
if (sampleExtraX || sampleExtraY) {
float extraCoordX;
float extraCoordY;
half4 extraColorX;
half4 extraColorY;
if (sampleExtraX) {
extraCoordX = error.x > 0 ? insetClamp.x : insetClamp.z;
extraColorX = sample(s, float2(extraCoordX, clampedPos.y) / imgSize);
}
if (sampleExtraY) {
extraCoordY = error.y > 0 ? insetClamp.y : insetClamp.w;
extraColorY = sample(s, float2(clampedPos.x, extraCoordY) / imgSize);
}
if (sampleExtraX && sampleExtraY) {
half4 extraColorXY = sample(s, float2(extraCoordX, extraCoordY) / imgSize);
color = mix(mix(color, extraColorX, absError.x),
mix(extraColorY, extraColorXY, absError.x),
absError.y);
} else if (sampleExtraX) {
color = mix(color, extraColorX, absError.x);
} else if (sampleExtraY) {
color = mix(color, extraColorY, absError.y);
}
}
// Do soft edge shader filtering for decal tiling and linear filtering using the error
// values calculated above.
if (tileModeX == $kTileModeDecal) {
color *= max(1 - absError.x, 0);
}
if (tileModeY == $kTileModeDecal) {
color *= max(1 - absError.y, 0);
}
}
return color;
}
$pure half4 $cubic_filter_image(float2 pos,
float2 imgSize,
float4 subset,
int tileModeX,
int tileModeY,
float4x4 coeffs,
sampler2D s) {
// Determine pos's fractional offset f between texel centers.
float2 f = fract(pos - 0.5);
// Sample 16 points at 1-pixel intervals from [p - 1.5 ... p + 1.5].
pos -= 1.5;
// Snap to texel centers to prevent sampling neighboring texels.
pos = floor(pos) + 0.5;
float4 wx = coeffs * float4(1.0, f.x, f.x * f.x, f.x * f.x * f.x);
float4 wy = coeffs * float4(1.0, f.y, f.y * f.y, f.y * f.y * f.y);
float4 color = float4(0);
for (int y = 0; y < 4; ++y) {
float4 rowColor = float4(0);
for (int x = 0; x < 4; ++x) {
rowColor += wx[x] * $sample_image(pos + float2(x, y), imgSize, subset,
tileModeX, tileModeY, $kFilterModeNearest, s);
}
color += wy[y] * rowColor;
}
return half4(color);
}
$pure half4 sk_image_shader(float2 coords,
float2 imgSize,
float4 subset,
int tileModeX,
int tileModeY,
int filterMode,
int useCubic,
float4x4 cubicCoeffs,
int csXformFlags,
int csXformSrcKind,
int csXformDstKind,
half csXformSrcCoeffs[7],
half csXformDstCoeffs[7],
half3x3 csXformGamutTransform,
sampler2D s) {
half4 sampleColor = (useCubic != 0)
? $cubic_filter_image(coords, imgSize, subset, tileModeX, tileModeY, cubicCoeffs, s)
: $sample_image(coords, imgSize, subset, tileModeX, tileModeY, filterMode, s);
return sk_color_space_transform(sampleColor, csXformFlags, csXformSrcKind, csXformDstKind,
csXformSrcCoeffs, csXformDstCoeffs, csXformGamutTransform);
}
$pure float2 $tile_grad(int tileMode, float2 t) {
switch (tileMode) {
case $kTileModeClamp:
t.x = clamp(t.x, 0, 1);
break;
case $kTileModeRepeat:
t.x = fract(t.x);
break;
case $kTileModeMirror: {
float t_1 = t.x - 1;
t.x = t_1 - 2 * floor(t_1 * 0.5) - 1;
if (sk_Caps.mustDoOpBetweenFloorAndAbs) {
// At this point the expected value of tiled_t should between -1 and 1, so this
// clamp has no effect other than to break up the floor and abs calls and make sure
// the compiler doesn't merge them back together.
t.x = clamp(t.x, -1, 1);
}
t.x = abs(t.x);
break;
}
case $kTileModeDecal:
if (t.x < 0 || t.x > 1) {
return float2(0, -1);
}
break;
}
return t;
}
$pure half4 $colorize_grad_4(float4 colorsParam[4], float offsetsParam[4], float2 t) {
if (t.y < 0) {
return half4(0);
} else if (t.x <= offsetsParam[0]) {
return half4(colorsParam[0]);
} else if (t.x < offsetsParam[1]) {
return half4(mix(colorsParam[0], colorsParam[1], (t.x - offsetsParam[0]) /
(offsetsParam[1] - offsetsParam[0])));
} else if (t.x < offsetsParam[2]) {
return half4(mix(colorsParam[1], colorsParam[2], (t.x - offsetsParam[1]) /
(offsetsParam[2] - offsetsParam[1])));
} else if (t.x < offsetsParam[3]) {
return half4(mix(colorsParam[2], colorsParam[3], (t.x - offsetsParam[2]) /
(offsetsParam[3] - offsetsParam[2])));
} else {
return half4(colorsParam[3]);
}
}
$pure half4 $colorize_grad_8(float4 colorsParam[8], float offsetsParam[8], float2 t) {
if (t.y < 0) {
return half4(0);
// Unrolled binary search through intervals
// ( .. 0), (0 .. 1), (1 .. 2), (2 .. 3), (3 .. 4), (4 .. 5), (5 .. 6), (6 .. 7), (7 .. ).
} else if (t.x < offsetsParam[4]) {
if (t.x < offsetsParam[2]) {
if (t.x <= offsetsParam[0]) {
return half4(colorsParam[0]);
} else if (t.x < offsetsParam[1]) {
return half4(mix(colorsParam[0], colorsParam[1],
(t.x - offsetsParam[0]) /
(offsetsParam[1] - offsetsParam[0])));
} else {
return half4(mix(colorsParam[1], colorsParam[2],
(t.x - offsetsParam[1]) /
(offsetsParam[2] - offsetsParam[1])));
}
} else {
if (t.x < offsetsParam[3]) {
return half4(mix(colorsParam[2], colorsParam[3],
(t.x - offsetsParam[2]) /
(offsetsParam[3] - offsetsParam[2])));
} else {
return half4(mix(colorsParam[3], colorsParam[4],
(t.x - offsetsParam[3]) /
(offsetsParam[4] - offsetsParam[3])));
}
}
} else {
if (t.x < offsetsParam[6]) {
if (t.x < offsetsParam[5]) {
return half4(mix(colorsParam[4], colorsParam[5],
(t.x - offsetsParam[4]) /
(offsetsParam[5] - offsetsParam[4])));
} else {
return half4(mix(colorsParam[5], colorsParam[6],
(t.x - offsetsParam[5]) /
(offsetsParam[6] - offsetsParam[5])));
}
} else {
if (t.x < offsetsParam[7]) {
return half4(mix(colorsParam[6], colorsParam[7],
(t.x - offsetsParam[6]) /
(offsetsParam[7] - offsetsParam[6])));
} else {
return half4(colorsParam[7]);
}
}
}
}
$pure float2 $linear_grad_layout(float2 point0Param, float2 point1Param, float2 pos) {
pos -= point0Param;
float2 delta = point1Param - point0Param;
float t = dot(pos, delta) / dot(delta, delta);
return float2(t, 1);
}
$pure float2 $radial_grad_layout(float2 centerParam, float radiusParam, float2 pos) {
float t = distance(pos, centerParam) / radiusParam;
return float2(t, 1);
}
$pure float2 $sweep_grad_layout(float2 centerParam, float biasParam, float scaleParam, float2 pos) {
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(-pos.y, length(pos) - pos.x)
: atan(-pos.y, -pos.x);
// 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi]
float t = (angle * 0.1591549430918 + 0.5 + biasParam) * scaleParam;
return float2(t, 1);
}
$pure float3x3 $map_to_unit_x(float2 p0, float2 p1) {
// Returns a matrix that maps [p0, p1] to [(0, 0), (1, 0)]. Results are undefined if p0 = p1.
// From skia/src/core/SkMatrix.cpp, SkMatrix::setPolyToPoly.
return float3x3(
0, -1, 0,
1, 0, 0,
0, 0, 1
) * inverse(float3x3(
p1.y - p0.y, p0.x - p1.x, 0,
p1.x - p0.x, p1.y - p0.y, 0,
p0.x, p0.y, 1
));
}
$pure float2 $conical_grad_layout(float2 point0Param,
float2 point1Param,
float radius0Param,
float radius1Param,
float2 pos) {
const float SK_ScalarNearlyZero = 1.0 / (1 << 12);
float dCenter = distance(point0Param, point1Param);
float dRadius = radius1Param - radius0Param;
// Degenerate case: a radial gradient (p0 = p1).
bool radial = dCenter < SK_ScalarNearlyZero;
// Degenerate case: a strip with bandwidth 2r (r0 = r1).
bool strip = abs(dRadius) < SK_ScalarNearlyZero;
if (radial) {
if (strip) {
// The start and end inputs are the same in both position and radius.
// We don't expect to see this input, but just in case we avoid dividing by zero.
return float2(0, -1);
}
float scale = 1 / dRadius;
float scaleSign = sign(dRadius);
float bias = radius0Param / dRadius;
float2 pt = (pos - point0Param) * scale;
float t = length(pt) * scaleSign - bias;
return float2(t, 1);
} else if (strip) {
float3x3 transform = $map_to_unit_x(point0Param, point1Param);
float r = radius0Param / dCenter;
float r_2 = r * r;
float2 pt = (transform * pos.xy1).xy;
float t = r_2 - pt.y * pt.y;
if (t < 0) {
return float2(0, -1);
}
t = pt.x + sqrt(t);
return float2(t, 1);
} else {
// See https://skia.org/docs/dev/design/conical/ for details on how this algorithm works.
// Calculate f and swap inputs if necessary (steps 1 and 2).
float f = radius0Param / (radius0Param - radius1Param);
bool isSwapped = abs(f - 1) < SK_ScalarNearlyZero;
if (isSwapped) {
float2 tmpPt = point0Param;
point0Param = point1Param;
point1Param = tmpPt;
f = 0;
}
// Apply mapping from [Cf, C1] to unit x, and apply the precalculations from steps 3 and 4,
// all in the same transformation.
float2 Cf = point0Param * (1 - f) + point1Param * f;
float3x3 transform = $map_to_unit_x(Cf, point1Param);
float scaleX = abs(1 - f);
float scaleY = scaleX;
float r1 = abs(radius1Param - radius0Param) / dCenter;
bool isFocalOnCircle = abs(r1 - 1) < SK_ScalarNearlyZero;
if (isFocalOnCircle) {
scaleX *= 0.5;
scaleY *= 0.5;
} else {
scaleX *= r1 / (r1 * r1 - 1);
scaleY /= sqrt(abs(r1 * r1 - 1));
}
transform = float3x3(
scaleX, 0, 0,
0, scaleY, 0,
0, 0, 1
) * transform;
float2 pt = (transform * pos.xy1).xy;
// Continue with step 5 onward.
float invR1 = 1 / r1;
float dRadiusSign = sign(1 - f);
bool isWellBehaved = !isFocalOnCircle && r1 > 1;
float x_t = -1;
if (isFocalOnCircle) {
x_t = dot(pt, pt) / pt.x;
} else if (isWellBehaved) {
x_t = length(pt) - pt.x * invR1;
} else {
float temp = pt.x * pt.x - pt.y * pt.y;
if (temp >= 0) {
if (isSwapped || dRadiusSign < 0) {
x_t = -sqrt(temp) - pt.x * invR1;
} else {
x_t = sqrt(temp) - pt.x * invR1;
}
}
}
if (!isWellBehaved && x_t < 0) {
return float2(0, -1);
}
float t = f + dRadiusSign * x_t;
if (isSwapped) {
t = 1 - t;
}
return float2(t, 1);
}
}
$pure half4 sk_linear_grad_4_shader(float2 coords,
float4 colorsParam[4],
float offsetsParam[4],
float2 point0Param,
float2 point1Param,
int tileMode) {
float2 t = $linear_grad_layout(point0Param, point1Param, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_4(colorsParam, offsetsParam, t);
}
$pure half4 sk_linear_grad_8_shader(float2 coords,
float4 colorsParam[8],
float offsetsParam[8],
float2 point0Param,
float2 point1Param,
int tileMode) {
float2 t = $linear_grad_layout(point0Param, point1Param, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_8(colorsParam, offsetsParam, t);
}
$pure half4 sk_radial_grad_4_shader(float2 coords,
float4 colorsParam[4],
float offsetsParam[4],
float2 centerParam,
float radiusParam,
int tileMode) {
float2 t = $radial_grad_layout(centerParam, radiusParam, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_4(colorsParam, offsetsParam, t);
}
$pure half4 sk_radial_grad_8_shader(float2 coords,
float4 colorsParam[8],
float offsetsParam[8],
float2 centerParam,
float radiusParam,
int tileMode) {
float2 t = $radial_grad_layout(centerParam, radiusParam, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_8(colorsParam, offsetsParam, t);
}
$pure half4 sk_sweep_grad_4_shader(float2 coords,
float4 colorsParam[4],
float offsetsParam[4],
float2 centerParam,
float biasParam,
float scaleParam,
int tileMode) {
float2 t = $sweep_grad_layout(centerParam, biasParam, scaleParam, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_4(colorsParam, offsetsParam, t);
}
$pure half4 sk_sweep_grad_8_shader(float2 coords,
float4 colorsParam[8],
float offsetsParam[8],
float2 centerParam,
float biasParam,
float scaleParam,
int tileMode) {
float2 t = $sweep_grad_layout(centerParam, biasParam, scaleParam, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_8(colorsParam, offsetsParam, t);
}
$pure half4 sk_conical_grad_4_shader(float2 coords,
float4 colorsParam[4],
float offsetsParam[4],
float2 point0Param,
float2 point1Param,
float radius0Param,
float radius1Param,
int tileMode) {
float2 t = $conical_grad_layout(point0Param, point1Param, radius0Param, radius1Param, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_4(colorsParam, offsetsParam, t);
}
$pure half4 sk_conical_grad_8_shader(float2 coords,
float4 colorsParam[8],
float offsetsParam[8],
float2 point0Param,
float2 point1Param,
float radius0Param,
float radius1Param,
int tileMode) {
float2 t = $conical_grad_layout(point0Param, point1Param, radius0Param, radius1Param, coords);
t = $tile_grad(tileMode, t);
return $colorize_grad_8(colorsParam, offsetsParam, t);
}
$pure half4 sk_matrix_colorfilter(half4 colorIn, float4x4 m, float4 v, int inHSLA) {
if (bool(inHSLA)) {
colorIn = $rgb_to_hsl(colorIn.rgb, colorIn.a); // includes unpremul
} else {
colorIn = unpremul(colorIn);
}
half4 colorOut = half4((m * colorIn) + v);
if (bool(inHSLA)) {
colorOut = $hsl_to_rgb(colorOut.rgb, colorOut.a); // includes clamp and premul
} else {
colorOut = saturate(colorOut);
colorOut.rgb *= colorOut.a;
}
return colorOut;
}
$pure 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(half4(1, 0, 0, -1), src, dst); }
case kDstOver: { return blend_porter_duff(half4(0, 1, -1, 0), src, dst); }
case kSrcIn: { return blend_porter_duff(half4(0, 0, 1, 0), src, dst); }
case kDstIn: { return blend_porter_duff(half4(0, 0, 0, 1), src, dst); }
case kSrcOut: { return blend_porter_duff(half4(0, 0, -1, 0), src, dst); }
case kDstOut: { return blend_porter_duff(half4(0, 0, 0, -1), src, dst); }
case kSrcATop: { return blend_porter_duff(half4(0, 0, 1, -1), src, dst); }
case kDstATop: { return blend_porter_duff(half4(0, 0, -1, 1), src, dst); }
case kXor: { return blend_porter_duff(half4(0, 0, -1, -1), src, dst); }
case kPlus: { return blend_porter_duff(half4(1, 1, 0, 0), src, dst); }
case kModulate: { return blend_modulate(src, dst); }
case kScreen: { return blend_screen(src, dst); }
case kOverlay: { return blend_overlay(/*flip=*/0, src, dst); }
case kDarken: { return blend_darken(/*mode=*/1, src, dst); }
case kLighten: { return blend_darken(/*mode=*/-1, src, dst); }
case kColorDodge: { return blend_color_dodge(src, dst); }
case kColorBurn: { return blend_color_burn(src, dst); }
case kHardLight: { return blend_overlay(/*flip=*/1, src, dst); }
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(/*flipSat=*/half2(0, 1), src, dst); }
case kSaturation: { return blend_hslc(/*flipSat=*/half2(1), src, dst); }
case kColor: { return blend_hslc(/*flipSat=*/half2(0), src, dst); }
case kLuminosity: { return blend_hslc(/*flipSat=*/half2(1, 0), src, dst); }
default: return half4(0); // Avoids 'blend can exit without returning a value' error
}
}
$pure half4 sk_blend_shader(int blendMode, half4 dst, half4 src) {
return sk_blend(blendMode, src, dst);
}
$pure half4 porter_duff_blend_shader(half4 blendOp, half4 dst, half4 src) {
return blend_porter_duff(blendOp, src, dst);
}
$pure half4 sk_blend_colorfilter(half4 dstColor, int blendMode, float4 srcColor) {
return sk_blend(blendMode, half4(srcColor), dstColor);
}
$pure half4 sk_table_colorfilter(half4 inColor, sampler2D s) {
half4 coords = unpremul(inColor) * 255.0/256.0 + 0.5/256.0;
half4 color = half4(sample(s, half2(coords.r, 3.0/8.0)).r,
sample(s, half2(coords.g, 5.0/8.0)).r,
sample(s, half2(coords.b, 7.0/8.0)).r,
1);
return color * sample(s, half2(coords.a, 1.0/8.0)).r;
}
$pure half4 sk_gaussian_colorfilter(half4 inColor) {
half factor = 1 - inColor.a;
factor = exp(-factor * factor * 4) - 0.018;
return half4(factor);
}