aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Romanko <me@blankhex.com>2025-07-25 10:41:15 +0300
committerMikhail Romanko <me@blankhex.com>2025-07-25 10:41:15 +0300
commit92fab9dbbae5d2a88c790f5d61cd53570826b370 (patch)
tree1fb582e72c7967e2668067b67d08269319b610ab
parentd527bd4686dbbc948d4d0dfbdc805f21a5695794 (diff)
downloadbhlib-92fab9dbbae5d2a88c790f5d61cd53570826b370.tar.gz
Add color manipulation utilities
-rw-r--r--.gitignore4
-rw-r--r--CMakeLists.txt2
-rw-r--r--Makefile.srcs1
-rw-r--r--doc/Manual/en/BH_Color.pod314
-rw-r--r--doc/Manual/en/Makefile2
-rw-r--r--doc/Manual/ru/BH_Color.pod319
-rw-r--r--doc/Manual/ru/Makefile2
-rw-r--r--include/BH/Color.h151
-rw-r--r--src/Color.c618
9 files changed, 1413 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 26e5a1b..feb75f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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;
+ }
+}