ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
ANSI Utilities

Data Structures

struct  ansi_rle_context_t
struct  ansi_timing_t
struct  rgb_error_t

Enumerations

enum  ansi_color_mode_t { ANSI_MODE_FOREGROUND = 0 , ANSI_MODE_BACKGROUND , ANSI_MODE_FOREGROUND_BACKGROUND }

Functions

char * ansi_strip_escapes (const char *input, size_t input_len)
 When code needs to work with rgb error, it keeps r, g, b here instead of scattering it across globals. Initialization establishes the handles and sizes; teardown must release any allocation, lock, thread, or callback context recorded in those fields.
const char * ansi_skip_escape (const char *ptr, const char *end)
 Find the end of an ANSI CSI escape sequence.
bool ansi_is_already_colorized (const char *message, size_t pos)
 Check if a position in text is already colored.
char * append_truecolor_fg (char *dst, uint8_t r, uint8_t g, uint8_t b)
char * append_truecolor_bg (char *dst, uint8_t r, uint8_t g, uint8_t b)
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)
void ansi_rle_init (ansi_rle_context_t *ctx, char *buffer, size_t capacity, ansi_color_mode_t mode)
void ansi_rle_add_pixel (ansi_rle_context_t *ctx, uint8_t r, uint8_t g, uint8_t b, char ascii_char)
void ansi_rle_finish (ansi_rle_context_t *ctx)
void ansi_fast_init (void)
void ansi_fast_init_256color (void)
void ansi_fast_init_16color (void)
char * append_256color_fg (char *dst, uint8_t color_index)
char * append_256color_bg (char *dst, uint8_t color_index)
uint8_t rgb_to_256color (uint8_t r, uint8_t g, uint8_t b)
void get_256color_rgb (uint8_t index, uint8_t *r, uint8_t *g, uint8_t *b)
char * append_16color_fg (char *dst, uint8_t color_index)
char * append_16color_bg (char *dst, uint8_t color_index)
uint8_t rgb_to_16color (uint8_t r, uint8_t g, uint8_t b)
void get_16color_rgb (uint8_t color_index, uint8_t *r, uint8_t *g, uint8_t *b)
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)
char * append_color_fg_for_mode (char *dst, uint8_t r, uint8_t g, uint8_t b, terminal_color_mode_t mode)

Detailed Description

Enumeration Type Documentation

◆ ansi_color_mode_t

#include <include/ascii-chat/video/terminal/ansi.h>

Enumerator
ANSI_MODE_FOREGROUND 
ANSI_MODE_BACKGROUND 
ANSI_MODE_FOREGROUND_BACKGROUND 

Definition at line 39 of file ansi.h.

39 {
ansi_color_mode_t
Definition ansi.h:39
@ ANSI_MODE_FOREGROUND_BACKGROUND
Definition ansi.h:42
@ ANSI_MODE_FOREGROUND
Definition ansi.h:40
@ ANSI_MODE_BACKGROUND
Definition ansi.h:41

Function Documentation

◆ ansi_fast_init()

void ansi_fast_init ( void )

#include <include/ascii-chat/video/terminal/ansi.h>

Initialize fast ANSI generation (call once at startup)

Definition at line 227 of file ansi.c.

227 {
228 // Initialize the dec3 cache used by truecolor functions
230}
void ascii_simd_init(void)
Initialize SIMD subsystem.

References ascii_simd_init().

Referenced by client_init(), mirror_init(), and session_display_create().

◆ ansi_fast_init_16color()

void ansi_fast_init_16color ( void )

#include <include/ascii-chat/video/terminal/ansi.h>

Initialize 16-color mode (thread-safe, called on first use)

Definition at line 315 of file ansi.c.

315 {
316 if (!lifecycle_init(&g_color16_lc, "ansi_16color")) {
317 return; // Already initialized
318 }
319 do_init_16color();
320}

Referenced by append_16color_bg(), append_16color_fg(), and session_display_create().

◆ ansi_fast_init_256color()

void ansi_fast_init_256color ( void )

#include <include/ascii-chat/video/terminal/ansi.h>

Initialize 256-color mode (thread-safe, called on first use)

Definition at line 232 of file ansi.c.

232 {
234}
void prewarm_sgr256_cache(void)
Prewarm 256-color foreground/background cache for benchmarks.
Definition sgr.c:95

References prewarm_sgr256_cache().

Referenced by session_display_create().

◆ ansi_is_already_colorized()

bool ansi_is_already_colorized ( const char * message,
size_t pos )

#include <include/ascii-chat/video/terminal/ansi.h>

Check if a position in text is already colored.

Scans from the start of the message to the given position, tracking ANSI escape codes. Returns true if there is an active color code (not in reset state).

Detects reset codes: \x1b[0m and \x1b[m

Parameters
messageThe full message
posPosition to check
Returns
true if already colored (not in reset state), false if in reset state

Definition at line 102 of file ansi.c.

102 {
103 if (!message) {
104 return false;
105 }
106
107 bool in_reset = true; // Start in reset state
108
109 // Scan from beginning to current position looking for ANSI codes
110 for (size_t i = 0; i < pos && message[i] != '\0'; i++) {
111 if (message[i] == '\x1b' && message[i + 1] == '[') {
112 // Found ANSI escape sequence start
113 // Look for the end marker 'm'
114 size_t j = i + 2;
115 while (message[j] != '\0' && message[j] != 'm') {
116 j++;
117 }
118
119 if (message[j] == 'm') {
120 // Extract the color code part (between [ and m)
121 // Check if it's a reset code: \x1b[0m or \x1b[m
122 in_reset = ((j == i + 3 && message[i + 2] == '0') || (j == i + 2));
123 }
124 }
125 }
126
127 // Return true if NOT in reset state (already colorized)
128 return !in_reset;
129}

Referenced by colorize_log_message_with_color().

◆ ansi_rle_add_pixel()

void ansi_rle_add_pixel ( ansi_rle_context_t * ctx,
uint8_t r,
uint8_t g,
uint8_t b,
char ascii_char )

#include <include/ascii-chat/video/terminal/ansi.h>

Add pixel with run-length encoding (only emits SGR when color changes)

Parameters
ctxRLE context
rRed component
gGreen component
bBlue component
ascii_charASCII character to append

Definition at line 169 of file ansi.c.

169 {
170 // Check if we need to emit a new SGR sequence
171 bool color_changed = ctx->first_pixel || (r != ctx->last_r) || (g != ctx->last_g) || (b != ctx->last_b);
172
173 if (color_changed) {
174 if (ctx->length + 40 >= ctx->capacity) {
175 // Log when we can't add a color change due to buffer capacity
176 log_warn_every(US_PER_SEC_INT, "RLE_CAPACITY: Cannot add SGR at length=%zu, capacity=%zu (need 40 bytes)",
177 ctx->length, ctx->capacity);
178 }
179 }
180
181 if (color_changed && (ctx->length + 40 < ctx->capacity)) { // Reserve 40 bytes for SGR (FG+BG max is 38)
182 char *pos = ctx->buffer + ctx->length;
183
184 switch (ctx->mode) {
186 pos = append_truecolor_fg(pos, r, g, b);
187 break;
189 pos = append_truecolor_bg(pos, r, g, b);
190 break;
192 // For FG+BG mode, use a default background (black) or implement dual-color logic
193 pos = append_truecolor_fg_bg(pos, r, g, b, 0, 0, 0);
194 break;
195 }
196
197 ctx->length = (size_t)(pos - ctx->buffer);
198 ctx->last_r = r;
199 ctx->last_g = g;
200 ctx->last_b = b;
201 ctx->first_pixel = false;
202 }
203
204 // Add the ASCII character
205 if (ctx->length < ctx->capacity - 1) {
206 ctx->buffer[ctx->length++] = ascii_char;
207 }
208}
char * append_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:141
char * append_truecolor_fg_bg(char *dst, uint8_t fr, uint8_t fg, uint8_t fb, uint8_t br, uint8_t bg, uint8_t bb)
Definition ansi.c:149
char * append_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:145
#define US_PER_SEC_INT
Definition time.h:173
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.
Definition log/log.h:755
uint8_t last_b
Definition ansi.h:54
uint8_t last_g
Definition ansi.h:53
size_t capacity
Definition ansi.h:48
uint8_t last_r
Definition ansi.h:52
char * buffer
Definition ansi.h:47
bool first_pixel
Definition ansi.h:51
size_t length
Definition ansi.h:49
ansi_color_mode_t mode
Definition ansi.h:50

References ANSI_MODE_BACKGROUND, ANSI_MODE_FOREGROUND, ANSI_MODE_FOREGROUND_BACKGROUND, append_truecolor_bg(), append_truecolor_fg(), append_truecolor_fg_bg(), ansi_rle_context_t::buffer, ansi_rle_context_t::capacity, ansi_rle_context_t::first_pixel, ansi_rle_context_t::last_b, ansi_rle_context_t::last_g, ansi_rle_context_t::last_r, ansi_rle_context_t::length, log_warn_every, ansi_rle_context_t::mode, and US_PER_SEC_INT.

◆ ansi_rle_finish()

void ansi_rle_finish ( ansi_rle_context_t * ctx)

#include <include/ascii-chat/video/terminal/ansi.h>

Finish RLE sequence with reset and null terminator

Parameters
ctxRLE context

Definition at line 211 of file ansi.c.

211 {
212 // Add reset sequence (requires 4 bytes + 1 for null terminator = 5 bytes total)
213 if (ctx->length + 5 <= ctx->capacity) {
214 SAFE_MEMCPY(ctx->buffer + ctx->length, 4, "\033[0m", 4);
215 ctx->length += 4;
216 }
217
218 // Null terminate
219 if (ctx->length < ctx->capacity) {
220 ctx->buffer[ctx->length] = '\0';
221 }
222}
#define SAFE_MEMCPY(dest, dest_size, src, count)
Definition common.h:483

References ansi_rle_context_t::buffer, ansi_rle_context_t::capacity, ansi_rle_context_t::length, and SAFE_MEMCPY.

◆ ansi_rle_init()

void ansi_rle_init ( ansi_rle_context_t * ctx,
char * buffer,
size_t capacity,
ansi_color_mode_t mode )

#include <include/ascii-chat/video/terminal/ansi.h>

Initialize RLE context for efficient ANSI generation

Parameters
ctxContext to initialize
bufferOutput buffer
capacityBuffer capacity
modeColor mode (foreground/background/both)

Definition at line 156 of file ansi.c.

156 {
157 ctx->buffer = buffer;
158 ctx->capacity = capacity;
159 ctx->length = 0;
160 ctx->mode = mode;
161 ctx->first_pixel = true;
162 // Initialize with impossible color values to force first SGR
163 ctx->last_r = 0xFF;
164 ctx->last_g = 0xFF;
165 ctx->last_b = 0xFF;
166}

References ansi_rle_context_t::buffer, ansi_rle_context_t::capacity, ansi_rle_context_t::first_pixel, ansi_rle_context_t::last_b, ansi_rle_context_t::last_g, ansi_rle_context_t::last_r, ansi_rle_context_t::length, and ansi_rle_context_t::mode.

◆ ansi_skip_escape()

const char * ansi_skip_escape ( const char * ptr,
const char * end )

#include <include/ascii-chat/video/terminal/ansi.h>

Find the end of an ANSI CSI escape sequence.

Given a pointer to an ESC character (\x1b) that starts a CSI sequence, returns a pointer past the final byte of the sequence. If the pointer does not point to a valid CSI sequence start, returns ptr + 1.

Parameters
ptrPointer to the ESC (\x1b) character
endPointer past the end of the string
Returns
Pointer to the first byte after the escape sequence

Definition at line 78 of file ansi.c.

78 {
79 if (!ptr || ptr >= end || *ptr != '\x1b') {
80 return ptr ? ptr + 1 : ptr;
81 }
82
83 const char *p = ptr + 1;
84 if (p >= end || *p != '[') {
85 return p; // Not a CSI sequence, skip just the ESC byte
86 }
87 p++; // Skip '['
88
89 // Skip parameter bytes (0x30-0x3F) and intermediate bytes (0x20-0x2F)
90 while (p < end && ((*p >= 0x30 && *p <= 0x3F) || (*p >= 0x20 && *p <= 0x2F))) {
91 p++;
92 }
93
94 // Skip final byte (0x40-0x7E)
95 if (p < end && *p >= 0x40 && *p <= 0x7E) {
96 p++;
97 }
98
99 return p;
100}

◆ ansi_strip_escapes()

char * ansi_strip_escapes ( const char * input,
size_t input_len )

#include <include/ascii-chat/video/terminal/ansi.h>

When code needs to work with rgb error, it keeps r, g, b here instead of scattering it across globals. Initialization establishes the handles and sizes; teardown must release any allocation, lock, thread, or callback context recorded in those fields.

Strip all ANSI escape sequences from a string

Removes all ANSI CSI sequences (ESC [ ... final_byte) from the input, leaving only printable text. Useful for creating plain text output from colorized ASCII art.

Parameters
inputInput string containing ANSI escape sequences
input_lenLength of input string
Returns
Newly allocated string with escapes removed (caller must free), or NULL on error

Definition at line 27 of file ansi.c.

27 {
28 if (!input || input_len == 0) {
29 return NULL;
30 }
31
32 // Allocate buffer directly for output (at most input_len bytes)
33 char *output = SAFE_MALLOC(input_len + 1, char *);
34 if (!output) {
35 return NULL;
36 }
37
38 size_t out_pos = 0;
39 size_t i = 0;
40
41 while (i < input_len) {
42 // Check for ESC character (start of ANSI sequence)
43 if (input[i] == '\x1b' && i + 1 < input_len && input[i + 1] == '[') {
44 // Skip ESC[
45 i += 2;
46
47 // Skip parameter bytes (digits, semicolons, and intermediate bytes)
48 while (i < input_len) {
49 char c = input[i];
50 // Parameter bytes: 0x30-0x3F (digits, semicolon, etc.)
51 // Intermediate bytes: 0x20-0x2F (space, !, ", etc.)
52 if ((c >= 0x30 && c <= 0x3F) || (c >= 0x20 && c <= 0x2F)) {
53 i++;
54 } else {
55 break;
56 }
57 }
58
59 // Skip final byte (0x40-0x7E: @, A-Z, [, \, ], ^, _, `, a-z, {, |, }, ~)
60 if (i < input_len) {
61 char final = input[i];
62 if (final >= 0x40 && final <= 0x7E) {
63 i++;
64 }
65 }
66 } else {
67 // Regular character - copy to output
68 output[out_pos++] = input[i];
69 i++;
70 }
71 }
72
73 // Null-terminate the output
74 output[out_pos] = '\0';
75 return output;
76}
#define SAFE_MALLOC(size, cast)
Definition common.h:265

References SAFE_MALLOC.

Referenced by display_height(), display_width(), layout_print_two_column_row(), and log_mmap_write().

◆ append_16color_bg()

char * append_16color_bg ( char * dst,
uint8_t color_index )

#include <include/ascii-chat/video/terminal/ansi.h>

Append 16-color ANSI background SGR sequence

Parameters
dstDestination buffer
color_index16-color index (0-15)
Returns
Pointer to end of written data

Definition at line 338 of file ansi.c.

338 {
339 // Ensure initialization before use
341
342 if (color_index >= 16) {
343 color_index = 0; // Default to black background
344 }
345
346 const char *color_str = color16_bg_strings[color_index];
347 while (*color_str) {
348 *dst++ = *color_str++;
349 }
350
351 return dst;
352}
void ansi_fast_init_16color(void)
Definition ansi.c:315

References ansi_fast_init_16color().

◆ append_16color_fg()

char * append_16color_fg ( char * dst,
uint8_t color_index )

#include <include/ascii-chat/video/terminal/ansi.h>

Append 16-color ANSI foreground SGR sequence

Parameters
dstDestination buffer
color_index16-color index (0-15)
Returns
Pointer to end of written data

Definition at line 322 of file ansi.c.

322 {
323 // Ensure initialization before use
325
326 if (color_index >= 16) {
327 color_index = 7; // Default to white
328 }
329
330 const char *color_str = color16_fg_strings[color_index];
331 while (*color_str) {
332 *dst++ = *color_str++;
333 }
334
335 return dst;
336}

References ansi_fast_init_16color().

Referenced by append_color_fg_for_mode().

◆ append_256color_bg()

char * append_256color_bg ( char * dst,
uint8_t color_index )

#include <include/ascii-chat/video/terminal/ansi.h>

Append 256-color background SGR sequence

Parameters
dstDestination buffer
color_index256-color palette index (0-255)
Returns
Pointer to end of written data

Definition at line 243 of file ansi.c.

243 {
244 uint8_t length;
245 const char *sequence = get_sgr256_bg_string(index, &length);
246 memcpy(dst, sequence, length);
247 return dst + length;
248}
unsigned char uint8_t
Definition common.h:60
char * get_sgr256_bg_string(uint8_t bg, uint8_t *len_out)
Get 256-color background ANSI sequence string.
Definition sgr.c:108
static bool size_t index
Definition queries.h:8

References get_sgr256_bg_string(), and index.

◆ append_256color_fg()

char * append_256color_fg ( char * dst,
uint8_t color_index )

#include <include/ascii-chat/video/terminal/ansi.h>

Append 256-color foreground SGR sequence

Parameters
dstDestination buffer
color_index256-color palette index (0-255)
Returns
Pointer to end of written data

Definition at line 236 of file ansi.c.

236 {
237 uint8_t length;
238 const char *sequence = get_sgr256_fg_string(index, &length);
239 memcpy(dst, sequence, length);
240 return dst + length;
241}
char * get_sgr256_fg_string(uint8_t fg, uint8_t *len_out)
Get 256-color foreground ANSI sequence string.
Definition sgr.c:100

References get_sgr256_fg_string(), and index.

Referenced by append_color_fg_for_mode().

◆ append_color_fg_for_mode()

char * append_color_fg_for_mode ( char * dst,
uint8_t r,
uint8_t g,
uint8_t b,
terminal_color_mode_t mode )

#include <include/ascii-chat/video/terminal/ansi.h>

Append color SGR sequence for the appropriate terminal color mode

Parameters
dstDestination buffer
rRed component
gGreen component
bBlue component
modeTerminal color mode capability
Returns
Pointer to end of written data

Definition at line 505 of file ansi.c.

505 {
506 switch (mode) {
508 return append_truecolor_fg(dst, r, g, b);
509
511 uint8_t color_index = rgb_to_256color(r, g, b);
512 return append_256color_fg(dst, color_index);
513 }
514
515 case COLOR_MODE_16_COLOR: {
516 uint8_t color_index = rgb_to_16color(r, g, b);
517 return append_16color_fg(dst, color_index);
518 }
519
520 case COLOR_MODE_NONE:
521 case COLOR_MODE_AUTO:
522 default:
523 // No color output for monochrome mode or auto mode (fallback)
524 return dst;
525 }
526}
char * append_16color_fg(char *dst, uint8_t color_index)
Definition ansi.c:322
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:354
char * append_256color_fg(char *dst, uint8_t index)
Definition ansi.c:236
uint8_t rgb_to_256color(uint8_t r, uint8_t g, uint8_t b)
Definition ansi.c:251
#define COLOR_MODE_16_COLOR
16-color mode (full name)
Definition options.h:225
#define COLOR_MODE_256_COLOR
256-color mode (full name)
Definition options.h:227
#define COLOR_MODE_TRUECOLOR
24-bit truecolor mode
Definition options.h:228
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
Definition options.h:222
#define COLOR_MODE_NONE
Monochrome mode.
Definition options.h:223

References append_16color_fg(), append_256color_fg(), append_truecolor_fg(), COLOR_MODE_16_COLOR, COLOR_MODE_256_COLOR, COLOR_MODE_AUTO, COLOR_MODE_NONE, COLOR_MODE_TRUECOLOR, rgb_to_16color(), and rgb_to_256color().

◆ append_truecolor_bg()

char * append_truecolor_bg ( char * dst,
uint8_t r,
uint8_t g,
uint8_t b )

#include <include/ascii-chat/video/terminal/ansi.h>

Append truecolor background SGR sequence Maximum output: 19 bytes (\033[48;2;255;255;255m)

Parameters
dstDestination buffer
rRed component
gGreen component
bBlue component
Returns
Pointer to end of written data

Definition at line 145 of file ansi.c.

145 {
146 return append_sgr_truecolor_bg(dst, r, g, b);
147}
char * append_sgr_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition sgr.c:146

References append_sgr_truecolor_bg().

Referenced by ansi_rle_add_pixel(), and grep_highlight_colored().

◆ append_truecolor_fg()

char * append_truecolor_fg ( char * dst,
uint8_t r,
uint8_t g,
uint8_t b )

#include <include/ascii-chat/video/terminal/ansi.h>

Append truecolor foreground SGR sequence Maximum output: 19 bytes (\033[38;2;255;255;255m)

Parameters
dstDestination buffer
rRed component
gGreen component
bBlue component
Returns
Pointer to end of written data

Definition at line 141 of file ansi.c.

141 {
142 return append_sgr_truecolor_fg(dst, r, g, b);
143}
char * append_sgr_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition sgr.c:142

References append_sgr_truecolor_fg().

Referenced by ansi_rle_add_pixel(), and append_color_fg_for_mode().

◆ append_truecolor_fg_bg()

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 )

#include <include/ascii-chat/video/terminal/ansi.h>

Append combined foreground + background truecolor SGR Maximum output: 38 bytes (\033[38;2;255;255;255;48;2;255;255;255m)

Parameters
dstDestination buffer
fg_rForeground red component
fg_gForeground green component
fg_bForeground blue component
bg_rBackground red component
bg_gBackground green component
bg_bBackground blue component
Returns
Pointer to end of written data

Definition at line 149 of file ansi.c.

149 {
150 return append_sgr_truecolor_fg_bg(dst, fr, fg, fb, br, bg, bb);
151}
char * append_sgr_truecolor_fg_bg(char *dst, uint8_t fr, uint8_t fg, uint8_t fb, uint8_t br, uint8_t bg, uint8_t bb)
Definition sgr.c:150

References append_sgr_truecolor_fg_bg().

Referenced by ansi_rle_add_pixel().

◆ get_16color_rgb()

void get_16color_rgb ( uint8_t color_index,
uint8_t * r,
uint8_t * g,
uint8_t * b )

#include <include/ascii-chat/video/terminal/ansi.h>

Get RGB values for a 16-color ANSI index

Parameters
color_index16-color index (0-15)
rOutput red component
gOutput green component
bOutput blue component

Definition at line 397 of file ansi.c.

397 {
398 // Same color table as rgb_to_16color()
399 static const uint8_t ansi_colors[16][3] = {
400 {0, 0, 0}, // 0: Black
401 {128, 0, 0}, // 1: Dark Red
402 {0, 128, 0}, // 2: Dark Green
403 {128, 128, 0}, // 3: Dark Yellow (Brown)
404 {0, 0, 128}, // 4: Dark Blue
405 {128, 0, 128}, // 5: Dark Magenta
406 {0, 128, 128}, // 6: Dark Cyan
407 {192, 192, 192}, // 7: Light Gray
408 {128, 128, 128}, // 8: Dark Gray
409 {255, 0, 0}, // 9: Bright Red
410 {0, 255, 0}, // 10: Bright Green
411 {255, 255, 0}, // 11: Bright Yellow
412 {0, 0, 255}, // 12: Bright Blue
413 {255, 0, 255}, // 13: Bright Magenta
414 {0, 255, 255}, // 14: Bright Cyan
415 {255, 255, 255} // 15: White
416 };
417
418 if (color_index >= 16) {
419 color_index = 7; // Default to light gray
420 }
421
422 *r = ansi_colors[color_index][0];
423 *g = ansi_colors[color_index][1];
424 *b = ansi_colors[color_index][2];
425}

Referenced by ascii_frame_emit_row(), get_256color_rgb(), and rgb_to_16color_dithered().

◆ get_256color_rgb()

void get_256color_rgb ( uint8_t index,
uint8_t * r,
uint8_t * g,
uint8_t * b )

#include <include/ascii-chat/video/terminal/ansi.h>

Expand an xterm 256-color palette entry to RGB.

Definition at line 278 of file ansi.c.

278 {
279 if (!r || !g || !b) {
280 SET_ERRNO(ERROR_INVALID_PARAM, "Missing palette RGB output");
281 return;
282 }
283 if (index < 16) {
284 get_16color_rgb(index, r, g, b);
285 return;
286 }
287 if (index >= 232) {
288 *r = *g = *b = (uint8_t)(8 + (index - 232) * 10);
289 return;
290 }
291 static const uint8_t levels[] = {0, 95, 135, 175, 215, 255};
292 int cube = index - 16;
293 *r = levels[cube / 36];
294 *g = levels[(cube / 6) % 6];
295 *b = levels[cube % 6];
296}
#define SET_ERRNO(code,...)
void get_16color_rgb(uint8_t color_index, uint8_t *r, uint8_t *g, uint8_t *b)
Definition ansi.c:397
@ ERROR_INVALID_PARAM

References ERROR_INVALID_PARAM, get_16color_rgb(), index, and SET_ERRNO.

◆ rgb_to_16color()

uint8_t rgb_to_16color ( uint8_t r,
uint8_t g,
uint8_t b )

#include <include/ascii-chat/video/terminal/ansi.h>

Convert RGB to closest 16-color ANSI index Uses distance-based matching against standard ANSI colors

Parameters
rRed component
gGreen component
bBlue component
Returns
16-color index (0-15)

Definition at line 354 of file ansi.c.

354 {
355 // Convert RGB to the closest 16-color ANSI color
356 // This uses a simple distance-based approach
357
358 // Define the 16 ANSI colors in RGB
359 static const uint8_t ansi_colors[16][3] = {
360 {0, 0, 0}, // 0: Black
361 {128, 0, 0}, // 1: Dark Red
362 {0, 128, 0}, // 2: Dark Green
363 {128, 128, 0}, // 3: Dark Yellow (Brown)
364 {0, 0, 128}, // 4: Dark Blue
365 {128, 0, 128}, // 5: Dark Magenta
366 {0, 128, 128}, // 6: Dark Cyan
367 {192, 192, 192}, // 7: Light Gray
368 {128, 128, 128}, // 8: Dark Gray
369 {255, 0, 0}, // 9: Bright Red
370 {0, 255, 0}, // 10: Bright Green
371 {255, 255, 0}, // 11: Bright Yellow
372 {0, 0, 255}, // 12: Bright Blue
373 {255, 0, 255}, // 13: Bright Magenta
374 {0, 255, 255}, // 14: Bright Cyan
375 {255, 255, 255} // 15: White
376 };
377
378 uint8_t best_match = 0;
379 int min_distance = INT_MAX;
380
381 for (int i = 0; i < 16; i++) {
382 int dr = (int)r - (int)ansi_colors[i][0];
383 int dg = (int)g - (int)ansi_colors[i][1];
384 int db = (int)b - (int)ansi_colors[i][2];
385 int distance = dr * dr + dg * dg + db * db;
386
387 if (distance < min_distance) {
388 min_distance = distance;
389 best_match = i;
390 }
391 }
392
393 return best_match;
394}

Referenced by append_color_fg_for_mode(), colorscheme_compile_scheme(), colorscheme_current_palette(), rgb_to_16color_dithered(), and simd_quantize_scalar().

◆ rgb_to_16color_dithered()

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 )

#include <include/ascii-chat/video/terminal/ansi.h>

Convert RGB to 16-color with Floyd-Steinberg dithering

Parameters
rRed component
gGreen component
bBlue component
xCurrent pixel X coordinate
yCurrent pixel Y coordinate
widthImage width
heightImage height
error_bufferDithering error buffer (width*height elements)
Returns
16-color index with dithering applied

Definition at line 428 of file ansi.c.

428 {
429 // Add accumulated error from previous pixels
430 if (error_buffer) {
431 // Use size_t for index calculation to prevent integer overflow on large images
432 size_t error_idx = (size_t)y * (size_t)width + (size_t)x;
433 r += error_buffer[error_idx].r;
434 g += error_buffer[error_idx].g;
435 b += error_buffer[error_idx].b;
436
437 // Reset error for this pixel
438 error_buffer[error_idx].r = 0;
439 error_buffer[error_idx].g = 0;
440 error_buffer[error_idx].b = 0;
441 }
442
443 // Clamp values to [0, 255]
444 uint8_t r_clamped = clamp_rgb((int)r);
445 uint8_t g_clamped = clamp_rgb((int)g);
446 uint8_t b_clamped = clamp_rgb((int)b);
447
448 // Find the closest 16-color match
449 uint8_t closest_color = rgb_to_16color(r_clamped, g_clamped, b_clamped);
450
451 // Calculate quantization error if dithering is enabled
452 if (error_buffer) {
453 uint8_t actual_r, actual_g, actual_b;
454 get_16color_rgb(closest_color, &actual_r, &actual_g, &actual_b);
455
456 int error_r = r - (int)actual_r;
457 int error_g = g - (int)actual_g;
458 int error_b = b - (int)actual_b;
459
460 // Distribute error using Floyd-Steinberg weights:
461 // * 7/16
462 // 3/16 5/16 1/16
463
464 // Error to right pixel (x+1, y)
465 // Use size_t for all index calculations to prevent integer overflow
466 if (x + 1 < width) {
467 size_t right_idx = (size_t)y * (size_t)width + (size_t)(x + 1);
468 error_buffer[right_idx].r += (error_r * 7) / 16;
469 error_buffer[right_idx].g += (error_g * 7) / 16;
470 error_buffer[right_idx].b += (error_b * 7) / 16;
471 }
472
473 // Error to pixels on next row (y+1)
474 if (y + 1 < height) {
475 // Bottom-left pixel (x-1, y+1)
476 if (x - 1 >= 0) {
477 size_t bl_idx = (size_t)(y + 1) * (size_t)width + (size_t)(x - 1);
478 error_buffer[bl_idx].r += (error_r * 3) / 16;
479 error_buffer[bl_idx].g += (error_g * 3) / 16;
480 error_buffer[bl_idx].b += (error_b * 3) / 16;
481 }
482
483 // Bottom pixel (x, y+1)
484 size_t bottom_idx = (size_t)(y + 1) * (size_t)width + (size_t)x;
485 error_buffer[bottom_idx].r += (error_r * 5) / 16;
486 error_buffer[bottom_idx].g += (error_g * 5) / 16;
487 error_buffer[bottom_idx].b += (error_b * 5) / 16;
488
489 // Bottom-right pixel (x+1, y+1)
490 if (x + 1 < width) {
491 size_t br_idx = (size_t)(y + 1) * (size_t)width + (size_t)(x + 1);
492 error_buffer[br_idx].r += (error_r * 1) / 16;
493 error_buffer[br_idx].g += (error_g * 1) / 16;
494 error_buffer[br_idx].b += (error_b * 1) / 16;
495 }
496 }
497 }
498
499 return closest_color;
500}
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
int b
Definition ansi.h:72
int g
Definition ansi.h:71
int r
Definition ansi.h:70

References rgb_error_t::b, rgb_error_t::g, get_16color_rgb(), height, rgb_error_t::r, rgb_to_16color(), and width.

Referenced by ascii_render().

◆ rgb_to_256color()

uint8_t rgb_to_256color ( uint8_t r,
uint8_t g,
uint8_t b )

#include <include/ascii-chat/video/terminal/ansi.h>

Convert RGB to closest 256-color palette index Uses 6x6x6 color cube + grayscale ramp

Parameters
rRed component
gGreen component
bBlue component
Returns
256-color palette index (0-255)

Definition at line 251 of file ansi.c.

251 {
252 // Xterm cube levels are nonuniform. Compare the nearest cube entry with
253 // the nearest gray, including cube black/white rather than clipping them.
254 static const int levels[] = {0, 95, 135, 175, 215, 255};
255 int values[] = {r, g, b}, cube[3], error = 0;
256 for (int c = 0; c < 3; c++) {
257 cube[c] = 0;
258 for (int i = 1; i < 6; i++)
259 if (abs(values[c] - levels[i]) < abs(values[c] - levels[cube[c]]))
260 cube[c] = i;
261 int delta = values[c] - levels[cube[c]];
262 error += delta * delta;
263 }
264 int gray = ((int)r + g + b - 9) / 30;
265 if (gray < 0)
266 gray = 0;
267 if (gray > 23)
268 gray = 23;
269 int level = 8 + 10 * gray;
270 int dr = r - level, dg = g - level, db = b - level;
271 if (dr * dr + dg * dg + db * db < error)
272 return (uint8_t)(232 + gray);
273 return (uint8_t)(16 + cube[0] * 36 + cube[1] * 6 + cube[2]);
274}

Referenced by append_color_fg_for_mode(), colorscheme_compile_scheme(), colorscheme_current_palette(), and simd_quantize_scalar().