colourpicker

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

util.c (8312B)


      1 /* See LICENSE for copyright details */
      2 #ifndef _UTIL_C_
      3 #define _UTIL_C_
      4 #include <emmintrin.h>
      5 #include <immintrin.h>
      6 
      7 #include <stddef.h>
      8 #include <stdint.h>
      9 
     10 #include "shader_inc.h"
     11 #include "lora_sb_0_inc.h"
     12 #include "lora_sb_1_inc.h"
     13 #include "config.h"
     14 
     15 #ifndef asm
     16 #define asm __asm__
     17 #endif
     18 
     19 #ifdef _DEBUG
     20 #define ASSERT(c) do { if (!(c)) asm("int3; nop"); } while (0);
     21 #define DEBUG_EXPORT
     22 #else
     23 #define ASSERT(c)
     24 #define DEBUG_EXPORT static
     25 #endif
     26 
     27 typedef uint8_t   u8;
     28 typedef int32_t   i32;
     29 typedef uint32_t  u32;
     30 typedef uint32_t  b32;
     31 typedef int64_t   i64;
     32 typedef float     f32;
     33 typedef double    f64;
     34 typedef ptrdiff_t size;
     35 
     36 typedef struct { size len; char *data; } s8;
     37 #define s8(s) (s8){.len = sizeof(s) - 1, .data = s}
     38 
     39 typedef union {
     40 	struct { u32 w, h; };
     41 	struct { u32 x, y; };
     42 	u32 E[2];
     43 } uv2;
     44 
     45 typedef union {
     46 	struct { f32 x, y; };
     47 	struct { f32 w, h; };
     48 	Vector2 rv;
     49 	f32 E[2];
     50 } v2;
     51 
     52 typedef union {
     53 	struct { f32 x, y, z, w; };
     54 	struct { f32 r, g, b, a; };
     55 	Vector4 rv;
     56 	f32 E[4];
     57 } v4;
     58 
     59 typedef union {
     60 	struct { v2 pos, size; };
     61 	Rectangle rr;
     62 } Rect;
     63 
     64 enum colour_mode {
     65 	CM_RGB  = 0,
     66 	CM_HSV  = 1,
     67 	CM_LAST
     68 };
     69 
     70 enum colour_picker_mode {
     71 	CPM_PICKER   = 0,
     72 	CPM_SLIDERS  = 1,
     73 	CPM_LAST
     74 };
     75 
     76 enum colour_picker_flags {
     77 	CPF_REFILL_TEXTURE = 1 << 0,
     78 	CPF_PRINT_DEBUG    = 1 << 30,
     79 };
     80 
     81 enum input_indices {
     82 	INPUT_HEX,
     83 	INPUT_R,
     84 	INPUT_G,
     85 	INPUT_B,
     86 	INPUT_A
     87 };
     88 
     89 enum mouse_pressed {
     90 	MOUSE_NONE   = 0 << 0,
     91 	MOUSE_LEFT   = 1 << 0,
     92 	MOUSE_RIGHT  = 1 << 1,
     93 };
     94 
     95 enum cardinal_direction { NORTH, EAST, SOUTH, WEST };
     96 
     97 #define WINDOW_ASPECT_RATIO    (4.3f/3.2f)
     98 
     99 #define BUTTON_HOVER_SPEED     8.0f
    100 
    101 #define SLIDER_BORDER_COLOUR   (Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0xCC}
    102 #define SLIDER_BORDER_WIDTH    3.0f
    103 #define SLIDER_ROUNDNESS       0.035f
    104 #define SLIDER_SCALE_SPEED     8.0f
    105 #define SLIDER_SCALE_TARGET    1.5f
    106 #define SLIDER_TRI_SIZE        (v2){.x = 6, .y = 8}
    107 
    108 #define SELECTOR_BORDER_COLOUR SLIDER_BORDER_COLOUR
    109 #define SELECTOR_BORDER_WIDTH  SLIDER_BORDER_WIDTH
    110 #define SELECTOR_ROUNDNESS     0.3f
    111 
    112 #define RECT_BTN_BORDER_WIDTH  (SLIDER_BORDER_WIDTH + 3.0f)
    113 
    114 #define TEXT_HOVER_SPEED       5.0f
    115 
    116 typedef struct {
    117 	f32 hover_t;
    118 } ButtonState;
    119 
    120 #define COLOUR_STACK_ITEMS 8
    121 typedef struct {
    122 	ButtonState buttons[COLOUR_STACK_ITEMS];
    123 	v4  items[COLOUR_STACK_ITEMS];
    124 	v4  last;
    125 	i32 widx;
    126 	f32 fade_param;
    127 	f32 y_off_t;
    128 	ButtonState tri_btn;
    129 } ColourStackState;
    130 
    131 typedef struct {
    132 	f32 scale_t[4];
    133 	f32 colour_t[4];
    134 } SliderState;
    135 
    136 typedef struct {
    137 	f32 hex_hover_t;
    138 	ButtonState mode;
    139 } StatusBarState;
    140 
    141 typedef struct {
    142 	ButtonState buttons[CPM_LAST];
    143 	f32 mode_visible_t;
    144 	i32 next_mode;
    145 } ModeChangeState;
    146 
    147 typedef struct {
    148 	f32 scale_t[3];
    149 	f32 base_hue;
    150 	f32 fractional_hue;
    151 } PickerModeState;
    152 
    153 typedef struct {
    154 	i32  idx;
    155 	i32  cursor;
    156 	f32  cursor_hover_p;
    157 	f32  cursor_t;
    158 	f32  cursor_t_target;
    159 	i32  buf_len;
    160 	char buf[64];
    161 } InputState;
    162 
    163 #ifdef _DEBUG
    164 enum clock_counts {
    165 	CC_WHOLE_RUN,
    166 	CC_DO_PICKER,
    167 	CC_DO_SLIDER,
    168 	CC_UPPER,
    169 	CC_LOWER,
    170 	CC_TEMP,
    171 	CC_LAST
    172 };
    173 
    174 static struct {
    175 	i64 cpu_cycles[CC_LAST];
    176 	i64 total_cycles[CC_LAST];
    177 	i64 hit_count[CC_LAST];
    178 } g_debug_clock_counts;
    179 
    180 #define BEGIN_CYCLE_COUNT(cc_name) \
    181 	g_debug_clock_counts.cpu_cycles[cc_name] = __rdtsc(); \
    182 	g_debug_clock_counts.hit_count[cc_name]++
    183 
    184 #define END_CYCLE_COUNT(cc_name) \
    185 	g_debug_clock_counts.cpu_cycles[cc_name] = __rdtsc() - g_debug_clock_counts.cpu_cycles[cc_name]; \
    186 	g_debug_clock_counts.total_cycles[cc_name] += g_debug_clock_counts.cpu_cycles[cc_name]
    187 
    188 #else
    189 #define BEGIN_CYCLE_COUNT(a)
    190 #define END_CYCLE_COUNT(a)
    191 #endif
    192 
    193 typedef struct {
    194 	v4 colour, previous_colour;
    195 	ColourStackState colour_stack;
    196 
    197 	uv2 window_size;
    198 	v2  window_pos;
    199 	v2  mouse_pos;
    200 
    201 	f32 dt;
    202 
    203 	Font  font;
    204 	Color bg, fg;
    205 
    206 	InputState      is;
    207 	ModeChangeState mcs;
    208 	PickerModeState pms;
    209 	SliderState     ss;
    210 	StatusBarState  sbs;
    211 	ButtonState     buttons[2];
    212 
    213 	i32 held_idx;
    214 
    215 	f32 selection_hover_t[2];
    216 	v4  hover_colour;
    217 	v4  cursor_colour;
    218 
    219 	Shader picker_shader;
    220 	RenderTexture slider_texture;
    221 	RenderTexture picker_texture;
    222 
    223 	i32 mode_id, colour_mode_id, colours_id;
    224 	i32 regions_id, radius_id, border_thick_id;
    225 
    226 	u32  flags;
    227 	enum colour_mode        colour_mode;
    228 	enum colour_picker_mode mode;
    229 } ColourPickerCtx;
    230 
    231 #define ARRAY_COUNT(a) (sizeof(a) / sizeof(*a))
    232 #define ABS(x)         ((x) < 0 ? (-x) : (x))
    233 #define MIN(a, b)      ((a) < (b) ? (a) : (b))
    234 #define CLAMP(x, a, b) ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
    235 #define CLAMP01(a)     CLAMP(a, 0, 1)
    236 
    237 #define ISDIGIT(a)     ((a) >= '0' && (a) <= '9')
    238 #define ISHEX(a)       (ISDIGIT(a) || ((a) >= 'a' && (a) <= 'f') || ((a) >= 'A' && (a) <= 'F'))
    239 
    240 static Color
    241 colour_from_normalized(v4 colour)
    242 {
    243 	__m128 colour_v = _mm_loadu_ps(colour.E);
    244 	__m128 scale    = _mm_set1_ps(255.0f);
    245 	__m128i result  = _mm_cvtps_epi32(_mm_mul_ps(colour_v, scale));
    246 	_Alignas(16) u32 outu[4];
    247 	_mm_store_si128((__m128i *)outu, result);
    248 	return (Color){.r = outu[0] & 0xFF, .g = outu[1] & 0xFF, .b = outu[2] & 0xFF, .a = outu[3] & 0xFF };
    249 }
    250 
    251 static v4
    252 rgb_to_hsv(v4 rgb)
    253 {
    254 	__m128 rgba = _mm_loadu_ps(rgb.E);
    255 	__m128 gbra = _mm_shuffle_ps(rgba, rgba, _MM_SHUFFLE(3, 0, 2, 1));
    256 	__m128 brga = _mm_shuffle_ps(gbra, gbra, _MM_SHUFFLE(3, 0, 2, 1));
    257 
    258 	__m128 Max  = _mm_max_ps(rgba, _mm_max_ps(gbra, brga));
    259 	__m128 Min  = _mm_min_ps(rgba, _mm_min_ps(gbra, brga));
    260 	__m128 C    = _mm_sub_ps(Max, Min);
    261 
    262 	__m128 zero = _mm_set1_ps(0);
    263 	__m128 six  = _mm_set1_ps(6);
    264 
    265 	_Alignas(16) f32 aval[4]  = {0, 2, 4, 0};
    266 	_Alignas(16) f32 scale[4] = {1.0/6.0f, 0, 0, 0};
    267 	/* NOTE if C == 0 then take H as 0/1 (which are equivalent in HSV) */
    268 	__m128 t    = _mm_div_ps(_mm_sub_ps(gbra, brga), C);
    269 	t           = _mm_and_ps(t, _mm_cmpneq_ps(zero, C));
    270 	t           = _mm_add_ps(t, _mm_load_ps(aval));
    271 	/* TODO: does (G - B) / C ever exceed 6.0? */
    272 	/* NOTE: Compute fmodf on element [0] */
    273 	t           = _mm_sub_ps(t, _mm_mul_ps(_mm_floor_ps(_mm_mul_ps(t, _mm_load_ps(scale))), six));
    274 
    275 	__m128 H = _mm_div_ps(_mm_and_ps(t, _mm_cmpeq_ps(rgba, Max)), six);
    276 	__m128 S = _mm_div_ps(C, Max);
    277 
    278 	/* NOTE: Make sure H & S are 0 instead of NaN when V == 0 */
    279 	H = _mm_and_ps(H, _mm_cmpneq_ps(zero, Max));
    280 	S = _mm_and_ps(S, _mm_cmpneq_ps(zero, Max));
    281 
    282 	__m128 H0 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 0, 0, 0));
    283 	__m128 H1 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 1, 1, 1));
    284 	__m128 H2 = _mm_shuffle_ps(H, H, _MM_SHUFFLE(3, 2, 2, 2));
    285 	H         = _mm_or_ps(H0, _mm_or_ps(H1, H2));
    286 
    287 	/* NOTE: keep only element [0] from H vector; Max contains V & A */
    288 	__m128 hva  = _mm_blend_ps(Max, H, 0x01);
    289 	__m128 hsva = _mm_blend_ps(hva, S, 0x02);
    290 	hsva        = _mm_min_ps(hsva, _mm_set1_ps(1));
    291 
    292 	v4 res;
    293 	_mm_storeu_ps(res.E, hsva);
    294 	return res;
    295 }
    296 
    297 static v4
    298 hsv_to_rgb(v4 hsv)
    299 {
    300 	/* f(k(n))   = V - V*S*max(0, min(k, min(4 - k, 1)))
    301 	 * k(n)      = fmod((n + H * 6), 6)
    302 	 * (R, G, B) = (f(n = 5), f(n = 3), f(n = 1))
    303 	 */
    304 	_Alignas(16) f32 nval[4] = {5.0f, 3.0f, 1.0f, 0.0f};
    305 	__m128 n   = _mm_load_ps(nval);
    306 	__m128 H   = _mm_set1_ps(hsv.x);
    307 	__m128 S   = _mm_set1_ps(hsv.y);
    308 	__m128 V   = _mm_set1_ps(hsv.z);
    309 	__m128 six = _mm_set1_ps(6);
    310 
    311 	__m128 t   = _mm_add_ps(n, _mm_mul_ps(six, H));
    312 	__m128 rem = _mm_floor_ps(_mm_div_ps(t, six));
    313 	__m128 k   = _mm_sub_ps(t, _mm_mul_ps(rem, six));
    314 
    315 	t = _mm_min_ps(_mm_sub_ps(_mm_set1_ps(4), k), _mm_set1_ps(1));
    316 	t = _mm_max_ps(_mm_set1_ps(0), _mm_min_ps(k, t));
    317 	t = _mm_mul_ps(t, _mm_mul_ps(S, V));
    318 
    319 	v4 rgba;
    320 	_mm_storeu_ps(rgba.E, _mm_sub_ps(V, t));
    321 	rgba.a = hsv.a;
    322 
    323 	return rgba;
    324 }
    325 
    326 static v4
    327 normalize_colour(u32 rgba)
    328 {
    329 	return (v4){
    330 		.r = ((rgba >> 24) & 0xFF) / 255.0f,
    331 		.g = ((rgba >> 16) & 0xFF) / 255.0f,
    332 		.b = ((rgba >>  8) & 0xFF) / 255.0f,
    333 		.a = ((rgba >>  0) & 0xFF) / 255.0f,
    334 	};
    335 }
    336 
    337 static u32
    338 pack_rl_colour(Color colour)
    339 {
    340 	return colour.r << 24 | colour.g << 16 | colour.b << 8 | colour.a << 0;
    341 }
    342 
    343 static u32
    344 parse_hex_u32(char *s)
    345 {
    346 	u32 res = 0;
    347 
    348 	/* NOTE: skip over '0x' or '0X' */
    349 	if (*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))
    350 		s += 2;
    351 
    352 	for (; *s; s++) {
    353 		res <<= 4;
    354 		if (ISDIGIT(*s)) {
    355 			res |= *s - '0';
    356 		} else if (ISHEX(*s)) {
    357 			/* NOTE: convert to lowercase first then convert to value */
    358 			*s  |= 0x20;
    359 			res |= *s - 0x57;
    360 		} else {
    361 			/* NOTE: do nothing (treat invalid value as 0) */
    362 		}
    363 	}
    364 	return res;
    365 }
    366 
    367 static f32
    368 parse_f32(char *s)
    369 {
    370 	return atof(s);
    371 }
    372 
    373 #endif /* _UTIL_C_ */