ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
sgr.c
Go to the documentation of this file.
1
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <time.h>
12#include <assert.h>
14#include <stdint.h>
15#include <stdbool.h>
17
18#include <ascii-chat/common.h>
21#include <ascii-chat/util/number.h> // For write_u8
23#include <ascii-chat/log/log.h>
24
25/* ============================================================================
26 * SIMD-Optimized Colored ASCII Generation
27 *
28 * This extends the basic SIMD luminance conversion to include full
29 * ANSI color code generation for maximum performance.
30 * ============================================================================
31 */
32
33// Background ASCII luminance threshold - same as NEON version
34#ifndef BGASCII_LUMA_THRESHOLD
35#define BGASCII_LUMA_THRESHOLD 128 // Y >= 128 -> black text; else white text
36#endif
37
38#ifndef CUBE_GRAY_THRESHOLD
39#define CUBE_GRAY_THRESHOLD 10
40#endif
41
42/* ============================================================================
43 * 256-Color ANSI Escape Sequence Generation (cached)
44 * ============================================================================
45 * Pre-generates all 256 color sequences at startup and caches them.
46 * This avoids repeated generation during per-pixel rendering.
47 */
48
49/* write_u8() is now in util/number.h */
50
54typedef struct {
55 char seq[12];
58
59static sgr256_seq_t sgr256_fg_cache[256];
60static sgr256_seq_t sgr256_bg_cache[256];
61static bool sgr256_cache_initialized = false;
62
63// Build and cache all 256 foreground and background color sequences
64static void init_sgr256_cache(void) {
65 if (sgr256_cache_initialized)
66 return;
67
68 // Cache foreground colors: \e[38;5;NNNm
69 for (int i = 0; i < 256; i++) {
70 char *p = sgr256_fg_cache[i].seq;
71 *p++ = '\033';
72 *p++ = '[';
73 *p++ = '3';
74 *p++ = '8';
75 *p++ = ';';
76 *p++ = '5';
77 *p++ = ';';
78 p = write_u8(p, (uint8_t)i);
79 *p++ = 'm';
80 sgr256_fg_cache[i].len = (uint8_t)(p - sgr256_fg_cache[i].seq);
81 }
82
83 // Cache background colors: \e[48;5;NNNm
84 for (int i = 0; i < 256; i++) {
85 char *p = sgr256_bg_cache[i].seq;
86 *p++ = '\033';
87 *p++ = '[';
88 *p++ = '4';
89 *p++ = '8';
90 *p++ = ';';
91 *p++ = '5';
92 *p++ = ';';
93 p = write_u8(p, (uint8_t)i);
94 *p++ = 'm';
95 sgr256_bg_cache[i].len = (uint8_t)(p - sgr256_bg_cache[i].seq);
96 }
97
98 sgr256_cache_initialized = true;
99}
100
101// Generate "\e[38;5;NNN;48;5;NNNm" (foreground + background)
102static inline char *build_sgr256_fgbg(char *buf, uint8_t fg, uint8_t bg, uint8_t *len_out) {
103 char *p = buf;
104 *p++ = '\033';
105 *p++ = '[';
106 *p++ = '3';
107 *p++ = '8';
108 *p++ = ';';
109 *p++ = '5';
110 *p++ = ';';
111 p = write_u8(p, fg);
112 *p++ = ';';
113 *p++ = '4';
114 *p++ = '8';
115 *p++ = ';';
116 *p++ = '5';
117 *p++ = ';';
118 p = write_u8(p, bg);
119 *p++ = 'm';
120 *len_out = (uint8_t)(p - buf);
121 return buf;
122}
123
124// Public API wrappers
126 init_sgr256_cache();
127}
128
130 init_sgr256_cache();
131}
132
133// Fast SGR generation for SIMD implementations - uses cached sequences
135 if (!sgr256_cache_initialized) {
136 init_sgr256_cache();
137 }
138 *len_out = sgr256_fg_cache[fg].len;
139 return sgr256_fg_cache[fg].seq;
140}
141
143 if (!sgr256_cache_initialized) {
144 init_sgr256_cache();
145 }
146 *len_out = sgr256_bg_cache[bg].len;
147 return sgr256_bg_cache[bg].seq;
148}
149
151 // For FG+BG, still build on-demand since we'd need 256*256 cache
152 static __thread char buf[32];
153 return build_sgr256_fgbg(buf, fg, bg, len_out);
154}
155
156inline char *append_sgr_reset(char *dst) {
157 // "\x1b[0m"
158 static const char RESET[] = "\033[0m";
159 memcpy(dst, RESET, sizeof(RESET) - 1);
160 return dst + (sizeof(RESET) - 1);
161}
162
163// OPTIMIZATION 9: Direct writes instead of memcpy - \x1b[38;2;R;G;Bm
164inline char *append_sgr_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b) {
165 // Constructor ensures initialization
166
167 // Direct character writes (compiler will optimize to word operations)
168 *dst++ = '\033';
169 *dst++ = '[';
170 *dst++ = '3';
171 *dst++ = '8';
172 *dst++ = ';';
173 *dst++ = '2';
174 *dst++ = ';';
175
176 // Fast digit copying for 1-3 digit numbers (avoid memcpy overhead)
177 const dec3_t *rd = &g_dec3_cache.dec3_table[r];
178 if (rd->len == 1) {
179 *dst++ = rd->s[0];
180 } else if (rd->len == 2) {
181 dst[0] = rd->s[0];
182 dst[1] = rd->s[1];
183 dst += 2;
184 } else {
185 dst[0] = rd->s[0];
186 dst[1] = rd->s[1];
187 dst[2] = rd->s[2];
188 dst += 3;
189 }
190 *dst++ = ';';
191
192 const dec3_t *gd = &g_dec3_cache.dec3_table[g];
193 if (gd->len == 1) {
194 *dst++ = gd->s[0];
195 } else if (gd->len == 2) {
196 dst[0] = gd->s[0];
197 dst[1] = gd->s[1];
198 dst += 2;
199 } else {
200 dst[0] = gd->s[0];
201 dst[1] = gd->s[1];
202 dst[2] = gd->s[2];
203 dst += 3;
204 }
205 *dst++ = ';';
206
207 const dec3_t *bd = &g_dec3_cache.dec3_table[b];
208 if (bd->len == 1) {
209 *dst++ = bd->s[0];
210 } else if (bd->len == 2) {
211 dst[0] = bd->s[0];
212 dst[1] = bd->s[1];
213 dst += 2;
214 } else {
215 dst[0] = bd->s[0];
216 dst[1] = bd->s[1];
217 dst[2] = bd->s[2];
218 dst += 3;
219 }
220 *dst++ = 'm';
221 return dst;
222}
223
224// OPTIMIZATION 9: Direct writes - \x1b[48;2;R;G;Bm
225inline char *append_sgr_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b) {
226 // Constructor ensures initialization
227
228 // Direct character writes for "\033[48;2;"
229 *dst++ = '\033';
230 *dst++ = '[';
231 *dst++ = '4';
232 *dst++ = '8';
233 *dst++ = ';';
234 *dst++ = '2';
235 *dst++ = ';';
236
237 // Optimized digit copying
238 const dec3_t *rd = &g_dec3_cache.dec3_table[r];
239 if (rd->len == 1) {
240 *dst++ = rd->s[0];
241 } else if (rd->len == 2) {
242 dst[0] = rd->s[0];
243 dst[1] = rd->s[1];
244 dst += 2;
245 } else {
246 dst[0] = rd->s[0];
247 dst[1] = rd->s[1];
248 dst[2] = rd->s[2];
249 dst += 3;
250 }
251 *dst++ = ';';
252
253 const dec3_t *gd = &g_dec3_cache.dec3_table[g];
254 if (gd->len == 1) {
255 *dst++ = gd->s[0];
256 } else if (gd->len == 2) {
257 dst[0] = gd->s[0];
258 dst[1] = gd->s[1];
259 dst += 2;
260 } else {
261 dst[0] = gd->s[0];
262 dst[1] = gd->s[1];
263 dst[2] = gd->s[2];
264 dst += 3;
265 }
266 *dst++ = ';';
267
268 const dec3_t *bd = &g_dec3_cache.dec3_table[b];
269 if (bd->len == 1) {
270 *dst++ = bd->s[0];
271 } else if (bd->len == 2) {
272 dst[0] = bd->s[0];
273 dst[1] = bd->s[1];
274 dst += 2;
275 } else {
276 dst[0] = bd->s[0];
277 dst[1] = bd->s[1];
278 dst[2] = bd->s[2];
279 dst += 3;
280 }
281 *dst++ = 'm';
282 return dst;
283}
284
285// OPTIMIZATION 9: Optimized FG+BG - \x1b[38;2;R;G;B;48;2;r;g;bm (eliminate all memcpy calls)
286inline char *append_sgr_truecolor_fg_bg(char *dst, uint8_t fr, uint8_t fg, uint8_t fb, uint8_t br, uint8_t bg,
287 uint8_t bb) {
288 // Constructor ensures initialization
289
290 // Write "\033[38;2;" directly (7 chars)
291 *dst++ = '\033';
292 *dst++ = '[';
293 *dst++ = '3';
294 *dst++ = '8';
295 *dst++ = ';';
296 *dst++ = '2';
297 *dst++ = ';';
298
299 // Foreground RGB digits
300 const dec3_t *d = &g_dec3_cache.dec3_table[fr];
301 if (d->len == 1) {
302 *dst++ = d->s[0];
303 } else if (d->len == 2) {
304 dst[0] = d->s[0];
305 dst[1] = d->s[1];
306 dst += 2;
307 } else {
308 dst[0] = d->s[0];
309 dst[1] = d->s[1];
310 dst[2] = d->s[2];
311 dst += 3;
312 }
313 *dst++ = ';';
314
315 d = &g_dec3_cache.dec3_table[fg];
316 if (d->len == 1) {
317 *dst++ = d->s[0];
318 } else if (d->len == 2) {
319 dst[0] = d->s[0];
320 dst[1] = d->s[1];
321 dst += 2;
322 } else {
323 dst[0] = d->s[0];
324 dst[1] = d->s[1];
325 dst[2] = d->s[2];
326 dst += 3;
327 }
328 *dst++ = ';';
329
330 d = &g_dec3_cache.dec3_table[fb];
331 if (d->len == 1) {
332 *dst++ = d->s[0];
333 } else if (d->len == 2) {
334 dst[0] = d->s[0];
335 dst[1] = d->s[1];
336 dst += 2;
337 } else {
338 dst[0] = d->s[0];
339 dst[1] = d->s[1];
340 dst[2] = d->s[2];
341 dst += 3;
342 }
343
344 // Write ";48;2;" directly (6 chars)
345 *dst++ = ';';
346 *dst++ = '4';
347 *dst++ = '8';
348 *dst++ = ';';
349 *dst++ = '2';
350 *dst++ = ';';
351
352 // Background RGB digits
353 d = &g_dec3_cache.dec3_table[br];
354 if (d->len == 1) {
355 *dst++ = d->s[0];
356 } else if (d->len == 2) {
357 dst[0] = d->s[0];
358 dst[1] = d->s[1];
359 dst += 2;
360 } else {
361 dst[0] = d->s[0];
362 dst[1] = d->s[1];
363 dst[2] = d->s[2];
364 dst += 3;
365 }
366 *dst++ = ';';
367
368 d = &g_dec3_cache.dec3_table[bg];
369 if (d->len == 1) {
370 *dst++ = d->s[0];
371 } else if (d->len == 2) {
372 dst[0] = d->s[0];
373 dst[1] = d->s[1];
374 dst += 2;
375 } else {
376 dst[0] = d->s[0];
377 dst[1] = d->s[1];
378 dst[2] = d->s[2];
379 dst += 3;
380 }
381 *dst++ = ';';
382
383 d = &g_dec3_cache.dec3_table[bb];
384 if (d->len == 1) {
385 *dst++ = d->s[0];
386 } else if (d->len == 2) {
387 dst[0] = d->s[0];
388 dst[1] = d->s[1];
389 dst += 2;
390 } else {
391 dst[0] = d->s[0];
392 dst[1] = d->s[1];
393 dst[2] = d->s[2];
394 dst += 3;
395 }
396
397 *dst++ = 'm';
398 return dst;
399}
400
401/* ============================================================================
402 * All platform-specific implementations moved to lib/video/simd/
403 * ============================================================================
404 */
405
406// Row-based scalar function removed - use image_print_color() instead
407
408/* ============================================================================
409 * OPTIMIZATION #4: Fast 256-color implementations (defined after SGR functions)
410 * ============================================================================
411 */
412
413char *image_print_color_simd(image_t *image, bool use_background_mode, bool use_256color, const char *ascii_chars) {
414 if (!image || !ascii_chars) {
415 SET_ERRNO(ERROR_INVALID_PARAM, "image_print_color_simd: image or ascii_chars is NULL");
416 return NULL;
417 }
418
419 log_dev_every(4500 * US_PER_MS_INT, "image_print_color_simd called: width=%d, height=%d, use_256color=%d", image->w,
420 image->h, use_256color);
421
422#if SIMD_SUPPORT_AVX2
423 log_debug_every(10 * US_PER_SEC_INT, "Taking AVX2 path: width=%d, height=%d", image->w, image->h);
424 // AVX2 implementation would go here
425 // For now, fall through to scalar implementation
426#endif
427
428 // Fallback to scalar implementation
429 if (use_background_mode) {
430 return image_print_16color_dithered_with_background(image, use_background_mode, ascii_chars);
431 } else if (use_256color) {
432 return image_print_256color(image, ascii_chars);
433 } else {
434 return image_print_color(image, ascii_chars);
435 }
436}
Platform abstraction layer umbrella header providing unified cross-platform API.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Error and exit codes - unified status values (0-255)
unsigned char uint8_t
Definition common.h:56
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_PARAM
#define US_PER_MS_INT
Definition time.h:160
#define US_PER_SEC_INT
Definition time.h:161
global_dec3_cache_t g_dec3_cache
Global decimal cache instance.
char * get_sgr256_fg_bg_string(uint8_t fg, uint8_t bg, uint8_t *len_out)
Get 256-color foreground/background ANSI sequence string.
Definition sgr.c:150
char * image_print_color(const image_t *p, const char *palette)
Print image as ASCII art with color.
Definition foreground.c:194
void prewarm_sgr256_cache(void)
Prewarm 256-color foreground/background cache for benchmarks.
Definition sgr.c:129
void prewarm_sgr256_fg_cache(void)
Prewarm 256-color foreground cache for benchmarks.
Definition sgr.c:125
char * get_sgr256_bg_string(uint8_t bg, uint8_t *len_out)
Get 256-color background ANSI sequence string.
Definition sgr.c:142
char * get_sgr256_fg_string(uint8_t fg, uint8_t *len_out)
Get 256-color foreground ANSI sequence string.
Definition sgr.c:134
char * image_print_16color_dithered_with_background(const image_t *image, bool use_background, const char *palette)
Print image using 16-color ANSI mode with dithering and background colors.
Definition foreground.c:746
char * image_print_256color(const image_t *image, const char *palette)
Print image using 256-color ANSI mode.
Definition foreground.c:431
char * image_print_color_simd(image_t *image, bool use_background_mode, bool use_256color, const char *ascii_chars)
Print image as ASCII with color using SIMD.
Definition sgr.c:413
📝 Logging API with multiple log levels and terminal output control
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
Definition log/log.h:702
#define log_dev_every(interval_us, fmt,...)
Rate-limited DEV logging.
Definition log/log.h:699
🔢 Number Formatting and Conversion Utilities
ASCII Palette Management for Video-to-ASCII Conversion.
char * append_sgr_reset(char *dst)
Definition sgr.c:156
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:286
char * append_sgr_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition sgr.c:225
char * append_sgr_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Definition sgr.c:164
ClangTool/LibTooling compatibility shim for stdbool.h.
Decimal conversion cache structure (1-3 digits)
Image structure.
int w
Image width in pixels (must be > 0)
int h
Image height in pixels (must be > 0)
Pre-computed 256-color ANSI SGR sequence.
Definition sgr.c:54
char seq[12]
ANSI sequence string (max 11 bytes for "\e[38;5;NNNm")
Definition sgr.c:55
uint8_t len
Length of sequence string.
Definition sgr.c:56
⏱️ High-precision timing utilities using sokol_time.h and uthash
SIMD-optimized ASCII conversion interface.