color-util: add helper to convert RGB → HSV
authorLennart Poettering <lennart@poettering.net>
Wed, 20 Dec 2023 11:08:32 +0000 (12:08 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 21 Dec 2023 18:15:01 +0000 (19:15 +0100)
We already have HSV → RGB, add the opposite operation.

src/shared/color-util.c
src/shared/color-util.h
src/test/meson.build
src/test/test-color-util.c [new file with mode: 0644]

index c02b4b62eb4c738a157de72d794e029483966793..776445ecfc5753bf53d3a4624517cd7db4538db5 100644 (file)
@@ -5,6 +5,43 @@
 #include "color-util.h"
 #include "macro.h"
 
+void rgb_to_hsv(double r, double g, double b,
+                double *ret_h, double *ret_s, double *ret_v) {
+
+        assert(r >= 0 && r <= 1);
+        assert(g >= 0 && g <= 1);
+        assert(b >= 0 && b <= 1);
+        assert(ret_h);
+        assert(ret_s);
+        assert(ret_v);
+
+        double max_color = fmax(r, fmax(g, b));
+        double min_color = fmin(r, fmin(g, b));
+        double delta = max_color - min_color;
+
+        *ret_v = max_color * 100.0;
+
+        if (max_color > 0)
+                *ret_s = delta / max_color * 100.0;
+        else {
+                *ret_s = 0;
+                *ret_h = NAN;
+                return;
+        }
+
+        if (delta > 0) {
+                if (r >= max_color)
+                        *ret_h = 60 * fmod((g - b) / delta, 6);
+                else if (g >= max_color)
+                        *ret_h = 60 * (((b - r) / delta) + 2);
+                else if (b >= max_color)
+                        *ret_h = 60 * (((r - g) / delta) + 4);
+
+                *ret_h = fmod(*ret_h, 360);
+        } else
+                *ret_h = NAN;
+}
+
 void hsv_to_rgb(double h, double s, double v,
                 uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b) {
 
index d527794d09d87f7bf116fee2adc3fc88f5da40eb..a4ae9eba09f08e7f29e2336b4fdc4b4327a46e4f 100644 (file)
@@ -3,6 +3,9 @@
 
 #include <inttypes.h>
 
+void rgb_to_hsv(double r, double g, double b,
+                double *ret_h, double *ret_s, double *ret_v);
+
 void hsv_to_rgb(
                 double h, double s, double v,
                 uint8_t* ret_r, uint8_t *ret_g, uint8_t *ret_b);
index b0a92bb43b9aa1a475f60b255387a83b121d03b0..aec125d483ac9d68fb4170f24c0bd341c1b6c0bb 100644 (file)
@@ -57,6 +57,7 @@ simple_tests += files(
         'test-cgroup.c',
         'test-chase.c',
         'test-clock.c',
+        'test-color-util.c',
         'test-compare-operator.c',
         'test-condition.c',
         'test-conf-files.c',
diff --git a/src/test/test-color-util.c b/src/test/test-color-util.c
new file mode 100644 (file)
index 0000000..3d00d87
--- /dev/null
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "color-util.h"
+#include "tests.h"
+
+TEST(hsv_to_rgb) {
+        uint8_t r, g, b;
+
+        hsv_to_rgb(0, 0, 0, &r, &g, &b);
+        assert(r == 0 && g == 0 && b == 0);
+
+        hsv_to_rgb(60, 0, 0, &r, &g, &b);
+        assert(r == 0 && g == 0 && b == 0);
+
+        hsv_to_rgb(0, 0, 100, &r, &g, &b);
+        assert(r == 255 && g == 255 && b == 255);
+
+        hsv_to_rgb(0, 100, 100, &r, &g, &b);
+        assert(r == 255 && g == 0 && b == 0);
+
+        hsv_to_rgb(120, 100, 100, &r, &g, &b);
+        assert(r == 0 && g == 255 && b == 0);
+
+        hsv_to_rgb(240, 100, 100, &r, &g, &b);
+        assert(r == 0 && g == 0 && b == 255);
+
+        hsv_to_rgb(311, 52, 62, &r, &g, &b);
+        assert(r == 158 && g == 75 && b == 143);
+}
+
+TEST(rgb_to_hsv) {
+
+        double h, s, v;
+        rgb_to_hsv(0, 0, 0, &h, &s, &v);
+        assert(s <= 0);
+        assert(v <= 0);
+
+        rgb_to_hsv(1, 1, 1, &h, &s, &v);
+        assert(s <= 0);
+        assert(v >= 100);
+
+        rgb_to_hsv(1, 0, 0, &h, &s, &v);
+        assert(h >= 359 || h <= 1);
+        assert(s >= 100);
+        assert(v >= 100);
+
+        rgb_to_hsv(0, 1, 0, &h, &s, &v);
+        assert(h >= 119 && h <= 121);
+        assert(s >= 100);
+        assert(v >= 100);
+
+        rgb_to_hsv(0, 0, 1, &h, &s, &v);
+        assert(h >= 239 && h <= 241);
+        assert(s >= 100);
+        assert(v >= 100);
+
+        rgb_to_hsv(0.5, 0.6, 0.7, &h, &s, &v);
+        assert(h >= 209 && h <= 211);
+        assert(s >= 28 && s <= 31);
+        assert(v >= 69 && v <= 71);
+}
+
+DEFINE_TEST_MAIN(LOG_DEBUG);