ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
ansi.c
Go to the documentation of this file.
1
7#include <ascii-chat/common.h>
14#include <string.h>
15#include <time.h>
16#ifndef _WIN32
17#include <unistd.h>
18#endif
19#include <limits.h>
20
21/* ===== BASIC ANSI UTILITIES ===== */
22
23char *ansi_strip_escapes(const char *input, size_t input_len) {
24 if (!input || input_len == 0) {
25 return NULL;
26 }
27
28 // Allocate buffer directly for output (at most input_len bytes)
29 char *output = SAFE_MALLOC(input_len + 1, char *);
30 if (!output) {
31 return NULL;
32 }
33
34 size_t out_pos = 0;
35 size_t i = 0;
36
37 while (i < input_len) {
38 // Check for ESC character (start of ANSI sequence)
39 if (input[i] == '\x1b' && i + 1 < input_len && input[i + 1] == '[') {
40 // Skip ESC[
41 i += 2;
42
43 // Skip parameter bytes (digits, semicolons, and intermediate bytes)
44 while (i < input_len) {
45 char c = input[i];
46 // Parameter bytes: 0x30-0x3F (digits, semicolon, etc.)
47 // Intermediate bytes: 0x20-0x2F (space, !, ", etc.)
48 if ((c >= 0x30 && c <= 0x3F) || (c >= 0x20 && c <= 0x2F)) {
49 i++;
50 } else {
51 break;
52 }
53 }
54
55 // Skip final byte (0x40-0x7E: @, A-Z, [, \, ], ^, _, `, a-z, {, |, }, ~)
56 if (i < input_len) {
57 char final = input[i];
58 if (final >= 0x40 && final <= 0x7E) {
59 i++;
60 }
61 }
62 } else {
63 // Regular character - copy to output
64 output[out_pos++] = input[i];
65 i++;
66 }
67 }
68
69 // Null-terminate the output
70 output[out_pos] = '\0';
71 return output;
72}
73
74const char *ansi_skip_escape(const char *ptr, const char *end) {
75 if (!ptr || ptr >= end || *ptr != '\x1b') {
76 return ptr ? ptr + 1 : ptr;
77 }
78
79 const char *p = ptr + 1;
80 if (p >= end || *p != '[') {
81 return p; // Not a CSI sequence, skip just the ESC byte
82 }
83 p++; // Skip '['
84
85 // Skip parameter bytes (0x30-0x3F) and intermediate bytes (0x20-0x2F)
86 while (p < end && ((*p >= 0x30 && *p <= 0x3F) || (*p >= 0x20 && *p <= 0x2F))) {
87 p++;
88 }
89
90 // Skip final byte (0x40-0x7E)
91 if (p < end && *p >= 0x40 && *p <= 0x7E) {
92 p++;
93 }
94
95 return p;
96}
97
98bool ansi_is_already_colorized(const char *message, size_t pos) {
99 if (!message) {
100 return false;
101 }
102
103 bool in_reset = true; // Start in reset state
104
105 // Scan from beginning to current position looking for ANSI codes
106 for (size_t i = 0; i < pos && message[i] != '\0'; i++) {
107 if (message[i] == '\x1b' && message[i + 1] == '[') {
108 // Found ANSI escape sequence start
109 // Look for the end marker 'm'
110 size_t j = i + 2;
111 while (message[j] != '\0' && message[j] != 'm') {
112 j++;
113 }
114
115 if (message[j] == 'm') {
116 // Extract the color code part (between [ and m)
117 // Check if it's a reset code: \x1b[0m or \x1b[m
118 in_reset = ((j == i + 3 && message[i + 2] == '0') || (j == i + 2));
119 }
120 }
121 }
122
123 // Return true if NOT in reset state (already colorized)
124 return !in_reset;
125}
126
127/* ===== 256-COLOR LOOKUP TABLES ===== */
128
129static char color256_strings[256][16]; // Pre-built SGR strings like "\033[38;5;123m"
130static char color256_bg_strings[256][16]; // Pre-built SGR strings like "\033[48;5;123m"
131static lifecycle_t g_color256_lc = LIFECYCLE_INIT;
132
133/* ===== 16-COLOR LOOKUP TABLES ===== */
134
135static char color16_fg_strings[16][16];
136static char color16_bg_strings[16][16];
137static lifecycle_t g_color16_lc = LIFECYCLE_INIT;
138
139/* ===== TRUECOLOR FUNCTIONS ===== */
140
141// Fast foreground color: \033[38;2;R;G;Bm
142// Maximum output: 19 bytes (\033[38;2;255;255;255m)
143char *append_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b) {
144 // Ensure dec3 cache is initialized
146 init_dec3();
147 }
148
149 // Static prefix - 7 bytes
150 SAFE_MEMCPY(dst, 19, "\033[38;2;", 7);
151 dst += 7;
152
153 // Red component + semicolon
155 dst += g_dec3_cache.dec3_table[r].len;
156 *dst++ = ';';
157
158 // Green component + semicolon
160 dst += g_dec3_cache.dec3_table[g].len;
161 *dst++ = ';';
162
163 // Blue component + suffix
165 dst += g_dec3_cache.dec3_table[b].len;
166 *dst++ = 'm';
167
168 return dst;
169}
170
171// Fast background color: \033[48;2;R;G;Bm
172// Maximum output: 19 bytes (\033[48;2;255;255;255m)
173char *append_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b) {
174 // Ensure dec3 cache is initialized
176 init_dec3();
177 }
178
179 SAFE_MEMCPY(dst, 19, "\033[48;2;", 7);
180 dst += 7;
181
183 dst += g_dec3_cache.dec3_table[r].len;
184 *dst++ = ';';
185
187 dst += g_dec3_cache.dec3_table[g].len;
188 *dst++ = ';';
189
191 dst += g_dec3_cache.dec3_table[b].len;
192 *dst++ = 'm';
193
194 return dst;
195}
196
197// Combined foreground + background: \033[38;2;R;G;B;48;2;r;g;bm
198// Maximum output: 38 bytes (\033[38;2;255;255;255;48;2;255;255;255m)
199char *append_truecolor_fg_bg(char *dst, uint8_t fg_r, uint8_t fg_g, uint8_t fg_b, uint8_t bg_r, uint8_t bg_g,
200 uint8_t bg_b) {
201 // Ensure dec3 cache is initialized
203 init_dec3();
204 }
205
206 SAFE_MEMCPY(dst, 38, "\033[38;2;", 7);
207 dst += 7;
208
209 // Foreground RGB (remaining: 31 bytes max)
211 dst += g_dec3_cache.dec3_table[fg_r].len;
212 *dst++ = ';';
213
214 // Remaining: 27 bytes max
216 dst += g_dec3_cache.dec3_table[fg_g].len;
217 *dst++ = ';';
218
219 // Remaining: 23 bytes max
221 dst += g_dec3_cache.dec3_table[fg_b].len;
222
223 // Background RGB (remaining: 20 bytes max)
224 SAFE_MEMCPY(dst, 20, ";48;2;", 6);
225 dst += 6;
226
227 // Remaining: 14 bytes max
229 dst += g_dec3_cache.dec3_table[bg_r].len;
230 *dst++ = ';';
231
232 // Remaining: 10 bytes max
234 dst += g_dec3_cache.dec3_table[bg_g].len;
235 *dst++ = ';';
236
237 // Remaining: 6 bytes max
239 dst += g_dec3_cache.dec3_table[bg_b].len;
240 *dst++ = 'm';
241
242 return dst;
243}
244
245/* ===== RUN-LENGTH ENCODING FUNCTIONS ===== */
246
247// Initialize run-length encoding context
248void ansi_rle_init(ansi_rle_context_t *ctx, char *buffer, size_t capacity, ansi_color_mode_t mode) {
249 ctx->buffer = buffer;
250 ctx->capacity = capacity;
251 ctx->length = 0;
252 ctx->mode = mode;
253 ctx->first_pixel = true;
254 // Initialize with impossible color values to force first SGR
255 ctx->last_r = 0xFF;
256 ctx->last_g = 0xFF;
257 ctx->last_b = 0xFF;
258}
259
260// Add pixel with run-length encoding - only emit SGR when color changes
261void ansi_rle_add_pixel(ansi_rle_context_t *ctx, uint8_t r, uint8_t g, uint8_t b, char ascii_char) {
262 // Check if we need to emit a new SGR sequence
263 bool color_changed = ctx->first_pixel || (r != ctx->last_r) || (g != ctx->last_g) || (b != ctx->last_b);
264
265 if (color_changed) {
266 if (ctx->length + 40 >= ctx->capacity) {
267 // Log when we can't add a color change due to buffer capacity
268 log_warn_every(US_PER_SEC_INT, "RLE_CAPACITY: Cannot add SGR at length=%zu, capacity=%zu (need 40 bytes)",
269 ctx->length, ctx->capacity);
270 }
271 }
272
273 if (color_changed && (ctx->length + 40 < ctx->capacity)) { // Reserve 40 bytes for SGR (FG+BG max is 38)
274 char *pos = ctx->buffer + ctx->length;
275
276 switch (ctx->mode) {
278 pos = append_truecolor_fg(pos, r, g, b);
279 break;
281 pos = append_truecolor_bg(pos, r, g, b);
282 break;
284 // For FG+BG mode, use a default background (black) or implement dual-color logic
285 pos = append_truecolor_fg_bg(pos, r, g, b, 0, 0, 0);
286 break;
287 }
288
289 ctx->length = (size_t)(pos - ctx->buffer);
290 ctx->last_r = r;
291 ctx->last_g = g;
292 ctx->last_b = b;
293 ctx->first_pixel = false;
294 }
295
296 // Add the ASCII character
297 if (ctx->length < ctx->capacity - 1) {
298 ctx->buffer[ctx->length++] = ascii_char;
299 }
300}
301
302// Finish RLE sequence with reset and null terminator
304 // Add reset sequence (requires 4 bytes + 1 for null terminator = 5 bytes total)
305 if (ctx->length + 5 <= ctx->capacity) {
306 SAFE_MEMCPY(ctx->buffer + ctx->length, 4, "\033[0m", 4);
307 ctx->length += 4;
308 }
309
310 // Null terminate
311 if (ctx->length < ctx->capacity) {
312 ctx->buffer[ctx->length] = '\0';
313 }
314}
315
316/* ===== INITIALIZATION FUNCTIONS ===== */
317
318// Initialize the decimal lookup table (call once at startup)
319void ansi_fast_init(void) {
320 // Initialize the dec3 cache used by truecolor functions
322}
323
324// 256-color mode initialization (optional high-speed mode)
325// Private initialization function (called exactly once via lifecycle)
326static void do_init_256color(void) {
327 for (int i = 0; i < 256; i++) {
328 SAFE_SNPRINTF(color256_strings[i], sizeof(color256_strings[i]), "\033[38;5;%dm", i);
329 SAFE_SNPRINTF(color256_bg_strings[i], sizeof(color256_bg_strings[i]), "\033[48;5;%dm", i);
330 }
331}
332
333// Public initialization wrapper - thread-safe using lifecycle API
335 if (!lifecycle_init(&g_color256_lc, "ansi_256color")) {
336 return; // Already initialized
337 }
338 do_init_256color();
339}
340
341/* ===== 256-COLOR FUNCTIONS ===== */
342
343// Fast 256-color foreground
344char *append_256color_fg(char *dst, uint8_t color_index) {
345 const char *color_str = color256_strings[color_index];
346 size_t len = strlen(color_str);
347 SAFE_MEMCPY(dst, len, color_str, len);
348 return dst + len;
349}
350
351// Fast 256-color background
352char *append_256color_bg(char *dst, uint8_t color_index) {
353 const char *color_str = color256_bg_strings[color_index];
354 size_t len = strlen(color_str);
355 SAFE_MEMCPY(dst, len, color_str, len);
356 return dst + len;
357}
358
359// Convert RGB to closest 256-color palette index
361 // Map to 6x6x6 color cube (216 colors) + grayscale ramp
362
363 // Check if it's close to grayscale
364 int avg = (r + g + b) / 3;
365 int gray_diff = abs(r - avg) + abs(g - avg) + abs(b - avg);
366
367 if (gray_diff < 30) {
368 // Use grayscale ramp (colors 232-255)
369 int gray_level = (avg * 23) / 255;
370 return (uint8_t)(232 + gray_level);
371 }
372
373 // Use 6x6x6 color cube (colors 16-231)
374 int r6 = (r * 5) / 255;
375 int g6 = (g * 5) / 255;
376 int b6 = (b * 5) / 255;
377
378 return (uint8_t)(16 + (r6 * 36) + (g6 * 6) + b6);
379}
380
381/* ===== 16-COLOR FUNCTIONS ===== */
382
383// Private initialization function (called exactly once via lifecycle)
384static void do_init_16color(void) {
385 // Standard ANSI color codes
386 const char *fg_codes[] = {"30", "31", "32", "33", "34", "35", "36", "37", // Normal colors (30-37)
387 "90", "91", "92", "93", "94", "95", "96", "97"}; // Bright colors (90-97)
388 const char *bg_codes[] = {"40", "41", "42", "43", "44", "45", "46", "47", // Normal colors (40-47)
389 "100", "101", "102", "103", "104", "105", "106", "107"}; // Bright colors (100-107)
390
391 for (int i = 0; i < 16; i++) {
392 SAFE_SNPRINTF(color16_fg_strings[i], sizeof(color16_fg_strings[i]), "\033[%sm", fg_codes[i]);
393 SAFE_SNPRINTF(color16_bg_strings[i], sizeof(color16_bg_strings[i]), "\033[%sm", bg_codes[i]);
394 }
395}
396
397// Public initialization wrapper - thread-safe using lifecycle API
399 if (!lifecycle_init(&g_color16_lc, "ansi_16color")) {
400 return; // Already initialized
401 }
402 do_init_16color();
403}
404
405char *append_16color_fg(char *dst, uint8_t color_index) {
406 // Ensure initialization before use
408
409 if (color_index >= 16) {
410 color_index = 7; // Default to white
411 }
412
413 const char *color_str = color16_fg_strings[color_index];
414 while (*color_str) {
415 *dst++ = *color_str++;
416 }
417
418 return dst;
419}
420
421char *append_16color_bg(char *dst, uint8_t color_index) {
422 // Ensure initialization before use
424
425 if (color_index >= 16) {
426 color_index = 0; // Default to black background
427 }
428
429 const char *color_str = color16_bg_strings[color_index];
430 while (*color_str) {
431 *dst++ = *color_str++;
432 }
433
434 return dst;
435}
436
438 // Convert RGB to the closest 16-color ANSI color
439 // This uses a simple distance-based approach
440
441 // Define the 16 ANSI colors in RGB
442 static const uint8_t ansi_colors[16][3] = {
443 {0, 0, 0}, // 0: Black
444 {128, 0, 0}, // 1: Dark Red
445 {0, 128, 0}, // 2: Dark Green
446 {128, 128, 0}, // 3: Dark Yellow (Brown)
447 {0, 0, 128}, // 4: Dark Blue
448 {128, 0, 128}, // 5: Dark Magenta
449 {0, 128, 128}, // 6: Dark Cyan
450 {192, 192, 192}, // 7: Light Gray
451 {128, 128, 128}, // 8: Dark Gray
452 {255, 0, 0}, // 9: Bright Red
453 {0, 255, 0}, // 10: Bright Green
454 {255, 255, 0}, // 11: Bright Yellow
455 {0, 0, 255}, // 12: Bright Blue
456 {255, 0, 255}, // 13: Bright Magenta
457 {0, 255, 255}, // 14: Bright Cyan
458 {255, 255, 255} // 15: White
459 };
460
461 uint8_t best_match = 0;
462 int min_distance = INT_MAX;
463
464 for (int i = 0; i < 16; i++) {
465 int dr = (int)r - (int)ansi_colors[i][0];
466 int dg = (int)g - (int)ansi_colors[i][1];
467 int db = (int)b - (int)ansi_colors[i][2];
468 int distance = dr * dr + dg * dg + db * db;
469
470 if (distance < min_distance) {
471 min_distance = distance;
472 best_match = i;
473 }
474 }
475
476 return best_match;
477}
478
479// Get the actual RGB values for a 16-color ANSI index
480void get_16color_rgb(uint8_t color_index, uint8_t *r, uint8_t *g, uint8_t *b) {
481 // Same color table as rgb_to_16color()
482 static const uint8_t ansi_colors[16][3] = {
483 {0, 0, 0}, // 0: Black
484 {128, 0, 0}, // 1: Dark Red
485 {0, 128, 0}, // 2: Dark Green
486 {128, 128, 0}, // 3: Dark Yellow (Brown)
487 {0, 0, 128}, // 4: Dark Blue
488 {128, 0, 128}, // 5: Dark Magenta
489 {0, 128, 128}, // 6: Dark Cyan
490 {192, 192, 192}, // 7: Light Gray
491 {128, 128, 128}, // 8: Dark Gray
492 {255, 0, 0}, // 9: Bright Red
493 {0, 255, 0}, // 10: Bright Green
494 {255, 255, 0}, // 11: Bright Yellow
495 {0, 0, 255}, // 12: Bright Blue
496 {255, 0, 255}, // 13: Bright Magenta
497 {0, 255, 255}, // 14: Bright Cyan
498 {255, 255, 255} // 15: White
499 };
500
501 if (color_index >= 16) {
502 color_index = 7; // Default to light gray
503 }
504
505 *r = ansi_colors[color_index][0];
506 *g = ansi_colors[color_index][1];
507 *b = ansi_colors[color_index][2];
508}
509
510// Floyd-Steinberg dithering for 16-color terminals
511uint8_t rgb_to_16color_dithered(int r, int g, int b, int x, int y, int width, int height, rgb_error_t *error_buffer) {
512 // Add accumulated error from previous pixels
513 if (error_buffer) {
514 // Use size_t for index calculation to prevent integer overflow on large images
515 size_t error_idx = (size_t)y * (size_t)width + (size_t)x;
516 r += error_buffer[error_idx].r;
517 g += error_buffer[error_idx].g;
518 b += error_buffer[error_idx].b;
519
520 // Reset error for this pixel
521 error_buffer[error_idx].r = 0;
522 error_buffer[error_idx].g = 0;
523 error_buffer[error_idx].b = 0;
524 }
525
526 // Clamp values to [0, 255]
527 uint8_t r_clamped = clamp_rgb((int)r);
528 uint8_t g_clamped = clamp_rgb((int)g);
529 uint8_t b_clamped = clamp_rgb((int)b);
530
531 // Find the closest 16-color match
532 uint8_t closest_color = rgb_to_16color(r_clamped, g_clamped, b_clamped);
533
534 // Calculate quantization error if dithering is enabled
535 if (error_buffer) {
536 uint8_t actual_r, actual_g, actual_b;
537 get_16color_rgb(closest_color, &actual_r, &actual_g, &actual_b);
538
539 int error_r = r - (int)actual_r;
540 int error_g = g - (int)actual_g;
541 int error_b = b - (int)actual_b;
542
543 // Distribute error using Floyd-Steinberg weights:
544 // * 7/16
545 // 3/16 5/16 1/16
546
547 // Error to right pixel (x+1, y)
548 // Use size_t for all index calculations to prevent integer overflow
549 if (x + 1 < width) {
550 size_t right_idx = (size_t)y * (size_t)width + (size_t)(x + 1);
551 error_buffer[right_idx].r += (error_r * 7) / 16;
552 error_buffer[right_idx].g += (error_g * 7) / 16;
553 error_buffer[right_idx].b += (error_b * 7) / 16;
554 }
555
556 // Error to pixels on next row (y+1)
557 if (y + 1 < height) {
558 // Bottom-left pixel (x-1, y+1)
559 if (x - 1 >= 0) {
560 size_t bl_idx = (size_t)(y + 1) * (size_t)width + (size_t)(x - 1);
561 error_buffer[bl_idx].r += (error_r * 3) / 16;
562 error_buffer[bl_idx].g += (error_g * 3) / 16;
563 error_buffer[bl_idx].b += (error_b * 3) / 16;
564 }
565
566 // Bottom pixel (x, y+1)
567 size_t bottom_idx = (size_t)(y + 1) * (size_t)width + (size_t)x;
568 error_buffer[bottom_idx].r += (error_r * 5) / 16;
569 error_buffer[bottom_idx].g += (error_g * 5) / 16;
570 error_buffer[bottom_idx].b += (error_b * 5) / 16;
571
572 // Bottom-right pixel (x+1, y+1)
573 if (x + 1 < width) {
574 size_t br_idx = (size_t)(y + 1) * (size_t)width + (size_t)(x + 1);
575 error_buffer[br_idx].r += (error_r * 1) / 16;
576 error_buffer[br_idx].g += (error_g * 1) / 16;
577 error_buffer[br_idx].b += (error_b * 1) / 16;
578 }
579 }
580 }
581
582 return closest_color;
583}
584
585/* ===== MODE-AWARE COLOR FUNCTIONS ===== */
586
587// Terminal capability-aware color function
589 switch (mode) {
591 return append_truecolor_fg(dst, r, g, b);
592
594 uint8_t color_index = rgb_to_256color(r, g, b);
595 return append_256color_fg(dst, color_index);
596 }
597
598 case COLOR_MODE_16_COLOR: {
599 uint8_t color_index = rgb_to_16color(r, g, b);
600 return append_16color_fg(dst, color_index);
601 }
602
603 case COLOR_MODE_NONE:
604 case COLOR_MODE_AUTO:
605 default:
606 // No color output for monochrome mode or auto mode (fallback)
607 return dst;
608 }
609}
ANSI escape sequence utilities and fast color code generation.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
char * append_16color_fg(char *dst, uint8_t color_index)
Definition ansi.c:405
char * append_16color_bg(char *dst, uint8_t color_index)
Definition ansi.c:421
char * append_256color_bg(char *dst, uint8_t color_index)
Definition ansi.c:352
char * append_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:143
void ansi_fast_init_256color(void)
Definition ansi.c:334
const char * ansi_skip_escape(const char *ptr, const char *end)
Find the end of an ANSI CSI escape sequence.
Definition ansi.c:74
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:437
uint8_t rgb_to_16color_dithered(int r, int g, int b, int x, int y, int width, int height, rgb_error_t *error_buffer)
Definition ansi.c:511
ansi_color_mode_t
Definition ansi.h:38
void ansi_rle_init(ansi_rle_context_t *ctx, char *buffer, size_t capacity, ansi_color_mode_t mode)
Definition ansi.c:248
char * ansi_strip_escapes(const char *input, size_t input_len)
Strip all ANSI escape sequences from a string.
Definition ansi.c:23
void ansi_fast_init(void)
Definition ansi.c:319
char * append_truecolor_fg_bg(char *dst, uint8_t fg_r, uint8_t fg_g, uint8_t fg_b, uint8_t bg_r, uint8_t bg_g, uint8_t bg_b)
Definition ansi.c:199
void ansi_rle_finish(ansi_rle_context_t *ctx)
Definition ansi.c:303
void get_16color_rgb(uint8_t color_index, uint8_t *r, uint8_t *g, uint8_t *b)
Definition ansi.c:480
void ansi_rle_add_pixel(ansi_rle_context_t *ctx, uint8_t r, uint8_t g, uint8_t b, char ascii_char)
Definition ansi.c:261
bool ansi_is_already_colorized(const char *message, size_t pos)
Check if a position in text is already colored.
Definition ansi.c:98
char * append_256color_fg(char *dst, uint8_t color_index)
Definition ansi.c:344
char * append_color_fg_for_mode(char *dst, uint8_t r, uint8_t g, uint8_t b, terminal_color_mode_t mode)
Definition ansi.c:588
uint8_t rgb_to_256color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:360
void ansi_fast_init_16color(void)
Definition ansi.c:398
char * append_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:173
@ ANSI_MODE_FOREGROUND_BACKGROUND
Definition ansi.h:41
@ ANSI_MODE_FOREGROUND
Definition ansi.h:39
@ ANSI_MODE_BACKGROUND
Definition ansi.h:40
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
unsigned char uint8_t
Definition common.h:56
#define SAFE_MEMCPY(dest, dest_size, src, count)
Definition common.h:468
#define US_PER_SEC_INT
Definition time.h:161
#define COLOR_MODE_16_COLOR
16-color mode (full name)
#define COLOR_MODE_256_COLOR
256-color mode (full name)
#define COLOR_MODE_TRUECOLOR
24-bit truecolor mode
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
#define COLOR_MODE_NONE
Monochrome mode.
global_dec3_cache_t g_dec3_cache
Global decimal cache instance.
void init_dec3(void)
Initialize decimal-to-ASCII cache (0-255 → "0" to "255")
void ascii_simd_init(void)
Initialize SIMD subsystem.
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
Platform initialization and static synchronization helpers.
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26
Lock-free module lifecycle state machine using pure stdatomic.
#define LIFECYCLE_INIT
Static initializer for module-global lifecycle variables (no sync primitive)
Definition lifecycle.h:68
Application limits and constraints.
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.
Definition log/log.h:708
🔢 Mathematical Utility Functions
uint8_t last_b
Definition ansi.h:53
uint8_t last_g
Definition ansi.h:52
size_t capacity
Definition ansi.h:47
uint8_t last_r
Definition ansi.h:51
char * buffer
Definition ansi.h:46
bool first_pixel
Definition ansi.h:50
size_t length
Definition ansi.h:48
ansi_color_mode_t mode
Definition ansi.h:49
int b
Definition ansi.h:66
int g
Definition ansi.h:65
int r
Definition ansi.h:64
terminal_color_mode_t
Terminal color support levels.
Definition terminal.h:578
⏱️ High-precision timing utilities using sokol_time.h and uthash
SIMD-optimized ASCII conversion interface.