colourpicker

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

util.c (10336B)


      1 /* See LICENSE for copyright details */
      2 #ifndef _UTIL_C_
      3 #define _UTIL_C_
      4 #include <stddef.h>
      5 #include <stdint.h>
      6 
      7 typedef uint8_t   u8;
      8 typedef int32_t   s32;
      9 typedef uint32_t  u32;
     10 typedef uint32_t  b32;
     11 typedef int64_t   s64;
     12 typedef uint64_t  u64;
     13 typedef float     f32;
     14 typedef double    f64;
     15 typedef ptrdiff_t sz;
     16 
     17 #define function      static
     18 #define global        static
     19 #define local_persist static
     20 
     21 #include "lora_sb_0_inc.h"
     22 #include "lora_sb_1_inc.h"
     23 #include "config.h"
     24 
     25 /* NOTE(rnp): symbols in release builds shaders are embedded in the binary */
     26 #ifndef _DEBUG
     27 extern char _binary_slider_lerp_glsl_start[];
     28 #endif
     29 
     30 #ifndef asm
     31 #define asm __asm__
     32 #endif
     33 
     34 #define FORCE_INLINE inline __attribute__((always_inline))
     35 
     36 #define fmod_f32(a, b) __builtin_fmodf((a), (b))
     37 
     38 #ifdef __ARM_ARCH_ISA_A64
     39 #define debugbreak() asm volatile ("brk 0xf000")
     40 
     41 function FORCE_INLINE u64
     42 rdtsc(void)
     43 {
     44 	register u64 cntvct asm("x0");
     45 	asm volatile ("mrs x0, cntvct_el0" : "=x"(cntvct));
     46 	return cntvct;
     47 }
     48 
     49 #elif __x86_64__
     50 #include <immintrin.h>
     51 #define debugbreak() asm volatile ("int3; nop")
     52 
     53 #define rdtsc() __rdtsc()
     54 #endif
     55 
     56 #ifdef _DEBUG
     57 #define ASSERT(c) do { if (!(c)) debugbreak(); } while (0)
     58 #define DEBUG_EXPORT
     59 #else
     60 #define ASSERT(c)
     61 #define DEBUG_EXPORT function
     62 #endif
     63 
     64 typedef struct { sz len; u8 *data; } str8;
     65 #define str8(s) (str8){.len = sizeof(s) - 1, .data = (u8 *)s}
     66 
     67 typedef struct {
     68 	u8  *data;
     69 	u32 cap;
     70 	u32 widx;
     71 	s32 fd;
     72 	b32 errors;
     73 } Stream;
     74 
     75 typedef union {
     76 	struct { u32 w, h; };
     77 	struct { u32 x, y; };
     78 	u32 E[2];
     79 } uv2;
     80 
     81 typedef union {
     82 	struct { f32 x, y; };
     83 	struct { f32 w, h; };
     84 	Vector2 rv;
     85 	f32 E[2];
     86 } v2;
     87 
     88 typedef union {
     89 	struct { f32 x, y, z, w; };
     90 	struct { f32 r, g, b, a; };
     91 	Vector4 rv;
     92 	f32 E[4];
     93 } v4;
     94 
     95 typedef union {
     96 	struct { v2 pos, size; };
     97 	Rectangle rr;
     98 } Rect;
     99 
    100 typedef enum c{
    101 	ColourKind_RGB,
    102 	ColourKind_HSV,
    103 	ColourKind_Last,
    104 } ColourKind;
    105 
    106 enum colour_picker_mode {
    107 	CPM_PICKER   = 0,
    108 	CPM_SLIDERS  = 1,
    109 	CPM_LAST
    110 };
    111 
    112 typedef enum {
    113 	ColourPickerFlag_Ready         = 1 << 0,
    114 	ColourPickerFlag_RefillTexture = 1 << 1,
    115 	ColourPickerFlag_PrintDebug    = 1 << 30,
    116 } ColourPickerFlags;
    117 
    118 enum input_indices {
    119 	INPUT_HEX,
    120 	INPUT_R,
    121 	INPUT_G,
    122 	INPUT_B,
    123 	INPUT_A
    124 };
    125 
    126 enum mouse_pressed {
    127 	MOUSE_NONE   = 0 << 0,
    128 	MOUSE_LEFT   = 1 << 0,
    129 	MOUSE_RIGHT  = 1 << 1,
    130 };
    131 
    132 enum cardinal_direction { NORTH, EAST, SOUTH, WEST };
    133 
    134 #define WINDOW_ASPECT_RATIO    (4.3f/3.2f)
    135 
    136 #define BUTTON_HOVER_SPEED     8.0f
    137 
    138 #define SLIDER_BORDER_COLOUR   (Color){.r = 0x00, .g = 0x00, .b = 0x00, .a = 0xCC}
    139 #define SLIDER_BORDER_WIDTH    3.0f
    140 #define SLIDER_ROUNDNESS       0.035f
    141 #define SLIDER_SCALE_SPEED     8.0f
    142 #define SLIDER_SCALE_TARGET    1.5f
    143 #define SLIDER_TRI_SIZE        (v2){.x = 6, .y = 8}
    144 
    145 #define SELECTOR_BORDER_COLOUR SLIDER_BORDER_COLOUR
    146 #define SELECTOR_BORDER_WIDTH  SLIDER_BORDER_WIDTH
    147 #define SELECTOR_ROUNDNESS     0.3f
    148 
    149 #define RECT_BTN_BORDER_WIDTH  (SLIDER_BORDER_WIDTH + 3.0f)
    150 
    151 #define HOVER_SPEED            5.0f
    152 
    153 typedef struct {
    154 	f32 hover_t;
    155 } ButtonState;
    156 
    157 #define COLOUR_STACK_ITEMS 8
    158 typedef struct {
    159 	ButtonState buttons[COLOUR_STACK_ITEMS];
    160 	v4  items[COLOUR_STACK_ITEMS];
    161 	v4  last;
    162 	s32 widx;
    163 	f32 fade_param;
    164 	f32 y_off_t;
    165 	ButtonState tri_btn;
    166 } ColourStackState;
    167 
    168 typedef struct {
    169 	f32 scale_t[4];
    170 	f32 colour_t[4];
    171 } SliderState;
    172 
    173 typedef struct {
    174 	f32 hex_hover_t;
    175 	ButtonState mode;
    176 } StatusBarState;
    177 
    178 typedef struct {
    179 	ButtonState buttons[CPM_LAST];
    180 	f32 mode_visible_t;
    181 	s32 next_mode;
    182 } ModeChangeState;
    183 
    184 typedef struct {
    185 	f32 scale_t[3];
    186 	f32 base_hue;
    187 	f32 fractional_hue;
    188 } PickerModeState;
    189 
    190 typedef struct {
    191 	s32  idx; /* TODO(rnp): remove */
    192 	s32  count;
    193 	s32  cursor;
    194 	f32  cursor_hover_p; /* TODO(rnp): remove */
    195 	f32  cursor_t;
    196 	f32  cursor_t_target; /* TODO(rnp): remove */
    197 	u8   buf[64];
    198 } TextInputState;
    199 
    200 typedef struct {
    201 	str8 *labels;
    202 	u32   state;
    203 	u32   length;
    204 } Cycler;
    205 
    206 typedef struct Variable Variable;
    207 typedef enum {
    208 	InteractionKind_None,
    209 	InteractionKind_Set,
    210 	InteractionKind_Text,
    211 	InteractionKind_Drag,
    212 	InteractionKind_Scroll,
    213 } InteractionKind;
    214 
    215 typedef struct {
    216 	Variable *active;
    217 	Variable *hot;
    218 	Variable *next_hot;
    219 
    220 	Rect rect;
    221 	Rect hot_rect;
    222 
    223 	InteractionKind kind;
    224 } InteractionState;
    225 
    226 typedef enum {
    227 	VariableFlag_Text             = 1 << 0,
    228 	VariableFlag_UpdateStoredMode = 1 << 30,
    229 } VariableFlags;
    230 
    231 typedef enum {
    232 	VariableKind_F32,
    233 	VariableKind_U32,
    234 	VariableKind_F32Reference,
    235 	VariableKind_Button,
    236 	VariableKind_Cycler,
    237 	VariableKind_HexColourInput,
    238 } VariableKind;
    239 
    240 struct Variable {
    241 	union {
    242 		u32 U32;
    243 		f32 F32;
    244 		f32  *F32Reference;
    245 		void *generic;
    246 		Cycler  cycler;
    247 	};
    248 	VariableKind  kind;
    249 	VariableFlags flags;
    250 	f32           parameter;
    251 };
    252 
    253 typedef struct {
    254 	Variable colour_kind_cycler;
    255 } SliderModeState;
    256 
    257 typedef struct {
    258 	v4 colour, previous_colour;
    259 	ColourStackState colour_stack;
    260 
    261 	uv2 window_size;
    262 	v2  window_pos;
    263 	v2  mouse_pos;
    264 	v2  last_mouse;
    265 
    266 	Font  font;
    267 	Color bg, fg;
    268 
    269 	TextInputState   text_input_state;
    270 	InteractionState interaction;
    271 
    272 	ModeChangeState mcs;
    273 	PickerModeState pms;
    274 	SliderState     ss;
    275 	StatusBarState  sbs;
    276 	ButtonState     buttons[2];
    277 
    278 	SliderModeState slider_mode_state;
    279 
    280 	s32 held_idx;
    281 
    282 	f32 selection_hover_t[2];
    283 	v4  hover_colour;
    284 	v4  cursor_colour;
    285 
    286 	Shader picker_shader;
    287 	RenderTexture slider_texture;
    288 	RenderTexture picker_texture;
    289 
    290 	s32 mode_id, colour_mode_id, colours_id;
    291 	s32 regions_id, radius_id, border_thick_id;
    292 
    293 	ColourPickerFlags flags;
    294 	ColourKind        stored_colour_kind;
    295 	enum colour_picker_mode mode;
    296 } ColourPickerCtx;
    297 
    298 #define countof(a) (s64)(sizeof(a) / sizeof(*a))
    299 
    300 #define ABS(x)           ((x) < 0 ? (-x) : (x))
    301 #define BETWEEN(x, a, b) ((x) >= (a) && (x) <= (b))
    302 #define MIN(a, b)        ((a) < (b) ? (a) : (b))
    303 #define MAX(a, b)        ((a) < (b) ? (b) : (a))
    304 #define CLAMP(x, a, b)   ((x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x))
    305 #define CLAMP01(a)       CLAMP(a, 0, 1)
    306 
    307 #define ISDIGIT(a)     ((a) >= '0' && (a) <= '9')
    308 #define ISHEX(a)       (ISDIGIT(a) || ((a) >= 'a' && (a) <= 'f') || ((a) >= 'A' && (a) <= 'F'))
    309 
    310 #define InvalidDefaultCase default: ASSERT(0); break
    311 
    312 function v4
    313 rgb_to_hsv(v4 rgb)
    314 {
    315 	v4 hsv = {0};
    316 	f32 M = MAX(rgb.r, MAX(rgb.g, rgb.b));
    317 	f32 m = MIN(rgb.r, MIN(rgb.g, rgb.b));
    318 	if (M - m > 0) {
    319 		f32 C = M - m;
    320 		if (M == rgb.r) {
    321 			hsv.x = fmod_f32((rgb.g - rgb.b) / C, 6) / 6.0;
    322 		} else if (M == rgb.g) {
    323 			hsv.x = ((rgb.b - rgb.r) / C + 2) / 6.0;
    324 		} else {
    325 			hsv.x = ((rgb.r - rgb.g) / C + 4) / 6.0;
    326 		}
    327 		hsv.y = M? C / M : 0;
    328 		hsv.z = M;
    329 		hsv.a = rgb.a;
    330 	}
    331 	return hsv;
    332 }
    333 
    334 function v4
    335 hsv_to_rgb(v4 hsv)
    336 {
    337 	v4 rgba;
    338 	f32 k  = fmod_f32(5 + hsv.x * 6, 6);
    339 	rgba.r = hsv.z - hsv.z * hsv.y * MAX(0, MIN(1, MIN(k, 4 - k)));
    340 	k      = fmod_f32(3 + hsv.x * 6, 6);
    341 	rgba.g = hsv.z - hsv.z * hsv.y * MAX(0, MIN(1, MIN(k, 4 - k)));
    342 	k      = fmod_f32(1 + hsv.x * 6, 6);
    343 	rgba.b = hsv.z - hsv.z * hsv.y * MAX(0, MIN(1, MIN(k, 4 - k)));
    344 	rgba.a = hsv.a;
    345 	return rgba;
    346 }
    347 
    348 function v4
    349 normalize_colour(u32 rgba)
    350 {
    351 	v4 result;
    352 	result.r = ((rgba >> 24) & 0xFF) / 255.0f;
    353 	result.g = ((rgba >> 16) & 0xFF) / 255.0f;
    354 	result.b = ((rgba >>  8) & 0xFF) / 255.0f;
    355 	result.a = ((rgba >>  0) & 0xFF) / 255.0f;
    356 	return result;
    357 }
    358 
    359 function u32
    360 pack_rl_colour(Color colour)
    361 {
    362 	return colour.r << 24 | colour.g << 16 | colour.b << 8 | colour.a << 0;
    363 }
    364 
    365 function Color
    366 rl_colour_from_normalized(v4 colour)
    367 {
    368 	Color result;
    369 	result.r = colour.r * 255;
    370 	result.g = colour.g * 255;
    371 	result.b = colour.b * 255;
    372 	result.a = colour.a * 255;
    373 	return result;
    374 }
    375 
    376 function v2
    377 add_v2(v2 a, v2 b)
    378 {
    379 	v2 result;
    380 	result.x = a.x + b.x;
    381 	result.y = a.y + b.y;
    382 	return result;
    383 }
    384 
    385 function u32
    386 parse_hex_u32(str8 s)
    387 {
    388 	u32 res = 0;
    389 
    390 	/* NOTE: skip over '0x' or '0X' */
    391 	if (s.len > 2 && s.data[0] == '0' && (s.data[1] == 'x' || s.data[1] == 'X')) {
    392 		s.data += 2;
    393 		s.len  -= 2;
    394 	}
    395 
    396 	for (; s.len > 0; s.len--, s.data++) {
    397 		res <<= 4;
    398 		if (ISDIGIT(*s.data)) {
    399 			res |= *s.data - '0';
    400 		} else if (ISHEX(*s.data)) {
    401 			/* NOTE: convert to lowercase first then convert to value */
    402 			*s.data |= 0x20;
    403 			res     |= *s.data - 0x57;
    404 		} else {
    405 			/* NOTE: do nothing (treat invalid value as 0) */
    406 		}
    407 	}
    408 	return res;
    409 }
    410 
    411 function f64
    412 parse_f64(str8 s)
    413 {
    414 	f64 integral = 0, fractional = 0, sign = 1;
    415 
    416 	if (s.len && *s.data == '-') {
    417 		sign *= -1;
    418 		s.data++;
    419 		s.len--;
    420 	}
    421 
    422 	while (s.len && ISDIGIT(*s.data)) {
    423 		integral *= 10;
    424 		integral += *s.data - '0';
    425 		s.data++;
    426 		s.len--;
    427 	}
    428 
    429 	if (s.len && *s.data == '.') { s.data++; s.len--; }
    430 
    431 	while (s.len) {
    432 		ASSERT(s.data[s.len - 1] != '.');
    433 		fractional *= 0.1f;
    434 		fractional += (s.data[--s.len] - '0') * 0.1f;
    435 	}
    436 
    437 	f64 result = sign * (integral + fractional);
    438 
    439 	return result;
    440 }
    441 
    442 function str8
    443 str8_from_c_str(char *s)
    444 {
    445 	str8 result = {.data = (u8 *)s};
    446 	if (s) {
    447 		while (*s) s++;
    448 		result.len = (u8 *)s - result.data;
    449 	}
    450 	return result;
    451 }
    452 
    453 function void
    454 stream_append_byte(Stream *s, u8 b)
    455 {
    456 	s->errors |= s->widx + 1 > s->cap;
    457 	if (!s->errors)
    458 		s->data[s->widx++] = b;
    459 }
    460 
    461 function void
    462 stream_append_hex_u8(Stream *s, u32 n)
    463 {
    464 	local_persist u8 hex[16] = {"0123456789abcdef"};
    465 	s->errors |= (s->cap - s->widx) < 2;
    466 	if (!s->errors) {
    467 		s->data[s->widx + 1] = hex[(n >> 0) & 0x0f];
    468 		s->data[s->widx + 0] = hex[(n >> 4) & 0x0f];
    469 		s->widx += 2;
    470 	}
    471 }
    472 
    473 function void
    474 stream_append_str8(Stream *s, str8 str)
    475 {
    476 	s->errors |= (s->cap - s->widx) < str.len;
    477 	if (!s->errors) {
    478 		for (sz i = 0; i < str.len; i++)
    479 			s->data[s->widx++] = str.data[i];
    480 	}
    481 }
    482 
    483 function void
    484 stream_append_u64(Stream *s, u64 n)
    485 {
    486 	u8 tmp[64];
    487 	u8 *end = tmp + sizeof(tmp);
    488 	u8 *beg = end;
    489 	do { *--beg = '0' + (n % 10); } while (n /= 10);
    490 	stream_append_str8(s, (str8){.len = end - beg, .data = beg});
    491 }
    492 
    493 function void
    494 stream_append_f64(Stream *s, f64 f, s64 prec)
    495 {
    496 	if (f < 0) {
    497 		stream_append_byte(s, '-');
    498 		f *= -1;
    499 	}
    500 
    501 	/* NOTE: round last digit */
    502 	f += 0.5f / prec;
    503 
    504 	if (f >= (f64)(-1UL >> 1)) {
    505 		stream_append_str8(s, str8("inf"));
    506 	} else {
    507 		u64 integral = f;
    508 		u64 fraction = (f - integral) * prec;
    509 		stream_append_u64(s, integral);
    510 		stream_append_byte(s, '.');
    511 		for (u64 i = prec / 10; i > 1; i /= 10) {
    512 			if (i > fraction)
    513 				stream_append_byte(s, '0');
    514 		}
    515 		stream_append_u64(s, fraction);
    516 	}
    517 }
    518 
    519 function void
    520 stream_append_colour(Stream *s, Color c)
    521 {
    522 	stream_append_hex_u8(s, c.r);
    523 	stream_append_hex_u8(s, c.g);
    524 	stream_append_hex_u8(s, c.b);
    525 	stream_append_hex_u8(s, c.a);
    526 }
    527 
    528 #endif /* _UTIL_C_ */