<<
path:
root/public/0x2lib.git/html/0x2_Colored.jai
blob: a114bfdae0827d394fe3f6997ca77442184483d8
[raw]
[clear marker]
2#module_parameters(ASSERTION_ENABLED := true);
20hsl_to_integers :: (using hsl: HSL) -> (h: int, s: int, l: int) {
21 return xx h, xx s, xx l;
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);
32/** https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative */
33hsl_to_rgb :: (hsl: HSL) -> RGB {
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);
42 value := l - a * max(-1.0, min(min(k - 3.0, 9.0 - k), 1.0));
49 h := cast(float, hsl.h);
53 r := convert(MAGIC_RED, h, s, l);
54 g := convert(MAGIC_GREEN, h, s, l);
55 b := convert(MAGIC_BLUE, h, s, l);
57 #if ASSERTION_ENABLED then for int.[r, g, b] {
58 assert(it >= 0 && it <= U8_MAX, "Does not fit inside U8");
73rgb_to_integers :: (using rgb: RGB) -> (r: int, g: int, b: int) {
74 return xx r, xx g, xx b;
77/** Idk if people really need a runtime lower case option. We'll see. */
78rgb_to_hex :: (using rgb: RGB, $lower_case := false) -> string {
80 CHARS :: #run to_lower_copy(HEX_ALPHA);
85 result := alloc_string(RGB_HEX_STRING_LENGTH);
86 result.data[0] = #char "#";
89 result.data[it_index * 2 + 1] = CHARS[it >> 4];
90 result.data[it_index * 2 + 2] = CHARS[it & 0xF];
96/** https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB */
97rgb_to_hsl :: (rgb: RGB) -> HSL {
102 r := rgb.r / RGB_FMAX;
103 g := rgb.g / RGB_FMAX;
104 b := rgb.b / RGB_FMAX;
106 #if ASSERTION_ENABLED then for float.[r, g, b] {
107 assert(it >= 0.0 && it <= 1.0, "RGB value is not within bounds");
110 max_c := max(r, max(g, b));
111 min_c := min(r, min(g, b));
113 delta := max_c - min_c;
115 l := (max_c + min_c) / 2.0;
119 s = delta / (1.0 - abs(2.0 * l - 1.0));
125 h = fmod_cycling((g - b) / delta, MAGIC_RED);
128 h = (b - r) / delta + MAGIC_GREEN;
131 h = (r - g) / delta + MAGIC_BLUE;
135 if h < 0.0 then h += 360.0;
139 s = roundf(s * 100.0);
140 l = roundf(l * 100.0);
142 #if ASSERTION_ENABLED assert(h >= 0.0 && h <= 360.0, "Hue value is not within expected bounds");
144 #if ASSERTION_ENABLED then for float.[s, l] {
145 assert(it >= 0.0 && it <= 100, "SL value is not within expected bounds");
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);
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);
189/* ----------------- *
190 * string_to_* procs *
191 * ----------------- */
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);
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: '%'",
207 for digit: hsl_arr for digit {
209 #if do_logging then log_error("%: '%' is not a digit", loc, it);
215 h := to_integer(hsl_arr[0]);
216 s := to_integer(hsl_arr[1]);
217 l := to_integer(hsl_arr[2]);
219 hsl := integers_to_hsl(h, s, l, loc=loc);
224string_to_rgb :: (maybe_rgb: string, $disable_checks := false, $do_logging := true, loc := #caller_location) -> (r: RGB = {}, ok: bool = false) {
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);
233 byte := hi << 4 | lo;
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;
244 possibly_rgb: string;
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 '%'",
252 RGB_HEX_STRING_LENGTH,
258 if maybe_rgb.count > 0 && maybe_rgb[0] != "#" {
259 #if do_logging then log_error(
260 "%: % Does not start with '#' in '%'",
268 for maybe_rgb if (it >= #char "a" && it <= #char "z") {
269 #if do_logging then log_error(
270 "%: % Is not upper case in '%'",
278 possibly_rgb = advance(maybe_rgb);
281 if !contains(HEX_ALPHA, it) {
282 #if do_logging then log_error(
283 "%: '%' Invalid character: '%' in '%'",
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;
306HEX_ALPHA :: "0123456789ABCDEF";
310RGB_HEX_STRING_LENGTH :: 7;
312RGB_ERROR_MSG :: "Invalid RGB string.";
315roundf :: inline (x: float) -> float {
316 return floor(x + 0.5);
319round :: inline (x: float) -> int {
320 return xx floor(x + 0.5);
325 log_error, min, max, temp, assert, alloc_string, to_integer, advance, is_digit
326) Basic :: #import "Basic";
328using,only(slice, split, contains) String :: #import "String";
329using,only(fmod_cycling, U8_MAX, abs, floor) Math :: #import "Math";
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
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------------------------------------------------------------------------------