2017-02-27 15:27:48 +01:00
|
|
|
/*
|
2019-08-03 19:43:48 +02:00
|
|
|
* This software is licensed under the terms of the MIT License.
|
2017-02-27 15:27:48 +01:00
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
2024-05-16 23:30:41 +02:00
|
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
2017-02-27 15:27:48 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "color.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "util/stringops.h"
|
2017-02-27 15:27:48 +01:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
#define COLOR_OP(c1, op, c2) do { \
|
|
|
|
(c1)->r = (c1)->r op (c2)->r; \
|
|
|
|
(c1)->g = (c1)->g op (c2)->g; \
|
|
|
|
(c1)->b = (c1)->b op (c2)->b; \
|
|
|
|
(c1)->a = (c1)->a op (c2)->a; \
|
|
|
|
} while(0);
|
2017-02-27 15:27:48 +01:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_copy(Color *dst, const Color *src) {
|
|
|
|
*dst = *src;
|
|
|
|
return dst;
|
2017-02-27 15:27:48 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_add(Color *clr, const Color *clr2) {
|
|
|
|
COLOR_OP(clr, +, clr2);
|
|
|
|
return clr;
|
2017-02-27 15:27:48 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_sub(Color *clr, const Color *clr2) {
|
|
|
|
COLOR_OP(clr, -, clr2);
|
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_mul(Color *clr, const Color *clr2) {
|
|
|
|
COLOR_OP(clr, *, clr2);
|
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_mul_alpha(Color *clr) {
|
|
|
|
clr->r *= clr->a;
|
|
|
|
clr->g *= clr->a;
|
|
|
|
clr->b *= clr->a;
|
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_mul_scalar(Color *clr, float scalar) {
|
|
|
|
clr->r *= scalar;
|
|
|
|
clr->g *= scalar;
|
|
|
|
clr->b *= scalar;
|
|
|
|
clr->a *= scalar;
|
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_div(Color *clr, const Color *clr2) {
|
|
|
|
COLOR_OP(clr, /, clr2);
|
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_div_alpha(Color *clr) {
|
|
|
|
if(clr->a != 0) {
|
|
|
|
clr->r /= clr->a;
|
|
|
|
clr->g /= clr->a;
|
|
|
|
clr->b /= clr->a;
|
|
|
|
}
|
2017-02-26 21:59:51 +01:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_div_scalar(Color *clr, float scalar) {
|
|
|
|
clr->r /= scalar;
|
|
|
|
clr->g /= scalar;
|
|
|
|
clr->b /= scalar;
|
|
|
|
clr->a /= scalar;
|
|
|
|
return clr;
|
2017-02-26 21:59:51 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_lerp(Color *clr, const Color *clr2, float a) {
|
|
|
|
float ia = 1 - a;
|
|
|
|
clr->r = clr->r * ia + clr2->r * a;
|
|
|
|
clr->g = clr->g * ia + clr2->g * a;
|
|
|
|
clr->b = clr->b * ia + clr2->b * a;
|
|
|
|
clr->a = clr->a * ia + clr2->a * a;
|
|
|
|
return clr;
|
2017-03-06 22:39:38 +01:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_approach(Color *clr, const Color *clr2, float delta) {
|
|
|
|
clr->r += (clr2->r - clr->r) * delta;
|
|
|
|
clr->g += (clr2->g - clr->g) * delta;
|
|
|
|
clr->b += (clr2->b - clr->b) * delta;
|
|
|
|
clr->a += (clr2->a - clr->a) * delta;
|
|
|
|
return clr;
|
2017-03-10 19:54:36 +01:00
|
|
|
}
|
|
|
|
|
2017-10-10 08:31:48 +02:00
|
|
|
static float hue_to_rgb(float v1, float v2, float vH) {
|
2018-01-12 19:26:07 +01:00
|
|
|
if(vH < 0) {
|
|
|
|
vH += 1;
|
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if(vH > 1) {
|
|
|
|
vH -= 1;
|
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if((6 * vH) < 1) {
|
|
|
|
return (v1 + (v2 - v1) * 6 * vH);
|
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if((2 * vH) < 1) {
|
|
|
|
return v2;
|
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if((3 * vH) < 2) {
|
|
|
|
return (v1 + (v2 - v1) * ((2.0f / 3) - vH) * 6);
|
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
return v1;
|
2017-10-10 08:31:48 +02:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_hsla(Color *clr, float h, float s, float l, float a) {
|
2018-01-12 19:26:07 +01:00
|
|
|
if(s == 0) {
|
2018-07-23 19:07:59 +02:00
|
|
|
clr->r = clr->g = clr->b = l;
|
2018-01-12 19:26:07 +01:00
|
|
|
} else {
|
|
|
|
float v1, v2;
|
|
|
|
h = fmod(h, 1.0);
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
if(l < 0.5) {
|
|
|
|
v2 = l * (1.0 + s);
|
|
|
|
} else {
|
|
|
|
v2 = l + s - s * l;
|
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-01-12 19:26:07 +01:00
|
|
|
v1 = 2.0 * l - v2;
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
clr->r = hue_to_rgb(v1, v2, h + (1.0/3.0));
|
|
|
|
clr->g = hue_to_rgb(v1, v2, h);
|
|
|
|
clr->b = hue_to_rgb(v1, v2, h - (1.0/3.0));
|
2018-01-12 19:26:07 +01:00
|
|
|
}
|
2017-10-10 08:31:48 +02:00
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
clr->a = a;
|
|
|
|
return clr;
|
2017-10-10 08:31:48 +02:00
|
|
|
}
|
|
|
|
|
2019-01-04 23:59:39 +01:00
|
|
|
void color_get_hsl(const Color *c, float *out_h, float *out_s, float *out_l) {
|
|
|
|
float r = clamp(c->r, 0, 1);
|
|
|
|
float g = clamp(c->g, 0, 1);
|
|
|
|
float b = clamp(c->b, 0, 1);
|
|
|
|
|
2023-09-23 21:56:34 +02:00
|
|
|
float maxv = max(max(r, g), b);
|
|
|
|
float minv = min(min(r, g), b);
|
2019-01-04 23:59:39 +01:00
|
|
|
float h = 0, s = 0, d = maxv - minv, l = (maxv + minv) / 2;
|
|
|
|
|
|
|
|
if(maxv != minv) {
|
|
|
|
s = l > 0.5 ? d / (2 - maxv - minv) : d / (maxv + minv);
|
|
|
|
if(maxv == r) {
|
|
|
|
h = (g - b) / d + (g < b ? 6 : 0);
|
|
|
|
} else if(maxv == g) {
|
|
|
|
h = (b - r) / d + 2;
|
|
|
|
} else if(maxv == b) {
|
|
|
|
h = (r - g) / d + 4;
|
|
|
|
}
|
|
|
|
h /= 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(out_h) *out_h = h;
|
|
|
|
if(out_s) *out_s = s;
|
|
|
|
if(out_l) *out_l = l;
|
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
Color* color_set_opacity(Color *clr, float opacity) {
|
|
|
|
// FIXME: is this correct?
|
|
|
|
|
|
|
|
if(clr->a != 0) {
|
|
|
|
opacity /= clr->a;
|
|
|
|
}
|
|
|
|
|
|
|
|
color_mul_scalar(clr, opacity);
|
|
|
|
return clr;
|
2017-10-10 08:31:48 +02:00
|
|
|
}
|
|
|
|
|
2018-07-23 19:07:59 +02:00
|
|
|
bool color_equals(const Color *clr, const Color *clr2) {
|
|
|
|
return (
|
|
|
|
clr->r == clr2->r &&
|
|
|
|
clr->g == clr2->g &&
|
|
|
|
clr->b == clr2->b &&
|
|
|
|
clr->a == clr2->a
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-05-17 04:41:28 +02:00
|
|
|
char *color_str(const Color *clr) {
|
2018-07-23 19:07:59 +02:00
|
|
|
return strfmt(
|
|
|
|
"RGBA(%f, %f, %f, %f) at %p",
|
|
|
|
clr->r,
|
|
|
|
clr->g,
|
|
|
|
clr->b,
|
|
|
|
clr->a,
|
|
|
|
(void*)clr
|
|
|
|
);
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|