ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
palette.c
Go to the documentation of this file.
1
8#include <stdbool.h>
9#include <stddef.h>
10#include <string.h>
11#include <wchar.h>
12#include <locale.h>
13#include <stdlib.h>
14#ifndef _WIN32
15#include <langinfo.h>
16#include <unistd.h>
17#else
18// Windows compatibility - wcwidth is not available
19static int wcwidth(wchar_t wc) {
20 // Simple implementation for Windows - most characters are width 1
21 // Wide characters (CJK) are width 2, control characters are -1
22 if (wc < 32)
23 return -1; // Control characters
24 if (wc >= 0x1100 && wc <= 0x115F)
25 return 2; // Hangul Jamo
26 if (wc >= 0x2E80 && wc <= 0x9FFF)
27 return 2; // CJK range
28 if (wc >= 0xAC00 && wc <= 0xD7AF)
29 return 2; // Hangul Syllables
30 if (wc >= 0xF900 && wc <= 0xFAFF)
31 return 2; // CJK Compatibility Ideographs
32 if (wc >= 0xFE10 && wc <= 0xFE19)
33 return 2; // Vertical forms
34 if (wc >= 0xFE30 && wc <= 0xFE6F)
35 return 2; // CJK Compatibility Forms
36 if (wc >= 0xFF00 && wc <= 0xFF60)
37 return 2; // Fullwidth Forms
38 if (wc >= 0xFFE0 && wc <= 0xFFE6)
39 return 2; // Fullwidth Forms
40 return 1; // Most characters are width 1
41}
42#endif
43#include "palette.h"
44#include "common.h"
45#include "platform/terminal.h"
46
47/* Default palette constants for legacy functions */
49const size_t DEFAULT_ASCII_PALETTE_LEN = 23; // strlen(PALETTE_CHARS_STANDARD)
50
51// Built-in palette definitions
52static const palette_def_t builtin_palettes[PALETTE_COUNT] = {
53 [PALETTE_STANDARD] = {.name = "standard",
55 .length = 23,
56 .requires_utf8 = false,
57 .is_validated = true},
59 {.name = "blocks", .chars = PALETTE_CHARS_BLOCKS, .length = 11, .requires_utf8 = true, .is_validated = true},
61 {.name = "digital", .chars = PALETTE_CHARS_DIGITAL, .length = 10, .requires_utf8 = true, .is_validated = true},
63 {.name = "minimal", .chars = PALETTE_CHARS_MINIMAL, .length = 8, .requires_utf8 = false, .is_validated = true},
64 [PALETTE_COOL] =
65 {.name = "cool", .chars = PALETTE_CHARS_COOL, .length = 11, .requires_utf8 = true, .is_validated = true},
66 // PALETTE_CUSTOM is handled specially - no predefined entry
67};
68
69// Get a built-in palette definition
71 if (type >= PALETTE_COUNT || type == PALETTE_CUSTOM) {
72 return NULL;
73 }
74 return &builtin_palettes[type];
75}
76
77// Check if a palette string contains UTF-8 characters
78bool palette_requires_utf8_encoding(const char *chars, size_t len) {
79 // Handle NULL or empty string
80 if (!chars || len == 0) {
81 return false;
82 }
83
84 for (size_t i = 0; i < len; i++) {
85 // Any byte with high bit set indicates UTF-8
86 if ((unsigned char)chars[i] >= 128) {
87 return true;
88 }
89 }
90 return false;
91}
92
93// Validate UTF-8 character sequences and terminal width
94bool validate_palette_chars(const char *chars, size_t len) {
95 if (!chars || len == 0) {
96 SET_ERRNO(ERROR_INVALID_PARAM, "Palette validation failed: empty or NULL palette");
97 return false;
98 }
99
100 if (len > 256) {
101 SET_ERRNO(ERROR_INVALID_PARAM, "Palette validation failed: palette too long (%zu chars, max 256)", len);
102 return false;
103 }
104
105 // Set locale for UTF-8 support
106 char *current_locale = setlocale(LC_CTYPE, NULL);
107 char *old_locale = NULL;
108 if (current_locale) {
109 // Copy the locale string before calling setlocale again (CERT ENV30-C)
110 SAFE_STRDUP(old_locale, current_locale);
111 }
112 if (!setlocale(LC_CTYPE, "")) {
113 log_warn("Failed to set locale for UTF-8 validation, continuing anyway");
114 }
115
116 const char *p = chars;
117 size_t char_count = 0;
118 size_t byte_count = 0;
119
120 while (byte_count < len && *p) {
121 wchar_t wc;
122 int bytes = mbtowc(&wc, p, len - byte_count);
123
124 if (bytes <= 0) {
125 SET_ERRNO(ERROR_INVALID_PARAM, "Palette validation failed: invalid UTF-8 sequence at position %zu", char_count);
126 // Restore old locale
127 if (old_locale) {
128 (void)setlocale(LC_CTYPE, old_locale);
129 SAFE_FREE(old_locale);
130 }
131 return false;
132 }
133
134 // Check character width - allow width 1 and 2 (for emoji and wide characters)
135 int width = wcwidth(wc);
136 if (width < 0 || width > 2) {
138 "Palette validation failed: character at position %zu has invalid width %d (must be 1 or 2)",
139 char_count, width);
140 // Restore old locale
141 if (old_locale) {
142 (void)setlocale(LC_CTYPE, old_locale);
143 SAFE_FREE(old_locale);
144 }
145 return false;
146 }
147
148 // Check for control characters (except tab)
149 if (wc < 32 && wc != '\t') {
150 SET_ERRNO(ERROR_INVALID_PARAM, "Palette validation failed: control character at position %zu", char_count);
151 // Restore old locale
152 if (old_locale) {
153 (void)setlocale(LC_CTYPE, old_locale);
154 SAFE_FREE(old_locale);
155 }
156 return false;
157 }
158
159 char_count++;
160 byte_count += bytes;
161 p += bytes;
162 }
163
164 // Restore old locale
165 if (old_locale) {
166 (void)setlocale(LC_CTYPE, old_locale);
167 SAFE_FREE(old_locale);
168 }
169
170 log_debug("Palette validation successful: %zu characters validated", char_count);
171 return true;
172}
173
174// Detect client UTF-8 support from environment
176 if (!caps) {
177 return false;
178 }
179
180 // Initialize structure
181 SAFE_MEMSET(caps, sizeof(utf8_capabilities_t), 0, sizeof(utf8_capabilities_t));
182
183 // Check environment variables
184 const char *term = SAFE_GETENV("TERM");
185
186 // Store terminal type
187 if (term) {
188 SAFE_STRNCPY(caps->terminal_type, term, sizeof(caps->terminal_type));
189 }
190
191 // Use platform-specific UTF-8 detection from platform abstraction layer
193
194 if (caps->utf8_support) {
195 SAFE_STRNCPY(caps->locale_encoding, "UTF-8", sizeof(caps->locale_encoding));
196 } else {
197 // Try to detect encoding via locale
198 char *current_locale = setlocale(LC_CTYPE, NULL);
199 char *old_locale = NULL;
200 if (current_locale) {
201 SAFE_STRDUP(old_locale, current_locale);
202 }
203 if (setlocale(LC_CTYPE, "")) {
204#ifndef _WIN32
205 const char *codeset = nl_langinfo(CODESET);
206 if (codeset) {
207 SAFE_STRNCPY(caps->locale_encoding, codeset, sizeof(caps->locale_encoding));
208 }
209#else
210 // Windows may not have locale set but still support UTF-8
211 SAFE_STRNCPY(caps->locale_encoding, "CP1252", sizeof(caps->locale_encoding));
212#endif
213 // Restore old locale
214 if (old_locale) {
215 (void)setlocale(LC_CTYPE, old_locale);
216 }
217 }
218 // Always free old_locale regardless of setlocale success
219 if (old_locale) {
220 SAFE_FREE(old_locale);
221 }
222 }
223
224 // Check for known UTF-8 supporting terminals
225 if (term) {
226 const char *utf8_terminals[] = {
227 "xterm-256color", "screen-256color", "tmux-256color", "alacritty", "kitty", "iterm",
228 "iterm2", "gnome-terminal", "konsole", "terminology", NULL};
229
230 for (int i = 0; utf8_terminals[i]; i++) {
231 if (strstr(term, utf8_terminals[i])) {
232 caps->utf8_support = true;
233 break;
234 }
235 }
236 }
237
238 log_debug("UTF-8 support detection: %s (term=%s, encoding=%s)", caps->utf8_support ? "supported" : "not supported",
239 caps->terminal_type[0] ? caps->terminal_type : "unknown",
240 caps->locale_encoding[0] ? caps->locale_encoding : "unknown");
241
242 return caps->utf8_support;
243}
244
245// Select a compatible palette based on client capabilities
247 const palette_def_t *palette = get_builtin_palette(requested);
248
249 // Custom palettes are validated separately
250 if (requested == PALETTE_CUSTOM) {
251 return PALETTE_CUSTOM;
252 }
253
254 if (!palette) {
255 log_warn("Invalid palette type %d, falling back to standard", requested);
256 return PALETTE_STANDARD;
257 }
258
259 // If palette requires UTF-8 but client doesn't support it, find fallback
260 if (palette->requires_utf8 && !client_utf8) {
261 log_warn("Client doesn't support UTF-8, falling back from %s", palette->name);
262
263 // Fallback hierarchy
264 switch (requested) {
265 case PALETTE_BLOCKS:
266 case PALETTE_DIGITAL:
267 case PALETTE_COOL:
268 default:
269 return PALETTE_STANDARD; // ASCII equivalent
270 }
271 }
272
273 return requested; // Compatible, use as requested
274}
275
276// Apply palette configuration for client-side initialization only (no global cache)
277int apply_palette_config(palette_type_t type, const char *custom_chars) {
278 // This function is now used only for client-side initialization
279 // Server uses initialize_client_palette() for per-client palettes
280
281 log_info("Client palette config: type=%d, custom_chars=%s", type, custom_chars ? custom_chars : "(none)");
282
283 // Just validate the palette - no global state changes
284 if (type == PALETTE_CUSTOM) {
285 if (!custom_chars || strlen(custom_chars) == 0) {
286 SET_ERRNO(ERROR_INVALID_PARAM, "Custom palette requested but no characters provided");
287 return -1;
288 }
289
290 if (!validate_palette_chars(custom_chars, strlen(custom_chars))) {
291 SET_ERRNO(ERROR_INVALID_PARAM, "Custom palette validation failed");
292 return -1;
293 }
294 } else {
295 const palette_def_t *palette = get_builtin_palette(type);
296 if (!palette) {
297 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid palette type: %d", type);
298 return -1;
299 }
300 }
301
302 return 0; // Validation successful, no global state to update
303}
304
305// Build a per-client luminance palette without affecting global cache
306int build_client_luminance_palette(const char *palette_chars, size_t palette_len, char luminance_mapping[256]) {
307 if (!palette_chars || palette_len == 0 || !luminance_mapping) {
308 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters for client luminance palette");
309 return -1;
310 }
311
312 // Map 256 luminance values to palette indices for this specific client
313 for (int i = 0; i < 256; i++) {
314 // Linear mapping with proper rounding
315 size_t palette_index = (i * (palette_len - 1) + 127) / 255;
316 if (palette_index >= palette_len) {
317 palette_index = palette_len - 1;
318 }
319 luminance_mapping[i] = palette_chars[palette_index];
320 }
321
322 return 0;
323}
324
325// Initialize a client's palette cache from their capabilities
326int initialize_client_palette(palette_type_t palette_type, const char *custom_chars, char client_palette_chars[256],
327 size_t *client_palette_len, char client_luminance_palette[256]) {
328 const palette_def_t *palette = NULL;
329 const char *chars_to_use = NULL;
330 size_t len_to_use = 0;
331
332 if (palette_type == PALETTE_CUSTOM) {
333 if (!custom_chars) {
334 SET_ERRNO(ERROR_INVALID_PARAM, "Client requested custom palette but custom_chars is NULL");
335 return -1;
336 }
337
338 len_to_use = strlen(custom_chars);
339 if (len_to_use == 0) {
340 SET_ERRNO(ERROR_INVALID_PARAM, "Client requested custom palette but custom_chars is empty");
341 return -1;
342 }
343 if (len_to_use >= 256) {
344 SET_ERRNO(ERROR_INVALID_PARAM, "Client custom palette too long: %zu chars", len_to_use);
345 return -1;
346 }
347
348 // Validate custom palette
349 if (!validate_palette_chars(custom_chars, len_to_use)) {
350 SET_ERRNO(ERROR_INVALID_PARAM, "Client custom palette validation failed");
351 return -1;
352 }
353
354 chars_to_use = custom_chars;
355 } else {
356 palette = get_builtin_palette(palette_type);
357 if (!palette) {
358 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client palette type: %d", palette_type);
359 return -1;
360 }
361
362 chars_to_use = palette->chars;
363 len_to_use = strlen(palette->chars); // Use actual byte count for UTF-8, not character count
364
365 // Skip validation for built-in palettes since they're already validated
366 log_debug("Using built-in palette: %s, chars='%s', char_count=%zu, byte_len=%zu", palette->name, chars_to_use,
367 palette->length, len_to_use);
368 }
369
370 // Copy palette to client cache
371 SAFE_MEMCPY(client_palette_chars, len_to_use, chars_to_use, len_to_use);
372 client_palette_chars[len_to_use] = '\0';
373 *client_palette_len = len_to_use;
374
375 // Build client-specific luminance mapping
376 if (build_client_luminance_palette(chars_to_use, len_to_use, client_luminance_palette) != 0) {
377 SET_ERRNO(ERROR_INVALID_STATE, "Failed to build client luminance palette");
378 return -1;
379 }
380
381 log_info("Initialized client palette: type=%d, %zu chars, first_char='%c', last_char='%c'", palette_type, len_to_use,
382 chars_to_use[0], chars_to_use[len_to_use - 1]);
383
384 return 0;
385}
386
387/* UTF-8 Palette Functions Implementation */
388
389// Create a UTF-8 palette structure from a string
390utf8_palette_t *utf8_palette_create(const char *palette_string) {
391 if (!palette_string || *palette_string == '\0') {
392 return NULL;
393 }
394
395 utf8_palette_t *palette;
396 palette = SAFE_MALLOC(sizeof(utf8_palette_t), utf8_palette_t *);
397
398 // Count UTF-8 characters (not bytes)
399 size_t char_count = 0;
400 const char *p = palette_string;
401 size_t total_bytes = strlen(palette_string);
402
403 // First pass: count characters
404 size_t bytes_processed = 0;
405 while (bytes_processed < total_bytes) {
406 int bytes = 1;
407 unsigned char c = (unsigned char)p[0];
408
409 if ((c & 0x80) == 0) {
410 bytes = 1; // ASCII
411 } else if ((c & 0xE0) == 0xC0) {
412 bytes = 2; // 2-byte UTF-8
413 } else if ((c & 0xF0) == 0xE0) {
414 bytes = 3; // 3-byte UTF-8
415 } else if ((c & 0xF8) == 0xF0) {
416 bytes = 4; // 4-byte UTF-8
417 }
418
419 // Verify we have enough bytes left
420 if (bytes_processed + bytes > total_bytes) {
421 break;
422 }
423
424 p += bytes;
425 bytes_processed += bytes;
426 char_count++;
427 }
428
429 // Validate we got at least one character
430 if (char_count == 0) {
431 SET_ERRNO(ERROR_INVALID_PARAM, "Palette string contains no valid UTF-8 characters");
432 SAFE_FREE(palette);
433 return NULL;
434 }
435
436 // Allocate character array
437 palette->chars = SAFE_MALLOC(char_count * sizeof(utf8_char_info_t), utf8_char_info_t *);
438 palette->raw_string = SAFE_MALLOC(total_bytes + 1, char *);
439 // Explicit NULL checks to satisfy static analyzer (SAFE_MALLOC calls FATAL on failure)
440 if (palette->chars == NULL || palette->raw_string == NULL) {
441 SAFE_FREE(palette->chars);
442 SAFE_FREE(palette->raw_string);
443 SAFE_FREE(palette);
444 SET_ERRNO(ERROR_MEMORY, "Failed to allocate palette character array");
445 return NULL;
446 }
447
448 SAFE_MEMCPY(palette->raw_string, total_bytes + 1, palette_string, total_bytes + 1);
449 palette->char_count = char_count;
450 palette->total_bytes = total_bytes; // Use strlen() value
451
452 // Second pass: parse characters
453 p = palette_string;
454 size_t char_idx = 0;
455 bytes_processed = 0;
456
457 // Set locale for wcwidth - save a copy of the old locale
458 char old_locale[256] = {0};
459 char *current_locale = setlocale(LC_CTYPE, NULL);
460 if (current_locale) {
461 SAFE_STRNCPY(old_locale, current_locale, sizeof(old_locale));
462 }
463 (void)setlocale(LC_CTYPE, "");
464
465 while (char_idx < char_count && bytes_processed < total_bytes) {
466 utf8_char_info_t *char_info = &palette->chars[char_idx];
467
468 // Determine UTF-8 byte length
469 unsigned char c = (unsigned char)*p;
470 int bytes = 1;
471
472 if ((c & 0x80) == 0) {
473 bytes = 1;
474 } else if ((c & 0xE0) == 0xC0) {
475 bytes = 2;
476 } else if ((c & 0xF0) == 0xE0) {
477 bytes = 3;
478 } else if ((c & 0xF8) == 0xF0) {
479 bytes = 4;
480 }
481
482 // Verify we have enough bytes left
483 if (bytes_processed + bytes > total_bytes) {
484 break;
485 }
486
487 // Copy bytes and null-terminate
488 SAFE_MEMCPY(char_info->bytes, bytes, p, bytes);
489 if (bytes < 4) {
490 SAFE_MEMSET(char_info->bytes + bytes, 4 - bytes, 0, 4 - bytes);
491 }
492 char_info->byte_len = bytes;
493
494 // Get display width
495 wchar_t wc;
496 if (mbtowc(&wc, p, bytes) > 0) {
497 int width = wcwidth(wc);
498 char_info->display_width = (width > 0 && width <= 2) ? width : 1;
499 } else {
500 char_info->display_width = 1;
501 }
502
503 p += bytes;
504 bytes_processed += bytes;
505 char_idx++;
506 }
507
508 // Restore locale
509 if (old_locale[0] != '\0') {
510 (void)setlocale(LC_CTYPE, old_locale);
511 }
512
513 return palette;
514}
515
516// Destroy a UTF-8 palette structure
518 if (palette) {
519 SAFE_FREE(palette->chars);
520 SAFE_FREE(palette->raw_string);
521 SAFE_FREE(palette);
522 }
523}
524
525// Get the nth character from the palette
526const utf8_char_info_t *utf8_palette_get_char(const utf8_palette_t *palette, size_t index) {
527 if (!palette || index >= palette->char_count) {
528 return NULL;
529 }
530 return &palette->chars[index];
531}
532
533// Get the number of characters in the palette
535 if (!palette) {
536 return 0;
537 }
538 return palette->char_count;
539}
540
541// Check if palette contains a specific UTF-8 character
542bool utf8_palette_contains_char(const utf8_palette_t *palette, const char *utf8_char, size_t char_bytes) {
543 if (!palette || !utf8_char || char_bytes == 0 || char_bytes > 4) {
544 return false;
545 }
546
547 for (size_t i = 0; i < palette->char_count; i++) {
548 const utf8_char_info_t *char_info = &palette->chars[i];
549 if (char_info->byte_len == char_bytes && memcmp(char_info->bytes, utf8_char, char_bytes) == 0) {
550 return true;
551 }
552 }
553
554 return false;
555}
556
557// Find the index of a UTF-8 character in the palette
558size_t utf8_palette_find_char_index(const utf8_palette_t *palette, const char *utf8_char, size_t char_bytes) {
559 if (!palette || !utf8_char || char_bytes == 0 || char_bytes > 4) {
560 return (size_t)-1;
561 }
562
563 for (size_t i = 0; i < palette->char_count; i++) {
564 const utf8_char_info_t *char_info = &palette->chars[i];
565 if (char_info->byte_len == char_bytes && memcmp(char_info->bytes, utf8_char, char_bytes) == 0) {
566 return i;
567 }
568 }
569
570 return (size_t)-1;
571}
572
573// Find all indices of a UTF-8 character in the palette (handles duplicates)
574// Returns the number of indices found, fills indices array up to max_indices
575size_t utf8_palette_find_all_char_indices(const utf8_palette_t *palette, const char *utf8_char, size_t char_bytes,
576 size_t *indices, size_t max_indices) {
577 if (!palette || !utf8_char || char_bytes == 0 || char_bytes > 4 || !indices || max_indices == 0) {
578 return 0;
579 }
580
581 size_t found_count = 0;
582
583 for (size_t i = 0; i < palette->char_count && found_count < max_indices; i++) {
584 const utf8_char_info_t *char_info = &palette->chars[i];
585 if (char_info->byte_len == char_bytes && memcmp(char_info->bytes, utf8_char, char_bytes) == 0) {
586 indices[found_count++] = i;
587 }
588 }
589
590 return found_count;
591}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:389
#define SAFE_MALLOC(size, cast)
Definition common.h:208
#define SAFE_GETENV(name)
Definition common.h:378
#define SAFE_MEMCPY(dest, dest_size, src, count)
Definition common.h:388
#define SAFE_STRDUP(dst, src)
Definition common.h:362
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:53
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
uint8_t byte_len
Number of bytes in UTF-8 encoding (1-4)
Definition palette.h:215
void utf8_palette_destroy(utf8_palette_t *palette)
Destroy a UTF-8 palette and free resources.
Definition palette.c:517
char bytes[4]
UTF-8 byte sequence (max 4 bytes per character)
Definition palette.h:213
size_t utf8_palette_find_all_char_indices(const utf8_palette_t *palette, const char *utf8_char, size_t char_bytes, size_t *indices, size_t max_indices)
Find all indices of UTF-8 character in palette.
Definition palette.c:575
int initialize_client_palette(palette_type_t palette_type, const char *custom_chars, char client_palette_chars[256], size_t *client_palette_len, char client_luminance_palette[256])
Initialize client palette with full configuration.
Definition palette.c:326
size_t char_count
Number of characters (not bytes!)
Definition palette.h:238
bool palette_requires_utf8_encoding(const char *chars, size_t len)
Check if palette requires UTF-8 encoding.
Definition palette.c:78
size_t utf8_palette_get_char_count(const utf8_palette_t *palette)
Get number of characters in UTF-8 palette.
Definition palette.c:534
#define PALETTE_CHARS_MINIMAL
Minimal ASCII palette character string.
Definition palette.h:186
const char * name
Human-readable palette name.
Definition palette.h:116
bool detect_client_utf8_support(utf8_capabilities_t *caps)
Detect client UTF-8 support capabilities.
Definition palette.c:175
palette_type_t select_compatible_palette(palette_type_t requested, bool client_utf8)
Select compatible palette based on client capabilities.
Definition palette.c:246
bool utf8_support
True if terminal supports UTF-8 (detected automatically)
Definition palette.h:137
const size_t DEFAULT_ASCII_PALETTE_LEN
Length of default ASCII palette.
Definition palette.c:49
char terminal_type[32]
$TERM environment variable value (for detection)
Definition palette.h:141
utf8_char_info_t * chars
Array of UTF-8 character information.
Definition palette.h:236
const char DEFAULT_ASCII_PALETTE[]
Default ASCII palette for legacy functions.
Definition palette.c:48
uint8_t display_width
Terminal display width in character cells (1-2)
Definition palette.h:217
const palette_def_t * get_builtin_palette(palette_type_t type)
Get built-in palette definition.
Definition palette.c:70
#define PALETTE_CHARS_DIGITAL
Digital/glitch palette character string.
Definition palette.h:177
palette_type_t
Built-in palette type enumeration.
Definition palette.h:84
#define PALETTE_CHARS_STANDARD
Standard ASCII palette character string.
Definition palette.h:159
int apply_palette_config(palette_type_t type, const char *custom_chars)
Apply palette configuration (set global palette)
Definition palette.c:277
bool requires_utf8
True if palette contains UTF-8 multi-byte characters.
Definition palette.h:122
int build_client_luminance_palette(const char *palette_chars, size_t palette_len, char luminance_mapping[256])
Build luminance mapping table from palette characters.
Definition palette.c:306
size_t total_bytes
Total byte length of palette string.
Definition palette.h:240
size_t utf8_palette_find_char_index(const utf8_palette_t *palette, const char *utf8_char, size_t char_bytes)
Find index of UTF-8 character in palette.
Definition palette.c:558
bool validate_palette_chars(const char *chars, size_t len)
Validate palette character sequence.
Definition palette.c:94
const char * chars
Character sequence (ordered from dark to light)
Definition palette.h:118
const utf8_char_info_t * utf8_palette_get_char(const utf8_palette_t *palette, size_t index)
Get UTF-8 character information at index.
Definition palette.c:526
char * raw_string
Original palette string (for reference/debugging)
Definition palette.h:242
char locale_encoding[16]
Current locale encoding (e.g., "UTF-8")
Definition palette.h:143
size_t length
Number of characters in sequence.
Definition palette.h:120
#define PALETTE_CHARS_BLOCKS
Unicode block palette character string.
Definition palette.h:168
bool utf8_palette_contains_char(const utf8_palette_t *palette, const char *utf8_char, size_t char_bytes)
Check if UTF-8 palette contains a specific character.
Definition palette.c:542
#define PALETTE_CHARS_COOL
Cool ascending blocks palette character string.
Definition palette.h:195
utf8_palette_t * utf8_palette_create(const char *palette_string)
Create a UTF-8 palette from string.
Definition palette.c:390
@ PALETTE_COUNT
Number of palette types (not a valid palette)
Definition palette.h:98
@ PALETTE_BLOCKS
Unicode block characters: " ░░▒▒▓▓██".
Definition palette.h:88
@ PALETTE_CUSTOM
User-defined via –palette-chars.
Definition palette.h:96
@ PALETTE_COOL
Ascending blocks: " ▁▂▃▄▅▆▇█".
Definition palette.h:94
@ PALETTE_STANDARD
Standard ASCII palette: " ...',;:clodxkO0KXNWM".
Definition palette.h:86
@ PALETTE_DIGITAL
Digital/glitch aesthetic: " -=≡≣▰▱◼".
Definition palette.h:90
@ PALETTE_MINIMAL
Simple ASCII: " .-+*#".
Definition palette.h:92
bool terminal_supports_utf8(void)
Check if terminal supports UTF-8.
ASCII Palette Management for Video-to-ASCII Conversion.
Palette definition structure.
Definition palette.h:114
UTF-8 capability detection structure.
Definition palette.h:135
UTF-8 character information structure.
Definition palette.h:211
UTF-8 palette structure.
Definition palette.h:234
🖥️ Cross-platform terminal interface for ascii-chat