colourpicker

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

util.c (11189B)


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