blob: 1ac4bd6c361efdd12df8830312d76b620d7c4b21 [file] [log] [blame]
// 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
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,
vec3(lessThanEqual(cs, vec3(0.5)))
);
}
vec3 soft_light(vec3 cb, vec3 cs) {
vec3 d = mix(
sqrt(cb),
((16.0 * cb - vec3(12.0)) * cb + vec3(4.0)) * cb,
vec3(lessThanEqual(cb, vec3(0.25)))
);
return mix(
cb + (2.0 * cs - vec3(1.0)) * (d - cb),
cb - (vec3(1.0) - 2.0 * cs) * cb * (vec3(1.0) - cb),
vec3(lessThanEqual(cs, vec3(0.5)))
);
}
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;
}
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
#define Comp_PlusDarker 13
#define Comp_PlusLighter 14
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_PlusDarker:
return vec4(max(vec4(0.0), 1.0 - as * vec4(cs, as) + 1.0 - ab * vec4(cb, ab)).xyz,
max(0.0, 1.0 - as + 1.0 - ab));
case Comp_PlusLighter:
return vec4(min(vec4(1.0), as * vec4(cs, as) + ab * vec4(cb, ab)).xyz,
min(1.0, as + ab));
default:
break;
}
return as * fa * vec4(cs, as) + ab * fb * vec4(cb, ab);
}
#define BlendComp_default (Blend_Normal << 8 | Comp_SrcOver)