summaryrefslogtreecommitdiff
path: root/colors.c
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-09-07 19:30:51 -0400
committerpommicket <pommicket@gmail.com>2023-09-07 22:50:11 -0400
commitc7f323bf733160016247368883a58a9d467468b4 (patch)
tree6807738d64f94b9e0b5c136e6eb31f941fe7928d /colors.c
parent5cad1bee9b72610d9d97b5f97e7f1a245a2d2ba5 (diff)
move color-related functions from util.c to color.c
Diffstat (limited to 'colors.c')
-rw-r--r--colors.c101
1 files changed, 99 insertions, 2 deletions
diff --git a/colors.c b/colors.c
index d9ef7aa..289d286 100644
--- a/colors.c
+++ b/colors.c
@@ -195,7 +195,104 @@ float color_contrast_ratio(const float rgb1[3], const float rgb2[3]) {
float color_contrast_ratio_u32(u32 color1, u32 color2) {
float rgb1[4], rgb2[4];
- rgba_u32_to_floats(color1, rgb1);
- rgba_u32_to_floats(color2, rgb2);
+ color_u32_to_floats(color1, rgb1);
+ color_u32_to_floats(color2, rgb2);
return color_contrast_ratio(rgb1, rgb2);
}
+
+void color_u32_to_floats(u32 rgba, float floats[4]) {
+ floats[0] = (float)((rgba >> 24) & 0xff) / 255.f;
+ floats[1] = (float)((rgba >> 16) & 0xff) / 255.f;
+ floats[2] = (float)((rgba >> 8) & 0xff) / 255.f;
+ floats[3] = (float)((rgba >> 0) & 0xff) / 255.f;
+}
+
+vec4 color_u32_to_vec4(u32 rgba) {
+ float c[4];
+ color_u32_to_floats(rgba, c);
+ return (vec4){c[0], c[1], c[2], c[3]};
+}
+
+u32 color_vec4_to_u32(vec4 color) {
+ return (u32)(color.x * 255) << 24
+ | (u32)(color.y * 255) << 16
+ | (u32)(color.z * 255) << 8
+ | (u32)(color.w * 255);
+}
+
+static vec4 color_rgba_to_hsva(vec4 rgba) {
+ float R = rgba.x, G = rgba.y, B = rgba.z, A = rgba.w;
+ float M = maxf(R, maxf(G, B));
+ float m = minf(R, minf(G, B));
+ float C = M - m;
+ float H = 0;
+ if (C == 0)
+ H = 0;
+ else if (M == R)
+ H = fmodf((G - B) / C, 6);
+ else if (M == G)
+ H = (B - R) / C + 2;
+ else if (M == B)
+ H = (R - G) / C + 4;
+ H *= 60;
+ float V = M;
+ float S = V == 0 ? 0 : C / V;
+ return (vec4){H, S, V, A};
+}
+
+static vec4 color_hsva_to_rgba(vec4 hsva) {
+ float H = hsva.x, S = hsva.y, V = hsva.z, A = hsva.w;
+ H /= 60;
+ float C = S * V;
+ float X = C * (1 - fabsf(fmodf(H, 2) - 1));
+ float R, G, B;
+ if (H <= 1)
+ R=C, G=X, B=0;
+ else if (H <= 2)
+ R=X, G=C, B=0;
+ else if (H <= 3)
+ R=0, G=C, B=X;
+ else if (H <= 4)
+ R=0, G=X, B=C;
+ else if (H <= 5)
+ R=X, G=0, B=C;
+ else
+ R=C, G=0, B=X;
+
+ float m = V-C;
+ R += m;
+ G += m;
+ B += m;
+ return (vec4){R, G, B, A};
+}
+
+u32 color_interpolate(float x, u32 color1, u32 color2) {
+ x = x * x * (3 - 2*x); // hermite interpolation
+
+ vec4 c1 = color_u32_to_vec4(color1), c2 = color_u32_to_vec4(color2);
+ // to make it interpolate more nicely, convert to hsv, interpolate in that space, then convert back
+ c1 = color_rgba_to_hsva(c1);
+ c2 = color_rgba_to_hsva(c2);
+ // v_1/2 named differently to avoid shadowing
+ float h1 = c1.x, s1 = c1.y, v_1 = c1.z, a1 = c1.w;
+ float h2 = c2.x, s2 = c2.y, v_2 = c2.z, a2 = c2.w;
+
+ float s_out = lerpf(x, s1, s2);
+ float v_out = lerpf(x, v_1, v_2);
+ float a_out = lerpf(x, a1, a2);
+
+ float h_out;
+ // because hue is on a circle, we need to make sure we take the shorter route around the circle
+ if (fabsf(h1 - h2) < 180) {
+ h_out = lerpf(x, h1, h2);
+ } else if (h1 > h2) {
+ h_out = lerpf(x, h1, h2 + 360);
+ } else {
+ h_out = lerpf(x, h1 + 360, h2);
+ }
+ h_out = fmodf(h_out, 360);
+
+ vec4 c_out = (vec4){h_out, s_out, v_out, a_out};
+ c_out = color_hsva_to_rgba(c_out);
+ return color_vec4_to_u32(c_out);
+}