Logo

index : 0x2lib

Library extension for Jai

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/0x2lib.git/html/0x2_Colored.jai blob: a114bfdae0827d394fe3f6997ca77442184483d8 [raw] [clear marker]

        
0
1
2#module_parameters(ASSERTION_ENABLED := true);
3
4
5HSL :: struct {
6 h: u16;
7 s: u8;
8 l: u8;
9}
10
11RGB :: struct {
12 r, g, b: u8;
13}
14
15
16/* --------- *
17 * HSL procs *
18 * --------- */
19
20hsl_to_integers :: (using hsl: HSL) -> (h: int, s: int, l: int) {
21 return xx h, xx s, xx l;
22}
23
24hsl_to_rgb_hex :: (bg_color_hsl: string) -> string {
25 hsl := string_to_hsl(bg_color_hsl, disable_checks=true);
26 rgb := hsl_to_rgb(hsl);
27 hex := rgb_to_hex(rgb);
28
29 return hex;
30}
31
32/** https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative */
33hsl_to_rgb :: (hsl: HSL) -> RGB {
34 MAGIC_RED :: 0;
35 MAGIC_GREEN :: 8;
36 MAGIC_BLUE :: 4;
37
38 convert :: (n: float, h: float, s: float, l: float) -> int {
39 k := fmod_cycling(n + (h / 30.0), 12.0);
40 a := s * min(l, 1.0 - l);
41
42 value := l - a * max(-1.0, min(min(k - 3.0, 9.0 - k), 1.0));
43 value *= RGB_MAX;
44
45 return round(value);
46 }
47
48
49 h := cast(float, hsl.h);
50 s := hsl.s / 100.0;
51 l := hsl.l / 100.0;
52
53 r := convert(MAGIC_RED, h, s, l);
54 g := convert(MAGIC_GREEN, h, s, l);
55 b := convert(MAGIC_BLUE, h, s, l);
56
57 #if ASSERTION_ENABLED then for int.[r, g, b] {
58 assert(it >= 0 && it <= U8_MAX, "Does not fit inside U8");
59 }
60
61 rgb: RGB;
62 rgb.r = xx r;
63 rgb.g = xx g;
64 rgb.b = xx b;
65 return rgb;
66}
67
68
69/* --------- *
70 * RGB procs *
71 * --------- */
72
73rgb_to_integers :: (using rgb: RGB) -> (r: int, g: int, b: int) {
74 return xx r, xx g, xx b;
75}
76
77/** Idk if people really need a runtime lower case option. We'll see. */
78rgb_to_hex :: (using rgb: RGB, $lower_case := false) -> string {
79 #if lower_case {
80 CHARS :: #run to_lower_copy(HEX_ALPHA);
81 } else {
82 CHARS :: HEX_ALPHA;
83 }
84
85 result := alloc_string(RGB_HEX_STRING_LENGTH);
86 result.data[0] = #char "#";
87
88 for u8.[r, g, b] {
89 result.data[it_index * 2 + 1] = CHARS[it >> 4];
90 result.data[it_index * 2 + 2] = CHARS[it & 0xF];
91 }
92
93 return result;
94}
95
96/** https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB */
97rgb_to_hsl :: (rgb: RGB) -> HSL {
98 MAGIC_RED :: 6.0;
99 MAGIC_GREEN :: 2.0;
100 MAGIC_BLUE :: 4.0;
101
102 r := rgb.r / RGB_FMAX;
103 g := rgb.g / RGB_FMAX;
104 b := rgb.b / RGB_FMAX;
105
106 #if ASSERTION_ENABLED then for float.[r, g, b] {
107 assert(it >= 0.0 && it <= 1.0, "RGB value is not within bounds");
108 }
109
110 max_c := max(r, max(g, b));
111 min_c := min(r, min(g, b));
112
113 delta := max_c - min_c;
114
115 l := (max_c + min_c) / 2.0;
116
117 s: float;
118 if delta != 0.0 {
119 s = delta / (1.0 - abs(2.0 * l - 1.0));
120 }
121
122 h: float;
123 if delta != 0.0 {
124 if max_c == r {
125 h = fmod_cycling((g - b) / delta, MAGIC_RED);
126 }
127 else if max_c == g {
128 h = (b - r) / delta + MAGIC_GREEN;
129 }
130 else {
131 h = (r - g) / delta + MAGIC_BLUE;
132 }
133
134 h *= 60.0;
135 if h < 0.0 then h += 360.0;
136 }
137
138 h = roundf(h);
139 s = roundf(s * 100.0);
140 l = roundf(l * 100.0);
141
142 #if ASSERTION_ENABLED assert(h >= 0.0 && h <= 360.0, "Hue value is not within expected bounds");
143
144 #if ASSERTION_ENABLED then for float.[s, l] {
145 assert(it >= 0.0 && it <= 100, "SL value is not within expected bounds");
146 }
147
148 hsl: HSL;
149 hsl.h = xx h;
150 hsl.s = xx s;
151 hsl.l = xx l;
152 return hsl;
153}
154
155
156/* -------------- *
157 * int_to_* procs *
158 * -------------- */
159
160integers_to_hsl :: (h: int, s: int, l: int, loc := #caller_location) -> HSL {
161 #if ASSERTION_ENABLED {
162 assert(h >= 0 && h <= 360, "%: H value is not within expected bounds", loc);
163 assert(s >= 0 && s <= 100, "%: S value is not within expected bounds", loc);
164 assert(l >= 0 && l <= 100, "%: L value is not within expected bounds", loc);
165 }
166
167 hsl: HSL = ---;
168 hsl.h = xx h;
169 hsl.s = xx s;
170 hsl.l = xx l;
171 return hsl;
172}
173
174integers_to_rgb :: (r: int, g: int, b: int, loc := #caller_location) -> RGB {
175 #if ASSERTION_ENABLED {
176 assert(r >= 0 && r <= RGB_MAX, "%: R value is not within expected bounds", loc);
177 assert(g >= 0 && g <= RGB_MAX, "%: G value is not within expected bounds", loc);
178 assert(b >= 0 && b <= RGB_MAX, "%: B value is not within expected bounds", loc);
179 }
180
181 rgb: RGB = ---;
182 rgb.r = xx r;
183 rgb.g = xx g;
184 rgb.b = xx b;
185 return rgb;
186}
187
188
189/* ----------------- *
190 * string_to_* procs *
191 * ----------------- */
192
193string_to_hsl :: (maybe_hsl: string, $disable_checks := false, $do_logging := true, loc := #caller_location) -> (h: HSL = {}, ok: bool = false) {
194 hsl_arr := split(maybe_hsl, " ",, temp);
195
196 #if !disable_checks {
197 if hsl_arr.count != 3 {
198 #if do_logging then log_error(
199 "%: Could not convert HSL string to HSL values. Your string does not have three value pairs: '%'",
200 loc,
201 maybe_hsl,
202 flags=.ERROR,
203 );
204 return;
205 }
206
207 for digit: hsl_arr for digit {
208 if !is_digit(it) {
209 #if do_logging then log_error("%: '%' is not a digit", loc, it);
210 return;
211 }
212 }
213 }
214
215 h := to_integer(hsl_arr[0]);
216 s := to_integer(hsl_arr[1]);
217 l := to_integer(hsl_arr[2]);
218
219 hsl := integers_to_hsl(h, s, l, loc=loc);
220
221 return hsl, true;
222}
223
224string_to_rgb :: (maybe_rgb: string, $disable_checks := false, $do_logging := true, loc := #caller_location) -> (r: RGB = {}, ok: bool = false) {
225
226 parse_byte :: (s: string) -> (ok: bool, b: u8) #expand {
227 hi_ok, hi := char_to_hex(s[0]);
228 lo_ok, lo := char_to_hex(s[1]);
229 if !hi_ok || !lo_ok {
230 #if `do_logging log_error("%: RGB value '%' is not in hex range", `loc, s);
231 return false, 0;
232 }
233 byte := hi << 4 | lo;
234 return true, byte;
235 }
236
237 char_to_hex :: (c: u8) -> (ok: bool, u8) {
238 if c >= #char "0" && c <= #char "9" return true, c - #char "0";
239 if c >= #char "A" && c <= #char "F" return true, c - #char "A" + 10;
240 return false, 0;
241 }
242
243
244 possibly_rgb: string;
245
246 #if !disable_checks {
247 if maybe_rgb.count != RGB_HEX_STRING_LENGTH {
248 #if do_logging then log_error(
249 "%: % Does not equal % characters in '%'",
250 loc,
251 RGB_ERROR_MSG,
252 RGB_HEX_STRING_LENGTH,
253 maybe_rgb
254 );
255 return;
256 }
257
258 if maybe_rgb.count > 0 && maybe_rgb[0] != "#" {
259 #if do_logging then log_error(
260 "%: % Does not start with '#' in '%'",
261 loc,
262 RGB_ERROR_MSG,
263 maybe_rgb
264 );
265 return;
266 }
267
268 for maybe_rgb if (it >= #char "a" && it <= #char "z") {
269 #if do_logging then log_error(
270 "%: % Is not upper case in '%'",
271 loc,
272 RGB_ERROR_MSG,
273 maybe_rgb
274 );
275 return;
276 }
277
278 possibly_rgb = advance(maybe_rgb);
279
280 for possibly_rgb {
281 if !contains(HEX_ALPHA, it) {
282 #if do_logging then log_error(
283 "%: '%' Invalid character: '%' in '%'",
284 loc,
285 RGB_ERROR_MSG,
286 it,
287 maybe_rgb
288 );
289 return;
290 }
291 }
292 }
293
294 ok: bool;
295 rgb: RGB;
296 ok, rgb.r = parse_byte(slice(maybe_rgb, 1, 2)); if !ok return;
297 ok, rgb.g = parse_byte(slice(maybe_rgb, 3, 2)); if !ok return;
298 ok, rgb.b = parse_byte(slice(maybe_rgb, 5, 2)); if !ok return;
299 return rgb, true;
300}
301
302
303#scope_file
304
305
306HEX_ALPHA :: "0123456789ABCDEF";
307
308RGB_MAX :: 255;
309RGB_FMAX :: 255.0;
310RGB_HEX_STRING_LENGTH :: 7;
311
312RGB_ERROR_MSG :: "Invalid RGB string.";
313
314
315roundf :: inline (x: float) -> float {
316 return floor(x + 0.5);
317}
318
319round :: inline (x: float) -> int {
320 return xx floor(x + 0.5);
321}
322
323
324using,only(
325 log_error, min, max, temp, assert, alloc_string, to_integer, advance, is_digit
326) Basic :: #import "Basic";
327
328using,only(slice, split, contains) String :: #import "String";
329using,only(fmod_cycling, U8_MAX, abs, floor) Math :: #import "Math";
330
331
332/*
333------------------------------------------------------------------------------
334This software is available under 2 licenses -- choose whichever you prefer.
335------------------------------------------------------------------------------
336ALTERNATIVE A - MIT License
337Copyright (c) 2026 Adam Blazeowsky
338Permission is hereby granted, free of charge, to any person obtaining a copy of
339this software and associated documentation files (the "Software"), to deal in
340the Software without restriction, including without limitation the rights to
341use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
342of the Software, and to permit persons to whom the Software is furnished to do
343so, subject to the following conditions:
344The above copyright notice and this permission notice shall be included in all
345copies or substantial portions of the Software.
346THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
347IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
348FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
349AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
350LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
351OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
352SOFTWARE.
353------------------------------------------------------------------------------
354ALTERNATIVE B - Public Domain (www.unlicense.org)
355This is free and unencumbered software released into the public domain.
356Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
357software, either in source code form or as a compiled binary, for any purpose,
358commercial or non-commercial, and by any means.
359In jurisdictions that recognize copyright laws, the author or authors of this
360software dedicate any and all copyright interest in the software to the public
361domain. We make this dedication for the benefit of the public at large and to
362the detriment of our heirs and successors. We intend this dedication to be an
363overt act of relinquishment in perpetuity of all present and future rights to
364this software under copyright law.
365THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
366IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
367FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
368AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
369ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
370WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
371------------------------------------------------------------------------------
372*/
373
374
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit