All checks were successful
CI / build-and-analyze (push) Successful in 54s
635 lines
16 KiB
C
635 lines
16 KiB
C
#include "CgePix.h"
|
|
#include <math.h>
|
|
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
|
|
/* Conversion between RGB <-> HSL/HSV has error of around ~ 0.009% */
|
|
#define DEGREE_MULT 1.8204444e02f
|
|
|
|
void CgeColorRGBA8(const CgeColor *color, uint8_t *r, uint8_t *g, uint8_t *b,
|
|
uint8_t *a) {
|
|
CgeColor tmp;
|
|
|
|
CgeColorToRGBA(color, &tmp);
|
|
*r = tmp.data.rgba.r >> 8;
|
|
*g = tmp.data.rgba.g >> 8;
|
|
*b = tmp.data.rgba.b >> 8;
|
|
*a = tmp.data.rgba.a >> 8;
|
|
}
|
|
|
|
void CgeColorRGBA16(const CgeColor *color, uint16_t *r, uint16_t *g,
|
|
uint16_t *b, uint16_t *a) {
|
|
CgeColor tmp;
|
|
|
|
CgeColorToRGBA(color, &tmp);
|
|
*r = tmp.data.rgba.r;
|
|
*g = tmp.data.rgba.g;
|
|
*b = tmp.data.rgba.b;
|
|
*a = tmp.data.rgba.a;
|
|
}
|
|
|
|
void CgeColorRGBAf(const CgeColor *color, float *r, float *g, float *b,
|
|
float *a) {
|
|
CgeColor tmp;
|
|
|
|
CgeColorToRGBA(color, &tmp);
|
|
*r = tmp.data.rgba.r / 65535.0f;
|
|
*g = tmp.data.rgba.g / 65535.0f;
|
|
*b = tmp.data.rgba.b / 65535.0f;
|
|
*a = tmp.data.rgba.a / 65535.0f;
|
|
}
|
|
|
|
void CgeColorSetRGBA8(CgeColor *color, uint8_t r, uint8_t g, uint8_t b,
|
|
uint8_t a) {
|
|
color->type = CGE_COLOR_RGBA;
|
|
color->data.rgba.r = (uint16_t)r * 0x0101;
|
|
color->data.rgba.g = (uint16_t)g * 0x0101;
|
|
color->data.rgba.b = (uint16_t)b * 0x0101;
|
|
color->data.rgba.a = (uint16_t)a * 0x0101;
|
|
}
|
|
|
|
void CgeColorSetRGBA16(CgeColor *color, uint16_t r, uint16_t g, uint16_t b,
|
|
uint16_t a) {
|
|
color->type = CGE_COLOR_RGBA;
|
|
color->data.rgba.r = r;
|
|
color->data.rgba.g = g;
|
|
color->data.rgba.b = b;
|
|
color->data.rgba.a = a;
|
|
}
|
|
|
|
void CgeColorSetRGBAf(CgeColor *color, float r, float g, float b, float a) {
|
|
r = MAX(MIN(1.0f, r), 0.0f);
|
|
g = MAX(MIN(1.0f, g), 0.0f);
|
|
b = MAX(MIN(1.0f, b), 0.0f);
|
|
a = MAX(MIN(1.0f, a), 0.0f);
|
|
|
|
color->type = CGE_COLOR_RGBA;
|
|
color->data.rgba.r = r * 65535.0f;
|
|
color->data.rgba.g = g * 65535.0f;
|
|
color->data.rgba.b = b * 65535.0f;
|
|
color->data.rgba.a = a * 65535.0f;
|
|
}
|
|
|
|
void CgeColorHSVAf(const CgeColor *color, float *h, float *s, float *v,
|
|
float *a) {
|
|
CgeColor tmp;
|
|
|
|
CgeColorToHSVA(color, &tmp);
|
|
*h = tmp.data.hsva.h / DEGREE_MULT;
|
|
*s = tmp.data.hsva.s / 65535.0f;
|
|
*v = tmp.data.hsva.v / 65535.0f;
|
|
*a = tmp.data.hsva.a / 65535.0f;
|
|
}
|
|
|
|
void CgeColorSetHSVAf(CgeColor *color, float h, float s, float v, float a) {
|
|
while (h >= 360.0f)
|
|
h -= 360.0f;
|
|
while (h < 0.0f)
|
|
h += 360.0f;
|
|
|
|
s = MAX(MIN(1.0f, s), 0.0f);
|
|
v = MAX(MIN(1.0f, v), 0.0f);
|
|
|
|
color->type = CGE_COLOR_HSVA;
|
|
color->data.hsva.h = h * DEGREE_MULT;
|
|
color->data.hsva.s = s * 65535.0f;
|
|
color->data.hsva.v = v * 65535.0f;
|
|
color->data.hsva.a = a * 65535.0f;
|
|
}
|
|
|
|
void CgeColorHSLAf(const CgeColor *color, float *h, float *s, float *l,
|
|
float *a) {
|
|
CgeColor tmp;
|
|
|
|
CgeColorToHSLA(color, &tmp);
|
|
*h = tmp.data.hsla.h / DEGREE_MULT;
|
|
*s = tmp.data.hsla.s / 65535.0f;
|
|
*l = tmp.data.hsla.l / 65535.0f;
|
|
*a = tmp.data.hsla.a / 65535.0f;
|
|
}
|
|
|
|
void CgeColorSetHSLAf(CgeColor *color, float h, float s, float l, float a) {
|
|
while (h >= 360.0f)
|
|
h -= 360.0f;
|
|
while (h < 0.0f)
|
|
h += 360.0f;
|
|
|
|
s = MAX(MIN(1.0f, s), 0.0f);
|
|
l = MAX(MIN(1.0f, l), 0.0f);
|
|
|
|
color->type = CGE_COLOR_HSLA;
|
|
color->data.hsla.h = h * DEGREE_MULT;
|
|
color->data.hsla.s = s * 65535;
|
|
color->data.hsla.l = l * 65535;
|
|
color->data.hsla.a = a * 65535;
|
|
}
|
|
|
|
/*
|
|
* Transformations between color models are based on:
|
|
* Computer Graphics and Geometric Modeling by Max K. Agoston
|
|
*/
|
|
static void rgbToHsv(const CgeColor *color, CgeColor *out) {
|
|
float min, max, d, r, g, b, a, h, s, v;
|
|
|
|
CgeColorRGBAf(color, &r, &g, &b, &a);
|
|
max = MAX(MAX(r, g), b);
|
|
min = MIN(MIN(r, g), b);
|
|
|
|
v = max;
|
|
s = 0.0f;
|
|
h = 0.0f;
|
|
d = max - min;
|
|
|
|
if (max)
|
|
s = d / max;
|
|
|
|
if (s) {
|
|
if (r == max)
|
|
h = (g - b) / d;
|
|
else if (g == max)
|
|
h = 2 + (b - r) / d;
|
|
else
|
|
h = 4 + (r - g) / d;
|
|
}
|
|
|
|
CgeColorSetHSVAf(out, h * 60.0f, s, v, a);
|
|
}
|
|
|
|
static void hsvToRgb(const CgeColor *color, CgeColor *out) {
|
|
float h, s, v, a, r, g, b, fract, p, q, t;
|
|
int sextant;
|
|
CgeColorHSVAf(color, &h, &s, &v, &a);
|
|
|
|
r = v;
|
|
g = v;
|
|
b = v;
|
|
|
|
if (s) {
|
|
h /= 60.0f;
|
|
sextant = (int)h;
|
|
fract = h - sextant;
|
|
|
|
p = v * (1 - s);
|
|
q = v * (1 - (s * fract));
|
|
t = v * (1 - (s * (1 - fract)));
|
|
|
|
switch (sextant) {
|
|
case 0: r = v; g = t; b = p; break;
|
|
case 1: r = q; g = v; b = p; break;
|
|
case 2: r = p; g = v; b = t; break;
|
|
case 3: r = p; g = q; b = v; break;
|
|
case 4: r = t; g = p; b = v; break;
|
|
case 5: r = v; g = p; b = q; break;
|
|
}
|
|
}
|
|
|
|
CgeColorSetRGBAf(out, r, g, b, a);
|
|
}
|
|
|
|
static void rgbToHsl(const CgeColor *color, CgeColor *out) {
|
|
float min, max, d, r, g, b, a, h, s, l;
|
|
|
|
CgeColorRGBAf(color, &r, &g, &b, &a);
|
|
max = MAX(MAX(r, g), b);
|
|
min = MIN(MIN(r, g), b);
|
|
|
|
l = (max + min) / 2.0;
|
|
s = 0.0f;
|
|
h = 0.0f;
|
|
d = max - min;
|
|
|
|
if (max != min) {
|
|
if (l <= 0.5f)
|
|
s = d / (max + min);
|
|
else
|
|
s = d / (2.0f - max - min);
|
|
|
|
if (r == max)
|
|
h = (g - b) / d;
|
|
else if (g == max)
|
|
h = 2 + (b - r) / d;
|
|
else
|
|
h = 4 + (r - g) / d;
|
|
}
|
|
|
|
CgeColorSetHSLAf(out, h * 60.0f, s, l, a);
|
|
}
|
|
|
|
static void hslToHsv(const CgeColor *color, CgeColor *out) {
|
|
float h, s, l, v, a;
|
|
|
|
CgeColorHSLAf(color, &h, &s, &l, &a);
|
|
v = s * MIN(l, 1.0f - l) + l;
|
|
s = (v) ? (2.0f - 2.0f * l / v) : (0.0f);
|
|
|
|
CgeColorSetHSVAf(out, h, s, v, a);
|
|
}
|
|
|
|
static void hsvToHsl(const CgeColor *color, CgeColor *out) {
|
|
float h, s, v, l, a, m;
|
|
|
|
CgeColorHSVAf(color, &h, &s, &v, &a);
|
|
l = v - v * s / 2.0f;
|
|
m = MIN(l, 1.0f - l);
|
|
s = (m) ? ((v - l) / m) : (0);
|
|
CgeColorSetHSLAf(out, h, s, l, a);
|
|
}
|
|
|
|
void CgeColorToRGBA(const CgeColor *color, CgeColor *out) {
|
|
switch (color->type) {
|
|
default:
|
|
/* fallthrough */
|
|
case CGE_COLOR_RGBA:
|
|
*out = *color;
|
|
break;
|
|
|
|
case CGE_COLOR_HSVA:
|
|
hsvToRgb(color, out);
|
|
break;
|
|
|
|
case CGE_COLOR_HSLA:
|
|
hslToHsv(color, out);
|
|
hsvToRgb(out, out);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CgeColorToHSVA(const CgeColor *color, CgeColor *out) {
|
|
switch (color->type) {
|
|
case CGE_COLOR_RGBA:
|
|
rgbToHsv(color, out);
|
|
break;
|
|
|
|
default:
|
|
/* fallthrough */
|
|
case CGE_COLOR_HSVA:
|
|
*out = *color;
|
|
break;
|
|
|
|
case CGE_COLOR_HSLA:
|
|
hslToHsv(color, out);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CgeColorToHSLA(const CgeColor *color, CgeColor *out) {
|
|
switch (color->type) {
|
|
case CGE_COLOR_RGBA:
|
|
rgbToHsl(color, out);
|
|
break;
|
|
|
|
case CGE_COLOR_HSVA:
|
|
hsvToHsl(color, out);
|
|
break;
|
|
|
|
default:
|
|
/* fallthrough */
|
|
case CGE_COLOR_HSLA:
|
|
*out = *color;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint16_t mulDiv16(uint16_t x, uint16_t y) {
|
|
uint32_t product = (uint32_t)x * (uint32_t)y + 32767u;
|
|
return (uint16_t)((product + (product >> 16)) >> 16);
|
|
}
|
|
|
|
static uint16_t divScale16(uint16_t x, uint16_t denom) {
|
|
return (uint16_t)(((uint32_t)x * 65535u + denom/2 ) / denom);
|
|
}
|
|
|
|
void CgeColorPremultiply(const CgeColor *color, CgeColor *out) {
|
|
uint16_t r, g, b, a;
|
|
|
|
CgeColorRGBA16(color, &r, &g, &b, &a);
|
|
if (a) {
|
|
out->data.channel[0] = mulDiv16(r, a);
|
|
out->data.channel[1] = mulDiv16(g, a);
|
|
out->data.channel[2] = mulDiv16(b, a);
|
|
} else {
|
|
out->data.channel[0] = 0;
|
|
out->data.channel[1] = 0;
|
|
out->data.channel[2] = 0;
|
|
}
|
|
out->data.channel[3] = a;
|
|
out->type = CGE_COLOR_RGBA;
|
|
}
|
|
|
|
void CgeColorUnpremultiply(const CgeColor *color, CgeColor *out) {
|
|
uint16_t r, g, b, a;
|
|
|
|
CgeColorRGBA16(color, &r, &g, &b, &a);
|
|
if (a) {
|
|
out->data.channel[0] = divScale16(r, a);
|
|
out->data.channel[1] = divScale16(g, a);
|
|
out->data.channel[2] = divScale16(b, a);
|
|
} else {
|
|
out->data.channel[0] = 0;
|
|
out->data.channel[1] = 0;
|
|
out->data.channel[2] = 0;
|
|
}
|
|
out->data.channel[3] = a;
|
|
out->type = CGE_COLOR_RGBA;
|
|
}
|
|
|
|
static float blendMultiply(float fg, float bg) {
|
|
return fg * bg;
|
|
}
|
|
|
|
static float blendScreen(float fg, float bg) {
|
|
return fg + bg - (fg * bg);
|
|
}
|
|
|
|
static float blendDarken(float fg, float bg) {
|
|
return MIN(fg, bg);
|
|
}
|
|
|
|
static float blendLighten(float fg, float bg) {
|
|
return MAX(fg, bg);
|
|
}
|
|
|
|
static float blendColorDodge(float fg, float bg) {
|
|
if (fg != 1.0f)
|
|
return MIN(1.0f, bg / (1.0f - fg));
|
|
return fg;
|
|
}
|
|
|
|
static float blendColorBurn(float fg, float bg) {
|
|
if (fg != 0.0f)
|
|
return 1.0f - MIN(1.0f, (1.0f - bg) / fg);
|
|
return fg;
|
|
}
|
|
|
|
static float blendLinearDodge(float fg, float bg) {
|
|
return fg + bg;
|
|
}
|
|
|
|
static float blendLinearBurn(float fg, float bg) {
|
|
return fg + bg - 1.0f;
|
|
}
|
|
|
|
static float blendDifference(float fg, float bg) {
|
|
return fabs(bg - fg);
|
|
}
|
|
|
|
static float blendExclusion(float fg, float bg) {
|
|
return fg + bg - (2.0f * fg * bg);
|
|
}
|
|
|
|
static float blendHardLight(float fg, float bg) {
|
|
if (fg <= 0.5f)
|
|
return blendMultiply(2.0f * fg, bg);
|
|
else
|
|
return blendScreen(2.0f * fg - 1.0f, bg);
|
|
}
|
|
|
|
static float blendSoftLight(float fg, float bg) {
|
|
return (1.0f - 2.0f * fg) * bg * bg + 2.0f * fg * bg;
|
|
}
|
|
|
|
static float blendOverlay(float fg, float bg) {
|
|
return blendHardLight(bg, fg);
|
|
}
|
|
|
|
void CgeColorBlendNormal(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
/* Unused */
|
|
(void)bg;
|
|
*out = *fg;
|
|
}
|
|
|
|
void CgeColorBlendMultiply(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendMultiply(fgr, bgr);
|
|
fgg = blendMultiply(fgg, bgg);
|
|
fgb = blendMultiply(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendScreen(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendScreen(fgr, bgr);
|
|
fgg = blendScreen(fgg, bgg);
|
|
fgb = blendScreen(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendOverlay(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendOverlay(fgr, bgr);
|
|
fgg = blendOverlay(fgg, bgg);
|
|
fgb = blendOverlay(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendDarken(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendDarken(fgr, bgr);
|
|
fgg = blendDarken(fgg, bgg);
|
|
fgb = blendDarken(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendLighten(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendLighten(fgr, bgr);
|
|
fgg = blendLighten(fgg, bgg);
|
|
fgb = blendLighten(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendColorDodge(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendColorDodge(fgr, bgr);
|
|
fgg = blendColorDodge(fgg, bgg);
|
|
fgb = blendColorDodge(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendColorBurn(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendColorBurn(fgr, bgr);
|
|
fgg = blendColorBurn(fgg, bgg);
|
|
fgb = blendColorBurn(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendLinearDodge(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendLinearDodge(fgr, bgr);
|
|
fgg = blendLinearDodge(fgg, bgg);
|
|
fgb = blendLinearDodge(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendLinearBurn(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendLinearBurn(fgr, bgr);
|
|
fgg = blendLinearBurn(fgg, bgg);
|
|
fgb = blendLinearBurn(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendHardLight(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendHardLight(fgr, bgr);
|
|
fgg = blendHardLight(fgg, bgg);
|
|
fgb = blendHardLight(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendSoftLight(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendSoftLight(fgr, bgr);
|
|
fgg = blendSoftLight(fgg, bgg);
|
|
fgb = blendSoftLight(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendDifference(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorRGBAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorRGBAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendDifference(fgr, bgr);
|
|
fgg = blendDifference(fgg, bgg);
|
|
fgb = blendDifference(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendExclusion(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
|
|
fgr = blendExclusion(fgr, bgr);
|
|
fgg = blendExclusion(fgg, bgg);
|
|
fgb = blendExclusion(fgb, bgb);
|
|
|
|
CgeColorSetRGBAf(out, fgr, fgg, fgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendHue(const CgeColor *fg, const CgeColor *bg, CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
CgeColorSetHSLAf(out, fgr, bgg, bgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendSaturation(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
CgeColorSetHSLAf(out, bgr, fgg, bgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendColor(const CgeColor *fg, const CgeColor *bg, CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
CgeColorSetHSLAf(out, fgr, fgg, bgb, fga);
|
|
}
|
|
|
|
void CgeColorBlendLuminosity(const CgeColor *fg, const CgeColor *bg,
|
|
CgeColor *out) {
|
|
float bgr, bgg, bgb, bga;
|
|
float fgr, fgg, fgb, fga;
|
|
|
|
CgeColorHSLAf(fg, &fgr, &fgg, &fgb, &fga);
|
|
CgeColorHSLAf(bg, &bgr, &bgg, &bgb, &bga);
|
|
CgeColorSetHSLAf(out, bgr, bgg, fgb, fga);
|
|
}
|