2022-03-01 04:38:14 +11:00
|
|
|
// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
|
|
|
|
|
|
|
|
// Mode definitions and functions for blending and composition.
|
|
|
|
|
|
|
|
#define Blend_Normal 0
|
|
|
|
#define Blend_Multiply 1
|
|
|
|
#define Blend_Screen 2
|
|
|
|
#define Blend_Overlay 3
|
|
|
|
#define Blend_Darken 4
|
|
|
|
#define Blend_Lighten 5
|
|
|
|
#define Blend_ColorDodge 6
|
|
|
|
#define Blend_ColorBurn 7
|
|
|
|
#define Blend_HardLight 8
|
|
|
|
#define Blend_SoftLight 9
|
|
|
|
#define Blend_Difference 10
|
|
|
|
#define Blend_Exclusion 11
|
|
|
|
#define Blend_Hue 12
|
|
|
|
#define Blend_Saturation 13
|
|
|
|
#define Blend_Color 14
|
|
|
|
#define Blend_Luminosity 15
|
2022-05-18 07:34:14 +10:00
|
|
|
#define Blend_Clip 128
|
2022-03-01 04:38:14 +11:00
|
|
|
|
|
|
|
vec3 screen(vec3 cb, vec3 cs) {
|
|
|
|
return cb + cs - (cb * cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
float color_dodge(float cb, float cs) {
|
|
|
|
if (cb == 0.0)
|
|
|
|
return 0.0;
|
|
|
|
else if (cs == 1.0)
|
|
|
|
return 1.0;
|
|
|
|
else
|
|
|
|
return min(1.0, cb / (1.0 - cs));
|
|
|
|
}
|
|
|
|
|
|
|
|
float color_burn(float cb, float cs) {
|
|
|
|
if (cb == 1.0)
|
|
|
|
return 1.0;
|
|
|
|
else if (cs == 0.0)
|
|
|
|
return 0.0;
|
|
|
|
else
|
|
|
|
return 1.0 - min(1.0, (1.0 - cb) / cs);
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 hard_light(vec3 cb, vec3 cs) {
|
|
|
|
return mix(
|
|
|
|
screen(cb, 2.0 * cs - 1.0),
|
|
|
|
cb * 2.0 * cs,
|
2022-05-18 07:34:14 +10:00
|
|
|
lessThanEqual(cs, vec3(0.5))
|
2022-03-01 04:38:14 +11:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 soft_light(vec3 cb, vec3 cs) {
|
|
|
|
vec3 d = mix(
|
|
|
|
sqrt(cb),
|
|
|
|
((16.0 * cb - vec3(12.0)) * cb + vec3(4.0)) * cb,
|
2022-05-18 07:34:14 +10:00
|
|
|
lessThanEqual(cb, vec3(0.25))
|
2022-03-01 04:38:14 +11:00
|
|
|
);
|
|
|
|
return mix(
|
|
|
|
cb + (2.0 * cs - vec3(1.0)) * (d - cb),
|
|
|
|
cb - (vec3(1.0) - 2.0 * cs) * cb * (vec3(1.0) - cb),
|
2022-05-18 07:34:14 +10:00
|
|
|
lessThanEqual(cs, vec3(0.5))
|
2022-03-01 04:38:14 +11:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
float sat(vec3 c) {
|
|
|
|
return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));
|
|
|
|
}
|
|
|
|
|
|
|
|
float lum(vec3 c) {
|
|
|
|
vec3 f = vec3(0.3, 0.59, 0.11);
|
|
|
|
return dot(c, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 clip_color(vec3 c) {
|
|
|
|
float L = lum(c);
|
|
|
|
float n = min(c.r, min(c.g, c.b));
|
|
|
|
float x = max(c.r, max(c.g, c.b));
|
|
|
|
if (n < 0.0)
|
|
|
|
c = L + (((c - L) * L) / (L - n));
|
|
|
|
if (x > 1.0)
|
|
|
|
c = L + (((c - L) * (1.0 - L)) / (x - L));
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 set_lum(vec3 c, float l) {
|
|
|
|
return clip_color(c + (l - lum(c)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_sat_inner(inout float cmin, inout float cmid, inout float cmax, float s) {
|
|
|
|
if (cmax > cmin) {
|
|
|
|
cmid = (((cmid - cmin) * s) / (cmax - cmin));
|
|
|
|
cmax = s;
|
|
|
|
} else {
|
|
|
|
cmid = 0.0;
|
|
|
|
cmax = 0.0;
|
|
|
|
}
|
|
|
|
cmin = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 set_sat(vec3 c, float s) {
|
|
|
|
if (c.r <= c.g) {
|
|
|
|
if (c.g <= c.b) {
|
|
|
|
set_sat_inner(c.r, c.g, c.b, s);
|
|
|
|
} else {
|
|
|
|
if (c.r <= c.b) {
|
|
|
|
set_sat_inner(c.r, c.b, c.g, s);
|
|
|
|
} else {
|
|
|
|
set_sat_inner(c.b, c.r, c.g, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (c.r <= c.b) {
|
|
|
|
set_sat_inner(c.g, c.r, c.b, s);
|
|
|
|
} else {
|
|
|
|
if (c.g <= c.b) {
|
|
|
|
set_sat_inner(c.g, c.b, c.r, s);
|
|
|
|
} else {
|
|
|
|
set_sat_inner(c.b, c.g, c.r, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2022-05-14 03:18:29 +10:00
|
|
|
// Blends two RGB colors together. The colors are assumed to be in sRGB
|
|
|
|
// color space, and this function does not take alpha into account.
|
2022-03-01 04:38:14 +11:00
|
|
|
vec3 mix_blend(vec3 cb, vec3 cs, uint mode) {
|
|
|
|
vec3 b = vec3(0.0);
|
|
|
|
switch (mode) {
|
|
|
|
case Blend_Multiply:
|
|
|
|
b = cb * cs;
|
|
|
|
break;
|
|
|
|
case Blend_Screen:
|
|
|
|
b = screen(cb, cs);
|
|
|
|
break;
|
|
|
|
case Blend_Overlay:
|
|
|
|
b = hard_light(cs, cb);
|
|
|
|
break;
|
|
|
|
case Blend_Darken:
|
|
|
|
b = min(cb, cs);
|
|
|
|
break;
|
|
|
|
case Blend_Lighten:
|
|
|
|
b = max(cb, cs);
|
|
|
|
break;
|
|
|
|
case Blend_ColorDodge:
|
|
|
|
b = vec3(color_dodge(cb.x, cs.x), color_dodge(cb.y, cs.y), color_dodge(cb.z, cs.z));
|
|
|
|
break;
|
|
|
|
case Blend_ColorBurn:
|
|
|
|
b = vec3(color_burn(cb.x, cs.x), color_burn(cb.y, cs.y), color_burn(cb.z, cs.z));
|
|
|
|
break;
|
|
|
|
case Blend_HardLight:
|
|
|
|
b = hard_light(cb, cs);
|
|
|
|
break;
|
|
|
|
case Blend_SoftLight:
|
|
|
|
b = soft_light(cb, cs);
|
|
|
|
break;
|
|
|
|
case Blend_Difference:
|
|
|
|
b = abs(cb - cs);
|
|
|
|
break;
|
|
|
|
case Blend_Exclusion:
|
|
|
|
b = cb + cs - 2 * cb * cs;
|
|
|
|
break;
|
|
|
|
case Blend_Hue:
|
|
|
|
b = set_lum(set_sat(cs, sat(cb)), lum(cb));
|
|
|
|
break;
|
|
|
|
case Blend_Saturation:
|
|
|
|
b = set_lum(set_sat(cb, sat(cs)), lum(cb));
|
|
|
|
break;
|
|
|
|
case Blend_Color:
|
|
|
|
b = set_lum(cs, lum(cb));
|
|
|
|
break;
|
|
|
|
case Blend_Luminosity:
|
|
|
|
b = set_lum(cb, lum(cs));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
b = cs;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define Comp_Clear 0
|
|
|
|
#define Comp_Copy 1
|
|
|
|
#define Comp_Dest 2
|
|
|
|
#define Comp_SrcOver 3
|
|
|
|
#define Comp_DestOver 4
|
|
|
|
#define Comp_SrcIn 5
|
|
|
|
#define Comp_DestIn 6
|
|
|
|
#define Comp_SrcOut 7
|
|
|
|
#define Comp_DestOut 8
|
|
|
|
#define Comp_SrcAtop 9
|
|
|
|
#define Comp_DestAtop 10
|
|
|
|
#define Comp_Xor 11
|
|
|
|
#define Comp_Plus 12
|
2022-05-14 03:18:29 +10:00
|
|
|
#define Comp_PlusLighter 13
|
2022-03-01 04:38:14 +11:00
|
|
|
|
2022-05-14 03:18:29 +10:00
|
|
|
// Apply general compositing operation.
|
|
|
|
// Inputs are separated colors and alpha, output is premultiplied.
|
2022-03-01 04:38:14 +11:00
|
|
|
vec4 mix_compose(vec3 cb, vec3 cs, float ab, float as, uint mode) {
|
|
|
|
float fa = 0.0;
|
|
|
|
float fb = 0.0;
|
|
|
|
switch (mode) {
|
|
|
|
case Comp_Copy:
|
|
|
|
fa = 1.0;
|
|
|
|
fb = 0.0;
|
|
|
|
break;
|
|
|
|
case Comp_Dest:
|
|
|
|
fa = 0.0;
|
|
|
|
fb = 1.0;
|
|
|
|
break;
|
|
|
|
case Comp_SrcOver:
|
|
|
|
fa = 1.0;
|
|
|
|
fb = 1.0 - as;
|
|
|
|
break;
|
|
|
|
case Comp_DestOver:
|
|
|
|
fa = 1.0 - ab;
|
|
|
|
fb = 1.0;
|
|
|
|
break;
|
|
|
|
case Comp_SrcIn:
|
|
|
|
fa = ab;
|
|
|
|
fb = 0.0;
|
|
|
|
break;
|
|
|
|
case Comp_DestIn:
|
|
|
|
fa = 0.0;
|
|
|
|
fb = as;
|
|
|
|
break;
|
|
|
|
case Comp_SrcOut:
|
|
|
|
fa = 1.0 - ab;
|
|
|
|
fb = 0.0;
|
|
|
|
break;
|
|
|
|
case Comp_DestOut:
|
|
|
|
fa = 0.0;
|
|
|
|
fb = 1.0 - as;
|
|
|
|
break;
|
|
|
|
case Comp_SrcAtop:
|
|
|
|
fa = ab;
|
|
|
|
fb = 1.0 - as;
|
|
|
|
break;
|
|
|
|
case Comp_DestAtop:
|
|
|
|
fa = 1.0 - ab;
|
|
|
|
fb = as;
|
|
|
|
break;
|
|
|
|
case Comp_Xor:
|
|
|
|
fa = 1.0 - ab;
|
|
|
|
fb = 1.0 - as;
|
|
|
|
break;
|
|
|
|
case Comp_Plus:
|
|
|
|
fa = 1.0;
|
|
|
|
fb = 1.0;
|
|
|
|
break;
|
|
|
|
case Comp_PlusLighter:
|
2022-05-14 03:18:29 +10:00
|
|
|
return min(vec4(1.0), vec4(as * cs + ab * cb, as + ab));
|
2022-03-01 04:38:14 +11:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2022-05-14 03:18:29 +10:00
|
|
|
float as_fa = as * fa;
|
|
|
|
float ab_fb = ab * fb;
|
|
|
|
vec3 co = as_fa * cs + ab_fb * cb;
|
|
|
|
return vec4(co, as_fa + ab_fb);
|
2022-03-01 04:38:14 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
#define BlendComp_default (Blend_Normal << 8 | Comp_SrcOver)
|
2022-05-18 07:34:14 +10:00
|
|
|
#define BlendComp_clip (Blend_Clip << 8 | Comp_SrcOver)
|
2022-05-14 03:18:29 +10:00
|
|
|
|
|
|
|
// This is added to alpha to prevent divide-by-zero
|
|
|
|
#define EPSILON 1e-15
|
|
|
|
|
|
|
|
// Apply blending and composition. Both input and output colors are
|
|
|
|
// premultiplied RGB.
|
|
|
|
vec4 mix_blend_compose(vec4 backdrop, vec4 src, uint mode) {
|
2022-05-18 07:34:14 +10:00
|
|
|
if ((mode & 0x7fff) == BlendComp_default) {
|
|
|
|
// Both normal+src_over blend and clip case
|
2022-05-14 03:18:29 +10:00
|
|
|
return backdrop * (1.0 - src.a) + src;
|
|
|
|
}
|
|
|
|
// Un-premultiply colors for blending
|
|
|
|
float inv_src_a = 1.0 / (src.a + EPSILON);
|
|
|
|
vec3 cs = src.rgb * inv_src_a;
|
|
|
|
float inv_backdrop_a = 1.0 / (backdrop.a + EPSILON);
|
|
|
|
vec3 cb = backdrop.rgb * inv_backdrop_a;
|
|
|
|
uint blend_mode = mode >> 8;
|
2022-05-18 07:34:14 +10:00
|
|
|
vec3 blended = mix_blend(cb, cs, blend_mode);
|
2022-05-14 03:18:29 +10:00
|
|
|
cs = mix(cs, blended, backdrop.a);
|
2022-05-18 07:34:14 +10:00
|
|
|
uint comp_mode = mode & 0xff;
|
2022-05-14 03:18:29 +10:00
|
|
|
if (comp_mode == Comp_SrcOver) {
|
|
|
|
vec3 co = mix(backdrop.rgb, cs, src.a);
|
|
|
|
return vec4(co, src.a + backdrop.a * (1 - src.a));
|
|
|
|
} else {
|
|
|
|
return mix_compose(cb, cs, backdrop.a, src.a, comp_mode);
|
|
|
|
}
|
|
|
|
}
|