const CURVES: &str = r#"
uniform float p8;
uniform float p9;
uniform float p10;
uniform float p11;
float fx_curve(float x, float a, float b, float c) {
float y[5];
y[0] = 0.0; y[1] = a; y[2] = b; y[3] = c; y[4] = 1.0;
float d[4];
for (int i = 0; i < 4; i++) {
d[i] = (y[i + 1] - y[i]) * 4.0;
}
float m[5];
m[0] = d[0];
m[4] = d[3];
for (int i = 1; i < 4; i++) {
m[i] = (d[i - 1] * d[i] <= 0.0) ? 0.0 : (d[i - 1] + d[i]) * 0.5;
}
for (int i = 0; i < 4; i++) {
if (abs(d[i]) < 1e-6) {
m[i] = 0.0;
m[i + 1] = 0.0;
} else {
float ai = m[i] / d[i];
float bi = m[i + 1] / d[i];
float s = ai * ai + bi * bi;
if (s > 9.0) {
float k = 3.0 / sqrt(s);
m[i] = k * ai * d[i];
m[i + 1] = k * bi * d[i];
}
}
}
float xc = clamp(x, 0.0, 1.0);
int i = int(min(floor(xc * 4.0), 3.0));
float t = xc * 4.0 - float(i);
float t2 = t * t;
float t3 = t2 * t;
return (2.0 * t3 - 3.0 * t2 + 1.0) * y[i] + (t3 - 2.0 * t2 + t) * 0.25 * m[i]
+ (-2.0 * t3 + 3.0 * t2) * y[i + 1] + (t3 - t2) * 0.25 * m[i + 1];
}
void main() {
vec2 uv = gl_FragCoord.xy / u_res;
vec4 src = texture(tex, uv);
vec3 c = vec3(fx_curve(src.r, p3, p4, p5), fx_curve(src.g, p6, p7, p8), fx_curve(src.b, p9, p10, p11));
c = vec3(fx_curve(c.r, p0, p1, p2), fx_curve(c.g, p0, p1, p2), fx_curve(c.b, p0, p1, p2));
vec4 res = vec4(clamp(c, 0.0, 1.0), src.a);
float m = u_has_mask > 0.5 ? texture(u_mask, uv).r : 1.0;
out_color = mix(src, res, m);
}
"#;Expand description
Twelve knots (master, R, G, B) driving a monotone cubic through (0,0) (.25,a) (.5,b) (.75,c) (1,1).
Fritsch-Carlson tangents keep the spline monotone, so a curve never folds back on itself.
Declares p8..p11 itself: PRELUDE only carries p0..p7.