colourpicker

Simple Colour Picker written in C
git clone anongit@rnpnr.xyz:colourpicker.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 2474058570b92f13747294e04fbb851ab5b2ab6a
Parent: 42466716c04ecb1502bedf711f2e0e78d8fa7d8e
Author: Randy Palamar
Date:   Wed, 24 Jul 2024 20:24:47 -0600

dump colour to stdout on close

Diffstat:
Mcolourpicker.c | 90-------------------------------------------------------------------------------
Mmain.c | 17++++++++++++++++-
Mutil.c | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 105 insertions(+), 91 deletions(-)

diff --git a/colourpicker.c b/colourpicker.c @@ -1,7 +1,4 @@ /* See LICENSE for copyright details */ -#include <emmintrin.h> -#include <immintrin.h> - #include <raylib.h> #include <stdio.h> @@ -81,93 +78,6 @@ cut_rect_right(Rect r, f32 fraction) return r; } -static Color -colour_from_normalized(v4 colour) -{ - __m128 colour_v = _mm_loadu_ps(colour.E); - __m128 scale = _mm_set1_ps(255.0f); - __m128i result = _mm_cvtps_epi32(_mm_mul_ps(colour_v, scale)); - _Alignas(16) u32 outu[4]; - _mm_store_si128((__m128i *)outu, result); - return (Color){.r = outu[0] & 0xFF, .g = outu[1] & 0xFF, .b = outu[2] & 0xFF, .a = outu[3] & 0xFF }; -} - -static v4 -rgb_to_hsv(v4 rgb) -{ - __m128 rgba = _mm_loadu_ps(rgb.E); - __m128 gbra = _mm_shuffle_ps(rgba, rgba, _MM_SHUFFLE(3, 0, 2, 1)); - __m128 brga = _mm_shuffle_ps(gbra, gbra, _MM_SHUFFLE(3, 0, 2, 1)); - - __m128 Max = _mm_max_ps(rgba, _mm_max_ps(gbra, brga)); - __m128 Min = _mm_min_ps(rgba, _mm_min_ps(gbra, brga)); - __m128 C = _mm_sub_ps(Max, Min); - - __m128 t = _mm_div_ps(_mm_sub_ps(gbra, brga), C); - - _Alignas(16) f32 aval[4] = { 0, 2, 4, 0 }; - t = _mm_add_ps(t, _mm_load_ps(aval)); - - /* TODO: does (G - B) / C ever exceed 6.0? */ - /* NOTE: 1e9 ensures that the remainder after floor is 0. - * This limits the fmodf to apply only to element [0] */ - _Alignas(16) f32 div[4] = { 6, 1e9, 1e9, 1e9 }; - __m128 six = _mm_set1_ps(6); - __m128 rem = _mm_floor_ps(_mm_div_ps(t, _mm_load_ps(div))); - t = _mm_sub_ps(t, _mm_mul_ps(rem, six)); - - __m128 zero = _mm_set1_ps(0); - __m128 maxmask = _mm_cmpeq_ps(rgba, Max); - - __m128 H = _mm_div_ps(_mm_blendv_ps(zero, t, maxmask), six); - __m128 S = _mm_div_ps(C, Max); - - /* NOTE: Make sure H & S are 0 instead of NaN when V == 0 */ - __m128 zeromask = _mm_cmpeq_ps(zero, Max); - H = _mm_blendv_ps(H, zero, zeromask); - S = _mm_blendv_ps(S, zero, zeromask); - - __m128 H0 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 0, 0, 0)); - __m128 H1 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 1, 1, 1)); - __m128 H2 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 2, 2, 2)); - H = _mm_or_ps(H0, _mm_or_ps(H1, H2)); - - /* NOTE: keep only element [0] from H vector; Max contains V & A */ - __m128 hva = _mm_blend_ps(Max, H, 0x01); - v4 res; - _mm_storeu_ps(res.E, _mm_blend_ps(hva, S, 0x02)); - return res; -} - -static v4 -hsv_to_rgb(v4 hsv) -{ - /* f(k(n)) = V - V*S*max(0, min(k, min(4 - k, 1))) - * k(n) = fmod((n + H * 6), 6) - * (R, G, B) = (f(n = 5), f(n = 3), f(n = 1)) - */ - _Alignas(16) f32 nval[4] = {5.0f, 3.0f, 1.0f, 0.0f}; - __m128 n = _mm_load_ps(nval); - __m128 H = _mm_set1_ps(hsv.x); - __m128 S = _mm_set1_ps(hsv.y); - __m128 V = _mm_set1_ps(hsv.z); - __m128 six = _mm_set1_ps(6); - - __m128 t = _mm_add_ps(n, _mm_mul_ps(six, H)); - __m128 rem = _mm_floor_ps(_mm_div_ps(t, six)); - __m128 k = _mm_sub_ps(t, _mm_mul_ps(rem, six)); - - t = _mm_min_ps(_mm_sub_ps(_mm_set1_ps(4), k), _mm_set1_ps(1)); - t = _mm_max_ps(_mm_set1_ps(0), _mm_min_ps(k, t)); - t = _mm_mul_ps(t, _mm_mul_ps(S, V)); - - v4 rgba; - _mm_storeu_ps(rgba.E, _mm_sub_ps(V, t)); - rgba.a = hsv.a; - - return rgba; -} - static void fill_hsv_texture(RenderTexture texture, v4 hsv) { diff --git a/main.c b/main.c @@ -96,7 +96,7 @@ main(void) }; #ifndef _DEBUG - SetTraceLogLevel(LOG_ERROR); + SetTraceLogLevel(LOG_NONE); #endif SetConfigFlags(FLAG_VSYNC_HINT); @@ -114,4 +114,19 @@ main(void) do_colour_picker(&ctx); EndDrawing(); } + + v4 rgba = {0}; + switch (ctx.mode) { + case CPM_RGB: rgba = ctx.colour; break; + case CPM_HSV: rgba = hsv_to_rgb(ctx.colour); break; + default: ASSERT(0); break; + } + + Color rl = colour_from_normalized(rgba); + u32 packed_rgba = rl.r << 24 | rl.g << 16 | rl.b << 8 | rl.a << 0; + + printf("0x%08X|{.r = %0.02f, .g = %0.02f, .b = %0.02f, .a = %0.02f}\n", + packed_rgba, rgba.r, rgba.g, rgba.b, rgba.a); + + return 0; } diff --git a/util.c b/util.c @@ -1,6 +1,8 @@ /* See LICENSE for copyright details */ #ifndef _UTIL_C_ #define _UTIL_C_ +#include <emmintrin.h> +#include <immintrin.h> #include <stddef.h> #include <stdint.h> @@ -85,4 +87,91 @@ typedef struct { #define ABS(x) ((x) < 0 ? (-x) : (x)) #define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)) +static Color +colour_from_normalized(v4 colour) +{ + __m128 colour_v = _mm_loadu_ps(colour.E); + __m128 scale = _mm_set1_ps(255.0f); + __m128i result = _mm_cvtps_epi32(_mm_mul_ps(colour_v, scale)); + _Alignas(16) u32 outu[4]; + _mm_store_si128((__m128i *)outu, result); + return (Color){.r = outu[0] & 0xFF, .g = outu[1] & 0xFF, .b = outu[2] & 0xFF, .a = outu[3] & 0xFF }; +} + +static v4 +rgb_to_hsv(v4 rgb) +{ + __m128 rgba = _mm_loadu_ps(rgb.E); + __m128 gbra = _mm_shuffle_ps(rgba, rgba, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 brga = _mm_shuffle_ps(gbra, gbra, _MM_SHUFFLE(3, 0, 2, 1)); + + __m128 Max = _mm_max_ps(rgba, _mm_max_ps(gbra, brga)); + __m128 Min = _mm_min_ps(rgba, _mm_min_ps(gbra, brga)); + __m128 C = _mm_sub_ps(Max, Min); + + __m128 t = _mm_div_ps(_mm_sub_ps(gbra, brga), C); + + _Alignas(16) f32 aval[4] = { 0, 2, 4, 0 }; + t = _mm_add_ps(t, _mm_load_ps(aval)); + + /* TODO: does (G - B) / C ever exceed 6.0? */ + /* NOTE: 1e9 ensures that the remainder after floor is 0. + * This limits the fmodf to apply only to element [0] */ + _Alignas(16) f32 div[4] = { 6, 1e9, 1e9, 1e9 }; + __m128 six = _mm_set1_ps(6); + __m128 rem = _mm_floor_ps(_mm_div_ps(t, _mm_load_ps(div))); + t = _mm_sub_ps(t, _mm_mul_ps(rem, six)); + + __m128 zero = _mm_set1_ps(0); + __m128 maxmask = _mm_cmpeq_ps(rgba, Max); + + __m128 H = _mm_div_ps(_mm_blendv_ps(zero, t, maxmask), six); + __m128 S = _mm_div_ps(C, Max); + + /* NOTE: Make sure H & S are 0 instead of NaN when V == 0 */ + __m128 zeromask = _mm_cmpeq_ps(zero, Max); + H = _mm_blendv_ps(H, zero, zeromask); + S = _mm_blendv_ps(S, zero, zeromask); + + __m128 H0 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 0, 0, 0)); + __m128 H1 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 1, 1, 1)); + __m128 H2 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 2, 2, 2)); + H = _mm_or_ps(H0, _mm_or_ps(H1, H2)); + + /* NOTE: keep only element [0] from H vector; Max contains V & A */ + __m128 hva = _mm_blend_ps(Max, H, 0x01); + v4 res; + _mm_storeu_ps(res.E, _mm_blend_ps(hva, S, 0x02)); + return res; +} + +static v4 +hsv_to_rgb(v4 hsv) +{ + /* f(k(n)) = V - V*S*max(0, min(k, min(4 - k, 1))) + * k(n) = fmod((n + H * 6), 6) + * (R, G, B) = (f(n = 5), f(n = 3), f(n = 1)) + */ + _Alignas(16) f32 nval[4] = {5.0f, 3.0f, 1.0f, 0.0f}; + __m128 n = _mm_load_ps(nval); + __m128 H = _mm_set1_ps(hsv.x); + __m128 S = _mm_set1_ps(hsv.y); + __m128 V = _mm_set1_ps(hsv.z); + __m128 six = _mm_set1_ps(6); + + __m128 t = _mm_add_ps(n, _mm_mul_ps(six, H)); + __m128 rem = _mm_floor_ps(_mm_div_ps(t, six)); + __m128 k = _mm_sub_ps(t, _mm_mul_ps(rem, six)); + + t = _mm_min_ps(_mm_sub_ps(_mm_set1_ps(4), k), _mm_set1_ps(1)); + t = _mm_max_ps(_mm_set1_ps(0), _mm_min_ps(k, t)); + t = _mm_mul_ps(t, _mm_mul_ps(S, V)); + + v4 rgba; + _mm_storeu_ps(rgba.E, _mm_sub_ps(V, t)); + rgba.a = hsv.a; + + return rgba; +} + #endif /* _UTIL_C_ */