50 if (checked_size_mul(width, height, &total_pixels) !=
ASCIICHAT_OK) {
58 if (checked_size_mul(total_pixels,
sizeof(rgb_pixel_t), &pixels_size) !=
ASCIICHAT_OK) {
95 if (p->
w <= 0 || p->
h <= 0) {
101 size_t w = (size_t)p->
w;
102 size_t h = (size_t)p->
h;
104 if (checked_size_mul3(w, h,
sizeof(rgb_pixel_t), &pixels_size) !=
ASCIICHAT_OK) {
127 if (width == 0 || height == 0) {
141 if (checked_size_mul3(width, height,
sizeof(rgb_pixel_t), &pixels_size) !=
ASCIICHAT_OK) {
155 SET_ERRNO(
ERROR_MEMORY,
"image_new_from_pool: buffer pool allocation failed for %zu bytes (%zux%zu)", total_size,
162 image->
w = (int)width;
163 image->
h = (int)height;
178 if (image->
w <= 0 || image->
h <= 0) {
185 size_t w = (size_t)image->
w;
186 size_t h = (size_t)image->
h;
188 if (checked_size_mul3(w, h,
sizeof(rgb_pixel_t), &pixels_size) !=
ASCIICHAT_OK) {
209 unsigned long w_ul = (
unsigned long)p->
w;
210 unsigned long h_ul = (
unsigned long)p->
h;
211 if (w_ul > 0 && h_ul > ULONG_MAX / w_ul) {
215 unsigned long pixel_count = w_ul * h_ul;
216 if (pixel_count > ULONG_MAX /
sizeof(rgb_pixel_t)) {
220 size_t clear_size = pixel_count *
sizeof(rgb_pixel_t);
226 if (!p || !p->
pixels || x < 0 || x >= p->
w || y < 0 || y >= p->
h) {
229 return &p->
pixels[x + y * p->
w];
244 if (!source || !dest || !source->
pixels || !dest->
pixels) {
249 const int src_w = source->
w;
250 const int src_h = source->
h;
251 const int dst_w = dest->
w;
252 const int dst_h = dest->
h;
255 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
264 const rgb_pixel_t *src_pixels = source->
pixels;
265 rgb_pixel_t *dst_pixels = dest->
pixels;
267 for (
int y = 0; y < dst_h; y++) {
270 const rgb_pixel_t *src_row = src_pixels + (safe_src_y * (size_t)src_w);
272 rgb_pixel_t *dst_row = dst_pixels + ((size_t)y * (
size_t)dst_w);
274 for (
int x = 0; x < dst_w; x++) {
277 dst_row[x] = src_row[safe_src_x];
288 const float max_weight = 255.0f;
289 const float min_weight = -255.0f;
293#if !defined(NDEBUG) && !defined(__FAST_MATH__)
294 if (!isfinite(red) || !isfinite(green) || !isfinite(blue)) {
295 log_error(
"Invalid weight values (non-finite): red=%f, green=%f, blue=%f", red, green, blue);
301 if (red < min_weight || red > max_weight || green < min_weight || green > max_weight || blue < min_weight ||
304 "precalc_rgb_palettes: Weight values out of expected range: red=%f, green=%f, blue=%f (clamping to safe range)",
309 const float safe_red = (red < min_weight) ? min_weight : ((red > max_weight) ? max_weight : red);
310 const float safe_green = (green < min_weight) ? min_weight : ((green > max_weight) ? max_weight : green);
311 const float safe_blue = (blue < min_weight) ? min_weight : ((blue > max_weight) ? max_weight : blue);
313 const unsigned short max_ushort = 65535;
314 const unsigned short min_ushort = 0;
318 float red_val = (float)n * safe_red;
319 float green_val = (float)n * safe_green;
320 float blue_val = (float)n * safe_blue;
323 if (red_val < (
float)min_ushort) {
324 red_val = (float)min_ushort;
325 }
else if (red_val > (
float)max_ushort) {
326 red_val = (float)max_ushort;
329 if (green_val < (
float)min_ushort) {
330 green_val = (float)min_ushort;
331 }
else if (green_val > (
float)max_ushort) {
332 green_val = (float)max_ushort;
335 if (blue_val < (
float)min_ushort) {
336 blue_val = (float)min_ushort;
337 }
else if (blue_val > (
float)max_ushort) {
338 blue_val = (float)max_ushort;
341 RED[n] = (
unsigned short)red_val;
342 GREEN[n] = (
unsigned short)green_val;
343 BLUE[n] = (
unsigned short)blue_val;
344 GRAY[n] = (
unsigned short)n;
350 if (!p || !palette) {
362 if (h <= 0 || w <= 0) {
377 const size_t max_char_bytes = 4;
379 const rgb_pixel_t *pix = p->
pixels;
385 size_t w_times_bytes;
386 if (checked_size_mul((
size_t)w, max_char_bytes, &w_times_bytes) !=
ASCIICHAT_OK) {
391 size_t w_times_bytes_plus_one;
392 if (checked_size_add(w_times_bytes, 1, &w_times_bytes_plus_one) !=
ASCIICHAT_OK) {
397 if (checked_size_mul((
size_t)h, w_times_bytes_plus_one, &ob.
cap) !=
ASCIICHAT_OK) {
409 for (
int y = 0; y < h; y++) {
410 const int row_offset = y * w;
412 for (
int x = 0; x < w;) {
413 const rgb_pixel_t pixel = pix[row_offset + x];
415 const int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
418 uint8_t safe_luminance = clamp_rgb(luminance);
428 const rgb_pixel_t next_pixel = pix[row_offset + j];
429 const int next_luminance = (77 * next_pixel.r + 150 * next_pixel.g + 29 * next_pixel.b + 128) >> 8;
430 uint8_t next_safe_luminance = clamp_rgb(next_luminance);
433 if (next_char_idx != char_idx)
444 for (
uint32_t k = 1; k < run; k++) {
463 if (!r || !g || !b) {
473 int step = 256 / levels;
474 *r = (*r / step) * step;
475 *g = (*g / step) * step;
476 *b = (*b / step) * step;
514 if (!p || !palette) {
534 const size_t max_fg_ansi = 19;
535 const size_t max_bg_ansi = 19;
536 const size_t reset_len = 4;
538 const size_t h_sz = (size_t)h;
539 const size_t w_sz = (size_t)w;
542 if (h_sz > 0 && w_sz > SIZE_MAX / h_sz) {
547 const size_t total_pixels = h_sz * w_sz;
548 const size_t bytes_per_pixel = 1 + max_fg_ansi + max_bg_ansi;
551 if (total_pixels > SIZE_MAX / bytes_per_pixel) {
556 const size_t pixel_bytes = total_pixels * bytes_per_pixel;
559 const size_t total_resets = h_sz * reset_len;
560 const size_t total_newlines = (h_sz > 0) ? (h_sz - 1) : 0;
563 const size_t extra_bytes = total_resets + total_newlines + 1;
565 if (pixel_bytes > SIZE_MAX - extra_bytes) {
570 const size_t lines_size = pixel_bytes + extra_bytes;
574 const rgb_pixel_t *pix = p->
pixels;
585 for (
int y = 0; y < h; y++) {
586 const int row_offset = y * w;
588 for (
int x = 0; x < w; x++) {
589 const rgb_pixel_t pixel = pix[row_offset + x];
590 int r = pixel.r, g = pixel.g, b = pixel.b;
592 const int luminance = (77 * r + 150 * g + 29 * b + 128) >> 8;
595 uint8_t safe_luminance = clamp_rgb(luminance);
600 const char ascii_char = char_info->
utf8_bytes[0];
617 _Thread_local
static char color_code[20];
618 SAFE_SNPRINTF(color_code,
sizeof(color_code),
"\033[38;2;%d;%d;%dm", r, g, b);
623 _Thread_local
static char color_code[20];
624 SAFE_SNPRINTF(color_code,
sizeof(color_code),
"\033[48;2;%d;%d;%dm", r, g, b);
629 if (!fg_code || !bg_code) {
635 if (r == g && g == b) {
639 }
else if (r > 248) {
642 *fg_code = 232 + (r - 8) / 10;
646 int r_level = (r * 5) / 255;
647 int g_level = (g * 5) / 255;
648 int b_level = (b * 5) / 255;
649 *fg_code = 16 + 36 * r_level + 6 * g_level + b_level;
658 if (!image || !image->
pixels || !caps || !palette) {
660 image->
pixels, caps, palette);
669 return rgb_to_truecolor_halfblocks_neon(rgb_data, image->
w, image->
h, 0);
723 if (!image || !image->
pixels || !palette) {
740 if (!image || !image->
pixels || !palette) {
748 if (h <= 0 || w <= 0) {
760 if (checked_size_mul((
size_t)h, (
size_t)w, &h_times_w) !=
ASCIICHAT_OK) {
764 size_t h_times_w_times_12;
765 if (checked_size_mul(h_times_w, 12u, &h_times_w_times_12) !=
ASCIICHAT_OK) {
770 if (checked_size_add(h_times_w_times_12, (
size_t)h, &buffer_size) !=
ASCIICHAT_OK) {
778 const char *reset_code =
"\033[0m";
780 for (
int y = 0; y < h; y++) {
781 for (
int x = 0; x < w; x++) {
782 rgb_pixel_t pixel = image->
pixels[y * w + x];
789 int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
799 uint8_t safe_luminance = clamp_rgb(luminance);
804 char ascii_char = palette[char_idx];
805 const utf8_char_t *char_info = &utf8_cache->
cache[(
unsigned char)ascii_char];
809 for (
int byte_idx = 0; byte_idx < char_info->
byte_len; byte_idx++) {
814 size_t palette_len = strlen(palette);
815 int palette_index = (luminance * ((int)palette_len - 1) + 127) / 255;
816 *ptr++ = palette[palette_index];
822 ptr += strlen(reset_code);
834 if (!image || !image->
pixels || !palette) {
842 if (h <= 0 || w <= 0) {
851 size_t pixel_count = (size_t)h * (
size_t)w;
859 if (checked_size_mul((
size_t)h, (
size_t)w, &h_times_w2) !=
ASCIICHAT_OK) {
864 size_t h_times_w2_times_12;
865 if (checked_size_mul(h_times_w2, 12u, &h_times_w2_times_12) !=
ASCIICHAT_OK) {
871 if (checked_size_add(h_times_w2_times_12, (
size_t)h, &buffer_size) !=
ASCIICHAT_OK) {
880 const char *reset_code =
"\033[0m";
882 for (
int y = 0; y < h; y++) {
883 for (
int x = 0; x < w; x++) {
884 rgb_pixel_t pixel = image->
pixels[y * w + x];
891 int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
901 uint8_t safe_luminance = clamp_rgb(luminance);
906 char ascii_char = palette[char_idx];
907 const utf8_char_t *char_info = &utf8_cache->
cache[(
unsigned char)ascii_char];
911 for (
int byte_idx = 0; byte_idx < char_info->
byte_len; byte_idx++) {
916 size_t palette_len = strlen(palette);
917 int palette_index = (luminance * ((int)palette_len - 1) + 127) / 255;
918 *ptr++ = palette[palette_index];
924 ptr += strlen(reset_code);
937 if (!image || !image->
pixels || !palette) {
945 if (h <= 0 || w <= 0) {
954 size_t pixel_count = (size_t)h * (
size_t)w;
960 (size_t)h * (
size_t)w * (use_background ? 24 : 12) + (
size_t)h;
965 const char *reset_code =
"\033[0m";
967 for (
int y = 0; y < h; y++) {
968 for (
int x = 0; x < w; x++) {
969 rgb_pixel_t pixel = image->
pixels[y * w + x];
974 if (use_background) {
980 int bg_luminance = (bg_r * 77 + bg_g * 150 + bg_b * 29) / 256;
981 uint8_t fg_color = (bg_luminance < 127) ? 15 : 0;
991 int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
1001 uint8_t safe_luminance = clamp_rgb(luminance);
1006 char ascii_char = palette[char_idx];
1007 const utf8_char_t *char_info = &utf8_cache->
cache[(
unsigned char)ascii_char];
1011 for (
int byte_idx = 0; byte_idx < char_info->
byte_len; byte_idx++) {
1016 size_t palette_len = strlen(palette);
1017 int palette_index = (luminance * ((int)palette_len - 1) + 127) / 255;
1018 *ptr++ = palette[palette_index];
1024 ptr += strlen(reset_code);
Fast ANSI escape sequence generation.
🖼️ ASCII Art Conversion and Output Interface
SIMD-optimized ASCII conversion interface.
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
#define SAFE_MALLOC_SIMD(size, cast)
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MEMSET(dest, dest_size, ch, count)
#define SAFE_MALLOC(size, cast)
#define SAFE_SNPRINTF(buffer, buffer_size,...)
#define SAFE_CALLOC(count, size, cast)
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
unsigned short int GRAY[256]
Grayscale lookup table.
unsigned short int BLUE[256]
Blue channel lookup table.
unsigned short int GREEN[256]
Green channel lookup table.
unsigned short int RED[256]
Red channel lookup table.
char * append_16color_fg(char *dst, uint8_t color_index)
Append 16-color foreground ANSI sequence.
void quantize_color(int *r, int *g, int *b, int levels)
Quantize color to specified number of levels.
void precalc_rgb_palettes(const float red, const float green, const float blue)
Precalculate RGB palettes with color adjustment.
void image_resize_interpolation(const image_t *source, image_t *dest)
Resize image using bilinear interpolation.
char * append_16color_bg(char *dst, uint8_t color_index)
Append 16-color background ANSI sequence.
char * image_print_16color_dithered(const image_t *image, const char *palette)
Print image using 16-color ANSI mode with dithering.
char * image_print(const image_t *p, const char *palette)
Print image as ASCII art (monochrome)
utf8_palette_cache_t * get_utf8_palette_cache(const char *ascii_chars)
Get or create UTF-8 palette cache.
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Convert RGB to 16-color ANSI index.
void image_destroy_to_pool(image_t *image)
Destroy an image allocated from buffer pool.
void ob_term(outbuf_t *ob)
Append null terminator to buffer.
char * image_print_color(const image_t *p, const char *palette)
Print image as ASCII art with color.
char * rgb_to_ansi_bg(int r, int g, int b)
Convert RGB to ANSI background color code.
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)
Convert RGB to 16-color with Floyd-Steinberg dithering.
void rgb_to_ansi_8bit(int r, int g, int b, int *fg_code, int *bg_code)
Convert RGB to 8-bit ANSI color codes.
void ob_putc(outbuf_t *ob, char c)
Append a character to buffer.
ansi_color_mode_t
Color mode for ANSI generation.
bool rep_is_profitable(uint32_t runlen)
Check if run-length encoding is profitable.
void ansi_rle_init(ansi_rle_context_t *ctx, char *buffer, size_t capacity, ansi_color_mode_t mode)
Initialize run-length encoding context.
#define ASCII_LUMINANCE_LEVELS
Number of luminance levels supported (256)
char * image_print_16color(const image_t *image, const char *palette)
Print image using 16-color ANSI mode.
void emit_rep(outbuf_t *ob, uint32_t extra)
Emit run-length encoded sequence.
uint8_t char_index_ramp[64]
#define IMAGE_MAX_WIDTH
Maximum image width (4K resolution)
void ob_write(outbuf_t *ob, const char *s, size_t n)
Append a string to buffer.
void ansi_rle_finish(ansi_rle_context_t *ctx)
Finish RLE sequence.
#define IMAGE_MAX_PIXELS_SIZE
Maximum pixel data size in bytes.
void get_16color_rgb(uint8_t color_index, uint8_t *r, uint8_t *g, uint8_t *b)
Get the actual RGB values for a 16-color ANSI index.
#define IMAGE_MAX_HEIGHT
Maximum image height (4K resolution)
void ansi_rle_add_pixel(ansi_rle_context_t *ctx, uint8_t r, uint8_t g, uint8_t b, char ascii_char)
Add a pixel with run-length encoding.
char * rgb_to_ansi_fg(int r, int g, int b)
Convert RGB to ANSI foreground color code.
void image_clear(image_t *p)
Clear image (set all pixels to black)
void image_resize(const image_t *s, image_t *d)
Resize image using nearest-neighbor interpolation.
char * image_print_simd(image_t *image, const char *ascii_chars)
Print image as ASCII using SIMD (monochrome)
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.
char * image_print_256color(const image_t *image, const char *palette)
Print image using 256-color ANSI mode.
image_t * image_new_from_pool(size_t width, size_t height)
Create a new image from buffer pool.
void ansi_fast_init_16color(void)
Initialize 16-color mode lookup tables.
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.
void image_destroy(image_t *p)
Destroy an image allocated with image_new()
image_t * image_new(size_t width, size_t height)
Create a new image with standard allocation.
@ IMAGE_ALLOC_POOL
Pixels allocated with buffer_pool_alloc()
@ IMAGE_ALLOC_SIMD
Pixels allocated with SAFE_MALLOC_SIMD()
Application limits and constraints.
🔢 Mathematical Utility Functions
⚙️ Command-line options parsing and configuration management for ascii-chat
Dynamic Output Buffer with ANSI Sequence Support.
✅ Safe Integer Arithmetic and Overflow Detection
Run-length encoded color output context.
int w
Image width in pixels (must be > 0)
uint8_t alloc_method
Allocation method (image_alloc_method_t) for correct deallocation.
int h
Image height in pixels (must be > 0)
rgb_pixel_t * pixels
Pixel data array (width * height RGB pixels, row-major order)
Dynamic output buffer (auto-expanding)
size_t cap
Buffer capacity in bytes (maximum length before reallocation)
char * buf
Buffer pointer (allocated, owned by caller, must be freed)
RGB error structure for dithering.
Complete terminal capabilities structure.
terminal_color_mode_t color_level
Detected color support level (terminal_color_mode_t)
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
UTF-8 character structure.
UTF-8 palette cache structure.
asciichat_error_t image_validate_dimensions(size_t width, size_t height)
Validate image dimensions (non-zero, within limits)
🖼️ Safe overflow-checked buffer size calculations for images and video frames
rgb_pixel_t * image_pixel(image_t *p, const int x, const int y)
char * image_print_with_capabilities(const image_t *image, const terminal_capabilities_t *caps, const char *palette, const char luminance_palette[256] __attribute__((unused)))
Image Data Structures and Operations.
Common SIMD utilities and structures.