diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | Makefile.srcs | 1 | ||||
| -rw-r--r-- | doc/Manual/en/BH_Color.pod | 314 | ||||
| -rw-r--r-- | doc/Manual/en/Makefile | 2 | ||||
| -rw-r--r-- | doc/Manual/ru/BH_Color.pod | 319 | ||||
| -rw-r--r-- | doc/Manual/ru/Makefile | 2 | ||||
| -rw-r--r-- | include/BH/Color.h | 151 | ||||
| -rw-r--r-- | src/Color.c | 618 |
9 files changed, 1413 insertions, 0 deletions
@@ -77,3 +77,7 @@ _deps *.html *.3 *.zip + +# Exclude temporary and backup files +*.tmp +*.bak
\ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d2ce61..62e4d04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ set(BH_SOURCE src/Args.c src/Buffer.c src/Bytes.c + src/Color.c src/Hashmap.c src/IO.c src/Math/Box2f.c @@ -110,6 +111,7 @@ set(BH_SOURCE set(BH_HEADER include/BH/Algo.h include/BH/Args.h + include/BH/Color.h include/BH/Common.h include/BH/Hashmap.h include/BH/IO.h diff --git a/Makefile.srcs b/Makefile.srcs index 87b4ed3..3d0d91a 100644 --- a/Makefile.srcs +++ b/Makefile.srcs @@ -4,6 +4,7 @@ SRCS = src/Algo.c \ src/Args.c \ src/Buffer.c \ src/Bytes.c \ + src/Color.c \ src/Hashmap.c \ src/IO.c \ src/Math/Box2f.c \ diff --git a/doc/Manual/en/BH_Color.pod b/doc/Manual/en/BH_Color.pod new file mode 100644 index 0000000..99e3b64 --- /dev/null +++ b/doc/Manual/en/BH_Color.pod @@ -0,0 +1,314 @@ +=encoding UTF-8 + + +=head1 NAME + +BH_Color - color manipulation utility + + +=head1 SYNTAX + + #include <BH/Math/Box3f.h> + + cc prog.c -o prog -lbh + + +=head1 DESCRIPTION + +The BH_Color module provides mechanism for color representation and translation +between different color spaces color spaces (RGB, HSL and HSV). Additionally, +this module provides mechanism for blending two colors. + + +=head1 NOTES + +Internally, color is stored in four 16-bit integers. + +When converting between RGB and HSV/HSL there is ~0.009% error. While this is +not an issue, if you are using 8-bit RGB value, this may become a problem for +the 16-bit RGB values (not every RGB value can be represented in unique HSV/HSL +value). If you need this kind of precision, then this module is not for you. + +Almost all color blending modes work like in other software packages. The +exception is I<BH_COLOR_MODE_SOFT_LIGHT>. This library opted-in for the Pegtop's +version of the formula (which might yield different results). This might be an +issue if you are trying to implement standard complient SVG or PDF renderer. + +The I<BH_COLOR_MODE_HUE>, I<BH_COLOR_MODE_SATURATION>, I<BH_COLOR_MODE_COLOR> +and I<BH_COLOR_MODE_LUMINOSITY> are implemented in HSL color space. + + +=head1 API CALLS + + +=head2 BH_ColorRGBA8 + + void BH_ColorRGBA8(const BH_Color *color, + uint8_t *r, + uint8_t *g, + uint8_t *b, + uint8_t *a); + +Gets 8-bit RGBA representation of the I<color>. + +Parameters I<r>, I<g>, I<b>, I<a> represent color components in RGB color space. + + +=head2 BH_ColorRGBA16 + + void BH_ColorRGBA16(const BH_Color *color, + uint16_t *r, + uint16_t *g, + uint16_t *b, + uint16_t *a); + +Gets 16-bit RGBA representation of the I<color>. + +Parameters I<r>, I<g>, I<b>, I<a> represent color components in RGB color space. + + +=head2 BH_ColorRGBAf + + void BH_ColorRGBAf(const BH_Color *color, + float *r, + float *g, + float *b, + float *a); + +Gets floating-point RGBA representation of the I<color>. + +Parameters I<r>, I<g>, I<b>, I<a> represent color components in RGB color space. + +=head2 BH_ColorSetRGBA8 + + void BH_ColorSetRGBA8(BH_Color *color, + uint8_t r, + uint8_t g, + uint8_t b, + uint8_t a); + +Sets 8-bit I<r>, I<g>, I<b>, I<a> components of the color. + + +=head2 BH_ColorSetRGBA16 + + void BH_ColorSetRGBA16(BH_Color *color, + uint16_t r, + uint16_t g, + uint16_t b, + uint16_t a); + +Sets 16-bit I<r>, I<g>, I<b>, I<a> components of the color. + + +=head2 BH_ColorSetRGBAf + + void BH_ColorSetRGBAf(BH_Color *color, + float r, + float g, + float b, + float a); + +Sets floating-point I<r>, I<g>, I<b>, I<a> components of the color. + + +=head2 BH_ColorHSVAf + + void BH_ColorHSVAf(const BH_Color *color, + float *h, + float *s, + float *v, + float *a); + +Gets floating-point HSV representation of the I<color>. + +Parameters I<h>, I<s>, I<v>, I<a> represent color components in HSV color space. + + +=head2 BH_ColorSetHSVAf + + void BH_ColorSetHSVAf(BH_Color *color, + float h, + float s, + float v, + float a); + +Sets floating-point I<h>, I<s>, I<v>, I<a> components of the color. + + +=head2 BH_ColorHSLAf + + void BH_ColorHSLAf(const BH_Color *color, + float *h, + float *s, + float *l, + float *a); + +Gets floating-point HSL representation of the I<color>. + +Parameters I<h>, I<s>, I<l>, I<a> represent color components in HSV color space. + + +=head2 BH_ColorSetHSLAf + + void BH_ColorSetHSLAf(BH_Color *color, + float h, + float s, + float l, + float a); + +Sets floating-point I<h>, I<s>, I<l>, I<a> components of the color. + + +=head2 BH_ColorToRGBA + + void BH_ColorToRGBA(const BH_Color *color, + BH_Color *out); + +Converts I<color> to RGB color space. + + +=head2 BH_ColorToHSVA + + void BH_ColorToHSVA(const BH_Color *color, + BH_Color *out); + +Converts I<color> to HSV color space. + + +=head2 BH_ColorToHSLA + + void BH_ColorToHSLA(const BH_Color *color, + BH_Color *out); + +Converts I<color> to HSL color space. + + +=head2 BH_ColorBlend + + void BH_ColorBlend(const BH_Color *foreground, + const BH_Color *background, + int mode, + BH_Color *dest); + +Blends I<foreground> and I<background> color, using specified blending I<mode>. + +The following modes can be used: + +=over + +=item B<BH_COLOR_MODE_NORMAL> + + r = f + +=item B<BH_COLOR_MODE_MULTIPLY> + + r = f * b + +=item B<BH_COLOR_MODE_SCREEN> + + r = 1 - (1 - f) * (1 - b) + +=item B<BH_COLOR_MODE_OVERLAY> + + r = HardLight(b, f) + +=item B<BH_COLOR_MODE_DARKEN> + + r = min(f, b) + +=item B<BH_COLOR_MODE_LIGHTEN> + + r = max(f, b) + +=item B<BH_COLOR_MODE_COLOR_DODGE> + + r = min(1, b / (1 - f)) + +=item B<BH_COLOR_MODE_COLOR_BURN> + + r = 1 - min(1, (1 - b) / f) + +=item B<BH_COLOR_MODE_LINEAR_DODGE> + + r = f + b + +=item B<BH_COLOR_MODE_LINEAR_BURN> + + r = f + b - 1 + +=item B<BH_COLOR_MODE_HARD_LIGHT> + + if (f < 0.5) + r = Multipy(2 * f, b) + else + r = Screen(2 * f - 1, b) + +=item B<BH_COLOR_MODE_SOFT_LIGHT> + + r = (1 - 2 * f) * b^2 + 2 * f * b + +=item B<BH_COLOR_MODE_DIFFERENCE> + + r = abs(f - b) + +=item B<BH_COLOR_MODE_EXCLUSION> + + r = f + b - (2 * f * b) + +=item B<BH_COLOR_MODE_HUE> + + r = HSL(H(f), S(b), L(b)) + +=item B<BH_COLOR_MODE_SATURATION> + + r = HSL(H(b), S(f), L(b)) + +=item B<BH_COLOR_MODE_COLOR> + + r = HSL(H(f), S(f), L(b)) + +=item B<BH_COLOR_MODE_LUMINOSITY> + + r = HSL(H(b), S(b), L(f)) + +=back + + +=head1 STRUCTURES + + +=head2 BH_Color + + typedef struct BH_Color + { + int type; + union + { + struct + { + uint16_t r; + uint16_t g; + uint16_t b; + uint16_t a; + } rgba; + struct + { + uint16_t h; + uint16_t s; + uint16_t v; + uint16_t a; + } hsva; + struct + { + uint16_t h; + uint16_t s; + uint16_t l; + uint16_t a; + } hsla; + } data; + } BH_Color; + +=head1 SEE ALSO + +L<BH>
\ No newline at end of file diff --git a/doc/Manual/en/Makefile b/doc/Manual/en/Makefile index c42065a..6175844 100644 --- a/doc/Manual/en/Makefile +++ b/doc/Manual/en/Makefile @@ -5,6 +5,7 @@ HTMLS = BH_Algo.html \ BH_Args.html \ BH_Box2f.html \ BH_Box3f.html \ + BH_Color.html \ BH_Hashmap.html \ BH_IO.html \ BH_Line.html \ @@ -31,6 +32,7 @@ MANS = BH_Algo.3 \ BH_Args.3 \ BH_Box2f.3 \ BH_Box3f.3 \ + BH_Color.3 \ BH_Hashmap.3 \ BH_IO.3 \ BH_Line.3 \ diff --git a/doc/Manual/ru/BH_Color.pod b/doc/Manual/ru/BH_Color.pod new file mode 100644 index 0000000..10d0492 --- /dev/null +++ b/doc/Manual/ru/BH_Color.pod @@ -0,0 +1,319 @@ +=encoding UTF-8 + + +=head1 НАИМЕНОВАНИЕ + +BH_Color - утилиты для работы с цветом + + +=head1 СИНТАКСИС + + #include <BH/Math/Box3f.h> + + cc prog.c -o prog -lbh + + +=head1 ОПИСАНИЕ + +Модуль BH_Color предоставляет механизм для представления цвета и трансляции +между различными цветовыми пространствами (RGB, HSL и HSV). Кроме того, этот +модуль предоставляет механизм для смешивания двух цветов. + + +=head1 ЗАМЕТКИ + +Внутренне, цвет хранится в четырех 16-разрядных целых числах. + +При преобразовании между RGB и HSV/HSL возникает ошибка ~0,009%. Это не является +проблемой, если вы используете 8-битные RGB значения. Это может стать проблемой +если вы используете 16-битные значения RGB (не каждое значение RGB сможет быть +представлено в уникальном значении HSV/HSL). Если вам нужна такая точность, то +этот модуль не для вас. + +Почти все режимы смешивания цветов работают так же, как и в других программных +пакетах. Исключением является I<BH_COLOR_MODE_SOFT_LIGHT>. Эта библиотека +использует Pegtop весию формулы (которая может привести к другому результату). +Это может быть проблемой, если вы пытаетесь реализовать стандартный рендеринг +для форматов SVG или PDF. + +I<BH_COLOR_MODE_HUE>, I<BH_COLOR_MODE_SATURATION>, I<BH_COLOR_MODE_COLOR> +и I<BH_COLOR_MODE_LUMINOSITY> реализованы в цветовом пространстве HSL. + + +=head1 API ВЫЗОВЫ + + +=head2 BH_ColorRGBA8 + + void BH_ColorRGBA8(const BH_Color *color, + uint8_t *r, + uint8_t *g, + uint8_t *b, + uint8_t *a); + +Возвращает 8-битное RGB представление цвета I<color>. + +Параметры I<r>, I<g>, I<b>, I<a> представляю компоненты цвета в RGB. + + +=head2 BH_ColorRGBA16 + + void BH_ColorRGBA16(const BH_Color *color, + uint16_t *r, + uint16_t *g, + uint16_t *b, + uint16_t *a); + +Возвращает 16-битное RGB представление цвета I<color>. + +Параметры I<r>, I<g>, I<b>, I<a> представляют компоненты цвета в RGB. + + + +=head2 BH_ColorRGBAf + + void BH_ColorRGBAf(const BH_Color *color, + float *r, + float *g, + float *b, + float *a); + +Возвращает вещественное RGB представление цвета I<color>. + +Параметры I<r>, I<g>, I<b>, I<a> представляют компоненты цвета в RGB. + + +=head2 BH_ColorSetRGBA8 + + void BH_ColorSetRGBA8(BH_Color *color, + uint8_t r, + uint8_t g, + uint8_t b, + uint8_t a); + +Устанавливает 8-битные компонеты I<r>, I<g>, I<b>, I<a> цвета. + + +=head2 BH_ColorSetRGBA16 + + void BH_ColorSetRGBA16(BH_Color *color, + uint16_t r, + uint16_t g, + uint16_t b, + uint16_t a); + +Устанавливает 16-битные компонеты I<r>, I<g>, I<b>, I<a> цвета. + + +=head2 BH_ColorSetRGBAf + + void BH_ColorSetRGBAf(BH_Color *color, + float r, + float g, + float b, + float a); + +Устанавливает вещественные компонеты I<r>, I<g>, I<b>, I<a> цвета. + + +=head2 BH_ColorHSVAf + + void BH_ColorHSVAf(const BH_Color *color, + float *h, + float *s, + float *v, + float *a); + +Возвращает вещественное HSV представление цвета I<color>. + +Параметры I<h>, I<s>, I<v>, I<a> представляют компоненты цвета в RGB. + + +=head2 BH_ColorSetHSVAf + + void BH_ColorSetHSVAf(BH_Color *color, + float h, + float s, + float v, + float a); + +Устанавливает вещественные компонеты I<h>, I<s>, I<v>, I<a> цвета. + + +=head2 BH_ColorHSLAf + + void BH_ColorHSLAf(const BH_Color *color, + float *h, + float *s, + float *l, + float *a); + +Возвращает вещественное HSL представление цвета I<color>. + +Параметры I<h>, I<s>, I<l>, I<a> представляют компоненты цвета в RGB. + + +=head2 BH_ColorSetHSLAf + + void BH_ColorSetHSLAf(BH_Color *color, + float h, + float s, + float l, + float a); + +Устанавливает вещественные компонеты I<h>, I<s>, I<l>, I<a> цвета. + + +=head2 BH_ColorToRGBA + + void BH_ColorToRGBA(const BH_Color *color, + BH_Color *out); + +Преобразует цвет I<color> в цветовое представление RGB. + + +=head2 BH_ColorToHSVA + + void BH_ColorToHSVA(const BH_Color *color, + BH_Color *out); + +Преобразует цвет I<color> в цветовое представление HSV. + + +=head2 BH_ColorToHSLA + + void BH_ColorToHSLA(const BH_Color *color, + BH_Color *out); + +Преобразует цвет I<color> в цветовое представление HSL. + + +=head2 BH_ColorBlend + + void BH_ColorBlend(const BH_Color *foreground, + const BH_Color *background, + int mode, + BH_Color *dest); + +Смешивает цвета I<foreground> и I<background>, используя указанный режим +смешивания I<mode>. + +Могут использоваться следующие режимы: + +=over + +=item B<BH_COLOR_MODE_NORMAL> + + r = f + +=item B<BH_COLOR_MODE_MULTIPLY> + + r = f * b + +=item B<BH_COLOR_MODE_SCREEN> + + r = 1 - (1 - f) * (1 - b) + +=item B<BH_COLOR_MODE_OVERLAY> + + r = HardLight(b, f) + +=item B<BH_COLOR_MODE_DARKEN> + + r = min(f, b) + +=item B<BH_COLOR_MODE_LIGHTEN> + + r = max(f, b) + +=item B<BH_COLOR_MODE_COLOR_DODGE> + + r = min(1, b / (1 - f)) + +=item B<BH_COLOR_MODE_COLOR_BURN> + + r = 1 - min(1, (1 - b) / f) + +=item B<BH_COLOR_MODE_LINEAR_DODGE> + + r = f + b + +=item B<BH_COLOR_MODE_LINEAR_BURN> + + r = f + b - 1 + +=item B<BH_COLOR_MODE_HARD_LIGHT> + + if (f < 0.5) + r = Multipy(2 * f, b) + else + r = Screen(2 * f - 1, b) + +=item B<BH_COLOR_MODE_SOFT_LIGHT> + + r = (1 - 2 * f) * b^2 + 2 * f * b + +=item B<BH_COLOR_MODE_DIFFERENCE> + + r = abs(f - b) + +=item B<BH_COLOR_MODE_EXCLUSION> + + r = f + b - (2 * f * b) + +=item B<BH_COLOR_MODE_HUE> + + r = HSL(H(f), S(b), L(b)) + +=item B<BH_COLOR_MODE_SATURATION> + + r = HSL(H(b), S(f), L(b)) + +=item B<BH_COLOR_MODE_COLOR> + + r = HSL(H(f), S(f), L(b)) + +=item B<BH_COLOR_MODE_LUMINOSITY> + + r = HSL(H(b), S(b), L(f)) + +=back + + +=head1 СТРУКТУРЫ + + +=head2 BH_Color + + typedef struct BH_Color + { + int type; + union + { + struct + { + uint16_t r; + uint16_t g; + uint16_t b; + uint16_t a; + } rgba; + struct + { + uint16_t h; + uint16_t s; + uint16_t v; + uint16_t a; + } hsva; + struct + { + uint16_t h; + uint16_t s; + uint16_t l; + uint16_t a; + } hsla; + } data; + } BH_Color; + +=head1 СМ. ТАКЖЕ + +L<BH>
\ No newline at end of file diff --git a/doc/Manual/ru/Makefile b/doc/Manual/ru/Makefile index c42065a..6175844 100644 --- a/doc/Manual/ru/Makefile +++ b/doc/Manual/ru/Makefile @@ -5,6 +5,7 @@ HTMLS = BH_Algo.html \ BH_Args.html \ BH_Box2f.html \ BH_Box3f.html \ + BH_Color.html \ BH_Hashmap.html \ BH_IO.html \ BH_Line.html \ @@ -31,6 +32,7 @@ MANS = BH_Algo.3 \ BH_Args.3 \ BH_Box2f.3 \ BH_Box3f.3 \ + BH_Color.3 \ BH_Hashmap.3 \ BH_IO.3 \ BH_Line.3 \ diff --git a/include/BH/Color.h b/include/BH/Color.h new file mode 100644 index 0000000..1fb2d73 --- /dev/null +++ b/include/BH/Color.h @@ -0,0 +1,151 @@ +#ifndef BH_COLOR_H +#define BH_COLOR_H + + +#include "Common.h" + + +#define BH_COLOR_TYPE_RGBA 0x0000 +#define BH_COLOR_TYPE_HSVA 0x0001 +#define BH_COLOR_TYPE_HSLA 0x0002 + + +#define BH_COLOR_MODE_NORMAL 0x0000 +#define BH_COLOR_MODE_MULTIPLY 0x0001 +#define BH_COLOR_MODE_SCREEN 0x0002 +#define BH_COLOR_MODE_OVERLAY 0x0003 +#define BH_COLOR_MODE_DARKEN 0x0004 +#define BH_COLOR_MODE_LIGHTEN 0x0005 +#define BH_COLOR_MODE_COLOR_DODGE 0x0006 +#define BH_COLOR_MODE_COLOR_BURN 0x0007 +#define BH_COLOR_MODE_LINEAR_DODGE 0x0008 +#define BH_COLOR_MODE_LINEAR_BURN 0x0009 +#define BH_COLOR_MODE_HARD_LIGHT 0x000A +#define BH_COLOR_MODE_SOFT_LIGHT 0x000B +#define BH_COLOR_MODE_DIFFERENCE 0x000C +#define BH_COLOR_MODE_EXCLUSION 0x000D +#define BH_COLOR_MODE_HUE 0x000E +#define BH_COLOR_MODE_SATURATION 0x000F +#define BH_COLOR_MODE_COLOR 0x0010 +#define BH_COLOR_MODE_LUMINOSITY 0x0011 + + +typedef struct BH_Color +{ + int type; + union + { + struct + { + uint16_t r; + uint16_t g; + uint16_t b; + uint16_t a; + } rgba; + struct + { + uint16_t h; + uint16_t s; + uint16_t v; + uint16_t a; + } hsva; + struct + { + uint16_t h; + uint16_t s; + uint16_t l; + uint16_t a; + } hsla; + } data; +} BH_Color; + + +void BH_ColorRGBA8(const BH_Color *color, + uint8_t *r, + uint8_t *g, + uint8_t *b, + uint8_t *a); + + +void BH_ColorRGBA16(const BH_Color *color, + uint16_t *r, + uint16_t *g, + uint16_t *b, + uint16_t *a); + + +void BH_ColorRGBAf(const BH_Color *color, + float *r, + float *g, + float *b, + float *a); + + +void BH_ColorSetRGBA8(BH_Color *color, + uint8_t r, + uint8_t g, + uint8_t b, + uint8_t a); + + +void BH_ColorSetRGBA16(BH_Color *color, + uint16_t r, + uint16_t g, + uint16_t b, + uint16_t a); + + +void BH_ColorSetRGBAf(BH_Color *color, + float r, + float g, + float b, + float a); + + +void BH_ColorHSVAf(const BH_Color *color, + float *h, + float *s, + float *v, + float *a); + + +void BH_ColorSetHSVAf(BH_Color *color, + float h, + float s, + float v, + float a); + + +void BH_ColorHSLAf(const BH_Color *color, + float *h, + float *s, + float *l, + float *a); + + +void BH_ColorSetHSLAf(BH_Color *color, + float h, + float s, + float l, + float a); + + +void BH_ColorToRGBA(const BH_Color *color, + BH_Color *out); + + +void BH_ColorToHSVA(const BH_Color *color, + BH_Color *out); + + +void BH_ColorToHSLA(const BH_Color *color, + BH_Color *out); + + +void BH_ColorBlend(const BH_Color *foreground, + const BH_Color *background, + int mode, + BH_Color *dest); + + +#endif /* BH_COLOR_H */ diff --git a/src/Color.c b/src/Color.c new file mode 100644 index 0000000..d264d1e --- /dev/null +++ b/src/Color.c @@ -0,0 +1,618 @@ +#include "BH/Common.h" +#include <BH/Color.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 BH_ColorRGBA8(const BH_Color *color, + uint8_t *r, + uint8_t *g, + uint8_t *b, + uint8_t *a) +{ + BH_Color tmp; + + BH_ColorToRGBA(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 BH_ColorRGBA16(const BH_Color *color, + uint16_t *r, + uint16_t *g, + uint16_t *b, + uint16_t *a) +{ + BH_Color tmp; + + BH_ColorToRGBA(color, &tmp); + *r = tmp.data.rgba.r; + *g = tmp.data.rgba.g; + *b = tmp.data.rgba.b; + *a = tmp.data.rgba.a; +} + + +void BH_ColorRGBAf(const BH_Color *color, + float *r, + float *g, + float *b, + float *a) +{ + BH_Color tmp; + + BH_ColorToRGBA(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 BH_ColorSetRGBA8(BH_Color *color, + uint8_t r, + uint8_t g, + uint8_t b, + uint8_t a) +{ + color->type = BH_COLOR_TYPE_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 BH_ColorSetRGBA16(BH_Color *color, + uint16_t r, + uint16_t g, + uint16_t b, + uint16_t a) +{ + color->type = BH_COLOR_TYPE_RGBA; + color->data.rgba.r = r; + color->data.rgba.g = g; + color->data.rgba.b = b; + color->data.rgba.a = a; +} + + +void BH_ColorSetRGBAf(BH_Color *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 = BH_COLOR_TYPE_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 BH_ColorHSVAf(const BH_Color *color, + float *h, + float *s, + float *v, + float *a) +{ + BH_Color tmp; + + BH_ColorToHSVA(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 BH_ColorSetHSVAf(BH_Color *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 = BH_COLOR_TYPE_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 BH_ColorHSLAf(const BH_Color *color, + float *h, + float *s, + float *l, + float *a) +{ + BH_Color tmp; + + BH_ColorToHSLA(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 BH_ColorSetHSLAf(BH_Color *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 = BH_COLOR_TYPE_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 BH_Color *color, + BH_Color *out) +{ + float min, max, d, r, g, b, a, h, s, v; + + BH_ColorRGBAf(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; + } + + BH_ColorSetHSVAf(out, h * 60.0f, s, v, a); +} + + +static void hsvToRgb(const BH_Color *color, + BH_Color *out) +{ + float h, s, v, a, r, g, b, fract, p, q, t; + int sextant; + BH_ColorHSVAf(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; + } + } + + BH_ColorSetRGBAf(out, r, g, b, a); +} + + +static void rgbToHsl(const BH_Color *color, + BH_Color *out) +{ + float min, max, d, r, g, b, a, h, s, l; + + BH_ColorRGBAf(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; + } + + BH_ColorSetHSLAf(out, h * 60.0f, s, l, a); +} + + +static void hslToHsv(const BH_Color *color, + BH_Color *out) +{ + float h, s, l, v, a; + + BH_ColorHSLAf(color, &h, &s, &l, &a); + v = s * MIN(l, 1.0f - l) + l; + s = (v) ? (2.0f - 2.0f * l / v) : (0.0f); + + BH_ColorSetHSVAf(out, h, s, v, a); +} + + +static void hsvToHsl(const BH_Color *color, + BH_Color *out) +{ + float h, s, v, l, a, m; + + BH_ColorHSVAf(color, &h, &s, &v, &a); + l = v - v * s / 2.0f; + m = MIN(l, 1.0f - l); + s = (m) ? ((v - l) / m) : (0); + BH_ColorSetHSLAf(out, h, s, l, a); +} + + +void BH_ColorToRGBA(const BH_Color *color, + BH_Color *out) +{ + switch (color->type) + { + case BH_COLOR_TYPE_RGBA: + *out = *color; + break; + + case BH_COLOR_TYPE_HSVA: + hsvToRgb(color, out); + break; + + case BH_COLOR_TYPE_HSLA: + hslToHsv(color, out); + hsvToRgb(out, out); + break; + } +} + + +void BH_ColorToHSVA(const BH_Color *color, + BH_Color *out) +{ + switch (color->type) + { + case BH_COLOR_TYPE_RGBA: + rgbToHsv(color, out); + break; + + case BH_COLOR_TYPE_HSVA: + *out = *color; + break; + + case BH_COLOR_TYPE_HSLA: + hslToHsv(color, out); + break; + } +} + + +void BH_ColorToHSLA(const BH_Color *color, + BH_Color *out) +{ + switch (color->type) + { + case BH_COLOR_TYPE_RGBA: + rgbToHsl(color, out); + break; + + case BH_COLOR_TYPE_HSVA: + hsvToHsl(color, out); + break; + + case BH_COLOR_TYPE_HSLA: + *out = *color; + break; + } +} + + +static float blendNormal(float foreground, + float background) +{ + BH_UNUSED(background); + return foreground; +} + + +static float blendMultiply(float foreground, + float background) +{ + return foreground * background; +} + + +static float blendScreen(float foreground, + float background) +{ + return foreground + background - (foreground * background); +} + + +static float blendDarken(float foreground, + float background) +{ + return MIN(foreground, background); +} + + +static float blendLighten(float foreground, + float background) +{ + return MAX(foreground, background); +} + + +static float blendColorDodge(float foreground, + float background) +{ + if (foreground != 1.0f) + return MIN(1.0f, background / (1.0f - foreground)); + return foreground; +} + + +static float blendColorBurn(float foreground, + float background) +{ + if (foreground != 0.0f) + return 1.0f - MIN(1.0f, (1.0f - background) / foreground); + return foreground; +} + + +static float blendLinearDodge(float foreground, + float background) +{ + return foreground + background; +} + + +static float blendLinearBurn(float foreground, + float background) +{ + return foreground + background - 1.0f; +} + + +static float blendDifference(float foreground, + float background) +{ + return fabsf(background - foreground); +} + + +static float blendExclusion(float foreground, + float background) +{ + return foreground + background - (2.0f * foreground * background); +} + + +static float blendHardLight(float foreground, + float background) +{ + if (foreground <= 0.5f) + return blendMultiply(2.0f * foreground, background); + else + return blendScreen(2.0f * foreground - 1.0f, background); +} + + +static float blendSoftLight(float foreground, + float background) +{ + return (1.0f - 2.0f * foreground) * background * background + 2.0f * foreground * background; +} + + +static float blendOverlay(float foreground, + float background) +{ + return blendHardLight(background, foreground); +} + + +void BH_ColorBlend(const BH_Color *foreground, + const BH_Color *background, + int mode, + BH_Color *dest) +{ + float br, bg, bb, ba; + float fr, fg, fb, fa; + + if (mode == BH_COLOR_MODE_HUE || mode == BH_COLOR_MODE_SATURATION || + mode == BH_COLOR_MODE_COLOR || mode == BH_COLOR_MODE_LUMINOSITY) + { + BH_ColorHSLAf(foreground, &fr, &fg, &fb, &fa); + BH_ColorHSLAf(background, &br, &bg, &bb, &ba); + } + else + { + BH_ColorRGBAf(foreground, &fr, &fg, &fb, &fa); + BH_ColorRGBAf(background, &br, &bg, &bb, &ba); + } + + switch (mode) + { + default: + case BH_COLOR_MODE_NORMAL: + fr = blendNormal(fr, br); + fg = blendNormal(fg, bg); + fb = blendNormal(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_MULTIPLY: + fr = blendMultiply(fr, br); + fg = blendMultiply(fg, bg); + fb = blendMultiply(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_SCREEN: + fr = blendScreen(fr, br); + fg = blendScreen(fg, bg); + fb = blendScreen(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_OVERLAY: + fr = blendOverlay(fr, br); + fg = blendOverlay(fg, bg); + fb = blendOverlay(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_DARKEN: + fr = blendDarken(fr, br); + fg = blendDarken(fg, bg); + fb = blendDarken(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_LIGHTEN: + fr = blendLighten(fr, br); + fg = blendLighten(fg, bg); + fb = blendLighten(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_COLOR_DODGE: + fr = blendColorDodge(fr, br); + fg = blendColorDodge(fg, bg); + fb = blendColorDodge(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_COLOR_BURN: + fr = blendColorBurn(fr, br); + fg = blendColorBurn(fg, bg); + fb = blendColorBurn(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_LINEAR_DODGE: + fr = blendLinearDodge(fr, br); + fg = blendLinearDodge(fg, bg); + fb = blendLinearDodge(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_LINEAR_BURN: + fr = blendLinearBurn(fr, br); + fg = blendLinearBurn(fg, bg); + fb = blendLinearBurn(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_HARD_LIGHT: + fr = blendHardLight(fr, br); + fg = blendHardLight(fg, bg); + fb = blendHardLight(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_SOFT_LIGHT: + fr = blendSoftLight(fr, br); + fg = blendSoftLight(fg, bg); + fb = blendSoftLight(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_DIFFERENCE: + fr = blendDifference(fr, br); + fg = blendDifference(fg, bg); + fb = blendDifference(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_EXCLUSION: + fr = blendExclusion(fr, br); + fg = blendExclusion(fg, bg); + fb = blendExclusion(fb, bb); + BH_ColorSetRGBAf(dest, fr, fg, fb, fa); + break; + + case BH_COLOR_MODE_HUE: + BH_ColorSetHSLAf(dest, fr, bg, bb, fa); + break; + + case BH_COLOR_MODE_SATURATION: + BH_ColorSetHSLAf(dest, br, fg, bb, fa); + break; + + case BH_COLOR_MODE_COLOR: + BH_ColorSetHSLAf(dest, fr, fg, bb, fa); + break; + + case BH_COLOR_MODE_LUMINOSITY: + BH_ColorSetHSLAf(dest, br, bg, fb, fa); + break; + } +} |
