ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
Video to ASCII Conversion

🖼️ Everything necessary to turn pixels into text art More...

Modules

 Video Frame Buffers
 📹 High-performance video frame management with double-buffering and lock-free operations
 

Files

file  ansi.c
 ANSI escape sequence utilities.
 
file  ansi.h
 ANSI escape sequence utilities.
 
file  ansi_fast.c
 ⚡ Fast ANSI color code generation with SIMD-accelerated terminal output
 
file  ansi_fast.h
 Fast ANSI escape sequence generation.
 
file  ascii.c
 🖼️ Image-to-ASCII conversion with SIMD acceleration, color matching, and terminal optimization
 
file  ascii.h
 🖼️ ASCII Art Conversion and Output Interface
 
file  image.c
 🖨️ Image processing: format detection, decoding, scaling, and pixel format conversion
 
file  image.h
 Image Data Structures and Operations.
 
file  output_buffer.c
 📝 Output buffer helpers for efficient string building in ASCII rendering pipeline
 
file  output_buffer.h
 Dynamic Output Buffer with ANSI Sequence Support.
 
file  rle.c
 ANSI RLE (REP) sequence compression and expansion.
 
file  rle.h
 ANSI RLE (REP) sequence compression and expansion.
 
file  ascii_simd.c
 ⚡ Main SIMD ASCII rendering dispatcher with architecture detection and fallback handling
 
file  ascii_simd.h
 SIMD-optimized ASCII conversion interface.
 
file  ascii_simd_color.c
 🎨 SIMD-accelerated color matching and palette lookup for ASCII rendering
 
file  avx2.c
 🚀 AVX2-accelerated ASCII rendering with 256-bit vector operations for x86_64
 
file  avx2.h
 AVX2-optimized ASCII rendering functions.
 
file  common.c
 🔧 Shared SIMD utilities: initialization, cleanup, and architecture-specific resource management
 
file  common.h
 Common SIMD utilities and structures.
 
file  neon.c
 ⚡ ARM NEON-accelerated ASCII rendering with 128-bit vector operations for ARM64
 
file  neon.h
 NEON-optimized ASCII rendering functions.
 
file  sse2.c
 ⚡ SSE2-accelerated ASCII rendering with 128-bit vector operations (x86 baseline)
 
file  sse2.h
 SSE2-optimized ASCII rendering functions.
 
file  ssse3.c
 🚀 SSSE3-accelerated ASCII rendering with advanced shuffle operations for x86
 
file  ssse3.h
 SSSE3-optimized ASCII rendering functions.
 
file  sve.c
 🚀 ARM SVE (Scalable Vector Extension) ASCII rendering with variable-length vectors
 
file  sve.h
 SVE-optimized ASCII rendering functions.
 

Data Structures

struct  ansi_timing_t
 Timing breakdown for performance measurement. More...
 
struct  ansi_rle_context_t
 Run-length encoded color output context. More...
 
struct  rgb_error_t
 RGB error structure for dithering. More...
 
struct  ascii_frame_source_t
 Frame source structure for grid layout. More...
 
struct  __attribute__
 RGB pixel structure. More...
 
struct  image_t
 Image structure. More...
 
struct  outbuf_t
 Dynamic output buffer (auto-expanding) More...
 
struct  dec3_t
 Decimal conversion cache structure (1-3 digits) More...
 
struct  global_dec3_cache_t
 Global decimal cache for digit conversion. More...
 
struct  Str
 Dynamic string buffer structure. More...
 
struct  RLEState
 Run-length encoding state for ANSI color optimization. More...
 
struct  ImageRGB
 ImageRGB structure for NEON renderers. More...
 
struct  simd_benchmark_t
 SIMD benchmark results structure. More...
 
struct  utf8_char_t
 UTF-8 character structure. More...
 
struct  utf8_palette_cache_s
 UTF-8 palette cache structure. More...
 

Macros

#define ASCII_LUMINANCE_LEVELS   256
 Number of luminance levels supported (256)
 
#define ASCII_SLEEP_NS   50000L
 Sleep duration in nanoseconds between frames (50000 ns = 50 μs)
 
#define ANSI_FG_PREFIX   "\033[38;2;"
 ANSI foreground color prefix (truecolor mode)
 
#define ANSI_BG_PREFIX   "\033[48;2;"
 ANSI background color prefix (truecolor mode)
 
#define ANSI_COLOR_SUFFIX   "m"
 ANSI color suffix.
 
#define print(s)   fwrite(s, 1, sizeof(s) / sizeof((s)[0]), stdout)
 Print string to stdout.
 
#define console_clear(fd)   (terminal_clear_screen(), terminal_cursor_home(fd))
 Clear console and move cursor to home position.
 
#define cursor_reset(fd)   terminal_cursor_home(fd)
 Reset cursor to home position.
 
#define ascii_clear_screen(fd)   terminal_clear_screen()
 Clear terminal screen.
 
#define cursor_hide(fd)   terminal_hide_cursor(fd, true)
 Hide terminal cursor.
 
#define cursor_show(fd)   terminal_hide_cursor(fd, false)
 Show terminal cursor.
 
#define ascii_zzz()   nanosleep((struct timespec *)&ASCII_SLEEP_START, (struct timespec *)&ASCII_SLEEP_STOP)
 Sleep for frame rate limiting.
 
#define IMAGE_MAX_WIDTH   3840
 Maximum image width (4K resolution)
 
#define IMAGE_MAX_HEIGHT   2160
 Maximum image height (4K resolution)
 
#define IMAGE_MAX_PIXELS_SIZE   (IMAGE_MAX_WIDTH * IMAGE_MAX_HEIGHT * sizeof(rgb_pixel_t))
 Maximum pixel data size in bytes.
 
#define RAMP64_SIZE   64
 Ramp64 cache size (64 entries)
 

Typedefs

typedef struct utf8_palette_cache_s utf8_palette_cache_t
 UTF-8 palette cache structure.
 

Enumerations

enum  ansi_color_mode_t { ANSI_MODE_FOREGROUND , ANSI_MODE_BACKGROUND , ANSI_MODE_FOREGROUND_BACKGROUND }
 Color mode for ANSI generation. More...
 
enum  image_alloc_method_t { IMAGE_ALLOC_SIMD = 0 , IMAGE_ALLOC_POOL = 1 }
 Pixel allocation method tracking. More...
 

Functions

void ansi_fast_init (void)
 Initialize the decimal lookup table.
 
char * append_truecolor_fg (char *dst, uint8_t r, uint8_t g, uint8_t b)
 Append truecolor foreground ANSI sequence.
 
char * append_truecolor_bg (char *dst, uint8_t r, uint8_t g, uint8_t b)
 Append truecolor background ANSI sequence.
 
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)
 Append truecolor foreground and background ANSI sequence.
 
void ansi_rle_init (ansi_rle_context_t *ctx, char *buffer, size_t capacity, ansi_color_mode_t mode)
 Initialize run-length encoding context.
 
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.
 
void ansi_rle_finish (ansi_rle_context_t *ctx)
 Finish RLE sequence.
 
void ansi_fast_init_256color (void)
 Initialize 256-color mode lookup tables.
 
char * append_256color_fg (char *dst, uint8_t color_index)
 Append 256-color foreground ANSI sequence.
 
uint8_t rgb_to_256color (uint8_t r, uint8_t g, uint8_t b)
 Convert RGB to 256-color palette index.
 
void ansi_fast_init_16color (void)
 Initialize 16-color mode lookup tables.
 
char * append_16color_fg (char *dst, uint8_t color_index)
 Append 16-color foreground ANSI sequence.
 
char * append_16color_bg (char *dst, uint8_t color_index)
 Append 16-color background ANSI sequence.
 
uint8_t rgb_to_16color (uint8_t r, uint8_t g, uint8_t b)
 Convert RGB to 16-color ANSI index.
 
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.
 
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.
 
char * append_color_fg_for_mode (char *dst, uint8_t r, uint8_t g, uint8_t b, terminal_color_mode_t mode)
 Append color foreground sequence for specified mode.
 
asciichat_error_t ascii_read_init (unsigned short int webcam_index)
 Initialize ASCII read subsystem (e.g., webcam)
 
asciichat_error_t ascii_write_init (int fd, bool reset_terminal)
 Initialize ASCII write subsystem.
 
char * ascii_convert (image_t *original, const ssize_t width, const ssize_t height, const bool color, const bool aspect_ratio, const bool stretch, const char *palette_chars, const char luminance_palette[256])
 Convert image to ASCII art.
 
char * ascii_convert_with_capabilities (image_t *original, const ssize_t width, const ssize_t height, const terminal_capabilities_t *caps, const bool use_aspect_ratio, const bool stretch, const char *palette_chars, const char luminance_palette[256])
 Convert image to ASCII art with terminal capability awareness.
 
asciichat_error_t ascii_write (const char *frame)
 Write ASCII frame to terminal.
 
void ascii_read_destroy (void)
 Destroy ASCII read subsystem.
 
void ascii_write_destroy (int fd, bool reset_terminal)
 Destroy ASCII write subsystem.
 
char * ascii_pad_frame_width (const char *frame, size_t pad)
 Add leading spaces (left-padding) to each line of a frame.
 
char * ascii_pad_frame_height (const char *frame, size_t pad_top)
 Add blank lines (vertical padding) to center a frame vertically.
 
char * ascii_create_grid (ascii_frame_source_t *sources, int source_count, int width, int height, size_t *out_size)
 Create a grid layout from multiple ASCII frames.
 
char * get_lum_palette (void)
 Get luminance palette for character mapping.
 
image_timage_new (size_t width, size_t height)
 Create a new image with standard allocation.
 
void image_destroy (image_t *p)
 Destroy an image allocated with image_new()
 
image_timage_new_from_pool (size_t width, size_t height)
 Create a new image from buffer pool.
 
void image_destroy_to_pool (image_t *image)
 Destroy an image allocated from buffer pool.
 
void image_clear (image_t *p)
 Clear image (set all pixels to black)
 
char * image_print (const image_t *p, const char *palette)
 Print image as ASCII art (monochrome)
 
char * image_print_color (const image_t *p, const char *palette)
 Print image as ASCII art with color.
 
char * image_print_with_capabilities (const image_t *image, const terminal_capabilities_t *caps, const char *palette, const char luminance_palette[256])
 Print image with terminal capability awareness.
 
char * image_print_256color (const image_t *image, const char *palette)
 Print image using 256-color ANSI mode.
 
char * image_print_16color (const image_t *image, const char *palette)
 Print image using 16-color ANSI mode.
 
char * image_print_16color_dithered (const image_t *image, const char *palette)
 Print image using 16-color ANSI mode with dithering.
 
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.
 
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 (const image_t *source, image_t *dest)
 Resize image using nearest-neighbor interpolation.
 
void image_resize_interpolation (const image_t *source, image_t *dest)
 Resize image using bilinear interpolation.
 
char * rgb_to_ansi_fg (int r, int g, int b)
 Convert RGB to ANSI foreground color code.
 
char * rgb_to_ansi_bg (int r, int g, int b)
 Convert RGB to ANSI background color code.
 
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_reserve (outbuf_t *ob, size_t need)
 Reserve buffer space for upcoming writes.
 
void ob_putc (outbuf_t *ob, char c)
 Append a character to buffer.
 
void ob_write (outbuf_t *ob, const char *s, size_t n)
 Append a string to buffer.
 
void ob_term (outbuf_t *ob)
 Append null terminator to buffer.
 
void ob_u8 (outbuf_t *ob, uint8_t v)
 Append unsigned 8-bit integer as decimal string.
 
void ob_u32 (outbuf_t *ob, uint32_t v)
 Append unsigned 32-bit integer as decimal string.
 
void emit_set_truecolor_fg (outbuf_t *ob, uint8_t r, uint8_t g, uint8_t b)
 Emit truecolor foreground ANSI sequence.
 
void emit_set_truecolor_bg (outbuf_t *ob, uint8_t r, uint8_t g, uint8_t b)
 Emit truecolor background ANSI sequence.
 
void emit_set_256_color_fg (outbuf_t *ob, uint8_t color_idx)
 Emit 256-color foreground ANSI sequence.
 
void emit_set_256_color_bg (outbuf_t *ob, uint8_t color_idx)
 Emit 256-color background ANSI sequence.
 
void emit_reset (outbuf_t *ob)
 Emit ANSI reset sequence.
 
bool rep_is_profitable (uint32_t runlen)
 Check if run-length encoding is profitable.
 
void emit_rep (outbuf_t *ob, uint32_t extra)
 Emit run-length encoded sequence.
 
void emit_set_fg (outbuf_t *ob, uint8_t r, uint8_t g, uint8_t b)
 Emit foreground color sequence (auto-select mode)
 
void emit_set_bg (outbuf_t *ob, uint8_t r, uint8_t g, uint8_t b)
 Emit background color sequence (auto-select mode)
 
char * ansi_expand_rle (const char *input, size_t input_len)
 Expand RLE escape sequences in a string.
 
char * ansi_compress_rle (const char *input, size_t input_len)
 Compress repeated characters using RLE escape sequences.
 
void init_dec3 (void)
 Initialize decimal lookup table.
 
void ascii_simd_init (void)
 Initialize SIMD subsystem.
 
void init_default_luminance_palette (void)
 Initialize default luminance palette.
 
void str_init (Str *s)
 Initialize string buffer.
 
void str_free (Str *s)
 Free string buffer.
 
void str_reserve (Str *s, size_t need)
 Reserve space in string buffer.
 
void str_append_bytes (Str *s, const void *src, size_t n)
 Append bytes to string buffer.
 
void str_append_c (Str *s, char c)
 Append character to string buffer.
 
void str_printf (Str *s, const char *fmt,...)
 Append formatted string to buffer.
 
ImageRGB alloc_image (int w, int h)
 Allocate a new ImageRGB (RGB8 format)
 
void convert_pixels_scalar (const rgb_pixel_t *pixels, char *ascii_chars, int count, const char luminance_palette[256])
 Convert pixels to ASCII (scalar fallback)
 
char * convert_pixels_scalar_with_newlines (image_t *image, const char luminance_palette[256])
 Convert image to ASCII with newlines (scalar fallback)
 
simd_benchmark_t benchmark_simd_conversion (int width, int height, int iterations)
 Benchmark SIMD conversion methods (monochrome)
 
simd_benchmark_t benchmark_simd_color_conversion (int width, int height, int iterations, bool background_mode)
 Benchmark SIMD color conversion methods.
 
simd_benchmark_t benchmark_simd_conversion_with_source (int width, int height, int iterations, bool background_mode, const image_t *source_image, bool use_256color)
 Benchmark SIMD conversion with source image.
 
simd_benchmark_t benchmark_simd_color_conversion_with_source (int width, int height, int iterations, bool background_mode, const image_t *source_image, bool use_256color)
 Benchmark SIMD color conversion with source image.
 
void print_simd_capabilities (void)
 Print detected SIMD capabilities.
 
char * image_print_simd (image_t *image, const char *ascii_chars)
 Print image as ASCII using SIMD (monochrome)
 
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 set_color_quality_mode (bool high_quality)
 Set color quality mode.
 
bool get_256_color_fast_path (void)
 Get 256-color fast path setting.
 
void prewarm_sgr256_fg_cache (void)
 Prewarm 256-color foreground cache for benchmarks.
 
void prewarm_sgr256_cache (void)
 Prewarm 256-color foreground/background cache for benchmarks.
 
char * get_sgr256_fg_string (uint8_t fg, uint8_t *len_out)
 Get 256-color foreground ANSI sequence string.
 
char * get_sgr256_fg_bg_string (uint8_t fg, uint8_t bg, uint8_t *len_out)
 Get 256-color foreground/background ANSI sequence string.
 
size_t write_row_rep_from_arrays_enhanced (const uint8_t *fg_r, const uint8_t *fg_g, const uint8_t *fg_b, const uint8_t *bg_r, const uint8_t *bg_g, const uint8_t *bg_b, const uint8_t *fg_idx, const uint8_t *bg_idx, const char *ascii_chars, int width, char *dst, size_t cap, bool is_truecolor)
 Enhanced REP compression writer.
 
void build_ramp64 (uint8_t ramp64[64], const char *ascii_chars)
 Build 64-entry character ramp cache.
 
double calculate_cache_eviction_score (uint64_t last_access_time, uint32_t access_count, uint64_t creation_time, uint64_t current_time)
 Calculate cache eviction score.
 
uint64_t get_current_time_ns (void)
 Get current time in nanoseconds.
 
utf8_palette_cache_tget_utf8_palette_cache (const char *ascii_chars)
 Get or create UTF-8 palette cache.
 
void build_utf8_luminance_cache (const char *ascii_chars, utf8_char_t cache[256])
 Build UTF-8 luminance cache.
 
void build_utf8_ramp64_cache (const char *ascii_chars, utf8_char_t cache64[64], uint8_t char_index_ramp[64])
 Build UTF-8 ramp64 cache.
 
void simd_caches_destroy_all (void)
 Destroy all SIMD caches.
 
bool has_sse2_support (void)
 Check if SSE2 is supported.
 
bool has_ssse3_support (void)
 Check if SSSE3 is supported.
 
bool has_avx2_support (void)
 Check if AVX2 is supported.
 
bool has_neon_support (void)
 Check if NEON is supported.
 
bool has_sve_support (void)
 Check if SVE is supported.
 
char * append_sgr_truecolor_fg (char *dst, uint8_t r, uint8_t g, uint8_t b)
 Append truecolor foreground SGR sequence.
 
char * append_sgr_truecolor_bg (char *dst, uint8_t r, uint8_t g, uint8_t b)
 Append truecolor background SGR sequence.
 
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)
 Append truecolor foreground and background SGR sequence.
 
char * append_sgr_reset (char *dst)
 Append ANSI reset sequence.
 
size_t write_rgb_triplet (uint8_t value, char *dst)
 Write decimal RGB triplet using dec3 cache.
 

Variables

dec3_t dec3 [256]
 Access to internal decimal lookup table for testing.
 
char ascii_palette []
 Default ASCII palette characters.
 
uint8_t dec3_t::len
 
char dec3_t::s [3]
 
dec3_t global_dec3_cache_t::dec3_table [256]
 
bool global_dec3_cache_t::dec3_initialized
 
global_dec3_cache_t g_dec3_cache
 Global decimal cache instance.
 
char g_default_luminance_palette [256]
 Default luminance palette (256 characters)
 
char * Str::data
 
size_t Str::len
 
size_t Str::cap
 
int RLEState::cFR
 
int RLEState::cFG
 
int RLEState::cFB
 
int RLEState::cBR
 
int RLEState::cBG
 
int RLEState::cBB
 
int RLEState::runLen
 
int RLEState::seeded
 
int ImageRGB::w
 
int ImageRGB::h
 
uint8_tImageRGB::pixels
 
double simd_benchmark_t::scalar_time
 
double simd_benchmark_t::sse2_time
 
double simd_benchmark_t::ssse3_time
 
double simd_benchmark_t::avx2_time
 
double simd_benchmark_t::neon_time
 
double simd_benchmark_t::sve_time
 
double simd_benchmark_t::speedup_best
 
const char * simd_benchmark_t::best_method
 
char utf8_char_t::utf8_bytes [4]
 
uint8_t utf8_char_t::byte_len
 
uint32_t utf8_palette_cache_s::key
 
utf8_char_t utf8_palette_cache_s::cache [256]
 
utf8_char_t utf8_palette_cache_s::cache64 [64]
 
uint8_t utf8_palette_cache_s::char_index_ramp [64]
 
char utf8_palette_cache_s::palette_hash [64]
 
bool utf8_palette_cache_s::is_valid
 
_Atomic uint64_t utf8_palette_cache_s::last_access_time
 
_Atomic uint32_t utf8_palette_cache_s::access_count
 
uint64_t utf8_palette_cache_s::creation_time
 
size_t utf8_palette_cache_s::heap_index
 
double utf8_palette_cache_s::cached_score
 
UT_hash_handle utf8_palette_cache_s::hh
 

Luminance Calculation Constants

#define LUMA_RED   77
 Luminance red coefficient (0.299 * 256 = 77)
 
#define LUMA_GREEN   150
 Luminance green coefficient (0.587 * 256 = 150)
 
#define LUMA_BLUE   29
 Luminance blue coefficient (0.114 * 256 = 29)
 
#define LUMA_THRESHOLD   128
 Luminance threshold for rounding.
 

Cache Eviction Algorithm Parameters

#define CACHE_FREQUENCY_DECAY_TIME   300.0
 Frequency bonus decay time (5 minutes in seconds)
 
#define CACHE_RECENCY_SCALE   60.0
 Recency importance scale (1 minute in seconds)
 
#define CACHE_RECENT_ACCESS_THRESHOLD   10
 Recent access protection threshold (10 seconds)
 
#define CACHE_MAX_LIFETIME   3600
 Maximum cache lifetime (1 hour in seconds)
 
#define CACHE_MIN_ACCESS_THRESHOLD   3
 Minimum accesses for frequency protection.
 

Detailed Description

🖼️ Everything necessary to turn pixels into text art

This header provides optimized ANSI escape sequence generation using precomputed lookup tables and run-length encoding for efficient terminal output.

The interface provides:

Note
Uses precomputed decimal lookup tables for maximum performance.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides comprehensive functions for converting images to ASCII art and outputting frames to the terminal. Supports multiple color modes, aspect ratio preservation, capability-aware conversion, and frame layout management.

CORE FEATURES:

CONVERSION MODES:

The system supports multiple conversion modes:

FRAME MANAGEMENT:

Frame management includes:

Note
All conversion functions return allocated strings that must be freed.
Terminal initialization functions must be matched with cleanup.
Capability-aware conversion is recommended for optimal results.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides comprehensive image data structures and operations for ascii-chat. Supports RGB images with standard and SIMD-optimized formats for efficient image processing and ASCII art conversion.

CORE FEATURES:

IMAGE FORMATS:

The system supports:

COLOR MODES:

ASCII conversion supports multiple color modes:

Note
All image allocation functions must be matched with corresponding destroy functions (image_destroy() or image_destroy_to_pool()).
ASCII conversion functions return allocated strings that must be freed by the caller.
Image structures are compatible with webcam capture (image_t).
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides a dynamic output buffer system with automatic expansion and optimized ANSI escape sequence emission functions. The buffer is designed for efficient ASCII art frame construction and terminal output.

CORE FEATURES:

BUFFER MANAGEMENT:

The output buffer automatically expands when space is needed:

ANSI ESCAPE SEQUENCES:

The system emits ANSI escape sequences for:

COLOR MODE AUTO-SELECTION:

Color emission functions automatically select optimal mode:

Note
All buffer operations ensure null-terminated strings.
Buffer memory must be freed by caller using free() on buf pointer.
ANSI sequences are optimized for minimal terminal I/O.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This module provides functions for working with ANSI RLE (Run-Length Encoding) escape sequences. The REP sequence (CSI Ps b) repeats the preceding graphic character Ps times.

RLE FORMAT:

The ANSI REP sequence format is: ESC [ N b

Example: "A\x1b[3b" means "A" followed by 3 more "A"s = "AAAA"

USE CASES:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025

This header provides SIMD-optimized functions for converting images to ASCII art. Supports multiple architectures including SSE2, SSSE3, AVX2 (x86), NEON (ARM), and SVE (ARM).

The interface provides:

Note
SIMD support is automatically detected and enabled based on CPU capabilities. CMake controls SIMD levels via ENABLE_SIMD_* options.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
October 2025

This header provides common utilities, cache systems, and helper functions shared across all SIMD implementations.

The interface provides:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
September 2025

This header provides NEON (Advanced SIMD) optimized functions for converting images to ASCII art on ARM processors.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
August 2025

This header provides SSE2 (Streaming SIMD Extensions 2) optimized functions for converting images to ASCII art on x86-64 CPUs.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
August 2025

This header provides SSSE3 (Supplemental Streaming SIMD Extensions 3) optimized functions for converting images to ASCII art on x86-64 CPUs.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
August 2025

This header provides SVE (Scalable Vector Extension) optimized functions for converting images to ASCII art on ARM processors with SVE support.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
August 2025

video README

The video module converts RGB image data into ASCII art suitable for terminal display, with SIMD-optimized processing for real-time video conversion at 60+ FPS.

Overview

ascii-chat renders live video as ASCII art in the terminal by:

  1. Capturing RGB frames from webcam (1920x1080 or configured resolution)
  2. Scaling frames to terminal dimensions (typically 80x24 to 200x60)
  3. Converting RGB pixels to grayscale luminance
  4. Mapping luminance values to ASCII characters from a palette
  5. Optionally preserving color information via ANSI escape sequences
  6. Generating optimized ANSI output for terminal rendering
  7. Minimizing escape sequences to reduce bandwidth and rendering time

The module is highly optimized using SIMD instructions, achieving:

  • 60+ FPS for 1920x1080 → 160x45 conversion
  • <5% CPU usage on modern processors
  • Sub-millisecond conversion latency

Features

Rendering Modes:

  • Full-block mode: One character per pixel (standard resolution)
  • Half-block mode: Two pixels per character using ▀ and ▄ (2x vertical resolution)
  • Color mode: Preserves RGB color via ANSI 24-bit true color escape sequences
  • Monochrome mode: Grayscale ASCII art using luminance only

SIMD Acceleration:

  • SSE2: 16-byte parallel processing (x86_64 baseline)
  • SSSE3: Enhanced shuffle operations for pixel reordering
  • AVX2: 32-byte parallel processing (2x throughput vs SSE2)
  • NEON: 16-byte parallel processing (ARM/ARM64)
  • SVE: Scalable vector processing (ARM v8.2+)
  • Runtime CPU detection: Automatically selects best SIMD level

Grid Layouts:

  • Multi-client video grid (2x2, 3x3, up to 9 clients)
  • Automatic aspect ratio correction for each grid cell
  • Border characters between grid cells
  • Client ID labels for each video stream

Palette Support:

  • Customizable ASCII character sets
  • UTF-8 multi-byte character support
  • Built-in palettes: standard, dense, blocks, custom
  • Brightness-based character selection

Aspect Ratio Correction:

  • Terminal characters are ~2:1 height:width ratio
  • Automatic scaling to maintain proper aspect ratio
  • Prevents stretched/squished video

Rendering Pipeline

1. Image Scaling

Resize RGB image from webcam resolution to terminal dimensions:

  • Input: RGB24 image (e.g., 1920x1080 = 6,220,800 bytes)
  • Output: Scaled RGB24 image (e.g., 160x45 = 21,600 bytes)
  • Algorithm: Bilinear interpolation for smooth scaling
  • SIMD: Process 16 pixels at a time (SSE2) or 32 pixels (AVX2)
  • Performance: ~200 µs for 1920x1080 → 160x45 on modern CPU

2. Brightness Calculation

Convert RGB pixels to luminance (grayscale):

  • Formula: Y = 0.299*R + 0.587*G + 0.114*B (BT.601 standard)
  • SIMD: Process 16 RGB triplets in parallel
  • Input: RGB24 array (3 bytes per pixel)
  • Output: Y8 array (1 byte per pixel)
  • Performance: ~50 µs for 160x45 image

SIMD implementation (SSE2 example):

// Load 16 RGB pixels (48 bytes)
__m128i r = _mm_loadu_si128((__m128i*)(rgb + 0)); // R values
__m128i g = _mm_loadu_si128((__m128i*)(rgb + 16)); // G values
__m128i b = _mm_loadu_si128((__m128i*)(rgb + 32)); // B values
// Apply BT.601 coefficients (0.299, 0.587, 0.114)
__m128i y = _mm_add_epi16(
_mm_mullo_epi16(r, _mm_set1_epi16(77)), // 0.299 * 256 ≈ 77
_mm_add_epi16(
_mm_mullo_epi16(g, _mm_set1_epi16(150)), // 0.587 * 256 ≈ 150
_mm_mullo_epi16(b, _mm_set1_epi16(29)) // 0.114 * 256 ≈ 29
)
);
// Shift right by 8 to get final Y values
y = _mm_srli_epi16(y, 8);

3. Palette Mapping

Map brightness values to ASCII characters:

  • Input: Luminance value (0-255)
  • Output: ASCII character from palette
  • Palette example: " .:-=+*#%@" (10 characters, dark to bright)
  • Mapping: luminance / (256 / palette_length)
  • SIMD: Lookup table with SSSE3 shuffle instruction

Half-block rendering uses special logic:

  • Combines two vertically adjacent pixels into one character
  • Uses ▀ (upper half block), ▄ (lower half block), or █ (full block)
  • Effectively doubles vertical resolution
  • Example: 80x48 terminal displays 80x96 effective resolution

4. ANSI Escape Sequence Generation

Generate optimized ANSI sequences for terminal:

  • Color changes only when pixel color changes (stateful tracking)
  • Cursor movement optimized (relative vs absolute positioning)
  • Typical escape sequence: \x1b[38;2;R;G;Bm (24-bit color)
  • Output buffering to minimize write() syscalls
  • Pre-allocated buffer pool to avoid malloc overhead

Optimization techniques:

  • Delta encoding: Only emit changes from previous frame
  • Run-length encoding: Combine consecutive identical characters
  • Color caching: Remember last foreground/background color
  • Escape sequence caching: Pre-compute common sequences

Example output:

\x1b[38;2;255;128;64m###\x1b[38;2;200;100;50m***\x1b[38;2;150;75;37m+++

5. Terminal Output

Write ANSI output to terminal:

  • Single write() call per frame (buffered output)
  • Double-buffering: Compose frame while displaying previous
  • Cursor hiding during frame update (reduces flicker)
  • Cursor positioning to (1,1) for each frame
  • Terminal raw mode for direct output control

SIMD Optimizations

SSE2 (x86_64 Baseline)

Capabilities:

  • 128-bit vector registers (XMM0-XMM15)
  • Process 16 bytes or 8 int16 or 4 int32 simultaneously
  • Integer arithmetic, logic, compare operations
  • Available on all x86_64 CPUs

Performance:

  • RGB to luminance: ~4x speedup vs scalar
  • Palette lookup: ~8x speedup vs scalar
  • Frame conversion: ~60 FPS for 1920x1080 → 160x45

SSSE3 (Supplemental SSE3)

Additional capabilities beyond SSE2:

  • PSHUFB: Arbitrary byte shuffle within 128-bit vector
  • Enables fast palette lookup via table shuffle
  • Horizontal operations for pixel combining

Performance improvement:

  • Palette lookup: ~12x speedup vs scalar (1.5x vs SSE2)
  • Reduced instruction count for complex permutations

AVX2 (Advanced Vector Extensions 2)

Capabilities:

  • 256-bit vector registers (YMM0-YMM15)
  • Process 32 bytes or 16 int16 or 8 int32 simultaneously
  • Double throughput vs SSE2 for same operations
  • Available on Intel Haswell+ (2013), AMD Excavator+ (2015)

Performance:

  • RGB to luminance: ~8x speedup vs scalar (2x vs SSE2)
  • Frame conversion: ~90 FPS for 1920x1080 → 160x45
  • Reduced loop overhead (process 2x data per iteration)

NEON (ARM)

Capabilities:

  • 128-bit vector registers (Q0-Q15 or D0-D31)
  • Process 16 bytes or 8 int16 or 4 int32 simultaneously
  • Similar performance to SSE2 on equivalent clock speeds
  • Available on ARM Cortex-A series, Apple Silicon

Performance on Apple M1:

  • RGB to luminance: ~6x speedup vs scalar
  • Frame conversion: ~120 FPS for 1920x1080 → 160x45 (higher clock)

SVE (ARM Scalable Vector Extension)

Capabilities:

  • Scalable vectors: 128-bit to 2048-bit (implementation defined)
  • Future-proof: Code runs on any SVE vector length
  • Predicate registers for advanced masking
  • Available on ARM v8.2+ (AWS Graviton3, Fujitsu A64FX)

Performance potential:

  • 512-bit vectors: ~4x SSE2 throughput
  • Automatically scales with future CPU generations

Runtime CPU Detection

ascii-chat automatically detects and uses the best SIMD level:

// At startup, detect CPU capabilities
if (cpu_has_avx2()) {
ascii_convert = ascii_convert_avx2; // Best performance
} else if (cpu_has_ssse3()) {
ascii_convert = ascii_convert_ssse3;
} else if (cpu_has_sse2()) {
ascii_convert = ascii_convert_sse2; // Baseline x86_64
} else if (cpu_has_neon()) {
ascii_convert = ascii_convert_neon; // ARM
} else {
ascii_convert = ascii_convert_scalar; // Fallback
}
char * ascii_convert(image_t *original, const ssize_t width, const ssize_t height, const bool color, const bool _aspect_ratio, const bool stretch, const char *palette_chars, const char luminance_palette[256])
Convert image to ASCII art.
Definition ascii.c:67

Usage Examples

Basic Conversion

// Initialize ASCII converter
ascii_context_t ascii_ctx;
ascii_config_t config = {
.width = 160,
.height = 45,
.palette = " .:-=+*#%@",
.color = true,
.half_blocks = false
};
ascii_init(&ascii_ctx, &config);
// Convert RGB frame to ASCII
uint8_t *rgb_data = ...; // From webcam (1920x1080 RGB24)
char *ascii_output;
size_t output_len;
ascii_convert(&ascii_ctx, rgb_data, &ascii_output, &output_len);
// Write to terminal
write(STDOUT_FILENO, "\x1b[2J\x1b[H", 7); // Clear screen, home cursor
write(STDOUT_FILENO, ascii_output, output_len);
// Cleanup
ascii_destroy(&ascii_ctx);
unsigned char uint8_t
Definition common.h:56

Half-Block Mode

// Half-block mode for 2x vertical resolution
ascii_config_t config = {
.width = 80,
.height = 48, // Will show 80x96 effective resolution
.palette = " ▀▄█", // Special half-block characters
.color = true,
.half_blocks = true
};
ascii_init(&ascii_ctx, &config);
// Conversion combines vertical pixel pairs
ascii_convert(&ascii_ctx, rgb_data, &ascii_output, &output_len);

Multi-Client Grid

// 2x2 grid for 4 clients
ascii_grid_config_t grid_config = {
.grid_width = 2,
.grid_height = 2,
.cell_width = 80,
.cell_height = 24,
.border_char = '|',
.show_labels = true
};
ascii_grid_t grid;
ascii_grid_init(&grid, &grid_config);
// Render each client's frame to grid cell
for (int i = 0; i < 4; i++) {
ascii_grid_set_cell(&grid, i, client_frames[i], client_labels[i]);
}
// Composite final grid output
char *grid_output;
size_t grid_len;
ascii_grid_render(&grid, &grid_output, &grid_len);
write(STDOUT_FILENO, grid_output, grid_len);
ascii_grid_destroy(&grid);

Performance Benchmarks

Measured on Intel i7-10700K @ 3.8 GHz, 1920x1080 → 160x45 conversion:

Implementation FPS Latency CPU %
Scalar 15 66 ms 25%
SSE2 60 16 ms 6%
SSSE3 75 13 ms 5%
AVX2 90 11 ms 4%

Measured on Apple M1 (ARM NEON):

  • NEON: 120 FPS, 8 ms latency, 5% CPU

Memory usage:

  • RGB frame buffer: 6 MB (1920x1080x3)
  • Scaled frame: 21 KB (160x45x3)
  • ASCII output: ~50 KB (with ANSI color codes)
  • Total: ~6.1 MB per frame

Terminal Compatibility

ascii-chat supports various terminal features:

True Color (24-bit RGB):

  • Supported: iTerm2, Alacritty, Windows Terminal, GNOME Terminal 3.16+
  • Escape sequence: \x1b[38;2;R;G;Bm (foreground), \x1b[48;2;R;G;Bm (background)
  • Falls back to 256-color if true color unavailable

256-Color Mode:

  • Supported: Most modern terminals
  • Escape sequence: \x1b[38;5;Nm where N is 0-255
  • Color palette approximation for RGB values

UTF-8 Support:

  • Required for half-block characters (▀▄█)
  • Required for custom Unicode palettes
  • Automatic detection via $LANG and $LC_ALL

Terminal Size Detection:

  • Uses TIOCGWINSZ ioctl on POSIX (lib/platform/posix/terminal.c)
  • Uses GetConsoleScreenBufferInfo on Windows (lib/platform/windows/terminal.c)
  • Falls back to $COLUMNS and $LINES environment variables
  • Default: 80x24 if detection fails
See also
video/ascii.h
video/ansi_fast.h
video/simd/ascii_simd.h
video/output_buffer.h
palette.h

Macro Definition Documentation

◆ ANSI_BG_PREFIX

#define ANSI_BG_PREFIX   "\033[48;2;"

#include <ascii.h>

ANSI background color prefix (truecolor mode)

ANSI escape sequence prefix for 24-bit truecolor background colors. Format: ESC[48;2;r;g;bm (where r, g, b are RGB values).

Note
Use with ANSI_COLOR_SUFFIX to form complete escape sequence.
Example: ANSI_BG_PREFIX "255;0;0" ANSI_COLOR_SUFFIX (red background).

Definition at line 471 of file ascii.h.

◆ ANSI_COLOR_SUFFIX

#define ANSI_COLOR_SUFFIX   "m"

#include <ascii.h>

ANSI color suffix.

ANSI escape sequence suffix for color escape sequences. Used with ANSI_FG_PREFIX or ANSI_BG_PREFIX to form complete escape sequences.

Note
Format: prefix + RGB + suffix = complete ANSI sequence.
Example: "\033[38;2;255;0;0m" (red foreground).

Definition at line 485 of file ascii.h.

◆ ANSI_FG_PREFIX

#define ANSI_FG_PREFIX   "\033[38;2;"

#include <ascii.h>

ANSI foreground color prefix (truecolor mode)

ANSI escape sequence prefix for 24-bit truecolor foreground colors. Format: ESC[38;2;r;g;bm (where r, g, b are RGB values).

Note
Use with ANSI_COLOR_SUFFIX to form complete escape sequence.
Example: ANSI_FG_PREFIX "255;0;0" ANSI_COLOR_SUFFIX (red foreground).

Definition at line 458 of file ascii.h.

◆ ascii_clear_screen

#define ascii_clear_screen (   fd)    terminal_clear_screen()

#include <ascii.h>

Clear terminal screen.

Parameters
fdFile descriptor for terminal

Clears terminal screen without moving cursor. Equivalent to terminal_clear_screen().

Definition at line 546 of file ascii.h.

◆ ASCII_LUMINANCE_LEVELS

#define ASCII_LUMINANCE_LEVELS   256

#include <ascii.h>

Number of luminance levels supported (256)

Number of luminance levels used for character mapping. Each pixel's brightness value (0-255) maps to a character in the luminance palette.

Note
256 levels correspond to 8-bit pixel brightness values.
Luminance palette must have 256 elements.

Definition at line 427 of file ascii.h.

◆ ASCII_SLEEP_NS

#define ASCII_SLEEP_NS   50000L

#include <ascii.h>

Sleep duration in nanoseconds between frames (50000 ns = 50 μs)

Sleep duration between frame outputs for frame rate limiting. Used to prevent excessive terminal I/O and control frame rate.

Note
50000 nanoseconds = 50 microseconds = 0.05 milliseconds
Used for frame rate limiting in video playback.

Definition at line 440 of file ascii.h.

◆ ascii_zzz

#define ascii_zzz ( )    nanosleep((struct timespec *)&ASCII_SLEEP_START, (struct timespec *)&ASCII_SLEEP_STOP)

#include <ascii.h>

Sleep for frame rate limiting.

Sleeps for a short duration (500 nanoseconds) to limit frame rate and prevent excessive terminal I/O. Uses nanosleep() for precise timing control.

Note
Sleep duration: 500 nanoseconds (very short, minimal overhead)
Used between frame outputs for rate limiting.
May not be needed on all systems (depends on terminal I/O speed).

Definition at line 602 of file ascii.h.

◆ CACHE_FREQUENCY_DECAY_TIME

#define CACHE_FREQUENCY_DECAY_TIME   300.0

#include <common.h>

Frequency bonus decay time (5 minutes in seconds)

Definition at line 42 of file video/simd/common.h.

◆ CACHE_MAX_LIFETIME

#define CACHE_MAX_LIFETIME   3600

#include <common.h>

Maximum cache lifetime (1 hour in seconds)

Definition at line 48 of file video/simd/common.h.

◆ CACHE_MIN_ACCESS_THRESHOLD

#define CACHE_MIN_ACCESS_THRESHOLD   3

#include <common.h>

Minimum accesses for frequency protection.

Definition at line 50 of file video/simd/common.h.

◆ CACHE_RECENCY_SCALE

#define CACHE_RECENCY_SCALE   60.0

#include <common.h>

Recency importance scale (1 minute in seconds)

Definition at line 44 of file video/simd/common.h.

◆ CACHE_RECENT_ACCESS_THRESHOLD

#define CACHE_RECENT_ACCESS_THRESHOLD   10

#include <common.h>

Recent access protection threshold (10 seconds)

Definition at line 46 of file video/simd/common.h.

◆ console_clear

#define console_clear (   fd)    (terminal_clear_screen(), terminal_cursor_home(fd))

#include <ascii.h>

Clear console and move cursor to home position.

Parameters
fdFile descriptor for terminal

Clears terminal screen and moves cursor to home position (top-left). Equivalent to terminal_clear_screen() followed by terminal_cursor_home().

Definition at line 524 of file ascii.h.

◆ cursor_hide

#define cursor_hide (   fd)    terminal_hide_cursor(fd, true)

#include <ascii.h>

Hide terminal cursor.

Parameters
fdFile descriptor for terminal

Hides terminal cursor for clean ASCII art display. Equivalent to terminal_hide_cursor(fd, true).

Definition at line 557 of file ascii.h.

◆ cursor_reset

#define cursor_reset (   fd)    terminal_cursor_home(fd)

#include <ascii.h>

Reset cursor to home position.

Parameters
fdFile descriptor for terminal

Moves cursor to home position (top-left, row 1, column 1). Equivalent to terminal_cursor_home().

Definition at line 535 of file ascii.h.

◆ cursor_show

#define cursor_show (   fd)    terminal_hide_cursor(fd, false)

#include <ascii.h>

Show terminal cursor.

Parameters
fdFile descriptor for terminal

Shows terminal cursor. Equivalent to terminal_hide_cursor(fd, false).

Definition at line 568 of file ascii.h.

◆ IMAGE_MAX_HEIGHT

#define IMAGE_MAX_HEIGHT   2160

#include <image.h>

Maximum image height (4K resolution)

Maximum supported image height in pixels. Set to 2160 pixels (4K UHD height) to support high-resolution video capture.

Note
Larger images may exceed memory limits.
Video pipeline may enforce lower limits for performance.

Definition at line 177 of file video/image.h.

◆ IMAGE_MAX_PIXELS_SIZE

#define IMAGE_MAX_PIXELS_SIZE   (IMAGE_MAX_WIDTH * IMAGE_MAX_HEIGHT * sizeof(rgb_pixel_t))

#include <image.h>

Maximum pixel data size in bytes.

Maximum size in bytes for pixel data array. Calculated as: IMAGE_MAX_WIDTH * IMAGE_MAX_HEIGHT * sizeof(rgb_pixel_t) Used for buffer allocation and validation.

Note
This is approximately 24.88 MB for 4K RGB images.
Actual memory usage may be higher due to alignment.

Definition at line 191 of file video/image.h.

◆ IMAGE_MAX_WIDTH

#define IMAGE_MAX_WIDTH   3840

#include <image.h>

Maximum image width (4K resolution)

Maximum supported image width in pixels. Set to 3840 pixels (4K UHD width) to support high-resolution video capture.

Note
Larger images may exceed memory limits.
Video pipeline may enforce lower limits for performance.

Definition at line 164 of file video/image.h.

◆ LUMA_BLUE

#define LUMA_BLUE   29

#include <ascii_simd.h>

Luminance blue coefficient (0.114 * 256 = 29)

Definition at line 76 of file ascii_simd.h.

◆ LUMA_GREEN

#define LUMA_GREEN   150

#include <ascii_simd.h>

Luminance green coefficient (0.587 * 256 = 150)

Definition at line 74 of file ascii_simd.h.

◆ LUMA_RED

#define LUMA_RED   77

#include <ascii_simd.h>

Luminance red coefficient (0.299 * 256 = 77)

Definition at line 72 of file ascii_simd.h.

◆ LUMA_THRESHOLD

#define LUMA_THRESHOLD   128

#include <ascii_simd.h>

Luminance threshold for rounding.

Definition at line 78 of file ascii_simd.h.

◆ print

#define print (   s)    fwrite(s, 1, sizeof(s) / sizeof((s)[0]), stdout)

#include <ascii.h>

Print string to stdout.

Parameters
sString literal to print

Utility macro for printing string literals to stdout. Uses fwrite() for efficient output without null terminator requirement.

Note
This is a macro that expands to fwrite() call.
Only works with string literals (compile-time known size).
Use fputs() or printf() for runtime strings.

Definition at line 505 of file ascii.h.

◆ RAMP64_SIZE

#define RAMP64_SIZE   64

#include <common.h>

Ramp64 cache size (64 entries)

Definition at line 34 of file video/simd/common.h.

Typedef Documentation

◆ utf8_palette_cache_t

#include <common.h>

UTF-8 palette cache structure.

Thread-safe cache system for UTF-8 character lookup with eviction tracking.

Enumeration Type Documentation

◆ ansi_color_mode_t

#include <ansi_fast.h>

Color mode for ANSI generation.

Enumerator
ANSI_MODE_FOREGROUND 

Foreground color mode: \033[38;2;R;G;Bm

ANSI_MODE_BACKGROUND 

Background color mode: \033[48;2;R;G;Bm

ANSI_MODE_FOREGROUND_BACKGROUND 

Both foreground and background: \033[38;2;R;G;B;48;2;r;g;bm

Definition at line 40 of file ansi_fast.h.

40 {
ansi_color_mode_t
Color mode for ANSI generation.
Definition ansi_fast.h:40
@ ANSI_MODE_FOREGROUND_BACKGROUND
Definition ansi_fast.h:43
@ ANSI_MODE_FOREGROUND
Definition ansi_fast.h:41
@ ANSI_MODE_BACKGROUND
Definition ansi_fast.h:42

◆ image_alloc_method_t

#include <image.h>

Pixel allocation method tracking.

Tracks which allocation method was used for image pixels to ensure correct deallocation. Must match the allocation method used when creating the image.

Enumerator
IMAGE_ALLOC_SIMD 

Pixels allocated with SAFE_MALLOC_SIMD()

IMAGE_ALLOC_POOL 

Pixels allocated with buffer_pool_alloc()

Definition at line 116 of file video/image.h.

116 {
117 IMAGE_ALLOC_SIMD = 0,
image_alloc_method_t
Pixel allocation method tracking.
@ IMAGE_ALLOC_POOL
Pixels allocated with buffer_pool_alloc()
@ IMAGE_ALLOC_SIMD
Pixels allocated with SAFE_MALLOC_SIMD()

Function Documentation

◆ alloc_image()

ImageRGB alloc_image ( int  w,
int  h 
)

#include <ascii_simd.h>

Allocate a new ImageRGB (RGB8 format)

Parameters
wImage width
hImage height
Returns
Allocated ImageRGB structure
Note
Aborts on out-of-memory condition.

Definition at line 98 of file ascii_simd.c.

98 {
99 ImageRGB out;
100 out.w = w;
101 out.h = h;
102 size_t n = (size_t)w * (size_t)h * 3u;
103 out.pixels = SAFE_MALLOC(n, uint8_t *);
104 return out;
105}
#define SAFE_MALLOC(size, cast)
Definition common.h:208
uint8_t * pixels
Definition ascii_simd.h:228
ImageRGB structure for NEON renderers.
Definition ascii_simd.h:225

References ImageRGB::h, ImageRGB::pixels, SAFE_MALLOC, and ImageRGB::w.

◆ ansi_compress_rle()

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

#include <rle.h>

Compress repeated characters using RLE escape sequences.

Parameters
inputInput string (may contain ANSI escape codes)
input_lenLength of input string
Returns
Newly allocated string with RLE compression (caller must free)

Compresses runs of repeated printable characters using ANSI RLE sequences. Only compresses when profitable (run length > RLE overhead). Preserves all existing escape sequences.

Note
The returned string must be freed by the caller using free().
Returns NULL if input is NULL or allocation fails.
Only compresses runs where RLE saves bytes (typically 4+ chars).
Example
const char *frame = "AAAAAAA\x1b[31mBBBBB\n";
char *compressed = ansi_compress_rle(frame, strlen(frame));
// compressed = "A\x1b[6b\x1b[31mB\x1b[4b\n"
free(compressed);
char * ansi_compress_rle(const char *input, size_t input_len)
Compress repeated characters using RLE escape sequences.
Definition rle.c:98

Definition at line 98 of file rle.c.

98 {
99 if (!input || input_len == 0) {
100 return NULL;
101 }
102
103 outbuf_t ob = {0};
104 ob_reserve(&ob, input_len);
105
106 size_t i = 0;
107 while (i < input_len) {
108 // Check for ESC character (start of ANSI sequence)
109 if (input[i] == '\x1b' && i + 1 < input_len && input[i + 1] == '[') {
110 size_t seq_start = i;
111 i += 2; // Skip ESC[
112
113 // Skip parameter bytes
114 while (i < input_len && ((input[i] >= '0' && input[i] <= '9') || input[i] == ';')) {
115 i++;
116 }
117
118 // Skip final byte
119 if (i < input_len) {
120 i++;
121 }
122
123 // Copy entire escape sequence as-is
124 ob_write(&ob, input + seq_start, i - seq_start);
125 } else {
126 // Regular character - check for runs
127 char c = input[i];
128
129 // Only compress printable characters (not newlines, not control chars)
130 if (c >= 0x20 && c != 0x7F) {
131 // Count run length
132 size_t run_len = 1;
133 i++;
134
135 while (i < input_len && input[i] == c) {
136 run_len++;
137 i++;
138 }
139
140 // Emit first character
141 ob_putc(&ob, c);
142
143 // Use RLE if profitable (run > overhead of ESC[Nb)
144 if (run_len > 1 && rep_is_profitable((uint32_t)run_len)) {
145 emit_rep(&ob, (uint32_t)(run_len - 1));
146 } else {
147 // Emit remaining characters directly
148 for (size_t k = 1; k < run_len; k++) {
149 ob_putc(&ob, c);
150 }
151 }
152 } else {
153 // Non-compressible character (newline, etc.)
154 ob_putc(&ob, c);
155 i++;
156 }
157 }
158 }
159
160 ob_term(&ob);
161 return ob.buf;
162}
unsigned int uint32_t
Definition common.h:58
void ob_term(outbuf_t *ob)
Append null terminator to buffer.
void ob_putc(outbuf_t *ob, char c)
Append a character to buffer.
bool rep_is_profitable(uint32_t runlen)
Check if run-length encoding is profitable.
void ob_reserve(outbuf_t *ob, size_t need)
Reserve buffer space for upcoming writes.
void emit_rep(outbuf_t *ob, uint32_t extra)
Emit run-length encoded sequence.
void ob_write(outbuf_t *ob, const char *s, size_t n)
Append a string to buffer.
Dynamic output buffer (auto-expanding)
char * buf
Buffer pointer (allocated, owned by caller, must be freed)

References outbuf_t::buf, emit_rep(), ob_putc(), ob_reserve(), ob_term(), ob_write(), and rep_is_profitable().

◆ ansi_expand_rle()

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

#include <rle.h>

Expand RLE escape sequences in a string.

Parameters
inputInput string containing ANSI escape codes
input_lenLength of input string
Returns
Newly allocated string with RLE expanded (caller must free)

Expands ANSI RLE (REP) sequences while preserving all other escape codes:

  • RLE sequences (ESC[Nb) are expanded: previous character repeated N times
  • All other escape sequences (colors, cursor, etc.) are preserved as-is

This is useful when outputting to files/pipes where terminals can't interpret RLE, but you still want to preserve color codes.

Note
The returned string must be freed by the caller using free().
Returns NULL if input is NULL or allocation fails.
Example
const char *frame = "A\x1b[3b\x1b[31mB\n"; // A + repeat 3 + red + B
char *expanded = ansi_expand_rle(frame, strlen(frame));
// expanded = "AAAA\x1b[31mB\n" (RLE expanded, color preserved)
free(expanded);
char * ansi_expand_rle(const char *input, size_t input_len)
Expand RLE escape sequences in a string.
Definition rle.c:13

Definition at line 13 of file rle.c.

13 {
14 if (!input || input_len == 0) {
15 return NULL;
16 }
17
18 // RLE expansion can make output larger than input, start with 2x capacity
19 outbuf_t ob = {0};
20 ob_reserve(&ob, input_len * 2);
21
22 // Track the last character/grapheme for RLE expansion
23 // UTF-8 characters can be up to 4 bytes
24 char last_char[5] = " ";
25 size_t last_char_len = 1;
26
27 size_t i = 0;
28 while (i < input_len) {
29 // Check for ESC character (start of ANSI sequence)
30 if (input[i] == '\x1b' && i + 1 < input_len && input[i + 1] == '[') {
31 size_t seq_start = i;
32 i += 2; // Skip ESC[
33
34 // Parse parameter bytes (digits and semicolons)
35 uint32_t param = 0;
36 while (i < input_len && ((input[i] >= '0' && input[i] <= '9') || input[i] == ';')) {
37 if (input[i] >= '0' && input[i] <= '9') {
38 param = param * 10 + (uint32_t)(input[i] - '0');
39 } else if (input[i] == ';') {
40 param = 0; // Reset for next parameter
41 }
42 i++;
43 }
44
45 // Check for final byte
46 if (i < input_len) {
47 char final_byte = input[i];
48 i++;
49
50 // Handle RLE: ESC[Nb repeats previous character N times
51 if (final_byte == 'b' && param > 0) {
52 for (uint32_t r = 0; r < param; r++) {
53 ob_write(&ob, last_char, last_char_len);
54 }
55 } else {
56 // Not RLE - copy the entire escape sequence as-is
57 ob_write(&ob, input + seq_start, i - seq_start);
58 }
59 }
60 } else {
61 // Regular character - copy to output and track for RLE
62 unsigned char c = (unsigned char)input[i];
63 size_t char_len = 1;
64
65 // Determine UTF-8 character length from first byte
66 if ((c & 0x80) == 0) {
67 char_len = 1; // ASCII
68 } else if ((c & 0xE0) == 0xC0) {
69 char_len = 2; // 2-byte UTF-8
70 } else if ((c & 0xF0) == 0xE0) {
71 char_len = 3; // 3-byte UTF-8
72 } else if ((c & 0xF8) == 0xF0) {
73 char_len = 4; // 4-byte UTF-8
74 }
75
76 // Make sure we don't read past end of input
77 if (i + char_len > input_len) {
78 char_len = input_len - i;
79 }
80
81 // Copy full UTF-8 character to output
82 ob_write(&ob, input + i, char_len);
83
84 // Track last printable character for RLE (skip control chars)
85 if (c >= 0x20 && c != 0x7F) {
86 SAFE_MEMCPY(last_char, sizeof(last_char), input + i, char_len);
87 last_char[char_len] = '\0';
88 last_char_len = char_len;
89 }
90 i += char_len;
91 }
92 }
93
94 ob_term(&ob);
95 return ob.buf;
96}
#define SAFE_MEMCPY(dest, dest_size, src, count)
Definition common.h:388

References outbuf_t::buf, ob_reserve(), ob_term(), ob_write(), and SAFE_MEMCPY.

◆ ansi_fast_init()

void ansi_fast_init ( void  )

#include <ansi_fast.h>

Initialize the decimal lookup table.

Must be called once at startup before using any ANSI generation functions.

Definition at line 173 of file ansi_fast.c.

173 {
174 // Initialize the dec3 cache used by truecolor functions
176}
void ascii_simd_init(void)
Initialize SIMD subsystem.
Definition ascii_simd.c:90

References ascii_simd_init().

Referenced by mirror_main().

◆ ansi_fast_init_16color()

void ansi_fast_init_16color ( void  )

#include <ansi_fast.h>

Initialize 16-color mode lookup tables.

Must be called before using 16-color functions.

Definition at line 225 of file ansi_fast.c.

225 {
226 if (color16_initialized)
227 return;
228
229 // Standard ANSI color codes
230 const char *fg_codes[] = {"30", "31", "32", "33", "34", "35", "36", "37", // Normal colors (30-37)
231 "90", "91", "92", "93", "94", "95", "96", "97"}; // Bright colors (90-97)
232 const char *bg_codes[] = {"40", "41", "42", "43", "44", "45", "46", "47", // Normal colors (40-47)
233 "100", "101", "102", "103", "104", "105", "106", "107"}; // Bright colors (100-107)
234
235 for (int i = 0; i < 16; i++) {
236 SAFE_SNPRINTF(color16_fg_strings[i], sizeof(color16_fg_strings[i]), "\033[%sm", fg_codes[i]);
237 SAFE_SNPRINTF(color16_bg_strings[i], sizeof(color16_bg_strings[i]), "\033[%sm", bg_codes[i]);
238 }
239
240 color16_initialized = true;
241}
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:412

References SAFE_SNPRINTF.

Referenced by append_16color_bg(), append_16color_fg(), image_print_16color(), image_print_16color_dithered(), image_print_16color_dithered_with_background(), and mirror_main().

◆ ansi_fast_init_256color()

void ansi_fast_init_256color ( void  )

#include <ansi_fast.h>

Initialize 256-color mode lookup tables.

Must be called before using 256-color functions.

Definition at line 179 of file ansi_fast.c.

179 {
180 if (color256_initialized)
181 return;
182
183 for (int i = 0; i < 256; i++) {
184 SAFE_SNPRINTF(color256_strings[i], sizeof(color256_strings[i]), "\033[38;5;%dm", i);
185 }
186
187 color256_initialized = true;
188}

References SAFE_SNPRINTF.

Referenced by mirror_main().

◆ 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 <ansi_fast.h>

Add a pixel with run-length encoding.

Parameters
ctxRLE context structure
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
ascii_charASCII character to output

Only emits SGR when color changes from previous pixel.

Definition at line 125 of file ansi_fast.c.

125 {
126 // Check if we need to emit a new SGR sequence
127 bool color_changed = ctx->first_pixel || (r != ctx->last_r) || (g != ctx->last_g) || (b != ctx->last_b);
128
129 if (color_changed && (ctx->length + 40 < ctx->capacity)) { // Reserve 40 bytes for SGR (FG+BG max is 38)
130 char *pos = ctx->buffer + ctx->length;
131
132 switch (ctx->mode) {
134 pos = append_truecolor_fg(pos, r, g, b);
135 break;
137 pos = append_truecolor_bg(pos, r, g, b);
138 break;
140 // For FG+BG mode, use a default background (black) or implement dual-color logic
141 pos = append_truecolor_fg_bg(pos, r, g, b, 0, 0, 0);
142 break;
143 }
144
145 ctx->length = (size_t)(pos - ctx->buffer);
146 ctx->last_r = r;
147 ctx->last_g = g;
148 ctx->last_b = b;
149 ctx->first_pixel = false;
150 }
151
152 // Add the ASCII character
153 if (ctx->length < ctx->capacity - 1) {
154 ctx->buffer[ctx->length++] = ascii_char;
155 }
156}
char * append_truecolor_fg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Append truecolor foreground ANSI sequence.
Definition ansi_fast.c:24
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)
Append truecolor foreground and background ANSI sequence.
Definition ansi_fast.c:70
char * append_truecolor_bg(char *dst, uint8_t r, uint8_t g, uint8_t b)
Append truecolor background ANSI sequence.
Definition ansi_fast.c:49
ansi_color_mode_t mode
Definition ansi_fast.h:120

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, and ansi_rle_context_t::mode.

Referenced by image_print_color().

◆ ansi_rle_finish()

void ansi_rle_finish ( ansi_rle_context_t ctx)

#include <ansi_fast.h>

Finish RLE sequence.

Parameters
ctxRLE context structure

Adds ANSI reset sequence and null terminator.

Definition at line 159 of file ansi_fast.c.

159 {
160 // Add reset sequence
161 if (ctx->length + 5 < ctx->capacity) {
162 SAFE_MEMCPY(ctx->buffer + ctx->length, 4, "\033[0m", 4);
163 ctx->length += 4;
164 }
165
166 // Null terminate
167 if (ctx->length < ctx->capacity) {
168 ctx->buffer[ctx->length] = '\0';
169 }
170}

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

Referenced by image_print_color().

◆ ansi_rle_init()

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

#include <ansi_fast.h>

Initialize run-length encoding context.

Parameters
ctxContext structure to initialize
bufferOutput buffer
capacityBuffer capacity in bytes
modeColor mode to use

Definition at line 112 of file ansi_fast.c.

112 {
113 ctx->buffer = buffer;
114 ctx->capacity = capacity;
115 ctx->length = 0;
116 ctx->mode = mode;
117 ctx->first_pixel = true;
118 // Initialize with impossible color values to force first SGR
119 ctx->last_r = 0xFF;
120 ctx->last_g = 0xFF;
121 ctx->last_b = 0xFF;
122}

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.

Referenced by image_print_color().

◆ append_16color_bg()

char * append_16color_bg ( char *  dst,
uint8_t  color_index 
)

#include <ansi_fast.h>

Append 16-color background ANSI sequence.

Parameters
dstDestination buffer pointer
color_index16-color ANSI index (0-15)
Returns
Pointer to end of appended sequence

Definition at line 260 of file ansi_fast.c.

260 {
261 if (!color16_initialized) {
263 }
264
265 if (color_index >= 16) {
266 color_index = 0; // Default to black background
267 }
268
269 const char *color_str = color16_bg_strings[color_index];
270 while (*color_str) {
271 *dst++ = *color_str++;
272 }
273
274 return dst;
275}
void ansi_fast_init_16color(void)
Initialize 16-color mode lookup tables.
Definition ansi_fast.c:225

References ansi_fast_init_16color().

Referenced by image_print_16color_dithered_with_background().

◆ append_16color_fg()

char * append_16color_fg ( char *  dst,
uint8_t  color_index 
)

#include <ansi_fast.h>

Append 16-color foreground ANSI sequence.

Parameters
dstDestination buffer pointer
color_index16-color ANSI index (0-15)
Returns
Pointer to end of appended sequence

Definition at line 243 of file ansi_fast.c.

243 {
244 if (!color16_initialized) {
246 }
247
248 if (color_index >= 16) {
249 color_index = 7; // Default to white
250 }
251
252 const char *color_str = color16_fg_strings[color_index];
253 while (*color_str) {
254 *dst++ = *color_str++;
255 }
256
257 return dst;
258}

References ansi_fast_init_16color().

Referenced by append_color_fg_for_mode(), image_print_16color(), image_print_16color_dithered(), and image_print_16color_dithered_with_background().

◆ append_256color_fg()

char * append_256color_fg ( char *  dst,
uint8_t  color_index 
)

#include <ansi_fast.h>

Append 256-color foreground ANSI sequence.

Parameters
dstDestination buffer pointer
color_index256-color palette index (0-255)
Returns
Pointer to end of appended sequence

Definition at line 191 of file ansi_fast.c.

191 {
192 const char *color_str = color256_strings[color_index];
193 size_t len = strlen(color_str);
194 SAFE_MEMCPY(dst, len, color_str, len);
195 return dst + len;
196}

References SAFE_MEMCPY.

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 <ansi_fast.h>

Append color foreground sequence for specified mode.

Parameters
dstDestination buffer pointer
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
modeColor mode to use
Returns
Pointer to end of appended sequence

Automatically selects appropriate ANSI sequence based on color mode.

Definition at line 426 of file ansi_fast.c.

426 {
427 switch (mode) {
429 return append_truecolor_fg(dst, r, g, b);
430
432 uint8_t color_index = rgb_to_256color(r, g, b);
433 return append_256color_fg(dst, color_index);
434 }
435
436 case COLOR_MODE_16_COLOR: {
437 uint8_t color_index = rgb_to_16color(r, g, b);
438 return append_16color_fg(dst, color_index);
439 }
440
441 case COLOR_MODE_NONE:
442 case COLOR_MODE_AUTO:
443 default:
444 // No color output for monochrome mode or auto mode (fallback)
445 return dst;
446 }
447}
#define COLOR_MODE_16_COLOR
16-color mode (full name)
Definition options.h:159
#define COLOR_MODE_256_COLOR
256-color mode (full name)
Definition options.h:161
#define COLOR_MODE_TRUECOLOR
24-bit truecolor mode
Definition options.h:162
#define COLOR_MODE_AUTO
Backward compatibility aliases for color mode enum values.
Definition options.h:156
#define COLOR_MODE_NONE
Monochrome mode.
Definition options.h:157
char * append_16color_fg(char *dst, uint8_t color_index)
Append 16-color foreground ANSI sequence.
Definition ansi_fast.c:243
uint8_t rgb_to_16color(uint8_t r, uint8_t g, uint8_t b)
Convert RGB to 16-color ANSI index.
Definition ansi_fast.c:277
char * append_256color_fg(char *dst, uint8_t color_index)
Append 256-color foreground ANSI sequence.
Definition ansi_fast.c:191
uint8_t rgb_to_256color(uint8_t r, uint8_t g, uint8_t b)
Convert RGB to 256-color palette index.
Definition ansi_fast.c:199

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_sgr_reset()

char * append_sgr_reset ( char *  dst)
inline

#include <common.h>

Append ANSI reset sequence.

Parameters
dstDestination buffer pointer
Returns
Pointer to end of appended sequence

Definition at line 107 of file ascii_simd_color.c.

107 {
108 // "\x1b[0m"
109 static const char RESET[] = "\033[0m";
110 memcpy(dst, RESET, sizeof(RESET) - 1);
111 return dst + (sizeof(RESET) - 1);
112}

◆ append_sgr_truecolor_bg()

char * append_sgr_truecolor_bg ( char *  dst,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)
inline

#include <common.h>

Append truecolor background SGR sequence.

Parameters
dstDestination buffer pointer
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
Returns
Pointer to end of appended sequence

Definition at line 176 of file ascii_simd_color.c.

176 {
177 // Constructor ensures initialization
178
179 // Direct character writes for "\033[48;2;"
180 *dst++ = '\033';
181 *dst++ = '[';
182 *dst++ = '4';
183 *dst++ = '8';
184 *dst++ = ';';
185 *dst++ = '2';
186 *dst++ = ';';
187
188 // Optimized digit copying
189 const dec3_t *rd = &g_dec3_cache.dec3_table[r];
190 if (rd->len == 1) {
191 *dst++ = rd->s[0];
192 } else if (rd->len == 2) {
193 dst[0] = rd->s[0];
194 dst[1] = rd->s[1];
195 dst += 2;
196 } else {
197 dst[0] = rd->s[0];
198 dst[1] = rd->s[1];
199 dst[2] = rd->s[2];
200 dst += 3;
201 }
202 *dst++ = ';';
203
204 const dec3_t *gd = &g_dec3_cache.dec3_table[g];
205 if (gd->len == 1) {
206 *dst++ = gd->s[0];
207 } else if (gd->len == 2) {
208 dst[0] = gd->s[0];
209 dst[1] = gd->s[1];
210 dst += 2;
211 } else {
212 dst[0] = gd->s[0];
213 dst[1] = gd->s[1];
214 dst[2] = gd->s[2];
215 dst += 3;
216 }
217 *dst++ = ';';
218
219 const dec3_t *bd = &g_dec3_cache.dec3_table[b];
220 if (bd->len == 1) {
221 *dst++ = bd->s[0];
222 } else if (bd->len == 2) {
223 dst[0] = bd->s[0];
224 dst[1] = bd->s[1];
225 dst += 2;
226 } else {
227 dst[0] = bd->s[0];
228 dst[1] = bd->s[1];
229 dst[2] = bd->s[2];
230 dst += 3;
231 }
232 *dst++ = 'm';
233 return dst;
234}
global_dec3_cache_t g_dec3_cache
Global decimal cache instance.
Definition ascii_simd.c:23
dec3_t dec3_table[256]
Definition ascii_simd.h:98
uint8_t len
Definition ascii_simd.h:88
char s[3]
Definition ascii_simd.h:89
Decimal conversion cache structure (1-3 digits)
Definition ascii_simd.h:87

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, and dec3_t::s.

◆ append_sgr_truecolor_fg()

char * append_sgr_truecolor_fg ( char *  dst,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)
inline

#include <common.h>

Append truecolor foreground SGR sequence.

Parameters
dstDestination buffer pointer
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
Returns
Pointer to end of appended sequence

Definition at line 115 of file ascii_simd_color.c.

115 {
116 // Constructor ensures initialization
117
118 // Direct character writes (compiler will optimize to word operations)
119 *dst++ = '\033';
120 *dst++ = '[';
121 *dst++ = '3';
122 *dst++ = '8';
123 *dst++ = ';';
124 *dst++ = '2';
125 *dst++ = ';';
126
127 // Fast digit copying for 1-3 digit numbers (avoid memcpy overhead)
128 const dec3_t *rd = &g_dec3_cache.dec3_table[r];
129 if (rd->len == 1) {
130 *dst++ = rd->s[0];
131 } else if (rd->len == 2) {
132 dst[0] = rd->s[0];
133 dst[1] = rd->s[1];
134 dst += 2;
135 } else {
136 dst[0] = rd->s[0];
137 dst[1] = rd->s[1];
138 dst[2] = rd->s[2];
139 dst += 3;
140 }
141 *dst++ = ';';
142
143 const dec3_t *gd = &g_dec3_cache.dec3_table[g];
144 if (gd->len == 1) {
145 *dst++ = gd->s[0];
146 } else if (gd->len == 2) {
147 dst[0] = gd->s[0];
148 dst[1] = gd->s[1];
149 dst += 2;
150 } else {
151 dst[0] = gd->s[0];
152 dst[1] = gd->s[1];
153 dst[2] = gd->s[2];
154 dst += 3;
155 }
156 *dst++ = ';';
157
158 const dec3_t *bd = &g_dec3_cache.dec3_table[b];
159 if (bd->len == 1) {
160 *dst++ = bd->s[0];
161 } else if (bd->len == 2) {
162 dst[0] = bd->s[0];
163 dst[1] = bd->s[1];
164 dst += 2;
165 } else {
166 dst[0] = bd->s[0];
167 dst[1] = bd->s[1];
168 dst[2] = bd->s[2];
169 dst += 3;
170 }
171 *dst++ = 'm';
172 return dst;
173}

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, and dec3_t::s.

◆ append_sgr_truecolor_fg_bg()

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 
)
inline

#include <common.h>

Append truecolor foreground and background SGR sequence.

Parameters
dstDestination buffer pointer
frForeground red component (0-255)
fgForeground green component (0-255)
fbForeground blue component (0-255)
brBackground red component (0-255)
bgBackground green component (0-255)
bbBackground blue component (0-255)
Returns
Pointer to end of appended sequence

Definition at line 237 of file ascii_simd_color.c.

238 {
239 // Constructor ensures initialization
240
241 // Write "\033[38;2;" directly (7 chars)
242 *dst++ = '\033';
243 *dst++ = '[';
244 *dst++ = '3';
245 *dst++ = '8';
246 *dst++ = ';';
247 *dst++ = '2';
248 *dst++ = ';';
249
250 // Foreground RGB digits
251 const dec3_t *d = &g_dec3_cache.dec3_table[fr];
252 if (d->len == 1) {
253 *dst++ = d->s[0];
254 } else if (d->len == 2) {
255 dst[0] = d->s[0];
256 dst[1] = d->s[1];
257 dst += 2;
258 } else {
259 dst[0] = d->s[0];
260 dst[1] = d->s[1];
261 dst[2] = d->s[2];
262 dst += 3;
263 }
264 *dst++ = ';';
265
266 d = &g_dec3_cache.dec3_table[fg];
267 if (d->len == 1) {
268 *dst++ = d->s[0];
269 } else if (d->len == 2) {
270 dst[0] = d->s[0];
271 dst[1] = d->s[1];
272 dst += 2;
273 } else {
274 dst[0] = d->s[0];
275 dst[1] = d->s[1];
276 dst[2] = d->s[2];
277 dst += 3;
278 }
279 *dst++ = ';';
280
281 d = &g_dec3_cache.dec3_table[fb];
282 if (d->len == 1) {
283 *dst++ = d->s[0];
284 } else if (d->len == 2) {
285 dst[0] = d->s[0];
286 dst[1] = d->s[1];
287 dst += 2;
288 } else {
289 dst[0] = d->s[0];
290 dst[1] = d->s[1];
291 dst[2] = d->s[2];
292 dst += 3;
293 }
294
295 // Write ";48;2;" directly (6 chars)
296 *dst++ = ';';
297 *dst++ = '4';
298 *dst++ = '8';
299 *dst++ = ';';
300 *dst++ = '2';
301 *dst++ = ';';
302
303 // Background RGB digits
304 d = &g_dec3_cache.dec3_table[br];
305 if (d->len == 1) {
306 *dst++ = d->s[0];
307 } else if (d->len == 2) {
308 dst[0] = d->s[0];
309 dst[1] = d->s[1];
310 dst += 2;
311 } else {
312 dst[0] = d->s[0];
313 dst[1] = d->s[1];
314 dst[2] = d->s[2];
315 dst += 3;
316 }
317 *dst++ = ';';
318
319 d = &g_dec3_cache.dec3_table[bg];
320 if (d->len == 1) {
321 *dst++ = d->s[0];
322 } else if (d->len == 2) {
323 dst[0] = d->s[0];
324 dst[1] = d->s[1];
325 dst += 2;
326 } else {
327 dst[0] = d->s[0];
328 dst[1] = d->s[1];
329 dst[2] = d->s[2];
330 dst += 3;
331 }
332 *dst++ = ';';
333
334 d = &g_dec3_cache.dec3_table[bb];
335 if (d->len == 1) {
336 *dst++ = d->s[0];
337 } else if (d->len == 2) {
338 dst[0] = d->s[0];
339 dst[1] = d->s[1];
340 dst += 2;
341 } else {
342 dst[0] = d->s[0];
343 dst[1] = d->s[1];
344 dst[2] = d->s[2];
345 dst += 3;
346 }
347
348 *dst++ = 'm';
349 return dst;
350}

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, and dec3_t::s.

◆ append_truecolor_bg()

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

#include <ansi_fast.h>

Append truecolor background ANSI sequence.

Parameters
dstDestination buffer pointer
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
Returns
Pointer to end of appended sequence

Definition at line 49 of file ansi_fast.c.

49 {
50 SAFE_MEMCPY(dst, 19, "\033[48;2;", 7);
51 dst += 7;
52
54 dst += g_dec3_cache.dec3_table[r].len;
55 *dst++ = ';';
56
58 dst += g_dec3_cache.dec3_table[g].len;
59 *dst++ = ';';
60
62 dst += g_dec3_cache.dec3_table[b].len;
63 *dst++ = 'm';
64
65 return dst;
66}

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, dec3_t::s, and SAFE_MEMCPY.

Referenced by ansi_rle_add_pixel().

◆ append_truecolor_fg()

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

#include <ansi_fast.h>

Append truecolor foreground ANSI sequence.

Parameters
dstDestination buffer pointer
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
Returns
Pointer to end of appended sequence

Definition at line 24 of file ansi_fast.c.

24 {
25 // Static prefix - 7 bytes
26 SAFE_MEMCPY(dst, 19, "\033[38;2;", 7);
27 dst += 7;
28
29 // Red component + semicolon
31 dst += g_dec3_cache.dec3_table[r].len;
32 *dst++ = ';';
33
34 // Green component + semicolon
36 dst += g_dec3_cache.dec3_table[g].len;
37 *dst++ = ';';
38
39 // Blue component + suffix
41 dst += g_dec3_cache.dec3_table[b].len;
42 *dst++ = 'm';
43
44 return dst;
45}

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, dec3_t::s, and SAFE_MEMCPY.

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 <ansi_fast.h>

Append truecolor foreground and background ANSI sequence.

Parameters
dstDestination buffer pointer
fg_rForeground red component (0-255)
fg_gForeground green component (0-255)
fg_bForeground blue component (0-255)
bg_rBackground red component (0-255)
bg_gBackground green component (0-255)
bg_bBackground blue component (0-255)
Returns
Pointer to end of appended sequence

Definition at line 70 of file ansi_fast.c.

71 {
72 SAFE_MEMCPY(dst, 38, "\033[38;2;", 7);
73 dst += 7;
74
75 // Foreground RGB (remaining: 31 bytes max)
77 dst += g_dec3_cache.dec3_table[fg_r].len;
78 *dst++ = ';';
79
80 // Remaining: 27 bytes max
82 dst += g_dec3_cache.dec3_table[fg_g].len;
83 *dst++ = ';';
84
85 // Remaining: 23 bytes max
87 dst += g_dec3_cache.dec3_table[fg_b].len;
88
89 // Background RGB (remaining: 20 bytes max)
90 SAFE_MEMCPY(dst, 20, ";48;2;", 6);
91 dst += 6;
92
93 // Remaining: 14 bytes max
95 dst += g_dec3_cache.dec3_table[bg_r].len;
96 *dst++ = ';';
97
98 // Remaining: 10 bytes max
100 dst += g_dec3_cache.dec3_table[bg_g].len;
101 *dst++ = ';';
102
103 // Remaining: 6 bytes max
105 dst += g_dec3_cache.dec3_table[bg_b].len;
106 *dst++ = 'm';
107
108 return dst;
109}

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, dec3_t::s, and SAFE_MEMCPY.

Referenced by ansi_rle_add_pixel().

◆ ascii_convert()

char * ascii_convert ( image_t original,
const ssize_t  width,
const ssize_t  height,
const bool  color,
const bool  aspect_ratio,
const bool  stretch,
const char *  palette_chars,
const char  luminance_palette[256] 
)

#include <ascii.h>

Convert image to ASCII art.

Parameters
originalSource image (must not be NULL)
widthTarget width in characters (must be > 0)
heightTarget height in characters (must be > 0)
colorEnable color output (true for color, false for monochrome)
aspect_ratioPreserve aspect ratio (true to maintain proportions, false to fill dimensions)
stretchStretch to fit (if aspect_ratio is false, true to fill exactly, false to fit within)
palette_charsCharacter palette to use (or NULL for default ASCII palette)
luminance_paletteLuminance-to-character mapping palette (must not be NULL, 256 elements)
Returns
Allocated ASCII frame string (caller must free), or NULL on error

Converts an image to ASCII art with specified dimensions and color mode. Uses character palette to map pixel luminance to ASCII characters and optionally applies color output using ANSI escape sequences.

CONVERSION PROCESS:

  • Resizes image to target dimensions (with aspect ratio preservation if enabled)
  • Maps each pixel's luminance to ASCII character using palette
  • Optionally applies color output using ANSI escape sequences
  • Returns null-terminated string containing ASCII frame
Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Aspect ratio preservation uses terminal character correction.
Color output requires terminal color support for proper display.

Definition at line 67 of file ascii.c.

69 {
70 if (original == NULL || !palette_chars || !luminance_palette) {
71 log_error("ascii_convert: invalid parameters");
72 return NULL;
73 }
74
75 // Check for empty strings
76 if (palette_chars[0] == '\0' || luminance_palette[0] == '\0') {
77 log_error("ascii_convert: empty palette strings");
78 return NULL;
79 }
80
81 // Start with the target dimensions requested by the user (or detected from
82 // the terminal). These can be modified by aspect_ratio() if stretching is
83 // disabled and one of the dimensions was left to be calculated
84 // automatically.
85 ssize_t resized_width = width;
86 ssize_t resized_height = height;
87
88 // If stretch is enabled, use full dimensions, otherwise calculate aspect ratio
89 if (_aspect_ratio) {
90 // The server now provides images at width*2 x height pixels
91 // The aspect_ratio function will handle terminal character aspect ratio
92 aspect_ratio(original->w, original->h, resized_width, resized_height, stretch, &resized_width, &resized_height);
93 }
94
95 // Calculate padding for centering
96 size_t pad_width = 0;
97 size_t pad_height = 0;
98
99 if (_aspect_ratio) {
100 // Only calculate padding when not stretching
101 ssize_t pad_width_ss = width > resized_width ? (width - resized_width) / 2 : 0;
102 pad_width = (size_t)pad_width_ss;
103
104 ssize_t pad_height_ss = height > resized_height ? (height - resized_height) / 2 : 0;
105 pad_height = (size_t)pad_height_ss;
106 }
107
108 // Resize the captured frame to the aspect-correct dimensions.
109 if (resized_width <= 0 || resized_height <= 0) {
110 log_error("Invalid dimensions for resize: width=%zd, height=%zd", resized_width, resized_height);
111 return NULL;
112 }
113
114 // Validate dimensions fit in image_t's int fields before casting
115 if (resized_width > INT_MAX || resized_height > INT_MAX) {
116 log_error("Dimensions exceed INT_MAX: width=%zd, height=%zd", resized_width, resized_height);
117 return NULL;
118 }
119
120 // Always resize to target dimensions
121 image_t *resized = image_new((size_t)resized_width, (size_t)resized_height);
122 if (!resized) {
123 log_error("Failed to allocate resized image");
124 return NULL;
125 }
126
127 image_clear(resized);
128 image_resize(original, resized);
129
130 char *ascii;
131 if (color) {
132 // Check for half-block mode first (requires NEON)
133 if (GET_OPTION(render_mode) == RENDER_MODE_HALF_BLOCK) {
134#if SIMD_SUPPORT_NEON
135 // Use NEON half-block renderer
136 const uint8_t *rgb_data = (const uint8_t *)resized->pixels;
137 ascii = rgb_to_truecolor_halfblocks_neon(rgb_data, resized->w, resized->h, 0);
138#else
139 log_error("Half-block mode requires NEON support (ARM architecture)");
140 image_destroy(resized);
141 return NULL;
142#endif
143 } else {
144#ifdef SIMD_SUPPORT
145 // Standard color modes (foreground/background)
146 bool use_background = (GET_OPTION(render_mode) == RENDER_MODE_BACKGROUND);
147 ascii = image_print_color_simd(resized, use_background, false, palette_chars);
148#else
149 ascii = image_print_color(resized, palette_chars);
150#endif
151 }
152 } else {
153 // Use grayscale/monochrome conversion with client's palette
154#ifdef SIMD_SUPPORT
155 ascii = image_print_simd(resized, luminance_palette);
156#else
157 ascii = image_print(resized, palette_chars);
158#endif
159 }
160
161 if (!ascii) {
162 log_error("Failed to convert image to ASCII");
163 image_destroy(resized);
164 return NULL;
165 }
166
167 size_t ascii_len = strlen(ascii);
168 if (ascii_len == 0) {
169 log_error("ASCII conversion returned empty string (resized dimensions: %dx%d)", resized->w, resized->h);
170 SAFE_FREE(ascii);
171 image_destroy(resized);
172 return NULL;
173 }
174
175 char *ascii_width_padded = ascii_pad_frame_width(ascii, pad_width);
176 SAFE_FREE(ascii);
177
178 char *ascii_padded = ascii_pad_frame_height(ascii_width_padded, pad_height);
179 SAFE_FREE(ascii_width_padded);
180
181 // Only destroy resized if we allocated it (not when using original directly)
182 image_destroy(resized);
183
184 return ascii_padded;
185}
#define SAFE_FREE(ptr)
Definition common.h:320
#define log_error(...)
Log an ERROR message.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644
@ RENDER_MODE_BACKGROUND
Background colors (block colors)
Definition terminal.h:471
@ RENDER_MODE_HALF_BLOCK
Unicode half-block characters (mixed foreground/background)
Definition terminal.h:473
void aspect_ratio(const ssize_t img_w, const ssize_t img_h, const ssize_t width, const ssize_t height, const bool stretch, ssize_t *out_width, ssize_t *out_height)
Calculate aspect ratio with terminal character correction.
char * image_print(const image_t *p, const char *palette)
Print image as ASCII art (monochrome)
char * image_print_color(const image_t *p, const char *palette)
Print image as ASCII art with color.
char * ascii_pad_frame_width(const char *frame, size_t pad_left)
Add leading spaces (left-padding) to each line of a frame.
Definition ascii.c:375
char * ascii_pad_frame_height(const char *frame, size_t pad_top)
Add blank lines (vertical padding) to center a frame vertically.
Definition ascii.c:738
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)
Definition ascii_simd.c:252
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()
Definition video/image.c:85
image_t * image_new(size_t width, size_t height)
Create a new image with standard allocation.
Definition video/image.c:36
Image structure.
int w
Image width in pixels (must be > 0)
int h
Image height in pixels (must be > 0)
rgb_pixel_t * pixels
Pixel data array (width * height RGB pixels, row-major order)

References ascii_pad_frame_height(), ascii_pad_frame_width(), aspect_ratio(), GET_OPTION, image_t::h, image_clear(), image_destroy(), image_new(), image_print(), image_print_color(), image_print_color_simd(), image_print_simd(), image_resize(), log_error, image_t::pixels, RENDER_MODE_BACKGROUND, RENDER_MODE_HALF_BLOCK, SAFE_FREE, and image_t::w.

Referenced by benchmark_simd_color_conversion_with_source().

◆ ascii_convert_with_capabilities()

char * ascii_convert_with_capabilities ( image_t original,
const ssize_t  width,
const ssize_t  height,
const terminal_capabilities_t caps,
const bool  use_aspect_ratio,
const bool  stretch,
const char *  palette_chars,
const char  luminance_palette[256] 
)

#include <ascii.h>

Convert image to ASCII art with terminal capability awareness.

Parameters
originalSource image (must not be NULL)
widthTarget width in characters (must be > 0)
heightTarget height in characters (must be > 0)
capsTerminal capabilities structure (must not be NULL)
use_aspect_ratioPreserve aspect ratio (true to maintain proportions, false to fill dimensions)
stretchStretch to fit (if use_aspect_ratio is false, true to fill exactly, false to fit within)
palette_charsCharacter palette to use (or NULL for default ASCII palette)
luminance_paletteLuminance-to-character mapping palette (must not be NULL, 256 elements)
Returns
Allocated ASCII frame string (caller must free), or NULL on error

Converts an image to ASCII art with automatic color mode selection based on terminal capabilities. Automatically chooses the best color mode (16-color, 256-color, or truecolor) for optimal display.

CAPABILITY-AWARE CONVERSION:

  • Detects terminal color capabilities from structure
  • Selects optimal color mode (truecolor > 256-color > 16-color > monochrome)
  • Applies terminal character aspect ratio correction
  • Uses capability-specific conversion algorithms
Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Color mode is selected automatically based on terminal capabilities.
This is the recommended function for capability-aware ASCII conversion.

Definition at line 188 of file ascii.c.

191 {
192
193 if (original == NULL || caps == NULL) {
194 log_error("Invalid parameters for ascii_convert_with_capabilities");
195 return NULL;
196 }
197
198 // Start with the target dimensions requested by the user
199 ssize_t resized_width = width;
200 ssize_t resized_height = height;
201
202 // Height doubling for half-block mode is now handled by the server
203
204 // If stretch is enabled, use full dimensions, otherwise calculate aspect ratio
205 if (use_aspect_ratio && caps->render_mode != RENDER_MODE_HALF_BLOCK) {
206 // Normal modes: apply aspect ratio correction
207 aspect_ratio(original->w, original->h, resized_width, resized_height, stretch, &resized_width, &resized_height);
208 }
209 // Half-block mode: skip aspect ratio to preserve full doubled dimensions for 2x resolution
210
211 // Calculate padding for centering
212 size_t pad_width = 0;
213 size_t pad_height = 0;
214
215 if (use_aspect_ratio) {
216 ssize_t pad_width_ss = width > resized_width ? (width - resized_width) / 2 : 0;
217 pad_width = (size_t)pad_width_ss;
218
219 ssize_t pad_height_ss = height > resized_height ? (height - resized_height) / 2 : 0;
220 pad_height = (size_t)pad_height_ss;
221 }
222
223 // Resize the captured frame to the aspect-correct dimensions
224 if (resized_width <= 0 || resized_height <= 0) {
225 log_error("Invalid dimensions for resize: width=%zd, height=%zd", resized_width, resized_height);
226 return NULL;
227 }
228
229 // Validate dimensions fit in image_t's int fields before casting
230 if (resized_width > INT_MAX || resized_height > INT_MAX) {
231 log_error("Dimensions exceed INT_MAX: width=%zd, height=%zd", resized_width, resized_height);
232 return NULL;
233 }
234
235 // PROFILING: Time image allocation and resize
236 struct timespec prof_alloc_start, prof_alloc_end, prof_resize_start, prof_resize_end;
237 (void)clock_gettime(CLOCK_MONOTONIC, &prof_alloc_start);
238
239 image_t *resized = image_new((size_t)resized_width, (size_t)resized_height);
240 if (!resized) {
241 log_error("Failed to allocate resized image");
242 return NULL;
243 }
244
245 image_clear(resized);
246
247 (void)clock_gettime(CLOCK_MONOTONIC, &prof_alloc_end);
248 (void)clock_gettime(CLOCK_MONOTONIC, &prof_resize_start);
249
250 image_resize(original, resized);
251
252 (void)clock_gettime(CLOCK_MONOTONIC, &prof_resize_end);
253
254 // PROFILING: Time ASCII print
255 struct timespec prof_print_start, prof_print_end;
256 (void)clock_gettime(CLOCK_MONOTONIC, &prof_print_start);
257
258 // Use the capability-aware image printing function with client's palette
259 char *ascii = image_print_with_capabilities(resized, caps, palette_chars, luminance_palette);
260
261 (void)clock_gettime(CLOCK_MONOTONIC, &prof_print_end);
262
263 uint64_t alloc_time_us = ((uint64_t)prof_alloc_end.tv_sec * 1000000 + (uint64_t)prof_alloc_end.tv_nsec / 1000) -
264 ((uint64_t)prof_alloc_start.tv_sec * 1000000 + (uint64_t)prof_alloc_start.tv_nsec / 1000);
265 uint64_t resize_time_us = ((uint64_t)prof_resize_end.tv_sec * 1000000 + (uint64_t)prof_resize_end.tv_nsec / 1000) -
266 ((uint64_t)prof_resize_start.tv_sec * 1000000 + (uint64_t)prof_resize_start.tv_nsec / 1000);
267 uint64_t print_time_us = ((uint64_t)prof_print_end.tv_sec * 1000000 + (uint64_t)prof_print_end.tv_nsec / 1000) -
268 ((uint64_t)prof_print_start.tv_sec * 1000000 + (uint64_t)prof_print_start.tv_nsec / 1000);
269
270 // PROFILING: Time padding
271 struct timespec prof_pad_start, prof_pad_end;
272 (void)clock_gettime(CLOCK_MONOTONIC, &prof_pad_start);
273
274 if (!ascii) {
275 log_error("Failed to convert image to ASCII using terminal capabilities");
276 image_destroy(resized);
277 return NULL;
278 }
279
280 size_t ascii_len = strlen(ascii);
281 if (ascii_len == 0) {
282 log_error("Capability-aware ASCII conversion returned empty string (resized dimensions: %dx%d)", resized->w,
283 resized->h);
284 SAFE_FREE(ascii);
285 image_destroy(resized);
286 return NULL;
287 }
288
289 char *ascii_width_padded = ascii_pad_frame_width(ascii, pad_width);
290 SAFE_FREE(ascii);
291
292 char *ascii_padded = ascii_pad_frame_height(ascii_width_padded, pad_height);
293 SAFE_FREE(ascii_width_padded);
294
295 (void)clock_gettime(CLOCK_MONOTONIC, &prof_pad_end);
296
297 uint64_t pad_time_us = ((uint64_t)prof_pad_end.tv_sec * 1000000 + (uint64_t)prof_pad_end.tv_nsec / 1000) -
298 ((uint64_t)prof_pad_start.tv_sec * 1000000 + (uint64_t)prof_pad_start.tv_nsec / 1000);
299 (void)alloc_time_us;
300 (void)resize_time_us;
301 (void)print_time_us;
302 (void)pad_time_us;
303
304 image_destroy(resized);
305
306 return ascii_padded;
307}
unsigned long long uint64_t
Definition common.h:59
render_mode_t render_mode
Preferred rendering mode (render_mode_t)
Definition terminal.h:497
char * image_print_with_capabilities(const image_t *image, const terminal_capabilities_t *caps, const char *palette, const char luminance_palette[256] __attribute__((unused)))

References ascii_pad_frame_height(), ascii_pad_frame_width(), aspect_ratio(), image_t::h, image_clear(), image_destroy(), image_new(), image_print_with_capabilities(), image_resize(), log_error, terminal_capabilities_t::render_mode, RENDER_MODE_HALF_BLOCK, SAFE_FREE, and image_t::w.

Referenced by mirror_main().

◆ ascii_create_grid()

char * ascii_create_grid ( ascii_frame_source_t sources,
int  source_count,
int  width,
int  height,
size_t *  out_size 
)

#include <ascii.h>

Create a grid layout from multiple ASCII frames.

Parameters
sourcesArray of frame sources (must not be NULL, must have source_count elements)
source_countNumber of frame sources (must be > 0)
widthGrid width in characters per frame (must be > 0)
heightGrid height in characters per frame (must be > 0)
out_sizePointer to store output size in bytes (can be NULL)
Returns
Allocated grid frame string (caller must free), or NULL on error

Combines multiple ASCII frames into a single grid layout. Arranges frames in a grid pattern suitable for multi-user display. Each frame is positioned in the grid according to its index in the sources array.

GRID LAYOUT:

  • Frames are arranged in grid pattern (rows and columns)
  • Each frame has specified width and height in characters
  • Grid dimensions are calculated based on source_count
  • Output frame contains all frames arranged in grid
Note
Returns NULL on error (invalid sources, memory allocation failure).
Grid frame string must be freed by caller using free().
Frame sources must have valid frame_data and frame_size.
Grid layout is useful for multi-user video display.
Example
// Initialize sources...
char *grid = ascii_create_grid(sources, 4, 80, 40, NULL);
if (grid) {
ascii_write(grid);
free(grid);
}
asciichat_error_t ascii_write(const char *frame)
Write ASCII frame to terminal.
Definition ascii.c:312
char * ascii_create_grid(ascii_frame_source_t *sources, int source_count, int width, int height, size_t *out_size)
Create a grid layout from multiple ASCII frames.
Definition ascii.c:450
Frame source structure for grid layout.
Definition ascii.h:342

Creates a grid layout from multiple ASCII frame sources with | and _ separators.

Parameters: sources Array of ASCII frame sources to combine source_count Number of sources in the array width Target width of the output grid height Target height of the output grid out_size Output parameter for the size of the returned buffer

Returns: A newly allocated, null-terminated string containing the grid layout, or NULL on error. Caller must free the returned buffer.

Definition at line 450 of file ascii.c.

450 {
451 if (!sources || source_count <= 0 || width <= 0 || height <= 0 || !out_size) {
452 return NULL;
453 }
454
455 // If no sources, return empty frame
456
457 // If only one source, center it properly to maintain aspect ratio and look good
458 if (source_count == 1) {
459 // Create a frame of the target size filled with spaces
460 // Check for integer overflow before multiplication
461 size_t w = (size_t)width;
462 size_t h = (size_t)height;
463 size_t w_times_h;
464 if (checked_size_mul(w, h, &w_times_h) != ASCIICHAT_OK) {
465 SET_ERRNO(ERROR_INVALID_PARAM, "ascii_create_grid: dimensions would overflow: %dx%d", width, height);
466 return NULL;
467 }
468
469 size_t w_times_h_plus_h;
470 if (checked_size_add(w_times_h, h, &w_times_h_plus_h) != ASCIICHAT_OK) {
471 SET_ERRNO(ERROR_INVALID_PARAM, "ascii_create_grid: buffer size would overflow: %dx%d", width, height);
472 return NULL;
473 }
474
475 size_t target_size;
476 if (checked_size_add(w_times_h_plus_h, 1, &target_size) != ASCIICHAT_OK) {
477 SET_ERRNO(ERROR_INVALID_PARAM, "ascii_create_grid: buffer size would overflow: %dx%d", width, height);
478 return NULL;
479 }
480 char *result;
481 result = SAFE_MALLOC(target_size, char *);
482 SAFE_MEMSET(result, target_size, ' ', target_size - 1);
483 result[target_size - 1] = '\0';
484
485 // Add newlines at the end of each row
486 for (int row = 0; row < height; row++) {
487 result[row * (width + 1) + width] = '\n';
488 }
489
490 // Copy the source frame into the result, line by line, centering it
491 // Handle NULL frame_data gracefully
492 const char *src_data = sources[0].frame_data;
493 int src_pos = 0;
494 int src_size = (int)sources[0].frame_size;
495
496 // If source data is NULL or empty, just return the empty frame
497 if (!src_data || src_size <= 0) {
498 *out_size = target_size - 1; // Don't count null terminator
499 return result;
500 }
501
502 // Count lines in source to calculate vertical padding
503 int src_lines = 0;
504 for (int i = 0; i < src_size; i++) {
505 if (src_data[i] == '\n')
506 src_lines++;
507 }
508
509 int v_padding = (height - src_lines) / 2;
510 if (v_padding < 0)
511 v_padding = 0;
512
513 int dst_row = v_padding;
514 src_pos = 0;
515
516 while (src_pos < src_size && dst_row < height) {
517 // Find end of current line in source
518 int line_start = src_pos;
519 int line_len = 0;
520 while (src_pos < src_size && src_data[src_pos] != '\n') {
521 line_len++;
522 src_pos++;
523 }
524
525 // Calculate horizontal padding to center the line
526 int h_padding = (width - line_len) / 2;
527 if (h_padding < 0)
528 h_padding = 0;
529
530 // Copy line to result with padding
531 // Use size_t for position calculation to prevent integer underflow
532 size_t row_offset = (size_t)dst_row * (size_t)(width + 1);
533 size_t dst_pos = row_offset + (size_t)h_padding;
534 int copy_len = (line_len > width - h_padding) ? width - h_padding : line_len;
535
536 if (copy_len > 0 && dst_pos + (size_t)copy_len < target_size) {
537 SAFE_MEMCPY(&result[dst_pos], target_size - dst_pos, &src_data[line_start], (size_t)copy_len);
538 }
539
540 // Skip newline in source
541 if (src_pos < src_size && src_data[src_pos] == '\n') {
542 src_pos++;
543 }
544
545 dst_row++;
546 }
547
548 *out_size = target_size - 1; // Don't count null terminator
549 return result;
550 }
551
552 // Multiple sources: create grid layout
553 // Calculate grid dimensions that maximize the use of terminal space
554 // Character aspect ratio: terminal chars are typically ~2x taller than wide
555 float char_aspect = 2.0f;
556
557 int grid_cols, grid_rows;
558 float best_score = -1.0f;
559 int best_cols = 1;
560 int best_rows = source_count;
561
562 // Try all possible grid configurations
563 for (int test_cols = 1; test_cols <= source_count; test_cols++) {
564 int test_rows = (int)ceil((double)source_count / test_cols);
565
566 // Skip configurations with too many empty cells
567 int empty_cells = (test_cols * test_rows) - source_count;
568 if (empty_cells > source_count / 2)
569 continue; // Don't waste more than 50% space
570
571 // Calculate the size each cell would have
572 int cell_width = (width - (test_cols - 1)) / test_cols; // -1 per separator
573 int cell_height = (height - (test_rows - 1)) / test_rows; // -1 per separator
574
575 // Skip if cells would be too small
576 if (cell_width < 10 || cell_height < 3)
577 continue;
578
579 // Calculate the aspect ratio of each cell (accounting for char aspect)
580 float cell_aspect = ((float)cell_width / (float)cell_height) / char_aspect;
581
582 // Score based on how close to square (1:1) each video cell would be
583 // This naturally adapts to any terminal size
584 float aspect_score = 1.0f - fabsf(logf(cell_aspect)); // log makes it symmetric around 1
585 if (aspect_score < 0)
586 aspect_score = 0;
587
588 // Bonus for better space utilization
589 float utilization = (float)source_count / (float)(test_cols * test_rows);
590
591 // For 2 clients specifically, heavily weight the aspect score
592 // This makes 2 clients naturally go horizontal on wide terminals and vertical on tall ones
593 float total_score;
594 if (source_count == 2) {
595 // For 2 clients, we want the layout that gives the most square-ish cells
596 total_score = aspect_score * 0.9f + utilization * 0.1f;
597 } else {
598 // For 3+ clients, balance aspect ratio with space utilization
599 total_score = aspect_score * 0.7f + utilization * 0.3f;
600 }
601
602 // Small bonus for simpler grids (prefer 2x2 over 3x1, etc.)
603 if (test_cols == test_rows) {
604 total_score += 0.05f; // Slight preference for square grids
605 }
606
607 if (total_score > best_score) {
608 best_score = total_score;
609 best_cols = test_cols;
610 best_rows = test_rows;
611 }
612 }
613
614 grid_cols = best_cols;
615 grid_rows = best_rows;
616
617 // Calculate dimensions for each cell (leave 1 char for separators)
618 int cell_width = (width - (grid_cols - 1)) / grid_cols;
619 int cell_height = (height - (grid_rows - 1)) / grid_rows;
620
621 if (cell_width < 10 || cell_height < 3) {
622 // Too small for grid layout, just use first source
623 char *result;
624 result = SAFE_MALLOC(sources[0].frame_size + 1, char *);
625 if (sources[0].frame_data && sources[0].frame_size > 0) {
626 SAFE_MEMCPY(result, sources[0].frame_size + 1, sources[0].frame_data, sources[0].frame_size);
627 result[sources[0].frame_size] = '\0';
628 *out_size = sources[0].frame_size;
629 } else {
630 // Handle NULL or empty frame data
631 result[0] = '\0';
632 *out_size = 0;
633 }
634 return result;
635 }
636
637 // Allocate mixed frame buffer
638 // Check for integer overflow before multiplication
639 size_t w_sz = (size_t)width;
640 size_t h_sz = (size_t)height;
641 if (w_sz > SIZE_MAX / h_sz) {
642 SET_ERRNO(ERROR_INVALID_PARAM, "ascii_create_grid: dimensions would overflow: %dx%d", width, height);
643 return NULL;
644 }
645 size_t mixed_size = w_sz * h_sz + h_sz + 1; // +1 for null terminator, +height for newlines
646 char *mixed_frame;
647 mixed_frame = SAFE_MALLOC(mixed_size, char *);
648
649 // Initialize mixed frame with spaces
650 SAFE_MEMSET(mixed_frame, mixed_size, ' ', mixed_size - 1);
651 mixed_frame[mixed_size - 1] = '\0';
652
653 // Add newlines at the end of each row
654 for (int row = 0; row < height; row++) {
655 mixed_frame[row * (width + 1) + width] = '\n';
656 }
657
658 // Place each video source in the grid
659 for (int src = 0; src < source_count; src++) {
660 int grid_row = src / grid_cols;
661 int grid_col = src % grid_cols;
662
663 // Calculate position in mixed frame
664 int start_row = grid_row * (cell_height + 1); // +1 for separator
665 int start_col = grid_col * (cell_width + 1); // +1 for separator
666
667 // Parse source frame line by line and place in grid
668 const char *src_data = sources[src].frame_data;
669 int src_row = 0;
670 int src_pos = 0;
671
672 while (src_pos < (int)sources[src].frame_size && src_row < cell_height && start_row + src_row < height) {
673 // Find end of current line in source
674 int line_start = src_pos;
675 while (src_pos < (int)sources[src].frame_size && src_data[src_pos] != '\n') {
676 src_pos++;
677 }
678 int line_len = src_pos - line_start;
679
680 // Copy line to mixed frame (truncate if too long)
681 int copy_len = (line_len < cell_width) ? line_len : cell_width;
682 if (copy_len > 0 && start_col + copy_len <= width) {
683 int mixed_pos = (start_row + src_row) * (width + 1) + start_col;
684 SAFE_MEMCPY(mixed_frame + mixed_pos, mixed_size - (size_t)mixed_pos, src_data + line_start, (size_t)copy_len);
685 }
686
687 // Move to next line
688 if (src_pos < (int)sources[src].frame_size && src_data[src_pos] == '\n') {
689 src_pos++;
690 }
691 src_row++;
692 }
693
694 // Draw separators with bounds checking to prevent buffer overflow
695 if (grid_col < grid_cols - 1 && start_col + cell_width < width) {
696 // Vertical separator
697 for (int row = start_row; row < start_row + cell_height && row < height; row++) {
698 size_t idx = (size_t)row * (size_t)(width + 1) + (size_t)(start_col + cell_width);
699 if (idx < mixed_size - 1) { // -1 to preserve null terminator
700 mixed_frame[idx] = '|';
701 }
702 }
703 }
704
705 if (grid_row < grid_rows - 1 && start_row + cell_height < height) {
706 // Horizontal separator
707 for (int col = start_col; col < start_col + cell_width && col < width; col++) {
708 size_t idx = (size_t)(start_row + cell_height) * (size_t)(width + 1) + (size_t)col;
709 if (idx < mixed_size - 1) { // -1 to preserve null terminator
710 mixed_frame[idx] = '_';
711 }
712 }
713 // Corner character where separators meet
714 if (grid_col < grid_cols - 1 && start_col + cell_width < width) {
715 size_t idx = (size_t)(start_row + cell_height) * (size_t)(width + 1) + (size_t)(start_col + cell_width);
716 if (idx < mixed_size - 1) { // -1 to preserve null terminator
717 mixed_frame[idx] = '+';
718 }
719 }
720 }
721 }
722
723 *out_size = strlen(mixed_frame);
724 return mixed_frame;
725}
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:389
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_INVALID_PARAM
size_t frame_size
Frame data size in bytes (length of frame string)
Definition ascii.h:344
const char * frame_data
Frame data pointer (ASCII frame string, not owned)
Definition ascii.h:343

References ASCIICHAT_OK, ERROR_INVALID_PARAM, ascii_frame_source_t::frame_data, ascii_frame_source_t::frame_size, SAFE_MALLOC, SAFE_MEMCPY, SAFE_MEMSET, and SET_ERRNO.

◆ ascii_pad_frame_height()

char * ascii_pad_frame_height ( const char *  frame,
size_t  pad_top 
)

#include <ascii.h>

Add blank lines (vertical padding) to center a frame vertically.

Parameters
frameASCII frame string (must not be NULL)
pad_topNumber of blank lines to add at top (must be >= 0)
Returns
Allocated padded frame string (caller must free with free()), or NULL on error

Adds blank lines (vertical padding) to the top of an ASCII frame. Useful for centering frames vertically or creating margins. Blank lines are added at the top of the frame.

Note
Returns NULL on error (invalid frame, memory allocation failure).
Padded frame string must be freed by caller using free().
Frame width is preserved (characters per line unchanged).
Frame height is increased by 'pad_top' lines.

Adds vertical padding (blank lines) to center a frame vertically.

Parameters: frame The input ASCII frame to pad vertically. pad_top Number of blank lines to add at the top.

Returns: A newly allocated, null-terminated string with vertical padding, or NULL if frame is NULL.

Definition at line 738 of file ascii.c.

738 {
739 if (!frame) {
740 return NULL;
741 }
742
743 if (pad_top == 0) {
744 // Nothing to do; return a copy because the caller knows to free() the value.
745 size_t orig_len = strlen(frame);
746 char *copy;
747 copy = SAFE_MALLOC(orig_len + 1, char *);
748 SAFE_MEMCPY(copy, orig_len + 1, frame, orig_len + 1);
749 return copy;
750 }
751
752 // Calculate buffer size needed
753 size_t frame_len = strlen(frame);
754 size_t top_padding_len = pad_top; // Just newlines, no spaces
755 size_t total_len = top_padding_len + frame_len;
756
757 char *buffer;
758 buffer = SAFE_MALLOC(total_len + 1, char *);
759
760 char *position = buffer;
761
762 // Add top padding (blank lines - just newlines)
763 for (size_t i = 0; i < pad_top; i++) {
764 *position++ = '\n';
765 }
766
767 // Copy the original frame
768 size_t remaining = total_len + 1 - pad_top;
769 SAFE_MEMCPY(position, remaining, frame, frame_len);
770 position += frame_len;
771 *position = '\0';
772
773 return buffer;
774}

References SAFE_MALLOC, and SAFE_MEMCPY.

Referenced by ascii_convert(), and ascii_convert_with_capabilities().

◆ ascii_pad_frame_width()

char * ascii_pad_frame_width ( const char *  frame,
size_t  pad 
)

#include <ascii.h>

Add leading spaces (left-padding) to each line of a frame.

Parameters
frameASCII frame string (must not be NULL)
padNumber of spaces to pad on left (must be >= 0)
Returns
Allocated padded frame string (caller must free with free()), or NULL on error

Adds leading spaces (left-padding) to each line of an ASCII frame. Useful for centering frames horizontally or creating margins. Each line in the frame is padded with the specified number of spaces.

Note
Returns NULL on error (invalid frame, memory allocation failure).
Padded frame string must be freed by caller using free().
Frame height is preserved (number of lines unchanged).
Frame width is increased by 'pad' characters.

Definition at line 375 of file ascii.c.

375 {
376 if (!frame) {
377 return NULL;
378 }
379
380 if (pad_left == 0) {
381 // Nothing to do; return a copy so the caller can free it safely without
382 // worrying about the original allocation strategy.
383 size_t orig_len = strlen(frame);
384 char *copy;
385 copy = SAFE_MALLOC(orig_len + 1, char *);
386 SAFE_MEMCPY(copy, orig_len + 1, frame, orig_len + 1);
387 return copy;
388 }
389
390 // Count how many visual rows we have (lines terminated by '\n') to determine
391 // the final buffer size.
392 size_t line_count = 1; // There is always at least the first line
393 const char *char_in_frame = frame;
394 while (*char_in_frame) {
395 if (*char_in_frame == '\n') {
396 line_count++;
397 }
398 char_in_frame++;
399 }
400
401 // Total length of the source plus padding.
402 const size_t frame_len = strlen(frame);
403 const size_t left_padding_len = line_count * pad_left;
404 const size_t total_len = frame_len + left_padding_len;
405
406 char *buffer;
407 buffer = SAFE_MALLOC(total_len + 1, char *);
408
409 // Build the padded frame.
410 bool at_line_start = true;
411 const char *src = frame;
412 char *position = buffer;
413
414 while (*src) {
415 if (at_line_start) {
416 // Insert the requested amount of spaces in front of every visual row.
417 size_t remaining = (size_t)((ptrdiff_t)(buffer + total_len + 1) - (ptrdiff_t)position);
418 SAFE_MEMSET(position, remaining, ' ', (size_t)pad_left);
419 position += pad_left;
420 at_line_start = false;
421 }
422
423 *position++ = *src;
424
425 if (*src == '\n') {
426 at_line_start = true;
427 }
428
429 src++;
430 }
431
432 *position = '\0';
433 return buffer;
434}

References SAFE_MALLOC, SAFE_MEMCPY, and SAFE_MEMSET.

Referenced by ascii_convert(), and ascii_convert_with_capabilities().

◆ ascii_read_destroy()

void ascii_read_destroy ( void  )

#include <ascii.h>

Destroy ASCII read subsystem.

Cleans up the ASCII read subsystem and releases resources. Closes webcam device and frees associated memory. Should be called when done with image capture.

Note
Safe to call multiple times (no-op after first call).
After cleanup, webcam capture will fail until re-initialized.

Definition at line 355 of file ascii.c.

355 {
357 log_debug("ASCII reader destroyed");
358}
#define log_debug(...)
Log a DEBUG message.
void webcam_cleanup(void)
Clean up global webcam interface.
Definition webcam.c:204

References log_debug, and webcam_cleanup().

◆ ascii_read_init()

asciichat_error_t ascii_read_init ( unsigned short int  webcam_index)

#include <ascii.h>

Initialize ASCII read subsystem (e.g., webcam)

Parameters
webcam_indexWebcam device index (0 for default device)
Returns
ASCIICHAT_OK on success, error code on failure

Initializes the ASCII read subsystem for image capture. Opens webcam device and prepares it for frame capture. This is a convenience wrapper around webcam initialization.

Note
This function initializes webcam capture for ASCII conversion.
Must call ascii_read_destroy() when done.
Use webcam_read() to capture frames after initialization.
Warning
On failure, use webcam_print_init_error_help() for diagnostics.

Definition at line 34 of file ascii.c.

34 {
35 log_info("Initializing ASCII reader with webcam index %u", webcam_index);
36 webcam_init(webcam_index);
37 return ASCIICHAT_OK;
38}
#define log_info(...)
Log an INFO message.
asciichat_error_t webcam_init(unsigned short int webcam_index)
Initialize global webcam interface.
Definition webcam.c:18

References ASCIICHAT_OK, log_info, and webcam_init().

◆ ascii_simd_init()

void ascii_simd_init ( void  )

#include <ascii_simd.h>

Initialize SIMD subsystem.

Initializes all SIMD subsystems and caches. Must be called once at startup.

Definition at line 90 of file ascii_simd.c.

90 {
91 // Initialize SIMD lookup tables manually (constructor disabled for musl compatibility)
92 // Both init functions have guards to prevent double-initialization
93 init_dec3();
95}
void init_dec3(void)
Initialize decimal lookup table.
Definition ascii_simd.c:58
void init_default_luminance_palette(void)
Initialize default luminance palette.
Definition ascii_simd.c:37

References init_dec3(), and init_default_luminance_palette().

Referenced by ansi_fast_init(), and server_main().

◆ ascii_write()

asciichat_error_t ascii_write ( const char *  frame)

#include <ascii.h>

Write ASCII frame to terminal.

Parameters
frameASCII frame string to write (must not be NULL)
Returns
ASCIICHAT_OK on success, error code on failure

Writes an ASCII frame string to the terminal output. Frame string may contain ANSI escape sequences for color output and cursor positioning. Output is written to the file descriptor configured during ascii_write_init().

Note
Frame string should be null-terminated.
ANSI escape sequences in frame string are processed by terminal.
Output is written immediately (no buffering).

Definition at line 312 of file ascii.c.

312 {
313 if (frame == NULL) {
314 log_warn("Attempted to write NULL frame");
315 return ERROR_INVALID_PARAM;
316 }
317
318 // Skip cursor reset in snapshot mode or when testing - just print raw ASCII
319 const char *testing_env = SAFE_GETENV("TESTING");
320 if (!GET_OPTION(snapshot_mode) && testing_env == NULL) {
321 cursor_reset(STDOUT_FILENO);
322 }
323
324 size_t frame_len = strlen(frame);
325 size_t written = fwrite(frame, 1, frame_len, stdout);
326 if (written != frame_len) {
327 log_error("Failed to write ASCII frame");
328 return ERROR_TERMINAL;
329 }
330
331 return ASCIICHAT_OK;
332}
#define SAFE_GETENV(name)
Definition common.h:378
@ ERROR_TERMINAL
Definition error_codes.h:66
#define log_warn(...)
Log a WARN message.
#define cursor_reset(fd)
Reset cursor to home position.
Definition ascii.h:535

References ASCIICHAT_OK, cursor_reset, ERROR_INVALID_PARAM, ERROR_TERMINAL, GET_OPTION, log_error, log_warn, and SAFE_GETENV.

◆ ascii_write_destroy()

void ascii_write_destroy ( int  fd,
bool  reset_terminal 
)

#include <ascii.h>

Destroy ASCII write subsystem.

Parameters
fdFile descriptor that was used for writing (must be valid)
reset_terminalWhether to reset terminal on cleanup (true to restore state, false to preserve)

Cleans up the ASCII write subsystem and restores terminal state. Restores cursor visibility, clears screen, and resets terminal attributes if reset_terminal is true.

TERMINAL CLEANUP:

  • Restores cursor visibility (if reset_terminal is true)
  • Resets terminal attributes (if reset_terminal is true)
  • Clears screen (if reset_terminal is true)
  • Restores terminal to default state
Note
Safe to call multiple times (no-op after first call).
Reset terminal restores terminal to default state.
After cleanup, terminal output functions may fail until re-initialized.

Definition at line 334 of file ascii.c.

334 {
335#if PLATFORM_WINDOWS
336 (void)fd; // Unused on Windows - terminal operations use stdout directly
337#endif
338 // console_clear(fd);
339 // cursor_reset(fd);
340 // Skip cursor show in snapshot mode - leave terminal as-is
341 if (!GET_OPTION(snapshot_mode) && reset_terminal) {
342 // Show cursor using platform abstraction
343 if (terminal_hide_cursor(fd, false) != 0) {
344 log_warn("Failed to show cursor");
345 }
346
347 // Re-enable echo using platform abstraction
348 if (terminal_set_echo(true) != 0) {
349 log_warn("Failed to re-enable echo");
350 }
351 }
352 log_debug("ASCII writer destroyed");
353}
asciichat_error_t terminal_set_echo(bool enable)
Set terminal echo mode.
asciichat_error_t terminal_hide_cursor(int fd, bool hide)
Hide or show cursor.

References GET_OPTION, log_debug, log_warn, terminal_hide_cursor(), and terminal_set_echo().

Referenced by display_cleanup().

◆ ascii_write_init()

asciichat_error_t ascii_write_init ( int  fd,
bool  reset_terminal 
)

#include <ascii.h>

Initialize ASCII write subsystem.

Parameters
fdFile descriptor to write to (must be valid file descriptor)
reset_terminalWhether to reset terminal on initialization (true to reset, false to preserve state)
Returns
ASCIICHAT_OK on success, error code on failure

Initializes the ASCII write subsystem for terminal output. Configures terminal for ASCII art display including cursor hiding, screen clearing, and terminal state management.

TERMINAL INITIALIZATION:

  • Hides cursor (if reset_terminal is true)
  • Clears screen (if reset_terminal is true)
  • Configures terminal for raw output
  • Prepares terminal for ASCII art display
Note
Must call ascii_write_destroy() when done.
Reset terminal clears screen and hides cursor for clean display.
File descriptor is used for terminal output (stdout, stderr, etc.).

Definition at line 40 of file ascii.c.

40 {
41 // Validate file descriptor
42 if (fd < 0) {
43 log_error("Invalid file descriptor %d", fd);
45 }
46
47 // Skip terminal control sequences in snapshot mode or when testing - just print raw ASCII
48 const char *testing_env = SAFE_GETENV("TESTING");
49 if (!GET_OPTION(snapshot_mode) && reset_terminal && testing_env == NULL) {
50 console_clear(fd);
51 cursor_reset(fd);
52
53 // Disable echo using platform abstraction
54 if (terminal_set_echo(false) != 0) {
55 log_error("Failed to disable echo for fd %d", fd);
56 return ERROR_TERMINAL;
57 }
58 // Hide cursor using platform abstraction
59 if (terminal_hide_cursor(fd, true) != 0) {
60 log_warn("Failed to hide cursor");
61 }
62 }
63 log_debug("ASCII writer initialized");
64 return ASCIICHAT_OK;
65}
#define console_clear(fd)
Clear console and move cursor to home position.
Definition ascii.h:524

References ASCIICHAT_OK, console_clear, cursor_reset, ERROR_INVALID_PARAM, ERROR_TERMINAL, GET_OPTION, log_debug, log_error, log_warn, SAFE_GETENV, terminal_hide_cursor(), and terminal_set_echo().

Referenced by display_init().

◆ benchmark_simd_color_conversion()

simd_benchmark_t benchmark_simd_color_conversion ( int  width,
int  height,
int  iterations,
bool  background_mode 
)

#include <ascii_simd.h>

Benchmark SIMD color conversion methods.

Parameters
widthImage width in pixels
heightImage height in pixels
iterationsNumber of benchmark iterations
background_modeUse background colors
Returns
Benchmark results structure

Definition at line 500 of file ascii_simd.c.

500 {
501 simd_benchmark_t result = {0};
502
503 // Check for integer overflow in pixel count calculation
504 size_t pixel_count;
505 if (checked_size_mul((size_t)width, (size_t)height, &pixel_count) != ASCIICHAT_OK) {
506 log_error("Image dimensions %d x %d too large (overflow)", width, height);
507 return result;
508 }
509
510 // Estimate output buffer size for colored ASCII (much larger than monochrome)
511 // Each pixel can generate ~25 bytes of ANSI escape codes + 1 char
512 size_t output_buffer_size = pixel_count * 30 + (size_t)width * 10; // Extra for newlines/reset codes
513
514 // Generate test data and test image for unified functions
515 rgb_pixel_t *test_pixels;
516 char *output_buffer;
517 test_pixels = SAFE_CALLOC_SIMD(pixel_count, sizeof(rgb_pixel_t), rgb_pixel_t *);
518 output_buffer = SAFE_MALLOC(output_buffer_size, char *);
519
520 // Create test image for new unified functions
521 image_t *frame = image_new(width, height);
522 if (!frame) {
523 SAFE_FREE(test_pixels);
524 SAFE_FREE(output_buffer);
525 return result;
526 }
527
528 // Use synthetic gradient data for consistent cross-platform benchmarking
529 printf("Using coherent gradient data for realistic color testing\n");
530 // NOLINTNEXTLINE(bugprone-random-generator-seed)
531 srand(12345); // For consistent gradient variation across runs
532 for (size_t i = 0; i < pixel_count; i++) {
533 int x = i % width;
534 int y = i / width;
535 // Create smooth gradients with some variation (mimics real images)
536 int base_r = (x * 255) / width;
537 int base_g = (y * 255) / height;
538 int base_b = ((x + y) * 127) / (width + height);
539
540 // Add realistic variation
541 int temp_r = base_r + (rand() % 32 - 16); // NOLINT(cert-msc30-c,cert-msc50-cpp)
542 int temp_g = base_g + (rand() % 32 - 16); // NOLINT(cert-msc30-c,cert-msc50-cpp)
543 int temp_b = base_b + (rand() % 32 - 16); // NOLINT(cert-msc30-c,cert-msc50-cpp)
544
545 test_pixels[i].r = clamp_rgb(temp_r);
546 test_pixels[i].g = clamp_rgb(temp_g);
547 test_pixels[i].b = clamp_rgb(temp_b);
548 }
549
550 // Populate test image with same data as test_pixels
551 frame->pixels = test_pixels;
552
553 const char *mode_str = background_mode ? "background" : "foreground";
554 printf("Benchmarking COLOR %s %dx%d (%zu pixels) x %d iterations...\n", mode_str, width, height, pixel_count,
555 iterations);
556
557 // Benchmark scalar color version
558 double start = get_time_seconds();
559 for (int i = 0; i < iterations; i++) {
560 char *result_str = image_print_color(frame, DEFAULT_ASCII_PALETTE);
561 if (result_str)
562 SAFE_FREE(result_str);
563 }
564 result.scalar_time = get_time_seconds() - start;
565
566#if SIMD_SUPPORT_SSE2
567 // Benchmark SSE2 color using unified function
568 start = get_time_seconds();
569 for (int i = 0; i < iterations; i++) {
570 char *ascii_output = render_ascii_sse2_unified_optimized(frame, background_mode, true, DEFAULT_ASCII_PALETTE);
571 if (ascii_output)
572 SAFE_FREE(ascii_output);
573 }
574 result.sse2_time = get_time_seconds() - start;
575#endif
576
577#if SIMD_SUPPORT_SSSE3
578 // Benchmark SSSE3 color using unified function
579 start = get_time_seconds();
580 for (int i = 0; i < iterations; i++) {
581 char *ascii_output = render_ascii_ssse3_unified_optimized(frame, background_mode, true, DEFAULT_ASCII_PALETTE);
582 if (ascii_output)
583 SAFE_FREE(ascii_output);
584 }
585 result.ssse3_time = get_time_seconds() - start;
586#endif
587
588#if SIMD_SUPPORT_AVX2
589 // Benchmark AVX2 color using unified function
590 start = get_time_seconds();
591 for (int i = 0; i < iterations; i++) {
592 char *ascii_output = render_ascii_avx2_unified_optimized(frame, background_mode, true, DEFAULT_ASCII_PALETTE);
593 if (ascii_output)
594 SAFE_FREE(ascii_output);
595 }
596 result.avx2_time = get_time_seconds() - start;
597#endif
598
599#if SIMD_SUPPORT_NEON
600 // Benchmark NEON color
601 start = get_time_seconds();
602 for (int i = 0; i < iterations; i++) {
603 // Create temporary image for unified function
604 image_t temp_image = {.pixels = test_pixels, .w = width, .h = height, .alloc_method = IMAGE_ALLOC_SIMD};
605 char *ascii_output = render_ascii_neon_unified_optimized(&temp_image, background_mode, true, DEFAULT_ASCII_PALETTE);
606 if (ascii_output)
607 SAFE_FREE(ascii_output);
608 }
609 result.neon_time = get_time_seconds() - start;
610#endif
611
612 // Find best method
613 double best_time = result.scalar_time;
614 result.best_method = "scalar";
615
616#if SIMD_SUPPORT_SSE2
617 if (result.sse2_time > 0 && result.sse2_time < best_time) {
618 best_time = result.sse2_time;
619 result.best_method = "SSE2";
620 }
621#endif
622
623#if SIMD_SUPPORT_SSSE3
624 if (result.ssse3_time > 0 && result.ssse3_time < best_time) {
625 best_time = result.ssse3_time;
626 result.best_method = "SSSE3";
627 }
628#endif
629
630#if SIMD_SUPPORT_AVX2
631 if (result.avx2_time > 0 && result.avx2_time < best_time) {
632 best_time = result.avx2_time;
633 result.best_method = "AVX2";
634 }
635#endif
636
637#if SIMD_SUPPORT_NEON
638 if (result.neon_time > 0 && result.neon_time < best_time) {
639 best_time = result.neon_time;
640 result.best_method = "NEON";
641 }
642#endif
643
644 result.speedup_best = result.scalar_time / best_time;
645
646 // Cleanup - frame owns test_pixels now
647 frame->pixels = NULL; // Don't double-free
648 image_destroy(frame);
649 SAFE_FREE(test_pixels);
650 SAFE_FREE(output_buffer);
651
652 return result;
653}
#define SAFE_CALLOC_SIMD(count, size, cast)
Definition common.h:311
const char DEFAULT_ASCII_PALETTE[]
Default ASCII palette for legacy functions.
Definition palette.c:48
const char * best_method
Definition ascii_simd.h:279
SIMD benchmark results structure.
Definition ascii_simd.h:271

References ASCIICHAT_OK, simd_benchmark_t::avx2_time, simd_benchmark_t::best_method, DEFAULT_ASCII_PALETTE, IMAGE_ALLOC_SIMD, image_destroy(), image_new(), image_print_color(), log_error, simd_benchmark_t::neon_time, image_t::pixels, SAFE_CALLOC_SIMD, SAFE_FREE, SAFE_MALLOC, simd_benchmark_t::scalar_time, simd_benchmark_t::speedup_best, simd_benchmark_t::sse2_time, and simd_benchmark_t::ssse3_time.

◆ benchmark_simd_color_conversion_with_source()

simd_benchmark_t benchmark_simd_color_conversion_with_source ( int  width,
int  height,
int  iterations,
bool  background_mode,
const image_t source_image,
bool  use_256color 
)

#include <ascii_simd.h>

Benchmark SIMD color conversion with source image.

Parameters
widthImage width in pixels
heightImage height in pixels
iterationsNumber of benchmark iterations
background_modeUse background colors
source_imageSource image for conversion
use_256colorUse 256-color mode
Returns
Benchmark results structure

◆ benchmark_simd_conversion()

simd_benchmark_t benchmark_simd_conversion ( int  width,
int  height,
int  iterations 
)

#include <ascii_simd.h>

Benchmark SIMD conversion methods (monochrome)

Parameters
widthImage width in pixels
heightImage height in pixels
iterationsNumber of benchmark iterations
Returns
Benchmark results structure

◆ benchmark_simd_conversion_with_source()

simd_benchmark_t benchmark_simd_conversion_with_source ( int  width,
int  height,
int  iterations,
bool  background_mode,
const image_t source_image,
bool  use_256color 
)

#include <ascii_simd.h>

Benchmark SIMD conversion with source image.

Parameters
widthImage width in pixels
heightImage height in pixels
iterationsNumber of benchmark iterations
background_modeUse background colors
source_imageSource image for conversion
use_256colorUse 256-color mode
Returns
Benchmark results structure

Definition at line 656 of file ascii_simd.c.

657 {
658 simd_benchmark_t result = {0};
659 (void)background_mode; // Suppress unused parameter warning
660 (void)use_256color; // Suppress unused parameter warning
661
662 // Check for integer overflow in pixel count calculation
663 size_t pixel_count;
664 if (checked_size_mul((size_t)width, (size_t)height, &pixel_count) != ASCIICHAT_OK) {
665 log_error("Image dimensions %d x %d too large (overflow)", width, height);
666 return result;
667 }
668
669 // Generate test data
670 rgb_pixel_t *test_pixels;
671 char *output_buffer;
672 const size_t output_buffer_size = pixel_count * 16;
673 test_pixels = SAFE_CALLOC_SIMD(pixel_count, sizeof(rgb_pixel_t), rgb_pixel_t *);
674 output_buffer = SAFE_MALLOC(output_buffer_size, char *);
675
676 if (source_image && source_image->pixels) {
677 printf("Using provided image data (%dx%d) for testing\n", source_image->w, source_image->h);
678
679 // Resize source image to test dimensions if needed
680 if (source_image->w == width && source_image->h == height) {
681 // Direct copy
682 for (size_t i = 0; i < pixel_count; i++) {
683 test_pixels[i].r = source_image->pixels[i].r;
684 test_pixels[i].g = source_image->pixels[i].g;
685 test_pixels[i].b = source_image->pixels[i].b;
686 }
687 } else {
688 // Simple nearest-neighbor resize
689 for (int y = 0; y < height; y++) {
690 for (int x = 0; x < width; x++) {
691 int src_x = (x * source_image->w) / width;
692 int src_y = (y * source_image->h) / height;
693 // Use size_t for index calculations to prevent integer overflow
694 size_t src_idx = (size_t)src_y * (size_t)source_image->w + (size_t)src_x;
695 size_t dst_idx = (size_t)y * (size_t)width + (size_t)x;
696
697 if (src_idx < (size_t)source_image->w * (size_t)source_image->h) {
698 test_pixels[dst_idx].r = source_image->pixels[src_idx].r;
699 test_pixels[dst_idx].g = source_image->pixels[src_idx].g;
700 test_pixels[dst_idx].b = source_image->pixels[src_idx].b;
701 }
702 }
703 }
704 printf("Resized image data from %dx%d to %dx%d\n", source_image->w, source_image->h, width, height);
705 }
706 } else {
707 // Fall back to synthetic gradient data
708 printf("No source image provided, using synthetic gradient data\n");
709 srand(12345); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
710 for (size_t i = 0; i < pixel_count; i++) {
711 int x = i % width;
712 int y = i / width;
713 int base_r = (x * 255 / width);
714 int base_g = (y * 255 / height);
715 int base_b = ((x + y) * 127 / (width + height));
716
717 int temp_r = base_r + (rand() % 16 - 8); // NOLINT(cert-msc30-c,cert-msc50-cpp)
718 int temp_g = base_g + (rand() % 16 - 8); // NOLINT(cert-msc30-c,cert-msc50-cpp)
719 int temp_b = base_b + (rand() % 16 - 8); // NOLINT(cert-msc30-c,cert-msc50-cpp)
720
721 test_pixels[i].r = clamp_rgb(temp_r);
722 test_pixels[i].g = clamp_rgb(temp_g);
723 test_pixels[i].b = clamp_rgb(temp_b);
724 }
725 }
726
727 // Calculate adaptive iterations for reliable timing
728 int adaptive_iterations = calculate_adaptive_iterations(pixel_count, 10.0);
729 printf("Benchmarking %dx%d (%zu pixels) using %d adaptive iterations (ignoring passed iterations)...\n", width,
730 height, pixel_count, adaptive_iterations);
731
732 // Benchmark all available SIMD variants using unified image-based API
733 image_t *frame = image_new(width, height);
734 memcpy(frame->pixels, test_pixels, pixel_count * sizeof(rgb_pixel_t));
735
736 // Benchmark scalar using color conversion
737 ensure_default_palette_ready();
738 double start_scalar = get_time_seconds();
739 for (int i = 0; i < iterations; i++) {
740 char *result_str = image_print_color(frame, DEFAULT_ASCII_PALETTE);
741 if (result_str)
742 SAFE_FREE(result_str);
743 }
744 result.scalar_time = (get_time_seconds() - start_scalar) / iterations;
745
746#if SIMD_SUPPORT_SSE2
747 // Benchmark SSE2 using unified optimized renderer
748 // Benchmark SSE2 color rendering
749 ensure_default_palette_ready();
750 double start_sse2_color = get_time_seconds();
751 for (int i = 0; i < iterations; i++) {
752 char *result_str = render_ascii_sse2_unified_optimized(frame, background_mode, use_256color, DEFAULT_ASCII_PALETTE);
753 if (result_str)
754 SAFE_FREE(result_str);
755 }
756 result.sse2_time = (get_time_seconds() - start_sse2_color) / iterations;
757#endif
758
759#if SIMD_SUPPORT_SSSE3
760 // Benchmark SSSE3 using unified optimized renderer
761 // Benchmark SSSE3 color rendering
762 ensure_default_palette_ready();
763 double start_ssse3_color = get_time_seconds();
764 for (int i = 0; i < iterations; i++) {
765 char *result_str =
766 render_ascii_ssse3_unified_optimized(frame, background_mode, use_256color, DEFAULT_ASCII_PALETTE);
767 if (result_str)
768 SAFE_FREE(result_str);
769 }
770 result.ssse3_time = (get_time_seconds() - start_ssse3_color) / iterations;
771#endif
772
773#if SIMD_SUPPORT_AVX2
774 // Benchmark AVX2 using unified optimized renderer
775 // Benchmark AVX2 color rendering
776 ensure_default_palette_ready();
777 double start_avx2_color = get_time_seconds();
778 for (int i = 0; i < iterations; i++) {
779 char *result_str = render_ascii_avx2_unified_optimized(frame, background_mode, use_256color, DEFAULT_ASCII_PALETTE);
780 if (result_str)
781 SAFE_FREE(result_str);
782 }
783 result.avx2_time = (get_time_seconds() - start_avx2_color) / iterations;
784#endif
785
786#if SIMD_SUPPORT_NEON
787 // Benchmark NEON using unified optimized renderer
788 // Benchmark NEON color rendering
789 ensure_default_palette_ready();
790 double start_neon_color = get_time_seconds();
791 for (int i = 0; i < iterations; i++) {
792 char *result_str = render_ascii_neon_unified_optimized(frame, background_mode, use_256color, DEFAULT_ASCII_PALETTE);
793 if (result_str)
794 SAFE_FREE(result_str);
795 }
796 result.neon_time = (get_time_seconds() - start_neon_color) / iterations;
797#endif
798
799#if SIMD_SUPPORT_SVE
800 // Benchmark SVE using unified optimized renderer
801 // Benchmark SVE color rendering
802 ensure_default_palette_ready();
803 double start_sve_color = get_time_seconds();
804 for (int i = 0; i < iterations; i++) {
805 char *result_str = render_ascii_sve_unified_optimized(frame, background_mode, use_256color, DEFAULT_ASCII_PALETTE);
806 if (result_str)
807 SAFE_FREE(result_str);
808 }
809 result.sve_time = (get_time_seconds() - start_sve_color) / iterations;
810#endif
811
812 // Find best method
813 double best_time = result.scalar_time;
814 result.best_method = "scalar";
815
816#if SIMD_SUPPORT_SSE2
817 if (result.sse2_time > 0 && result.sse2_time < best_time) {
818 best_time = result.sse2_time;
819 result.best_method = "SSE2";
820 }
821#endif
822
823#if SIMD_SUPPORT_SSSE3
824 if (result.ssse3_time > 0 && result.ssse3_time < best_time) {
825 best_time = result.ssse3_time;
826 result.best_method = "SSSE3";
827 }
828#endif
829
830#if SIMD_SUPPORT_AVX2
831 if (result.avx2_time > 0 && result.avx2_time < best_time) {
832 best_time = result.avx2_time;
833 result.best_method = "AVX2";
834 }
835#endif
836
837#if SIMD_SUPPORT_NEON
838 if (result.neon_time > 0 && result.neon_time < best_time) {
839 best_time = result.neon_time;
840 result.best_method = "NEON";
841 }
842#endif
843
844 result.speedup_best = result.scalar_time / best_time;
845
846#if SIMD_SUPPORT_SVE
847 if (result.sve_time > 0 && result.sve_time < best_time) {
848 best_time = result.sve_time;
849 result.best_method = "SVE";
850 }
851#endif
852
853 image_destroy(frame);
854 SAFE_FREE(test_pixels);
855 SAFE_FREE(output_buffer);
856
857 return result;
858}

References ASCIICHAT_OK, simd_benchmark_t::avx2_time, simd_benchmark_t::best_method, DEFAULT_ASCII_PALETTE, image_t::h, image_destroy(), image_new(), image_print_color(), log_error, simd_benchmark_t::neon_time, image_t::pixels, SAFE_CALLOC_SIMD, SAFE_FREE, SAFE_MALLOC, simd_benchmark_t::scalar_time, simd_benchmark_t::speedup_best, simd_benchmark_t::sse2_time, simd_benchmark_t::ssse3_time, simd_benchmark_t::sve_time, and image_t::w.

◆ build_ramp64()

void build_ramp64 ( uint8_t  ramp64[64],
const char *  ascii_chars 
)

#include <common.h>

Build 64-entry character ramp cache.

Parameters
ramp64Output array for ramp indices
ascii_charsCharacter palette string

Definition at line 31 of file video/simd/common.c.

31 {
32 if (!ascii_chars) {
33 // Fallback to space character
34 for (int i = 0; i < RAMP64_SIZE; i++) {
35 ramp64[i] = ' ';
36 }
37 return;
38 }
39
40 // Use UTF-8 palette functions for proper character handling
41 utf8_palette_t *utf8_pal = utf8_palette_create(ascii_chars);
42 if (!utf8_pal) {
43 // Fallback to space character
44 for (int i = 0; i < RAMP64_SIZE; i++) {
45 ramp64[i] = ' ';
46 }
47 return;
48 }
49
50 size_t char_count = utf8_palette_get_char_count(utf8_pal);
51 if (char_count == 0) {
52 // No valid characters found, use space
53 for (int i = 0; i < RAMP64_SIZE; i++) {
54 ramp64[i] = ' ';
55 }
56 utf8_palette_destroy(utf8_pal);
57 return;
58 }
59
60 // Build the ramp64 lookup using UTF-8 character indices
61 for (int i = 0; i < RAMP64_SIZE; i++) {
62 // Map 0-63 to 0-(char_count-1) using proper character indexing
63 size_t char_idx = (i * (char_count - 1) + (RAMP64_SIZE - 1) / 2) / (RAMP64_SIZE - 1);
64 if (char_idx >= char_count) {
65 char_idx = char_count - 1;
66 }
67
68 // Get the first byte of the character at this character index
69 const utf8_char_info_t *char_info = utf8_palette_get_char(utf8_pal, char_idx);
70 if (char_info) {
71 ramp64[i] = (uint8_t)char_info->bytes[0];
72 } else {
73 ramp64[i] = ' '; // Fallback
74 }
75 }
76
77 utf8_palette_destroy(utf8_pal);
78}
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_get_char_count(const utf8_palette_t *palette)
Get number of characters in UTF-8 palette.
Definition palette.c:534
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
utf8_palette_t * utf8_palette_create(const char *palette_string)
Create a UTF-8 palette from string.
Definition palette.c:390
#define RAMP64_SIZE
Ramp64 cache size (64 entries)
UTF-8 character information structure.
Definition palette.h:211
UTF-8 palette structure.
Definition palette.h:234

References utf8_char_info_t::bytes, RAMP64_SIZE, utf8_palette_create(), utf8_palette_destroy(), utf8_palette_get_char(), and utf8_palette_get_char_count().

◆ build_utf8_luminance_cache()

void build_utf8_luminance_cache ( const char *  ascii_chars,
utf8_char_t  cache[256] 
)

#include <common.h>

Build UTF-8 luminance cache.

Parameters
ascii_charsCharacter palette string
cacheOutput cache array (256 entries)

Definition at line 410 of file video/simd/common.c.

410 {
411 if (!ascii_chars || !cache)
412 return;
413
414 // Parse characters
415 typedef struct {
416 const char *start;
417 int byte_len;
418 } char_info_t;
419
420 char_info_t char_infos[256];
421 int char_count = 0;
422 const char *p = ascii_chars;
423
424 while (*p && char_count < 255) {
425 char_infos[char_count].start = p;
426
427 if ((*p & 0xE0) == 0xC0) {
428 char_infos[char_count].byte_len = 2;
429 p += 2;
430 } else if ((*p & 0xF0) == 0xE0) {
431 char_infos[char_count].byte_len = 3;
432 p += 3;
433 } else if ((*p & 0xF8) == 0xF0) {
434 char_infos[char_count].byte_len = 4;
435 p += 4;
436 } else {
437 // ASCII characters (0x00-0x7F) and invalid sequences: treat as single byte
438 char_infos[char_count].byte_len = 1;
439 p++;
440 }
441 char_count++;
442 }
443
444 // Handle empty string case
445 if (char_count == 0)
446 return;
447
448 // Build 256-entry cache
449 for (int i = 0; i < 256; i++) {
450 int char_idx = char_count > 1 ? (i * (char_count - 1) + 127) / 255 : 0;
451 if (char_idx >= char_count)
452 char_idx = char_count - 1;
453
454 cache[i].byte_len = char_infos[char_idx].byte_len;
455 memcpy(cache[i].utf8_bytes, char_infos[char_idx].start, char_infos[char_idx].byte_len);
456 if (cache[i].byte_len < 4) {
457 cache[i].utf8_bytes[cache[i].byte_len] = '\0';
458 }
459 }
460}

References utf8_char_t::byte_len, and utf8_char_t::utf8_bytes.

◆ build_utf8_ramp64_cache()

void build_utf8_ramp64_cache ( const char *  ascii_chars,
utf8_char_t  cache64[64],
uint8_t  char_index_ramp[64] 
)

#include <common.h>

Build UTF-8 ramp64 cache.

Parameters
ascii_charsCharacter palette string
cache64Output cache array (64 entries)
char_index_rampOutput character index ramp (64 entries)

Definition at line 463 of file video/simd/common.c.

463 {
464 if (!ascii_chars || !cache64 || !char_index_ramp)
465 return;
466
467 // Reuse the luminance cache building logic but for 64 entries
468 // (Same UTF-8 parsing as above, but map to 64 entries instead of 256)
469
470 // Parse characters (same as above)
471 typedef struct {
472 const char *start;
473 int byte_len;
474 } char_info_t;
475
476 char_info_t char_infos[256];
477 int char_count = 0;
478 const char *p = ascii_chars;
479
480 while (*p && char_count < 255) {
481 char_infos[char_count].start = p;
482
483 if ((*p & 0xE0) == 0xC0) {
484 char_infos[char_count].byte_len = 2;
485 p += 2;
486 } else if ((*p & 0xF0) == 0xE0) {
487 char_infos[char_count].byte_len = 3;
488 p += 3;
489 } else if ((*p & 0xF8) == 0xF0) {
490 char_infos[char_count].byte_len = 4;
491 p += 4;
492 } else {
493 // ASCII characters (0x00-0x7F) and invalid sequences: treat as single byte
494 char_infos[char_count].byte_len = 1;
495 p++;
496 }
497 char_count++;
498 }
499
500 // Handle empty string case
501 if (char_count == 0)
502 return;
503
504 // Build 64-entry cache and index ramp
505 for (int i = 0; i < 64; i++) {
506 int char_idx = char_count > 1 ? (i * (char_count - 1) + 31) / 63 : 0;
507 if (char_idx >= char_count)
508 char_idx = char_count - 1;
509
510 // Store character index for SIMD lookup
511 char_index_ramp[i] = (uint8_t)char_idx;
512
513 // Cache UTF-8 character
514 cache64[i].byte_len = char_infos[char_idx].byte_len;
515 memcpy(cache64[i].utf8_bytes, char_infos[char_idx].start, char_infos[char_idx].byte_len);
516 if (cache64[i].byte_len < 4) {
517 cache64[i].utf8_bytes[cache64[i].byte_len] = '\0';
518 }
519 }
520}

References utf8_char_t::byte_len, and utf8_char_t::utf8_bytes.

◆ calculate_cache_eviction_score()

double calculate_cache_eviction_score ( uint64_t  last_access_time,
uint32_t  access_count,
uint64_t  creation_time,
uint64_t  current_time 
)

#include <common.h>

Calculate cache eviction score.

Parameters
last_access_timeLast access timestamp (nanoseconds)
access_countTotal access count
creation_timeCache creation timestamp (nanoseconds)
current_timeCurrent timestamp (nanoseconds)
Returns
Eviction score (lower = more likely to evict)

Definition at line 87 of file video/simd/common.c.

88 {
89 // Protect against unsigned underflow if times are inconsistent (clock adjustments, etc.)
90 uint64_t age_ns = (current_time >= last_access_time) ? (current_time - last_access_time) : 0;
91 uint64_t total_age_ns = (current_time >= creation_time) ? (current_time - creation_time) : 0;
92
93 uint64_t age_seconds = age_ns / 1000000000ULL;
94 uint64_t total_age_seconds = total_age_ns / 1000000000ULL;
95
96 // Frequency factor: high-use palettes get protection (logarithmic scaling)
97 double frequency_factor = 1.0 + log10(1.0 + access_count);
98
99 // Aging factor: frequency bonus decays over time (5-minute half-life)
100 double aging_factor = exp(-(double)age_seconds / CACHE_FREQUENCY_DECAY_TIME);
101
102 // Recent access bonus: strong protection for recently used (1-minute protection)
103 double recency_bonus = exp(-(double)age_seconds / CACHE_RECENCY_SCALE);
104
105 // Cache lifetime penalty: prevent immortal caches (1-hour max lifetime)
106 double lifetime_penalty = total_age_seconds > CACHE_MAX_LIFETIME ? 0.5 : 1.0;
107
108 // Final score: higher = keep longer
109 return (frequency_factor * aging_factor + recency_bonus) * lifetime_penalty;
110}
#define CACHE_RECENCY_SCALE
Recency importance scale (1 minute in seconds)
#define CACHE_FREQUENCY_DECAY_TIME
Frequency bonus decay time (5 minutes in seconds)
#define CACHE_MAX_LIFETIME
Maximum cache lifetime (1 hour in seconds)

References CACHE_FREQUENCY_DECAY_TIME, CACHE_MAX_LIFETIME, and CACHE_RECENCY_SCALE.

Referenced by __attribute__().

◆ convert_pixels_scalar()

void convert_pixels_scalar ( const rgb_pixel_t *  pixels,
char *  ascii_chars,
int  count,
const char  luminance_palette[256] 
)

#include <ascii_simd.h>

Convert pixels to ASCII (scalar fallback)

Parameters
pixelsRGB pixel array
ascii_charsOutput buffer for ASCII characters
countNumber of pixels to convert
luminance_paletteLuminance-to-character mapping palette

Definition at line 167 of file ascii_simd.c.

167 {
168 for (int i = 0; i < count; i++) {
169 const rgb_pixel_t *p = &pixels[i];
170
171 // Calculate luminance using integer arithmetic
172 int luminance = (LUMA_RED * p->r + LUMA_GREEN * p->g + LUMA_BLUE * p->b) >> 8;
173
174 // Clamp to [0, 255]
175 if (luminance > 255)
176 luminance = 255;
177
178 ascii_chars[i] = luminance_palette[luminance];
179 }
180}
#define LUMA_BLUE
Luminance blue coefficient (0.114 * 256 = 29)
Definition ascii_simd.h:76
#define LUMA_GREEN
Luminance green coefficient (0.587 * 256 = 150)
Definition ascii_simd.h:74
#define LUMA_RED
Luminance red coefficient (0.299 * 256 = 77)
Definition ascii_simd.h:72

References LUMA_BLUE, LUMA_GREEN, and LUMA_RED.

◆ convert_pixels_scalar_with_newlines()

char * convert_pixels_scalar_with_newlines ( image_t image,
const char  luminance_palette[256] 
)

#include <ascii_simd.h>

Convert image to ASCII with newlines (scalar fallback)

Parameters
imageSource image
luminance_paletteLuminance-to-character mapping palette
Returns
Allocated ASCII string (caller must free), or NULL on error

Definition at line 182 of file ascii_simd.c.

182 {
183 const int h = image->h;
184 const int w = image->w;
185
186 // Get UTF-8 character cache for RLE emission
187 // Note: We need to reverse-engineer the palette chars from luminance_palette
188 // For now, use a simpler approach with direct luminance lookup
189
190 // Use outbuf_t for efficient UTF-8 RLE emission (same as SIMD renderers)
191 outbuf_t ob = {0};
192 const size_t max_char_bytes = 4; // Max UTF-8 character size
193 ob.cap = (size_t)h * ((size_t)w * max_char_bytes + 1);
194 ob.buf = SAFE_MALLOC(ob.cap ? ob.cap : 1, char *);
195 if (!ob.buf) {
196 log_error("Failed to allocate output buffer for scalar rendering");
197 return NULL;
198 }
199
200 // Process pixels with RLE optimization
201 for (int y = 0; y < h; y++) {
202 const rgb_pixel_t *row_pixels = (const rgb_pixel_t *)&image->pixels[y * w];
203
204 for (int x = 0; x < w;) {
205 const rgb_pixel_t *p = &row_pixels[x];
206
207 // Calculate luminance using integer arithmetic
208 int luminance = (LUMA_RED * p->r + LUMA_GREEN * p->g + LUMA_BLUE * p->b) >> 8;
209 if (luminance > 255)
210 luminance = 255;
211
212 char current_char = luminance_palette[luminance];
213
214 // Find run length for same character (RLE optimization)
215 int j = x + 1;
216 while (j < w) {
217 const rgb_pixel_t *next_p = &row_pixels[j];
218 int next_luminance = (LUMA_RED * next_p->r + LUMA_GREEN * next_p->g + LUMA_BLUE * next_p->b) >> 8;
219 if (next_luminance > 255)
220 next_luminance = 255;
221 char next_char = luminance_palette[next_luminance];
222 if (next_char != current_char)
223 break;
224 j++;
225 }
226 uint32_t run = (uint32_t)(j - x);
227
228 // Emit character with RLE (same as SIMD)
229 ob_putc(&ob, current_char);
230 if (rep_is_profitable(run)) {
231 emit_rep(&ob, run - 1);
232 } else {
233 for (uint32_t k = 1; k < run; k++) {
234 ob_putc(&ob, current_char);
235 }
236 }
237 x = j;
238 }
239
240 // Add newline (except for last row)
241 if (y != h - 1) {
242 ob_putc(&ob, '\n');
243 }
244 }
245
246 ob_term(&ob);
247 return ob.buf;
248}
size_t cap
Buffer capacity in bytes (maximum length before reallocation)

References outbuf_t::buf, outbuf_t::cap, emit_rep(), image_t::h, log_error, LUMA_BLUE, LUMA_GREEN, LUMA_RED, ob_putc(), ob_term(), image_t::pixels, rep_is_profitable(), SAFE_MALLOC, and image_t::w.

◆ emit_rep()

void emit_rep ( outbuf_t ob,
uint32_t  extra 
)

#include <output_buffer.h>

Emit run-length encoded sequence.

Parameters
obOutput buffer structure (must not be NULL)
extraAdditional characters to repeat (must be > 0)

Emits an ANSI run-length encoding sequence that repeats the last character in the buffer 'extra' times. Format: ESC[extram (where 'extra' is the repeat count and the last character is repeated).

Note
This function repeats the character at buffer[len-1].
Useful for compressing repeated characters (spaces, blocks, etc.).
Only profitable for runs exceeding threshold (see rep_is_profitable()).
Example
ob_putc(ob, ' '); // Append space
emit_rep(ob, 49); // Repeat space 49 more times (total 50)
}

Definition at line 149 of file output_buffer.c.

149 {
150 if (!ob)
151 return;
152 // ESC [ extra b
153 ob_putc(ob, 0x1b);
154 ob_putc(ob, '[');
155 ob_u32(ob, extra);
156 ob_putc(ob, 'b');
157}
void ob_u32(outbuf_t *ob, uint32_t v)
Append unsigned 32-bit integer as decimal string.

References ob_putc(), and ob_u32().

Referenced by ansi_compress_rle(), convert_pixels_scalar_with_newlines(), and image_print().

◆ emit_reset()

void emit_reset ( outbuf_t ob)

#include <output_buffer.h>

Emit ANSI reset sequence.

Parameters
obOutput buffer structure (must not be NULL)

Emits ANSI escape sequence to reset all terminal attributes to defaults. Format: ESC[0m or ESC[m. Resets colors, bold, underline, and other formatting attributes.

Note
This resets both foreground and background colors to default.
Sequence length: 3 bytes (ESC[0m).
Useful for resetting terminal state between frames.

Definition at line 131 of file output_buffer.c.

131 {
132 if (!ob)
133 return;
134 ob_putc(ob, 0x1b);
135 ob_putc(ob, '[');
136 ob_putc(ob, '0');
137 ob_putc(ob, 'm');
138}

References ob_putc().

◆ emit_set_256_color_bg()

void emit_set_256_color_bg ( outbuf_t ob,
uint8_t  color_idx 
)

#include <output_buffer.h>

Emit 256-color background ANSI sequence.

Parameters
obOutput buffer structure (must not be NULL)
color_idx256-color palette index (0-255)

Emits ANSI escape sequence for 256-color background color. Format: ESC[48;5;idxm (where idx is palette index 0-255). Uses extended ANSI color palette for 256-color mode.

Note
Requires terminal 256-color support for proper display.
Sequence length: ~9 bytes (ESC[48;5;255m).
Palette indices 0-15 are standard ANSI colors.
Palette indices 16-255 are extended palette.

Definition at line 172 of file output_buffer.c.

172 {
173 if (!ob)
174 return;
175 // ESC[48;5;Nm
176 ob_putc(ob, 0x1b);
177 ob_putc(ob, '[');
178 ob_write(ob, "48;5;", 5);
179 ob_u8(ob, color_idx);
180 ob_putc(ob, 'm');
181}
void ob_u8(outbuf_t *ob, uint8_t v)
Append unsigned 8-bit integer as decimal string.

References ob_putc(), ob_u8(), and ob_write().

◆ emit_set_256_color_fg()

void emit_set_256_color_fg ( outbuf_t ob,
uint8_t  color_idx 
)

#include <output_buffer.h>

Emit 256-color foreground ANSI sequence.

Parameters
obOutput buffer structure (must not be NULL)
color_idx256-color palette index (0-255)

Emits ANSI escape sequence for 256-color foreground color. Format: ESC[38;5;idxm (where idx is palette index 0-255). Uses extended ANSI color palette for 256-color mode.

Note
Requires terminal 256-color support for proper display.
Sequence length: ~9 bytes (ESC[38;5;255m).
Palette indices 0-15 are standard ANSI colors.
Palette indices 16-255 are extended palette.

Definition at line 160 of file output_buffer.c.

160 {
161 if (!ob)
162 return;
163 // ESC[38;5;Nm
164 ob_putc(ob, 0x1b);
165 ob_putc(ob, '[');
166 ob_write(ob, "38;5;", 5);
167 ob_u8(ob, color_idx);
168 ob_putc(ob, 'm');
169}

References ob_putc(), ob_u8(), and ob_write().

◆ emit_set_bg()

void emit_set_bg ( outbuf_t ob,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)

#include <output_buffer.h>

Emit background color sequence (auto-select mode)

Parameters
obOutput buffer structure (must not be NULL)
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)

Emits background color ANSI sequence using the optimal color mode based on terminal capabilities. Automatically selects:

  • Truecolor mode (if supported): 24-bit RGB
  • 256-color mode (if supported): Quantized to 256-color palette
  • 16-color mode (fallback): Quantized to standard ANSI colors
Note
Color mode is selected based on terminal_capabilities_t configuration.
This function uses the best available color mode for the terminal.
Color quantization is applied when truecolor is not available.

Definition at line 198 of file output_buffer.c.

198 {
199 if (!ob)
200 return;
201 // ESC[48;2;R;G;Bm
202 ob_putc(ob, 0x1b);
203 ob_putc(ob, '[');
204 ob_write(ob, "48;2;", 5);
205 ob_u8(ob, r);
206 ob_putc(ob, ';');
207 ob_u8(ob, g);
208 ob_putc(ob, ';');
209 ob_u8(ob, b);
210 ob_putc(ob, 'm');
211}

References ob_putc(), ob_u8(), and ob_write().

◆ emit_set_fg()

void emit_set_fg ( outbuf_t ob,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)

#include <output_buffer.h>

Emit foreground color sequence (auto-select mode)

Parameters
obOutput buffer structure (must not be NULL)
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)

Emits foreground color ANSI sequence using the optimal color mode based on terminal capabilities. Automatically selects:

  • Truecolor mode (if supported): 24-bit RGB
  • 256-color mode (if supported): Quantized to 256-color palette
  • 16-color mode (fallback): Quantized to standard ANSI colors
Note
Color mode is selected based on terminal_capabilities_t configuration.
This function uses the best available color mode for the terminal.
Color quantization is applied when truecolor is not available.

Definition at line 183 of file output_buffer.c.

183 {
184 if (!ob)
185 return;
186 // ESC[38;2;R;G;Bm
187 ob_putc(ob, 0x1b);
188 ob_putc(ob, '[');
189 ob_write(ob, "38;2;", 5);
190 ob_u8(ob, r);
191 ob_putc(ob, ';');
192 ob_u8(ob, g);
193 ob_putc(ob, ';');
194 ob_u8(ob, b);
195 ob_putc(ob, 'm');
196}

References ob_putc(), ob_u8(), and ob_write().

◆ emit_set_truecolor_bg()

void emit_set_truecolor_bg ( outbuf_t ob,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)

#include <output_buffer.h>

Emit truecolor background ANSI sequence.

Parameters
obOutput buffer structure (must not be NULL)
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)

Emits ANSI escape sequence for 24-bit truecolor background color. Format: ESC[48;2;r;g;bm (where r, g, b are RGB values). Uses 24-bit RGB color values for optimal color accuracy.

Note
Requires terminal truecolor support for proper display.
Sequence length: ~15 bytes (ESC[48;2;255;255;255m).

Definition at line 116 of file output_buffer.c.

116 {
117 if (!ob)
118 return;
119 // ESC[48;2;R;G;Bm
120 ob_putc(ob, 0x1b);
121 ob_putc(ob, '[');
122 ob_write(ob, "48;2;", 5);
123 ob_u8(ob, r);
124 ob_putc(ob, ';');
125 ob_u8(ob, g);
126 ob_putc(ob, ';');
127 ob_u8(ob, b);
128 ob_putc(ob, 'm');
129}

References ob_putc(), ob_u8(), and ob_write().

◆ emit_set_truecolor_fg()

void emit_set_truecolor_fg ( outbuf_t ob,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)

#include <output_buffer.h>

Emit truecolor foreground ANSI sequence.

Parameters
obOutput buffer structure (must not be NULL)
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)

Emits ANSI escape sequence for 24-bit truecolor foreground color. Format: ESC[38;2;r;g;bm (where r, g, b are RGB values). Uses 24-bit RGB color values for optimal color accuracy.

Note
Requires terminal truecolor support for proper display.
Sequence length: ~15 bytes (ESC[38;2;255;255;255m).

Definition at line 100 of file output_buffer.c.

100 {
101 if (!ob)
102 return;
103 // ESC[38;2;R;G;Bm
104 ob_putc(ob, 0x1b);
105 ob_putc(ob, '[');
106 ob_write(ob, "38;2;", 5);
107 ob_u8(ob, r);
108 ob_putc(ob, ';');
109 ob_u8(ob, g);
110 ob_putc(ob, ';');
111 ob_u8(ob, b);
112 ob_putc(ob, 'm');
113}

References ob_putc(), ob_u8(), and ob_write().

◆ get_16color_rgb()

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

#include <ansi_fast.h>

Get the actual RGB values for a 16-color ANSI index.

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

Definition at line 320 of file ansi_fast.c.

320 {
321 // Same color table as rgb_to_16color()
322 static const uint8_t ansi_colors[16][3] = {
323 {0, 0, 0}, // 0: Black
324 {128, 0, 0}, // 1: Dark Red
325 {0, 128, 0}, // 2: Dark Green
326 {128, 128, 0}, // 3: Dark Yellow (Brown)
327 {0, 0, 128}, // 4: Dark Blue
328 {128, 0, 128}, // 5: Dark Magenta
329 {0, 128, 128}, // 6: Dark Cyan
330 {192, 192, 192}, // 7: Light Gray
331 {128, 128, 128}, // 8: Dark Gray
332 {255, 0, 0}, // 9: Bright Red
333 {0, 255, 0}, // 10: Bright Green
334 {255, 255, 0}, // 11: Bright Yellow
335 {0, 0, 255}, // 12: Bright Blue
336 {255, 0, 255}, // 13: Bright Magenta
337 {0, 255, 255}, // 14: Bright Cyan
338 {255, 255, 255} // 15: White
339 };
340
341 if (color_index >= 16) {
342 color_index = 7; // Default to light gray
343 }
344
345 *r = ansi_colors[color_index][0];
346 *g = ansi_colors[color_index][1];
347 *b = ansi_colors[color_index][2];
348}

Referenced by image_print_16color_dithered_with_background(), and rgb_to_16color_dithered().

◆ get_256_color_fast_path()

bool get_256_color_fast_path ( void  )

#include <ascii_simd.h>

Get 256-color fast path setting.

Returns
true if 256-color mode is enabled, false for truecolor

◆ get_current_time_ns()

uint64_t get_current_time_ns ( void  )

#include <common.h>

Get current time in nanoseconds.

Returns
Current timestamp in nanoseconds since epoch

Definition at line 81 of file video/simd/common.c.

81 {
82 struct timespec now;
83 (void)clock_gettime(CLOCK_MONOTONIC, &now);
84 return now.tv_sec * 1000000000ULL + now.tv_nsec;
85}

Referenced by __attribute__().

◆ get_lum_palette()

char * get_lum_palette ( void  )

#include <ascii.h>

Get luminance palette for character mapping.

Returns
Pointer to luminance palette array (256 elements, do not free)

Returns the default luminance-to-character mapping palette. The palette contains 256 characters ordered by luminance (dark to light) for mapping pixel brightness values (0-255) to ASCII characters.

Note
Returns pointer to static array (do not free).
Palette has 256 elements (one for each luminance level).
Palette is used for monochrome ASCII conversion.

◆ get_sgr256_fg_bg_string()

char * get_sgr256_fg_bg_string ( uint8_t  fg,
uint8_t  bg,
uint8_t len_out 
)

#include <ascii_simd.h>

Get 256-color foreground/background ANSI sequence string.

Parameters
fgForeground palette index (0-255)
bgBackground palette index (0-255)
len_outOutput parameter for sequence length
Returns
Pointer to ANSI sequence string (cached)

Definition at line 102 of file ascii_simd_color.c.

102 {
103 static __thread char buf[32]; // Thread-local buffer
104 return build_sgr256_fgbg(buf, fg, bg, len_out);
105}

◆ get_sgr256_fg_string()

char * get_sgr256_fg_string ( uint8_t  fg,
uint8_t len_out 
)

#include <ascii_simd.h>

Get 256-color foreground ANSI sequence string.

Parameters
fg256-color palette index (0-255)
len_outOutput parameter for sequence length
Returns
Pointer to ANSI sequence string (cached)

Definition at line 97 of file ascii_simd_color.c.

97 {
98 static __thread char buf[16]; // Thread-local buffer
99 return build_sgr256_fg(buf, fg, len_out);
100}

◆ get_utf8_palette_cache()

utf8_palette_cache_t * get_utf8_palette_cache ( const char *  ascii_chars)

#include <common.h>

Get or create UTF-8 palette cache.

Parameters
ascii_charsCharacter palette string
Returns
Cache pointer (may be shared, do not free)

Thread-safe cache lookup with automatic creation and eviction.

Referenced by image_print(), image_print_16color(), image_print_16color_dithered(), image_print_16color_dithered_with_background(), and image_print_color().

◆ has_avx2_support()

bool has_avx2_support ( void  )

#include <common.h>

Check if AVX2 is supported.

Returns
true if AVX2 is available, false otherwise

◆ has_neon_support()

bool has_neon_support ( void  )

#include <common.h>

Check if NEON is supported.

Returns
true if NEON is available, false otherwise

◆ has_sse2_support()

bool has_sse2_support ( void  )

#include <common.h>

Check if SSE2 is supported.

Returns
true if SSE2 is available, false otherwise

◆ has_ssse3_support()

bool has_ssse3_support ( void  )

#include <common.h>

Check if SSSE3 is supported.

Returns
true if SSSE3 is available, false otherwise

◆ has_sve_support()

bool has_sve_support ( void  )

#include <common.h>

Check if SVE is supported.

Returns
true if SVE is available, false otherwise

◆ image_clear()

void image_clear ( image_t p)

#include <image.h>

Clear image (set all pixels to black)

Parameters
pImage to clear (must not be NULL)

Sets all pixels in the image to black (RGB = 0, 0, 0). Useful for resetting image state before filling with new data.

Note
Image dimensions are not modified (only pixel data is cleared).
This is equivalent to memset() of pixels array to zero.

Definition at line 203 of file video/image.c.

203 {
204 if (!p || !p->pixels) {
205 SET_ERRNO(ERROR_INVALID_PARAM, "image_clear: p or p->pixels is NULL");
206 return;
207 }
208 // CRITICAL: Check for integer overflow before multiplication
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) {
212 SET_ERRNO(ERROR_INVALID_PARAM, "image_clear: dimensions overflow: %d x %d", p->w, p->h);
213 return;
214 }
215 unsigned long pixel_count = w_ul * h_ul;
216 if (pixel_count > ULONG_MAX / sizeof(rgb_pixel_t)) {
217 SET_ERRNO(ERROR_INVALID_PARAM, "image_clear: buffer size overflow");
218 return;
219 }
220 size_t clear_size = pixel_count * sizeof(rgb_pixel_t);
221 SAFE_MEMSET(p->pixels, clear_size, 0, clear_size);
222}

References ERROR_INVALID_PARAM, image_t::h, image_t::pixels, SAFE_MEMSET, SET_ERRNO, and image_t::w.

Referenced by ascii_convert(), and ascii_convert_with_capabilities().

◆ image_destroy()

void image_destroy ( image_t p)

#include <image.h>

Destroy an image allocated with image_new()

Parameters
pImage to destroy (can be NULL, no-op if NULL)

Frees an image structure and its associated pixel data array allocated by image_new(). Frees both the image structure and the pixels array.

Note
Safe to call with NULL pointer (no-op).
Must NOT be used for images allocated with image_new_from_pool().
Use image_destroy_to_pool() for pool-allocated images.

Definition at line 85 of file video/image.c.

85 {
86 if (!p) {
87 SET_ERRNO(ERROR_INVALID_PARAM, "image_destroy: p is NULL");
88 return;
89 }
90
91 // Check allocation method and deallocate appropriately
93 // Pool-allocated images: free entire contiguous buffer (image + pixels)
94 // Validate dimensions before calculating size (guard against corruption)
95 if (p->w <= 0 || p->h <= 0) {
96 SET_ERRNO(ERROR_INVALID_PARAM, "image_destroy: invalid dimensions %dx%d (pool-allocated)", p->w, p->h);
97 return;
98 }
99
100 // Calculate original allocation size for proper buffer pool free
101 size_t w = (size_t)p->w;
102 size_t h = (size_t)p->h;
103 size_t pixels_size;
104 if (checked_size_mul3(w, h, sizeof(rgb_pixel_t), &pixels_size) != ASCIICHAT_OK) {
105 SET_ERRNO(ERROR_INVALID_STATE, "image_destroy: dimensions would overflow: %dx%d", p->w, p->h);
106 return;
107 }
108 size_t total_size;
109 if (checked_size_add(sizeof(image_t), pixels_size, &total_size) != ASCIICHAT_OK) {
110 SET_ERRNO(ERROR_INVALID_STATE, "image_destroy: total size would overflow: %dx%d", p->w, p->h);
111 return;
112 }
113
114 // Free the entire contiguous buffer back to pool
115 buffer_pool_free(NULL, p, total_size);
116 } else {
117 // SIMD-allocated images: free pixels and structure separately
118 // SAFE_MALLOC_SIMD allocates with aligned allocation (posix_memalign on macOS, aligned_alloc on Linux)
119 // These can be freed with standard free() / SAFE_FREE()
120 SAFE_FREE(p->pixels);
121 SAFE_FREE(p);
122 }
123}
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
Free a buffer back to the pool (lock-free)
@ ERROR_INVALID_STATE
uint8_t alloc_method
Allocation method (image_alloc_method_t) for correct deallocation.

References image_t::alloc_method, ASCIICHAT_OK, buffer_pool_free(), ERROR_INVALID_PARAM, ERROR_INVALID_STATE, image_t::h, IMAGE_ALLOC_POOL, image_t::pixels, SAFE_FREE, SET_ERRNO, and image_t::w.

Referenced by ascii_convert(), ascii_convert_with_capabilities(), benchmark_simd_color_conversion(), benchmark_simd_color_conversion_with_source(), benchmark_simd_conversion(), benchmark_simd_conversion_with_source(), and mirror_main().

◆ image_destroy_to_pool()

void image_destroy_to_pool ( image_t image)

#include <image.h>

Destroy an image allocated from buffer pool.

Parameters
imageImage to destroy (can be NULL, no-op if NULL)

Returns an image structure and its pixel data to the buffer pool for reuse. This is faster than standard free() because buffers are reused instead of being deallocated.

Note
Safe to call with NULL pointer (no-op).
Must NOT be used for images allocated with image_new().
Use image_destroy() for standard-allocated images.

Definition at line 171 of file video/image.c.

171 {
172 if (!image) {
173 SET_ERRNO(ERROR_INVALID_PARAM, "image_destroy_to_pool: image is NULL");
174 return;
175 }
176
177 // Validate dimensions before calculating size (guard against corruption)
178 if (image->w <= 0 || image->h <= 0) {
179 SET_ERRNO(ERROR_INVALID_PARAM, "image_destroy_to_pool: invalid dimensions %dx%d", image->w, image->h);
180 return;
181 }
182
183 // Calculate original allocation size for proper buffer pool free
184 // Check for overflow (defensive - should match original allocation)
185 size_t w = (size_t)image->w;
186 size_t h = (size_t)image->h;
187 size_t pixels_size;
188 if (checked_size_mul3(w, h, sizeof(rgb_pixel_t), &pixels_size) != ASCIICHAT_OK) {
189 SET_ERRNO(ERROR_INVALID_STATE, "image_destroy_to_pool: dimensions would overflow: %dx%d", image->w, image->h);
190 return;
191 }
192 size_t total_size;
193 if (checked_size_add(sizeof(image_t), pixels_size, &total_size) != ASCIICHAT_OK) {
194 SET_ERRNO(ERROR_INVALID_STATE, "image_destroy_to_pool: total size would overflow: %dx%d", image->w, image->h);
195 return;
196 }
197
198 // Free the entire contiguous buffer back to pool
199 buffer_pool_free(NULL, image, total_size);
200 // Note: Don't set image = NULL here since caller owns the pointer
201}

References ASCIICHAT_OK, buffer_pool_free(), ERROR_INVALID_PARAM, ERROR_INVALID_STATE, image_t::h, SET_ERRNO, and image_t::w.

Referenced by create_mixed_ascii_frame_for_client().

◆ image_new()

image_t * image_new ( size_t  width,
size_t  height 
)

#include <image.h>

Create a new image with standard allocation.

Parameters
widthImage width in pixels (must be > 0 and <= IMAGE_MAX_WIDTH)
heightImage height in pixels (must be > 0 and <= IMAGE_MAX_HEIGHT)
Returns
Allocated image structure, or NULL on error

Allocates a new image structure and pixel data array using standard malloc() allocation. Image is initialized with specified dimensions and all pixels set to black (0, 0, 0).

Note
Caller must free with image_destroy() when done.
Returns NULL on error (invalid dimensions, memory allocation failure).
Pixel data is allocated separately and freed automatically on destroy.
Example
image_t *img = image_new(1920, 1080);
if (img) {
// Use image...
}

Definition at line 36 of file video/image.c.

36 {
37 image_t *p;
38
39 p = SAFE_MALLOC(sizeof(image_t), image_t *);
40
41 // Validate dimensions are non-zero and within bounds
42 if (image_validate_dimensions(width, height) != ASCIICHAT_OK) {
43 SET_ERRNO(ERROR_INVALID_PARAM, "Image dimensions invalid or too large: %zu x %zu", width, height);
44 SAFE_FREE(p);
45 return NULL;
46 }
47
48 // Calculate pixel count with overflow checking
49 size_t total_pixels;
50 if (checked_size_mul(width, height, &total_pixels) != ASCIICHAT_OK) {
51 SET_ERRNO(ERROR_INVALID_PARAM, "Image dimensions would cause overflow: %zu x %zu", width, height);
52 SAFE_FREE(p);
53 return NULL;
54 }
55
56 // Calculate total pixel buffer size with overflow checking
57 size_t pixels_size;
58 if (checked_size_mul(total_pixels, sizeof(rgb_pixel_t), &pixels_size) != ASCIICHAT_OK) {
59 SET_ERRNO(ERROR_INVALID_PARAM, "Image pixel buffer size would cause overflow");
60 SAFE_FREE(p);
61 return NULL;
62 }
63
64 if (pixels_size > IMAGE_MAX_PIXELS_SIZE) {
65 SET_ERRNO(ERROR_INVALID_PARAM, "Image size exceeds maximum allowed: %zu x %zu (%zu bytes)", width, height,
66 pixels_size);
67 SAFE_FREE(p);
68 return NULL;
69 }
70
71 // Use SIMD-aligned allocation for optimal NEON/AVX performance with vld3q_u8
72 p->pixels = SAFE_MALLOC_SIMD(pixels_size, rgb_pixel_t *);
73 if (!p->pixels) {
74 SET_ERRNO(ERROR_MEMORY, "Failed to allocate image pixels: %zu bytes", pixels_size);
75 SAFE_FREE(p);
76 return NULL;
77 }
78
79 p->w = (int)width;
80 p->h = (int)height;
81 p->alloc_method = IMAGE_ALLOC_SIMD; // Track allocation method for correct deallocation
82 return p;
83}
#define SAFE_MALLOC_SIMD(size, cast)
Definition common.h:308
@ ERROR_MEMORY
Definition error_codes.h:53
#define IMAGE_MAX_PIXELS_SIZE
Maximum pixel data size in bytes.
asciichat_error_t image_validate_dimensions(size_t width, size_t height)
Validate image dimensions (non-zero, within limits)
Definition util/image.c:100

References image_t::alloc_method, ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_MEMORY, image_t::h, IMAGE_ALLOC_SIMD, IMAGE_MAX_PIXELS_SIZE, image_validate_dimensions(), image_t::pixels, SAFE_FREE, SAFE_MALLOC, SAFE_MALLOC_SIMD, SET_ERRNO, and image_t::w.

Referenced by ascii_convert(), ascii_convert_with_capabilities(), benchmark_simd_color_conversion(), benchmark_simd_color_conversion_with_source(), benchmark_simd_conversion(), benchmark_simd_conversion_with_source(), and webcam_read().

◆ image_new_from_pool()

image_t * image_new_from_pool ( size_t  width,
size_t  height 
)

#include <image.h>

Create a new image from buffer pool.

Parameters
widthImage width in pixels (must be > 0 and <= IMAGE_MAX_WIDTH)
heightImage height in pixels (must be > 0 and <= IMAGE_MAX_HEIGHT)
Returns
Allocated image structure from pool, or NULL on error

Allocates a new image structure and pixel data from the buffer pool for efficient memory management in video pipeline. Buffer pool reduces allocation overhead and improves performance for high-frequency operations (video frame capture/processing).

Note
Caller must free with image_destroy_to_pool() when done.
Returns NULL on error (invalid dimensions, pool exhaustion).
Buffer pool allocation is faster than standard malloc().
Example
image_t *img = image_new_from_pool(1920, 1080);
if (img) {
// Process video frame...
}
void image_destroy_to_pool(image_t *image)
Destroy an image allocated from buffer pool.
image_t * image_new_from_pool(size_t width, size_t height)
Create a new image from buffer pool.

Definition at line 126 of file video/image.c.

126 {
127 if (width == 0 || height == 0) {
128 SET_ERRNO(ERROR_INVALID_PARAM, "image_new_from_pool: invalid dimensions %zux%zu", width, height);
129 return NULL;
130 }
131
132 if (width > IMAGE_MAX_WIDTH || height > IMAGE_MAX_HEIGHT) {
133 SET_ERRNO(ERROR_INVALID_PARAM, "image_new_from_pool: dimensions %zux%zu exceed maximum %ux%u", width, height,
135 return NULL;
136 }
137
138 // Calculate total allocation size (structure + pixel data in single buffer)
139 // Check for integer overflow before multiplication
140 size_t pixels_size;
141 if (checked_size_mul3(width, height, sizeof(rgb_pixel_t), &pixels_size) != ASCIICHAT_OK) {
142 SET_ERRNO(ERROR_INVALID_PARAM, "image_new_from_pool: dimensions would overflow: %zux%zu", width, height);
143 return NULL;
144 }
145
146 size_t total_size;
147 if (checked_size_add(sizeof(image_t), pixels_size, &total_size) != ASCIICHAT_OK) {
148 SET_ERRNO(ERROR_INVALID_PARAM, "image_new_from_pool: total size would overflow");
149 return NULL;
150 }
151
152 // Allocate from buffer pool as single contiguous block
153 void *buffer = buffer_pool_alloc(NULL, total_size);
154 if (!buffer) {
155 SET_ERRNO(ERROR_MEMORY, "image_new_from_pool: buffer pool allocation failed for %zu bytes (%zux%zu)", total_size,
156 width, height);
157 return NULL;
158 }
159
160 // Set up image structure at start of buffer
161 image_t *image = (image_t *)buffer;
162 image->w = (int)width;
163 image->h = (int)height;
164 // Pixel data immediately follows the image structure
165 image->pixels = (rgb_pixel_t *)((uint8_t *)buffer + sizeof(image_t));
166 image->alloc_method = IMAGE_ALLOC_POOL; // Track allocation method for correct deallocation
167
168 return image;
169}
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
Allocate a buffer from the pool (lock-free fast path)
#define IMAGE_MAX_WIDTH
Maximum image width (4K resolution)
#define IMAGE_MAX_HEIGHT
Maximum image height (4K resolution)

References image_t::alloc_method, ASCIICHAT_OK, buffer_pool_alloc(), ERROR_INVALID_PARAM, ERROR_MEMORY, image_t::h, IMAGE_ALLOC_POOL, IMAGE_MAX_HEIGHT, IMAGE_MAX_WIDTH, image_t::pixels, SET_ERRNO, and image_t::w.

◆ image_print()

char * image_print ( const image_t p,
const char *  palette 
)

#include <image.h>

Print image as ASCII art (monochrome)

Parameters
pImage to print (must not be NULL)
paletteCharacter palette to use (or NULL for default ASCII palette)
Returns
Allocated ASCII string (caller must free), or NULL on error

Converts an image to ASCII art without color output. Uses character palette to map pixel luminance to ASCII characters. Returns a null-terminated string containing the ASCII art frame.

Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Character palette is used to map luminance to characters.
ASCII output has width*height characters (one character per pixel).

Definition at line 349 of file video/image.c.

349 {
350 if (!p || !palette) {
351 SET_ERRNO(ERROR_INVALID_PARAM, "image_print: p=%p or palette=%p is NULL", p, palette);
352 return NULL;
353 }
354 if (!p->pixels) {
355 SET_ERRNO(ERROR_INVALID_PARAM, "image_print: p->pixels is NULL");
356 return NULL;
357 }
358
359 const int h = p->h;
360 const int w = p->w;
361
362 if (h <= 0 || w <= 0) {
363 SET_ERRNO(ERROR_INVALID_PARAM, "image_print: invalid dimensions h=%d, w=%d", h, w);
364 return NULL;
365 }
366
367 // Get UTF-8 character cache for proper multi-byte character support
368 utf8_palette_cache_t *utf8_cache = get_utf8_palette_cache(palette);
369 if (!utf8_cache) {
370 SET_ERRNO(ERROR_INVALID_STATE, "Failed to get UTF-8 palette cache for scalar rendering");
371 return NULL;
372 }
373
374 // Character index ramp is now part of UTF-8 cache - no separate cache needed
375
376 // Need space for h rows with UTF-8 characters, plus h-1 newlines, plus null terminator
377 const size_t max_char_bytes = 4; // Max UTF-8 character size
378
379 const rgb_pixel_t *pix = p->pixels;
380
381 // Use outbuf_t for efficient UTF-8 RLE emission (same as SIMD renderers)
382 outbuf_t ob = {0};
383
384 // Calculate buffer size with overflow checking
385 size_t w_times_bytes;
386 if (checked_size_mul((size_t)w, max_char_bytes, &w_times_bytes) != ASCIICHAT_OK) {
387 SET_ERRNO(ERROR_INVALID_PARAM, "Buffer size overflow: width too large for UTF-8 encoding");
388 return NULL;
389 }
390
391 size_t w_times_bytes_plus_one;
392 if (checked_size_add(w_times_bytes, 1, &w_times_bytes_plus_one) != ASCIICHAT_OK) {
393 SET_ERRNO(ERROR_INVALID_PARAM, "Buffer size overflow: width * bytes + 1 overflow");
394 return NULL;
395 }
396
397 if (checked_size_mul((size_t)h, w_times_bytes_plus_one, &ob.cap) != ASCIICHAT_OK) {
398 SET_ERRNO(ERROR_INVALID_PARAM, "Buffer size overflow: height * (width * bytes + 1) overflow");
399 return NULL;
400 }
401
402 ob.buf = SAFE_MALLOC(ob.cap ? ob.cap : 1, char *);
403 if (!ob.buf) {
404 SET_ERRNO(ERROR_MEMORY, "Failed to allocate output buffer for scalar rendering");
405 return NULL;
406 }
407
408 // Process pixels with UTF-8 RLE emission (same approach as SIMD)
409 for (int y = 0; y < h; y++) {
410 const int row_offset = y * w;
411
412 for (int x = 0; x < w;) {
413 const rgb_pixel_t pixel = pix[row_offset + x];
414 // Use same luminance formula as SIMD: ITU-R BT.601 with rounding
415 const int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
416
417 // Use same 6-bit precision as SIMD: map luminance (0-255) to bucket (0-63) then to character
418 uint8_t safe_luminance = clamp_rgb(luminance);
419 uint8_t luma_idx = (uint8_t)(safe_luminance >> 2); // 0-63 index (same as SIMD)
420 uint8_t char_idx = utf8_cache->char_index_ramp[luma_idx]; // Map to character index (same as SIMD)
421
422 // Use same 64-entry cache as SIMD for consistency
423 const utf8_char_t *char_info = &utf8_cache->cache64[luma_idx];
424
425 // Find run length for same character (RLE optimization)
426 int j = x + 1;
427 while (j < w) {
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);
431 uint8_t next_luma_idx = (uint8_t)(next_safe_luminance >> 2); // 0-63 index (same as SIMD)
432 uint8_t next_char_idx = utf8_cache->char_index_ramp[next_luma_idx]; // Map to character index (same as SIMD)
433 if (next_char_idx != char_idx)
434 break;
435 j++;
436 }
437 uint32_t run = (uint32_t)(j - x);
438
439 // Emit UTF-8 character with RLE (same as SIMD)
440 ob_write(&ob, char_info->utf8_bytes, char_info->byte_len);
441 if (rep_is_profitable(run)) {
442 emit_rep(&ob, run - 1);
443 } else {
444 for (uint32_t k = 1; k < run; k++) {
445 ob_write(&ob, char_info->utf8_bytes, char_info->byte_len);
446 }
447 }
448 x = j;
449 }
450
451 // Add newline between rows (except last row)
452 if (y != h - 1) {
453 ob_putc(&ob, '\n');
454 }
455 }
456
457 ob_term(&ob);
458 return ob.buf;
459}
utf8_palette_cache_t * get_utf8_palette_cache(const char *ascii_chars)
Get or create UTF-8 palette cache.
UTF-8 character structure.
UTF-8 palette cache structure.

References ASCIICHAT_OK, outbuf_t::buf, utf8_char_t::byte_len, utf8_palette_cache_s::cache64, outbuf_t::cap, utf8_palette_cache_s::char_index_ramp, emit_rep(), ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, get_utf8_palette_cache(), image_t::h, ob_putc(), ob_term(), ob_write(), image_t::pixels, rep_is_profitable(), SAFE_MALLOC, SET_ERRNO, utf8_char_t::utf8_bytes, and image_t::w.

Referenced by ascii_convert(), benchmark_simd_conversion(), image_print_simd(), and image_print_with_capabilities().

◆ image_print_16color()

char * image_print_16color ( const image_t image,
const char *  palette 
)

#include <image.h>

Print image using 16-color ANSI mode.

Parameters
imageImage to print (must not be NULL)
paletteCharacter palette to use (or NULL for default ASCII palette)
Returns
Allocated ASCII string with 16-color ANSI codes (caller must free), or NULL on error

Converts an image to ASCII art using 16-color ANSI mode. Uses standard ANSI color palette (0-15) for color output. Colors are quantized to 16-color palette.

Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Requires terminal 16-color support for proper display.
Colors are quantized to 16-color palette automatically.

Definition at line 739 of file video/image.c.

739 {
740 if (!image || !image->pixels || !palette) {
741 SET_ERRNO(ERROR_INVALID_PARAM, "image=%p or image->pixels=%p or palette=%p is NULL", image, image->pixels, palette);
742 return NULL;
743 }
744
745 int h = image->h;
746 int w = image->w;
747
748 if (h <= 0 || w <= 0) {
749 SET_ERRNO(ERROR_INVALID_STATE, "image_print_16color: invalid dimensions h=%d, w=%d", h, w);
750 return NULL;
751 }
752
753 // Initialize 16-color lookup table
755
756 // Calculate buffer size (smaller than 256-color due to shorter ANSI sequences)
757 // Space for ANSI codes + newlines
758 // Calculate with overflow checking
759 size_t h_times_w;
760 if (checked_size_mul((size_t)h, (size_t)w, &h_times_w) != ASCIICHAT_OK) {
761 return NULL;
762 }
763
764 size_t h_times_w_times_12;
765 if (checked_size_mul(h_times_w, 12u, &h_times_w_times_12) != ASCIICHAT_OK) {
766 return NULL;
767 }
768
769 size_t buffer_size;
770 if (checked_size_add(h_times_w_times_12, (size_t)h, &buffer_size) != ASCIICHAT_OK) {
771 return NULL;
772 }
773
774 char *buffer;
775 buffer = SAFE_MALLOC(buffer_size, char *);
776
777 char *ptr = buffer;
778 const char *reset_code = "\033[0m";
779
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];
783
784 // Convert RGB to 16-color index and generate ANSI sequence
785 uint8_t color_index = rgb_to_16color(pixel.r, pixel.g, pixel.b);
786 ptr = append_16color_fg(ptr, color_index);
787
788 // Use same luminance formula as SIMD: ITU-R BT.601 with rounding
789 int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
790
791 // Use UTF-8 cache which contains character index ramp
792 utf8_palette_cache_t *utf8_cache = get_utf8_palette_cache(palette);
793 if (!utf8_cache) {
794 SET_ERRNO(ERROR_INVALID_STATE, "Failed to get UTF-8 cache");
795 return NULL;
796 }
797
798 // Use same 6-bit precision as SIMD: map luminance (0-255) to bucket (0-63) then to character
799 uint8_t safe_luminance = clamp_rgb(luminance);
800 uint8_t luma_idx = (uint8_t)(safe_luminance >> 2); // 0-63 index (same as SIMD)
801 uint8_t char_idx = utf8_cache->char_index_ramp[luma_idx]; // Map to character index (same as SIMD)
802
803 // Use direct palette character lookup (same as SIMD would do)
804 char ascii_char = palette[char_idx];
805 const utf8_char_t *char_info = &utf8_cache->cache[(unsigned char)ascii_char];
806
807 if (char_info) {
808 // Copy UTF-8 character bytes
809 for (int byte_idx = 0; byte_idx < char_info->byte_len; byte_idx++) {
810 *ptr++ = char_info->utf8_bytes[byte_idx];
811 }
812 } else {
813 // Fallback to simple ASCII if UTF-8 cache fails
814 size_t palette_len = strlen(palette);
815 int palette_index = (luminance * ((int)palette_len - 1) + 127) / 255;
816 *ptr++ = palette[palette_index];
817 }
818 }
819
820 // Add reset and newline at end of each row
821 SAFE_STRNCPY(ptr, reset_code, 5);
822 ptr += strlen(reset_code);
823 if (y < h - 1) {
824 *ptr++ = '\n';
825 }
826 }
827
828 *ptr = '\0';
829 return buffer;
830}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358

References ansi_fast_init_16color(), append_16color_fg(), ASCIICHAT_OK, utf8_char_t::byte_len, utf8_palette_cache_s::cache, utf8_palette_cache_s::char_index_ramp, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, get_utf8_palette_cache(), image_t::h, image_t::pixels, rgb_to_16color(), SAFE_MALLOC, SAFE_STRNCPY, SET_ERRNO, utf8_char_t::utf8_bytes, and image_t::w.

◆ image_print_16color_dithered()

char * image_print_16color_dithered ( const image_t image,
const char *  palette 
)

#include <image.h>

Print image using 16-color ANSI mode with dithering.

Parameters
imageImage to print (must not be NULL)
paletteCharacter palette to use (or NULL for default ASCII palette)
Returns
Allocated ASCII string with dithered 16-color ANSI codes (caller must free), or NULL on error

Converts an image to ASCII art using 16-color ANSI mode with Floyd-Steinberg dithering. Dithering improves color accuracy by distributing quantization errors across neighboring pixels.

DITHERING:

  • Floyd-Steinberg error diffusion algorithm
  • Distributes quantization errors to adjacent pixels
  • Improves perceived color accuracy in 16-color mode
  • Slight performance overhead compared to non-dithered mode
Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Dithering improves color accuracy but may add noise.
Requires terminal 16-color support for proper display.

Definition at line 833 of file video/image.c.

833 {
834 if (!image || !image->pixels || !palette) {
835 SET_ERRNO(ERROR_INVALID_PARAM, "image=%p or image->pixels=%p or palette=%p is NULL", image, image->pixels, palette);
836 return NULL;
837 }
838
839 int h = image->h;
840 int w = image->w;
841
842 if (h <= 0 || w <= 0) {
843 SET_ERRNO(ERROR_INVALID_STATE, "image_print_16color_dithered: invalid dimensions h=%d, w=%d", h, w);
844 return NULL;
845 }
846
847 // Initialize 16-color lookup table
849
850 // Allocate error buffer for Floyd-Steinberg dithering
851 size_t pixel_count = (size_t)h * (size_t)w;
852 rgb_error_t *error_buffer;
853 error_buffer = SAFE_CALLOC(pixel_count, sizeof(rgb_error_t), rgb_error_t *);
854
855 // Calculate buffer size (same as non-dithered version)
856 // Space for ANSI codes + newlines
857 // Calculate with overflow checking
858 size_t h_times_w2;
859 if (checked_size_mul((size_t)h, (size_t)w, &h_times_w2) != ASCIICHAT_OK) {
860 SAFE_FREE(error_buffer);
861 return NULL;
862 }
863
864 size_t h_times_w2_times_12;
865 if (checked_size_mul(h_times_w2, 12u, &h_times_w2_times_12) != ASCIICHAT_OK) {
866 SAFE_FREE(error_buffer);
867 return NULL;
868 }
869
870 size_t buffer_size;
871 if (checked_size_add(h_times_w2_times_12, (size_t)h, &buffer_size) != ASCIICHAT_OK) {
872 SAFE_FREE(error_buffer);
873 return NULL;
874 }
875
876 char *buffer;
877 buffer = SAFE_MALLOC(buffer_size, char *);
878
879 char *ptr = buffer;
880 const char *reset_code = "\033[0m";
881
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];
885
886 // Convert RGB to 16-color index using dithering
887 uint8_t color_index = rgb_to_16color_dithered(pixel.r, pixel.g, pixel.b, x, y, w, h, error_buffer);
888 ptr = append_16color_fg(ptr, color_index);
889
890 // Use same luminance formula as SIMD: ITU-R BT.601 with rounding
891 int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
892
893 // Use UTF-8 cache which contains character index ramp
894 utf8_palette_cache_t *utf8_cache = get_utf8_palette_cache(palette);
895 if (!utf8_cache) {
896 SET_ERRNO(ERROR_INVALID_STATE, "Failed to get UTF-8 cache");
897 return NULL;
898 }
899
900 // Use same 6-bit precision as SIMD: map luminance (0-255) to bucket (0-63) then to character
901 uint8_t safe_luminance = clamp_rgb(luminance);
902 uint8_t luma_idx = (uint8_t)(safe_luminance >> 2); // 0-63 index (same as SIMD)
903 uint8_t char_idx = utf8_cache->char_index_ramp[luma_idx]; // Map to character index (same as SIMD)
904
905 // Use direct palette character lookup (same as SIMD would do)
906 char ascii_char = palette[char_idx];
907 const utf8_char_t *char_info = &utf8_cache->cache[(unsigned char)ascii_char];
908
909 if (char_info) {
910 // Copy UTF-8 character bytes
911 for (int byte_idx = 0; byte_idx < char_info->byte_len; byte_idx++) {
912 *ptr++ = char_info->utf8_bytes[byte_idx];
913 }
914 } else {
915 // Fallback to simple ASCII if UTF-8 cache fails
916 size_t palette_len = strlen(palette);
917 int palette_index = (luminance * ((int)palette_len - 1) + 127) / 255;
918 *ptr++ = palette[palette_index];
919 }
920 }
921
922 // Add reset and newline at end of each row
923 SAFE_STRNCPY(ptr, reset_code, 5);
924 ptr += strlen(reset_code);
925 if (y < h - 1) {
926 *ptr++ = '\n';
927 }
928 }
929
930 *ptr = '\0';
931 SAFE_FREE(error_buffer); // Clean up error buffer
932 return buffer;
933}
#define SAFE_CALLOC(count, size, cast)
Definition common.h:218
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.
Definition ansi_fast.c:351
RGB error structure for dithering.
Definition ansi_fast.h:234

References ansi_fast_init_16color(), append_16color_fg(), ASCIICHAT_OK, utf8_char_t::byte_len, utf8_palette_cache_s::cache, utf8_palette_cache_s::char_index_ramp, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, get_utf8_palette_cache(), image_t::h, image_t::pixels, rgb_to_16color_dithered(), SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, SAFE_STRNCPY, SET_ERRNO, utf8_char_t::utf8_bytes, and image_t::w.

◆ image_print_16color_dithered_with_background()

char * image_print_16color_dithered_with_background ( const image_t image,
bool  use_background,
const char *  palette 
)

#include <image.h>

Print image using 16-color ANSI mode with dithering and background colors.

Parameters
imageImage to print (must not be NULL)
use_backgroundWhether to use background colors (true) or foreground colors only (false)
paletteCharacter palette to use (or NULL for default ASCII palette)
Returns
Allocated ASCII string with dithered 16-color ANSI codes (caller must free), or NULL on error

Converts an image to ASCII art using 16-color ANSI mode with Floyd-Steinberg dithering and optional background color support. Background colors provide better color coverage for block-style rendering.

RENDERING MODES:

  • Foreground colors: Character color is set (text color)
  • Background colors: Character background is set (block color)
  • Background mode provides better color coverage for block characters
Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Background colors require terminal background color support.
Dithering improves color accuracy but may add noise.

Definition at line 936 of file video/image.c.

936 {
937 if (!image || !image->pixels || !palette) {
938 SET_ERRNO(ERROR_INVALID_PARAM, "image=%p or image->pixels=%p or palette=%p is NULL", image, image->pixels, palette);
939 return NULL;
940 }
941
942 int h = image->h;
943 int w = image->w;
944
945 if (h <= 0 || w <= 0) {
946 SET_ERRNO(ERROR_INVALID_STATE, "image_print_16color_dithered_with_background: invalid dimensions h=%d, w=%d", h, w);
947 return NULL;
948 }
949
950 // Initialize 16-color lookup table
952
953 // Allocate error buffer for Floyd-Steinberg dithering
954 size_t pixel_count = (size_t)h * (size_t)w;
955 rgb_error_t *error_buffer;
956 error_buffer = SAFE_CALLOC(pixel_count, sizeof(rgb_error_t), rgb_error_t *);
957
958 // Calculate buffer size (larger for background mode due to more ANSI sequences)
959 size_t buffer_size =
960 (size_t)h * (size_t)w * (use_background ? 24 : 12) + (size_t)h; // Space for ANSI codes + newlines
961 char *buffer;
962 buffer = SAFE_MALLOC(buffer_size, char *);
963
964 char *ptr = buffer;
965 const char *reset_code = "\033[0m";
966
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];
970
971 // Convert RGB to 16-color index using dithering
972 uint8_t color_index = rgb_to_16color_dithered(pixel.r, pixel.g, pixel.b, x, y, w, h, error_buffer);
973
974 if (use_background) {
975 // Background mode: use contrasting foreground on background color
976 uint8_t bg_r, bg_g, bg_b;
977 get_16color_rgb(color_index, &bg_r, &bg_g, &bg_b);
978
979 // Calculate luminance to choose contrasting foreground
980 int bg_luminance = (bg_r * 77 + bg_g * 150 + bg_b * 29) / 256;
981 uint8_t fg_color = (bg_luminance < 127) ? 15 : 0; // White on dark, black on bright
982
983 ptr = append_16color_bg(ptr, color_index); // Set background to pixel color
984 ptr = append_16color_fg(ptr, fg_color); // Set contrasting foreground
985 } else {
986 // Foreground mode: set foreground to pixel color
987 ptr = append_16color_fg(ptr, color_index);
988 }
989
990 // Use same luminance formula as SIMD: ITU-R BT.601 with rounding
991 int luminance = (77 * pixel.r + 150 * pixel.g + 29 * pixel.b + 128) >> 8;
992
993 // Use UTF-8 cache which contains character index ramp
994 utf8_palette_cache_t *utf8_cache = get_utf8_palette_cache(palette);
995 if (!utf8_cache) {
996 SET_ERRNO(ERROR_INVALID_STATE, "Failed to get UTF-8 cache");
997 return NULL;
998 }
999
1000 // Use same 6-bit precision as SIMD: map luminance (0-255) to bucket (0-63) then to character
1001 uint8_t safe_luminance = clamp_rgb(luminance);
1002 uint8_t luma_idx = (uint8_t)(safe_luminance >> 2); // 0-63 index (same as SIMD)
1003 uint8_t char_idx = utf8_cache->char_index_ramp[luma_idx]; // Map to character index (same as SIMD)
1004
1005 // Use direct palette character lookup (same as SIMD would do)
1006 char ascii_char = palette[char_idx];
1007 const utf8_char_t *char_info = &utf8_cache->cache[(unsigned char)ascii_char];
1008
1009 if (char_info) {
1010 // Copy UTF-8 character bytes
1011 for (int byte_idx = 0; byte_idx < char_info->byte_len; byte_idx++) {
1012 *ptr++ = char_info->utf8_bytes[byte_idx];
1013 }
1014 } else {
1015 // Fallback to simple ASCII if UTF-8 cache fails
1016 size_t palette_len = strlen(palette);
1017 int palette_index = (luminance * ((int)palette_len - 1) + 127) / 255;
1018 *ptr++ = palette[palette_index];
1019 }
1020 }
1021
1022 // Add reset and newline at end of each row
1023 SAFE_STRNCPY(ptr, reset_code, 5);
1024 ptr += strlen(reset_code);
1025 if (y < h - 1) {
1026 *ptr++ = '\n';
1027 }
1028 }
1029
1030 *ptr = '\0';
1031 SAFE_FREE(error_buffer); // Clean up error buffer
1032 return buffer;
1033}
char * append_16color_bg(char *dst, uint8_t color_index)
Append 16-color background ANSI sequence.
Definition ansi_fast.c:260
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.
Definition ansi_fast.c:320

References ansi_fast_init_16color(), append_16color_bg(), append_16color_fg(), utf8_char_t::byte_len, utf8_palette_cache_s::cache, utf8_palette_cache_s::char_index_ramp, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, get_16color_rgb(), get_utf8_palette_cache(), image_t::h, image_t::pixels, rgb_to_16color_dithered(), SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, SAFE_STRNCPY, SET_ERRNO, utf8_char_t::utf8_bytes, and image_t::w.

Referenced by image_print_with_capabilities().

◆ image_print_256color()

char * image_print_256color ( const image_t image,
const char *  palette 
)

#include <image.h>

Print image using 256-color ANSI mode.

Parameters
imageImage to print (must not be NULL)
paletteCharacter palette to use (or NULL for default ASCII palette)
Returns
Allocated ASCII string with 256-color ANSI codes (caller must free), or NULL on error

Converts an image to ASCII art using 256-color ANSI mode. Uses extended ANSI color palette (0-255) for color output. Colors are quantized to 256-color palette.

Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Requires terminal 256-color support for proper display.
Colors are quantized to 256-color palette automatically.

Definition at line 722 of file video/image.c.

722 {
723 if (!image || !image->pixels || !palette) {
724 SET_ERRNO(ERROR_INVALID_PARAM, "image=%p or image->pixels=%p or palette=%p is NULL", image, image->pixels, palette);
725 return NULL;
726 }
727
728 // Use the existing optimized SIMD colored printing (no background for 256-color mode)
729#ifdef SIMD_SUPPORT
730 char *result = image_print_color_simd((image_t *)image, false, true, palette);
731#else
732 char *result = image_print_color(image, palette);
733#endif
734
735 return result;
736}

References ERROR_INVALID_PARAM, image_print_color(), image_print_color_simd(), image_t::pixels, and SET_ERRNO.

Referenced by image_print_with_capabilities().

◆ image_print_color()

char * image_print_color ( const image_t p,
const char *  palette 
)

#include <image.h>

Print image as ASCII art with color.

Parameters
pImage to print (must not be NULL)
paletteCharacter palette to use (or NULL for default ASCII palette)
Returns
Allocated ASCII string with ANSI color codes (caller must free), or NULL on error

Converts an image to ASCII art with color output. Uses character palette for character mapping and ANSI color codes for color output. Returns a null-terminated string containing the ASCII art frame with embedded ANSI escape sequences.

Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
ANSI color codes are embedded in the output string.
Color output requires terminal color support for proper display.

Converts an image to colored ASCII art with ANSI escape codes.

This function generates a string representation of an image where each pixel is converted to an ASCII character with ANSI color codes. The character is chosen based on luminance, and colors are applied using 24-bit RGB ANSI escape sequences.

Buffer allocation is precisely calculated to avoid waste and prevent overflows:

  • Each pixel: 1 ASCII char + foreground ANSI code (19 bytes max)
  • Background mode: adds background ANSI code (19 bytes max per pixel)
  • Each row: reset sequence (\033[0m = 4 bytes) + newline (except last row)
  • At the end: null terminator (1 byte)

Color modes:

  • Foreground only (default): ASCII characters with colored foreground
  • Background mode (opt_background_color): colored background with contrasting foreground (black on bright backgrounds, white on dark backgrounds)

ANSI escape code format:

  • Foreground: \033[38;2;R;G;Bm (11-19 bytes depending on RGB values)
  • Background: \033[48;2;R;G;Bm (11-19 bytes depending on RGB values)
  • Reset: \033[0m (4 bytes)
Parameters
pPointer to image_t structure containing pixel data
Returns
Dynamically allocated string containing colored ASCII art, or NULL on error. Caller is responsible for freeing the returned string.
Note
The function performs overflow checks to prevent integer overflow when calculating buffer sizes for very large images.
Uses global opt_background_color to determine color mode.
Exits with ERROR_BUFFER if buffer overflow is detected during string construction (should never happen with correct calculation).

Definition at line 513 of file video/image.c.

513 {
514 if (!p || !palette) {
515 SET_ERRNO(ERROR_INVALID_PARAM, "p=%p or palette=%p is NULL", p, palette);
516 return NULL;
517 }
518 if (!p->pixels) {
519 SET_ERRNO(ERROR_INVALID_PARAM, "p->pixels is NULL");
520 return NULL;
521 }
522
523 // Get UTF-8 character cache for proper multi-byte character support
524 utf8_palette_cache_t *utf8_cache = get_utf8_palette_cache(palette);
525 if (!utf8_cache) {
526 SET_ERRNO(ERROR_INVALID_STATE, "Failed to get UTF-8 palette cache for scalar color rendering");
527 return NULL;
528 }
529
530 const int h = p->h;
531 const int w = p->w;
532
533 // Constants for ANSI escape codes (using exact sizes from ansi_fast.c)
534 const size_t max_fg_ansi = 19; // \033[38;2;255;255;255m
535 const size_t max_bg_ansi = 19; // \033[48;2;255;255;255m
536 const size_t reset_len = 4; // \033[0m
537
538 const size_t h_sz = (size_t)h;
539 const size_t w_sz = (size_t)w;
540
541 // Ensure h * w won't overflow
542 if (h_sz > 0 && w_sz > SIZE_MAX / h_sz) {
543 SET_ERRNO(ERROR_INVALID_STATE, "Image dimensions too large: %d x %d", h, w);
544 return NULL;
545 }
546
547 const size_t total_pixels = h_sz * w_sz;
548 const size_t bytes_per_pixel = 1 + max_fg_ansi + max_bg_ansi; // Conservative estimate for max possible
549
550 // Ensure total_pixels * bytes_per_pixel won't overflow
551 if (total_pixels > SIZE_MAX / bytes_per_pixel) {
552 SET_ERRNO(ERROR_INVALID_STATE, "Pixel data too large for buffer: %d x %d", h, w);
553 return NULL;
554 }
555
556 const size_t pixel_bytes = total_pixels * bytes_per_pixel;
557
558 // Per row: reset sequence + newline (except last row)
559 const size_t total_resets = h_sz * reset_len;
560 const size_t total_newlines = (h_sz > 0) ? (h_sz - 1) : 0;
561
562 // Final buffer size: pixel bytes + per-row extras + null terminator
563 const size_t extra_bytes = total_resets + total_newlines + 1;
564
565 if (pixel_bytes > SIZE_MAX - extra_bytes) {
566 SET_ERRNO(ERROR_INVALID_STATE, "Final buffer size would overflow: %d x %d", h, w);
567 return NULL;
568 }
569
570 const size_t lines_size = pixel_bytes + extra_bytes;
571 char *lines;
572 lines = SAFE_MALLOC(lines_size, char *);
573
574 const rgb_pixel_t *pix = p->pixels;
575 // char *current_pos = lines;
576 // const char *buffer_end = lines + lines_size - 1; // reserve space for '\0'
577
578 // Initialize the optimized RLE context for color sequence caching
579 // Note: This function should be called via image_print_with_capabilities() for proper per-client rendering
580 ansi_rle_context_t rle_ctx;
581 ansi_color_mode_t color_mode = ANSI_MODE_FOREGROUND; // Default to foreground-only for legacy usage
582 ansi_rle_init(&rle_ctx, lines, lines_size, color_mode);
583
584 // Process each pixel using the optimized RLE context
585 for (int y = 0; y < h; y++) {
586 const int row_offset = y * w;
587
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;
591 // Standard ITU-R BT.601 luminance calculation
592 const int luminance = (77 * r + 150 * g + 29 * b + 128) >> 8;
593
594 // Use UTF-8 character cache for proper character selection
595 uint8_t safe_luminance = clamp_rgb(luminance);
596 const utf8_char_t *char_info = &utf8_cache->cache[safe_luminance];
597
598 // For RLE, we need to pass the first byte of the UTF-8 character
599 // Note: RLE system may need updates for full UTF-8 support
600 const char ascii_char = char_info->utf8_bytes[0];
601
602 ansi_rle_add_pixel(&rle_ctx, (uint8_t)r, (uint8_t)g, (uint8_t)b, ascii_char);
603 }
604
605 // Add newline between rows (except last row)
606 if (y != h - 1 && rle_ctx.length < rle_ctx.capacity - 1) {
607 rle_ctx.buffer[rle_ctx.length++] = '\n';
608 }
609 }
610
611 ansi_rle_finish(&rle_ctx);
612 return lines;
613}
void ansi_rle_init(ansi_rle_context_t *ctx, char *buffer, size_t capacity, ansi_color_mode_t mode)
Initialize run-length encoding context.
Definition ansi_fast.c:112
void ansi_rle_finish(ansi_rle_context_t *ctx)
Finish RLE sequence.
Definition ansi_fast.c:159
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.
Definition ansi_fast.c:125
Run-length encoded color output context.
Definition ansi_fast.h:115

References ANSI_MODE_FOREGROUND, ansi_rle_add_pixel(), ansi_rle_finish(), ansi_rle_init(), ansi_rle_context_t::buffer, utf8_palette_cache_s::cache, ansi_rle_context_t::capacity, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, get_utf8_palette_cache(), image_t::h, ansi_rle_context_t::length, image_t::pixels, SAFE_MALLOC, SET_ERRNO, utf8_char_t::utf8_bytes, and image_t::w.

Referenced by ascii_convert(), benchmark_simd_color_conversion(), benchmark_simd_conversion_with_source(), image_print_256color(), image_print_color_simd(), and image_print_with_capabilities().

◆ image_print_color_simd()

char * image_print_color_simd ( image_t image,
bool  use_background_mode,
bool  use_256color,
const char *  ascii_chars 
)

#include <ascii_simd.h>

Print image as ASCII with color using SIMD.

Parameters
imageSource image
use_background_modeUse background colors
use_256colorUse 256-color mode (vs truecolor)
ascii_charsCharacter palette
Returns
Allocated ASCII string with ANSI codes (caller must free), or NULL on error

Definition at line 375 of file ascii_simd_color.c.

375 {
376 (void)use_256color; // Suppress unused parameter warning when SIMD not available
377
378#if SIMD_SUPPORT_AVX2
379 (void)use_background_mode; // Suppress unused parameter warning when SIMD not available
380 // FIXME: my AVX2 implementation is dim and has vertical stripe artifacts. Use scalar until we fix it.
381 return image_print_color(image, ascii_chars);
382 // return render_ascii_avx2_unified_optimized(image, use_background_mode, use_256color, ascii_chars);
383#elif SIMD_SUPPORT_SSSE3
384 return render_ascii_ssse3_unified_optimized(image, use_background_mode, use_256color, ascii_chars);
385#elif SIMD_SUPPORT_SSE2
386 return render_ascii_sse2_unified_optimized(image, use_background_mode, use_256color, ascii_chars);
387#elif SIMD_SUPPORT_NEON
388 return render_ascii_neon_unified_optimized(image, use_background_mode, use_256color, ascii_chars);
389#else
390 // Fallback implementation for non-NEON platforms
391 // Use scalar image function for fallback path - no SIMD allocation needed
392 (void)use_background_mode; // Suppress unused parameter warning
393 return image_print_color(image, ascii_chars);
394#endif
395}

References image_print_color().

Referenced by ascii_convert(), image_print_256color(), and image_print_with_capabilities().

◆ image_print_simd()

char * image_print_simd ( image_t image,
const char *  ascii_chars 
)

#include <ascii_simd.h>

Print image as ASCII using SIMD (monochrome)

Parameters
imageSource image
ascii_charsCharacter palette
Returns
Allocated ASCII string (caller must free), or NULL on error

Definition at line 252 of file ascii_simd.c.

252 {
253#if SIMD_SUPPORT_AVX2
254 return render_ascii_image_monochrome_avx2(image, ascii_chars);
255#elif SIMD_SUPPORT_SSSE3
256 return render_ascii_image_monochrome_ssse3(image, ascii_chars);
257#elif SIMD_SUPPORT_SSE2
258 return render_ascii_image_monochrome_sse2(image, ascii_chars);
259#elif SIMD_SUPPORT_NEON
260 return render_ascii_image_monochrome_neon(image, ascii_chars);
261#else
262 // Fallback to scalar implementation - use image_print which properly handles
263 // the palette string (convert_pixels_scalar_with_newlines expects a 256-element
264 // luminance lookup table, not the raw palette string)
265 return image_print(image, ascii_chars);
266#endif
267}

References image_print().

Referenced by ascii_convert(), and image_print_with_capabilities().

◆ image_print_with_capabilities()

char * image_print_with_capabilities ( const image_t image,
const terminal_capabilities_t caps,
const char *  palette,
const char  luminance_palette[256] 
)

#include <image.h>

Print image with terminal capability awareness.

Parameters
imageImage to print (must not be NULL)
capsTerminal capabilities structure (must not be NULL)
paletteCharacter palette to use (or NULL for default ASCII palette)
luminance_paletteLuminance-to-character mapping palette (must not be NULL, 256 elements)
Returns
Allocated ASCII string (caller must free), or NULL on error

Converts an image to ASCII art with automatic color mode selection based on terminal capabilities. Automatically chooses the best color mode (16-color, 256-color, or truecolor) for the terminal.

COLOR MODE SELECTION:

  • Truecolor: If terminal supports 24-bit RGB colors
  • 256-color: If terminal supports extended ANSI palette
  • 16-color: Fallback to standard ANSI colors
  • Monochrome: If terminal has no color support
Note
Returns NULL on error (invalid image, memory allocation failure).
ASCII string must be freed by caller using free().
Color mode is selected automatically based on terminal capabilities.
This is the recommended function for capability-aware ASCII conversion.

◆ image_resize()

void image_resize ( const image_t source,
image_t dest 
)

#include <image.h>

Resize image using nearest-neighbor interpolation.

Parameters
sourceSource image (must not be NULL)
destDestination image (must be allocated and have valid dimensions)

Resizes an image using nearest-neighbor interpolation. Fast but may produce aliasing artifacts. Source pixels are sampled to nearest destination pixel positions.

NEAREST-NEIGHBOR INTERPOLATION:

  • Fast algorithm (no interpolation calculations)
  • May produce aliasing artifacts
  • Pixel values are copied directly (no averaging)
  • Suitable for integer scaling (2x, 3x, etc.)
Note
Destination image dimensions must be set before calling.
Source and destination images must be valid (not NULL).
Fast algorithm suitable for real-time resizing.

Definition at line 232 of file video/image.c.

232 {
233 if (!s || !d) {
234 SET_ERRNO(ERROR_INVALID_PARAM, "image_resize: s or d is NULL");
235 return;
236 }
237
239}
void image_resize_interpolation(const image_t *source, image_t *dest)
Resize image using bilinear interpolation.

References ERROR_INVALID_PARAM, image_resize_interpolation(), and SET_ERRNO.

Referenced by ascii_convert(), and ascii_convert_with_capabilities().

◆ image_resize_interpolation()

void image_resize_interpolation ( const image_t source,
image_t dest 
)

#include <image.h>

Resize image using bilinear interpolation.

Parameters
sourceSource image (must not be NULL)
destDestination image (must be allocated and have valid dimensions)

Resizes an image using bilinear interpolation. Produces smoother results than nearest-neighbor but slower. Pixel values are interpolated from four nearest source pixels.

BILINEAR INTERPOLATION:

  • Slower algorithm (interpolation calculations required)
  • Produces smoother results (reduced aliasing)
  • Pixel values are interpolated from 4 nearest neighbors
  • Suitable for fractional scaling (1.5x, 2.3x, etc.)
Note
Destination image dimensions must be set before calling.
Source and destination images must be valid (not NULL).
Slower algorithm but produces better quality.

Definition at line 243 of file video/image.c.

243 {
244 if (!source || !dest || !source->pixels || !dest->pixels) {
245 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters to image_resize_interpolation");
246 return;
247 }
248
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;
253
254 // Handle edge cases
255 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
256 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid image dimensions for resize");
257 return;
258 }
259
260 // Use fixed-point arithmetic for better performance
261 const uint32_t x_ratio = (((uint32_t)(unsigned int)src_w << 16) / (uint32_t)(unsigned int)dst_w) + 1;
262 const uint32_t y_ratio = (((uint32_t)(unsigned int)src_h << 16) / (uint32_t)(unsigned int)dst_h) + 1;
263
264 const rgb_pixel_t *src_pixels = source->pixels;
265 rgb_pixel_t *dst_pixels = dest->pixels;
266
267 for (int y = 0; y < dst_h; y++) {
268 const uint32_t src_y = ((uint32_t)(unsigned int)y * y_ratio) >> 16;
269 const uint32_t safe_src_y = (src_y >= (uint32_t)(unsigned int)src_h) ? (uint32_t)(src_h - 1) : src_y;
270 const rgb_pixel_t *src_row = src_pixels + (safe_src_y * (size_t)src_w);
271
272 rgb_pixel_t *dst_row = dst_pixels + ((size_t)y * (size_t)dst_w);
273
274 for (int x = 0; x < dst_w; x++) {
275 const uint32_t src_x = ((uint32_t)(unsigned int)x * x_ratio) >> 16;
276 const uint32_t safe_src_x = (src_x >= (uint32_t)(unsigned int)src_w) ? (uint32_t)(src_w - 1) : src_x;
277 dst_row[x] = src_row[safe_src_x];
278 }
279 }
280}

References ERROR_INVALID_PARAM, image_t::h, image_t::pixels, SET_ERRNO, and image_t::w.

Referenced by image_resize().

◆ init_dec3()

void init_dec3 ( void  )

#include <ascii_simd.h>

Initialize decimal lookup table.

Must be called before using any SIMD conversion functions.

Definition at line 58 of file ascii_simd.c.

58 {
60 return;
61 for (int v = 0; v < 256; ++v) {
62 int d2 = v / 100; // 0..2
63 int r = v - d2 * 100; // 0..99
64 int d1 = r / 10; // 0..9
65 int d0 = r - d1 * 10; // 0..9
66
67 if (d2) {
69 g_dec3_cache.dec3_table[v].s[0] = '0' + d2;
70 g_dec3_cache.dec3_table[v].s[1] = '0' + d1;
71 g_dec3_cache.dec3_table[v].s[2] = '0' + d0;
72 } else if (d1) {
74 g_dec3_cache.dec3_table[v].s[0] = '0' + d1;
75 g_dec3_cache.dec3_table[v].s[1] = '0' + d0;
76 } else {
78 g_dec3_cache.dec3_table[v].s[0] = '0' + d0;
79 }
80 }
82}

References global_dec3_cache_t::dec3_initialized, global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, and dec3_t::s.

Referenced by ascii_simd_init().

◆ init_default_luminance_palette()

void init_default_luminance_palette ( void  )

#include <ascii_simd.h>

Initialize default luminance palette.

Definition at line 37 of file ascii_simd.c.

37 {
38 if (g_default_palette_initialized)
39 return;
40
41 // Build default luminance mapping using standard palette
42 const size_t len = DEFAULT_ASCII_PALETTE_LEN;
43 for (int i = 0; i < 256; i++) {
44 size_t palette_index = (i * (len - 1) + 127) / 255;
45 if (palette_index >= len) {
46 palette_index = len - 1;
47 }
49 }
50 g_default_palette_initialized = true;
51}
const size_t DEFAULT_ASCII_PALETTE_LEN
Length of default ASCII palette.
Definition palette.c:49
char g_default_luminance_palette[256]
Default luminance palette (256 characters)
Definition ascii_simd.c:33

References DEFAULT_ASCII_PALETTE, DEFAULT_ASCII_PALETTE_LEN, and g_default_luminance_palette.

Referenced by ascii_simd_init().

◆ ob_putc()

void ob_putc ( outbuf_t ob,
char  c 
)

#include <output_buffer.h>

Append a character to buffer.

Parameters
obOutput buffer structure (must not be NULL)
cCharacter to append

Appends a single character to the output buffer. Automatically expands the buffer if necessary. Buffer remains null-terminated after append.

Note
Buffer is automatically expanded if needed.
Length is incremented by 1 after append.
Null terminator is updated automatically.

Definition at line 42 of file output_buffer.c.

42 {
43 if (!ob)
44 return;
45 ob_reserve(ob, 1);
46 ob->buf[ob->len++] = c;
47}
size_t len
Current length in bytes (excluding null terminator)

References outbuf_t::buf, outbuf_t::len, and ob_reserve().

Referenced by ansi_compress_rle(), ansi_strip_escapes(), convert_pixels_scalar_with_newlines(), emit_rep(), emit_reset(), emit_set_256_color_bg(), emit_set_256_color_fg(), emit_set_bg(), emit_set_fg(), emit_set_truecolor_bg(), emit_set_truecolor_fg(), image_print(), ob_term(), and ob_u8().

◆ ob_reserve()

void ob_reserve ( outbuf_t ob,
size_t  need 
)

#include <output_buffer.h>

Reserve buffer space for upcoming writes.

Parameters
obOutput buffer structure (must not be NULL)
needMinimum bytes needed (must be > 0)

Ensures the buffer has at least 'need' bytes of available space. Automatically expands the buffer if necessary. Buffer grows exponentially (doubles in size) to minimize reallocation overhead.

Note
Buffer is automatically allocated on first reservation.
Buffer doubles in size when capacity is exceeded.
This function does not modify buffer length (only capacity).

Definition at line 21 of file output_buffer.c.

21 {
22 if (!ob)
23 return;
24 if (ob->cap == 0) {
25 // Always allocate at least default capacity on first call
26 size_t ncap = 4096;
27 while (ncap < ob->len + need)
28 ncap = (ncap * 3) / 2;
29 // SAFE_REALLOC handles NULL ptr as malloc - this is safe
30 ob->buf = SAFE_REALLOC(ob->buf, ncap, char *);
31 ob->cap = ncap;
32 } else if (ob->len + need > ob->cap) {
33 // Expand existing buffer
34 size_t ncap = ob->cap;
35 while (ncap < ob->len + need)
36 ncap = (ncap * 3) / 2;
37 ob->buf = SAFE_REALLOC(ob->buf, ncap, char *);
38 ob->cap = ncap;
39 }
40}
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:228

References outbuf_t::buf, outbuf_t::cap, outbuf_t::len, and SAFE_REALLOC.

Referenced by ansi_compress_rle(), ansi_expand_rle(), ansi_strip_escapes(), ob_putc(), ob_u32(), and ob_write().

◆ ob_term()

void ob_term ( outbuf_t ob)

#include <output_buffer.h>

Append null terminator to buffer.

Parameters
obOutput buffer structure (must not be NULL)

Ensures the buffer is null-terminated by appending a null character. Does not increment length counter (null terminator is not counted). Useful for ensuring buffer is a valid C string.

Note
This function ensures buffer[len] == '\0'.
Length counter is not modified (null terminator excluded from length).

Definition at line 57 of file output_buffer.c.

57 {
58 if (!ob)
59 return;
60 ob_putc(ob, '\0');
61}

References ob_putc().

Referenced by ansi_compress_rle(), ansi_expand_rle(), ansi_strip_escapes(), convert_pixels_scalar_with_newlines(), and image_print().

◆ ob_u32()

void ob_u32 ( outbuf_t ob,
uint32_t  v 
)

#include <output_buffer.h>

Append unsigned 32-bit integer as decimal string.

Parameters
obOutput buffer structure (must not be NULL)
vValue to append (0 to 4294967295)

Converts an unsigned 32-bit integer to decimal string representation and appends it to the buffer. Useful for formatting numeric values (dimensions, counts, timestamps, etc.).

Note
Buffer is automatically expanded if needed.
String representation is appended (1-10 characters for 0-4294967295).

Definition at line 85 of file output_buffer.c.

85 {
86 if (!ob)
87 return;
88 char tmp[10];
89 int i = 0;
90 do {
91 tmp[i++] = '0' + (v % 10u);
92 v /= 10u;
93 } while (v);
94 ob_reserve(ob, (size_t)i);
95 while (i--)
96 ob->buf[ob->len++] = tmp[i];
97}

References outbuf_t::buf, outbuf_t::len, and ob_reserve().

Referenced by emit_rep().

◆ ob_u8()

void ob_u8 ( outbuf_t ob,
uint8_t  v 
)

#include <output_buffer.h>

Append unsigned 8-bit integer as decimal string.

Parameters
obOutput buffer structure (must not be NULL)
vValue to append (0-255)

Converts an unsigned 8-bit integer to decimal string representation and appends it to the buffer. Useful for formatting small numeric values (color indices, counts, etc.).

Note
Buffer is automatically expanded if needed.
String representation is appended (1-3 characters for 0-255).

Definition at line 64 of file output_buffer.c.

64 {
65 if (!ob)
66 return;
67 if (v >= 100) {
68 uint8_t d0 = v / 100;
69 uint8_t r = v % 100;
70 uint8_t d1 = r / 10;
71 uint8_t d2 = r % 10;
72 ob_putc(ob, '0' + d0);
73 ob_putc(ob, '0' + d1);
74 ob_putc(ob, '0' + d2);
75 } else if (v >= 10) {
76 uint8_t d1 = v / 10;
77 uint8_t d2 = v % 10;
78 ob_putc(ob, '0' + d1);
79 ob_putc(ob, '0' + d2);
80 } else {
81 ob_putc(ob, '0' + v);
82 }
83}

References ob_putc().

Referenced by emit_set_256_color_bg(), emit_set_256_color_fg(), emit_set_bg(), emit_set_fg(), emit_set_truecolor_bg(), and emit_set_truecolor_fg().

◆ ob_write()

void ob_write ( outbuf_t ob,
const char *  s,
size_t  n 
)

#include <output_buffer.h>

Append a string to buffer.

Parameters
obOutput buffer structure (must not be NULL)
sString to append (must not be NULL)
nNumber of bytes to append (must be > 0)

Appends n bytes from string s to the output buffer. Automatically expands the buffer if necessary. Buffer remains null-terminated after append.

Note
Buffer is automatically expanded if needed.
Length is incremented by n after append.
Null terminator is updated automatically.
This function copies n bytes (may include embedded nulls).

Definition at line 49 of file output_buffer.c.

49 {
50 if (!ob || n == 0)
51 return;
52 ob_reserve(ob, n);
53 SAFE_MEMCPY(ob->buf + ob->len, ob->cap - ob->len, s, n);
54 ob->len += n;
55}

References outbuf_t::buf, outbuf_t::cap, outbuf_t::len, ob_reserve(), and SAFE_MEMCPY.

Referenced by ansi_compress_rle(), ansi_expand_rle(), emit_set_256_color_bg(), emit_set_256_color_fg(), emit_set_bg(), emit_set_fg(), emit_set_truecolor_bg(), emit_set_truecolor_fg(), and image_print().

◆ precalc_rgb_palettes()

void precalc_rgb_palettes ( const float  red,
const float  green,
const float  blue 
)

#include <image.h>

Precalculate RGB palettes with color adjustment.

Parameters
redRed adjustment factor (1.0 = no adjustment, >1.0 = increase, <1.0 = decrease)
greenGreen adjustment factor (1.0 = no adjustment, >1.0 = increase, <1.0 = decrease)
blueBlue adjustment factor (1.0 = no adjustment, >1.0 = increase, <1.0 = decrease)

Precalculates RGB color palettes with color adjustment factors. Color adjustment allows brightness, contrast, and color balance adjustments to be applied efficiently during ASCII conversion.

COLOR ADJUSTMENT:

  • Adjustment factors are multiplied with color components
  • Factors > 1.0 increase color intensity
  • Factors < 1.0 decrease color intensity
  • Precalculated palettes improve conversion performance
Note
Precalculated palettes are cached for efficient conversion.
Color adjustment is applied during ASCII conversion.
Useful for brightness/contrast control.

Definition at line 284 of file video/image.c.

284 {
285 // Validate input parameters to prevent overflow
286 // Luminance weights should typically be in range 0.0-1.0
287 // But allow slightly larger values for brightness adjustment
288 const float max_weight = 255.0f; // Maximum value that won't overflow when multiplied by 255
289 const float min_weight = -255.0f; // Allow negative for color correction
290
291 // Note: isfinite() checks are skipped in Release builds due to -ffinite-math-only flag
292 // which disables NaN/infinity support for performance. Range checks below are sufficient.
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);
296 SET_ERRNO(ERROR_INVALID_PARAM, "precalc_rgb_palettes: non-finite weight values");
297 return;
298 }
299#endif
300
301 if (red < min_weight || red > max_weight || green < min_weight || green > max_weight || blue < min_weight ||
302 blue > max_weight) {
303 log_warn(
304 "precalc_rgb_palettes: Weight values out of expected range: red=%f, green=%f, blue=%f (clamping to safe range)",
305 red, green, blue);
306 }
307
308 // Clamp weights to safe range to prevent overflow
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);
312
313 const unsigned short max_ushort = 65535;
314 const unsigned short min_ushort = 0;
315
316 for (int n = 0; n < ASCII_LUMINANCE_LEVELS; ++n) {
317 // Compute with float, then clamp to unsigned short range
318 float red_val = (float)n * safe_red;
319 float green_val = (float)n * safe_green;
320 float blue_val = (float)n * safe_blue;
321
322 // Clamp to unsigned short range before assignment
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;
327 }
328
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;
333 }
334
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;
339 }
340
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;
345 }
346}
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.
#define ASCII_LUMINANCE_LEVELS
Number of luminance levels supported (256)
Definition ascii.h:427

References ASCII_LUMINANCE_LEVELS, BLUE, ERROR_INVALID_PARAM, GRAY, GREEN, log_error, log_warn, RED, and SET_ERRNO.

Referenced by server_main().

◆ prewarm_sgr256_cache()

void prewarm_sgr256_cache ( void  )

#include <ascii_simd.h>

Prewarm 256-color foreground/background cache for benchmarks.

Prevents first-frame performance penalty in benchmarks.

Definition at line 92 of file ascii_simd_color.c.

92 {
93 // No-op: cache removed
94}

Referenced by benchmark_simd_color_conversion_with_source().

◆ prewarm_sgr256_fg_cache()

void prewarm_sgr256_fg_cache ( void  )

#include <ascii_simd.h>

Prewarm 256-color foreground cache for benchmarks.

Prevents first-frame performance penalty in benchmarks.

Definition at line 88 of file ascii_simd_color.c.

88 {
89 // No-op: cache removed
90}

Referenced by benchmark_simd_color_conversion_with_source().

◆ print_simd_capabilities()

void print_simd_capabilities ( void  )

#include <ascii_simd.h>

Print detected SIMD capabilities.

Prints available SIMD instruction sets to stdout.

Definition at line 276 of file ascii_simd.c.

276 {
277 printf("SIMD Support:\n");
278#if SIMD_SUPPORT_AVX2
279 printf(" ✓ AVX2 (32 pixels/cycle)\n");
280#endif
281#if SIMD_SUPPORT_NEON
282 printf(" ✓ ARM NEON (16 pixels/cycle)\n");
283#endif
284#if SIMD_SUPPORT_SVE
285 printf(" ✓ ARM SVE (scalable pixels/cycle)\n");
286#endif
287#if SIMD_SUPPORT_SSSE3
288 printf(" ✓ SSSE3 (16 pixels/cycle)\n");
289#endif
290#if SIMD_SUPPORT_SSE2
291 printf(" ✓ SSE2 (16 pixels/cycle)\n");
292#endif
293 printf(" ✓ Scalar fallback (1 pixel/cycle)\n");
294}

◆ quantize_color()

void quantize_color ( int *  r,
int *  g,
int *  b,
int  levels 
)

#include <image.h>

Quantize color to specified number of levels.

Parameters
rRed component (input/output, must not be NULL, range 0-255)
gGreen component (input/output, must not be NULL, range 0-255)
bBlue component (input/output, must not be NULL, range 0-255)
levelsNumber of quantization levels (must be > 0)

Quantizes RGB color components to a specified number of levels. Reduces color precision for color palette mapping. Input colors are modified in-place to quantized values.

QUANTIZATION:

  • Divides color range (0-255) into 'levels' bins
  • Maps each component to nearest bin center
  • Reduces color precision for palette mapping
  • Used for 16-color and 256-color mode conversion
Note
Input/output parameters are modified in-place.
Quantization reduces color accuracy but enables palette mapping.
Typical values: 2 levels for 16-color, 6 levels for 256-color.

Definition at line 462 of file video/image.c.

462 {
463 if (!r || !g || !b) {
464 SET_ERRNO(ERROR_INVALID_PARAM, "quantize_color: r, g, or b is NULL");
465 return;
466 }
467
468 if (levels <= 0) {
469 SET_ERRNO(ERROR_INVALID_PARAM, "quantize_color: levels must be positive, got %d", levels);
470 return;
471 }
472
473 int step = 256 / levels;
474 *r = (*r / step) * step;
475 *g = (*g / step) * step;
476 *b = (*b / step) * step;
477}

References ERROR_INVALID_PARAM, and SET_ERRNO.

◆ rep_is_profitable()

bool rep_is_profitable ( uint32_t  runlen)

#include <output_buffer.h>

Check if run-length encoding is profitable.

Parameters
runlenRun length to check (number of repeated characters)
Returns
true if RLE is profitable, false otherwise

Determines whether run-length encoding (RLE) would reduce output size compared to emitting individual characters. RLE is profitable when the overhead of the repeat sequence is less than the characters it replaces.

RLE PROFITABILITY:

  • RLE uses format: ESC[runs (e.g., ESC[100X repeats 'X' 100 times)
  • Overhead: ~6-8 bytes for repeat sequence
  • Profit: runlen - overhead bytes saved
  • Profitable when runlen > ~10-15 characters
Note
Returns true if runlen exceeds RLE overhead threshold.
Threshold is tuned for typical ASCII art character runs.

Definition at line 141 of file output_buffer.c.

141 {
142 if (runlen <= 2)
143 return false;
144 uint32_t k = runlen - 1; // Extra repetitions beyond the first character
145 uint32_t rep_cost = (uint32_t)(digits_u32(k) + 3); // ESC [ digits b
146 return k > rep_cost; // Manual repetition cost vs REP cost
147}

Referenced by ansi_compress_rle(), convert_pixels_scalar_with_newlines(), and image_print().

◆ rgb_to_16color()

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

#include <ansi_fast.h>

Convert RGB to 16-color ANSI index.

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

Definition at line 277 of file ansi_fast.c.

277 {
278 // Convert RGB to the closest 16-color ANSI color
279 // This uses a simple distance-based approach
280
281 // Define the 16 ANSI colors in RGB
282 static const uint8_t ansi_colors[16][3] = {
283 {0, 0, 0}, // 0: Black
284 {128, 0, 0}, // 1: Dark Red
285 {0, 128, 0}, // 2: Dark Green
286 {128, 128, 0}, // 3: Dark Yellow (Brown)
287 {0, 0, 128}, // 4: Dark Blue
288 {128, 0, 128}, // 5: Dark Magenta
289 {0, 128, 128}, // 6: Dark Cyan
290 {192, 192, 192}, // 7: Light Gray
291 {128, 128, 128}, // 8: Dark Gray
292 {255, 0, 0}, // 9: Bright Red
293 {0, 255, 0}, // 10: Bright Green
294 {255, 255, 0}, // 11: Bright Yellow
295 {0, 0, 255}, // 12: Bright Blue
296 {255, 0, 255}, // 13: Bright Magenta
297 {0, 255, 255}, // 14: Bright Cyan
298 {255, 255, 255} // 15: White
299 };
300
301 uint8_t best_match = 0;
302 int min_distance = INT_MAX;
303
304 for (int i = 0; i < 16; i++) {
305 int dr = (int)r - (int)ansi_colors[i][0];
306 int dg = (int)g - (int)ansi_colors[i][1];
307 int db = (int)b - (int)ansi_colors[i][2];
308 int distance = dr * dr + dg * dg + db * db;
309
310 if (distance < min_distance) {
311 min_distance = distance;
312 best_match = i;
313 }
314 }
315
316 return best_match;
317}

Referenced by append_color_fg_for_mode(), image_print_16color(), and rgb_to_16color_dithered().

◆ 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 <ansi_fast.h>

Convert RGB to 16-color with Floyd-Steinberg dithering.

Parameters
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
xPixel X coordinate
yPixel Y coordinate
widthImage width
heightImage height
error_bufferError diffusion buffer
Returns
16-color ANSI index (0-15)

Uses Floyd-Steinberg dithering algorithm for improved color approximation.

Definition at line 351 of file ansi_fast.c.

351 {
352 // Add accumulated error from previous pixels
353 if (error_buffer) {
354 // Use size_t for index calculation to prevent integer overflow on large images
355 size_t error_idx = (size_t)y * (size_t)width + (size_t)x;
356 r += error_buffer[error_idx].r;
357 g += error_buffer[error_idx].g;
358 b += error_buffer[error_idx].b;
359
360 // Reset error for this pixel
361 error_buffer[error_idx].r = 0;
362 error_buffer[error_idx].g = 0;
363 error_buffer[error_idx].b = 0;
364 }
365
366 // Clamp values to [0, 255]
367 uint8_t r_clamped = clamp_rgb((int)r);
368 uint8_t g_clamped = clamp_rgb((int)g);
369 uint8_t b_clamped = clamp_rgb((int)b);
370
371 // Find the closest 16-color match
372 uint8_t closest_color = rgb_to_16color(r_clamped, g_clamped, b_clamped);
373
374 // Calculate quantization error if dithering is enabled
375 if (error_buffer) {
376 uint8_t actual_r, actual_g, actual_b;
377 get_16color_rgb(closest_color, &actual_r, &actual_g, &actual_b);
378
379 int error_r = r - (int)actual_r;
380 int error_g = g - (int)actual_g;
381 int error_b = b - (int)actual_b;
382
383 // Distribute error using Floyd-Steinberg weights:
384 // * 7/16
385 // 3/16 5/16 1/16
386
387 // Error to right pixel (x+1, y)
388 // Use size_t for all index calculations to prevent integer overflow
389 if (x + 1 < width) {
390 size_t right_idx = (size_t)y * (size_t)width + (size_t)(x + 1);
391 error_buffer[right_idx].r += (error_r * 7) / 16;
392 error_buffer[right_idx].g += (error_g * 7) / 16;
393 error_buffer[right_idx].b += (error_b * 7) / 16;
394 }
395
396 // Error to pixels on next row (y+1)
397 if (y + 1 < height) {
398 // Bottom-left pixel (x-1, y+1)
399 if (x - 1 >= 0) {
400 size_t bl_idx = (size_t)(y + 1) * (size_t)width + (size_t)(x - 1);
401 error_buffer[bl_idx].r += (error_r * 3) / 16;
402 error_buffer[bl_idx].g += (error_g * 3) / 16;
403 error_buffer[bl_idx].b += (error_b * 3) / 16;
404 }
405
406 // Bottom pixel (x, y+1)
407 size_t bottom_idx = (size_t)(y + 1) * (size_t)width + (size_t)x;
408 error_buffer[bottom_idx].r += (error_r * 5) / 16;
409 error_buffer[bottom_idx].g += (error_g * 5) / 16;
410 error_buffer[bottom_idx].b += (error_b * 5) / 16;
411
412 // Bottom-right pixel (x+1, y+1)
413 if (x + 1 < width) {
414 size_t br_idx = (size_t)(y + 1) * (size_t)width + (size_t)(x + 1);
415 error_buffer[br_idx].r += (error_r * 1) / 16;
416 error_buffer[br_idx].g += (error_g * 1) / 16;
417 error_buffer[br_idx].b += (error_b * 1) / 16;
418 }
419 }
420 }
421
422 return closest_color;
423}

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

Referenced by image_print_16color_dithered(), and image_print_16color_dithered_with_background().

◆ rgb_to_256color()

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

#include <ansi_fast.h>

Convert RGB to 256-color palette index.

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

Definition at line 199 of file ansi_fast.c.

199 {
200 // Map to 6x6x6 color cube (216 colors) + grayscale ramp
201
202 // Check if it's close to grayscale
203 int avg = (r + g + b) / 3;
204 int gray_diff = abs(r - avg) + abs(g - avg) + abs(b - avg);
205
206 if (gray_diff < 30) {
207 // Use grayscale ramp (colors 232-255)
208 int gray_level = (avg * 23) / 255;
209 return (uint8_t)(232 + gray_level);
210 }
211
212 // Use 6x6x6 color cube (colors 16-231)
213 int r6 = (r * 5) / 255;
214 int g6 = (g * 5) / 255;
215 int b6 = (b * 5) / 255;
216
217 return (uint8_t)(16 + (r6 * 36) + (g6 * 6) + b6);
218}

Referenced by append_color_fg_for_mode().

◆ rgb_to_ansi_8bit()

void rgb_to_ansi_8bit ( int  r,
int  g,
int  b,
int *  fg_code,
int *  bg_code 
)

#include <image.h>

Convert RGB to 8-bit ANSI color codes.

Parameters
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
fg_codeOutput foreground color code (must not be NULL, range 0-255)
bg_codeOutput background color code (must not be NULL, range 0-255)

Converts RGB color values to 8-bit ANSI color codes (256-color palette). Quantizes RGB colors to 256-color palette indices for foreground and background colors. Output codes are indices into 256-color ANSI palette.

COLOR QUANTIZATION:

  • RGB colors are quantized to 256-color palette
  • Quantization uses standard 6x6x6 color cube
  • Output codes are palette indices (0-255)
  • Foreground and background codes may differ
Note
Output parameters are modified to contain palette indices.
Color codes are indices into 256-color ANSI palette.
Quantization reduces color accuracy but enables 256-color mode.

Definition at line 628 of file video/image.c.

628 {
629 if (!fg_code || !bg_code) {
630 SET_ERRNO(ERROR_INVALID_PARAM, "rgb_to_ansi_8bit: fg_code or bg_code is NULL");
631 return;
632 }
633
634 // Convert RGB to 8-bit color code (216 color cube + 24 grayscale)
635 if (r == g && g == b) {
636 // Grayscale
637 if (r < 8) {
638 *fg_code = 16;
639 } else if (r > 248) {
640 *fg_code = 231;
641 } else {
642 *fg_code = 232 + (r - 8) / 10;
643 }
644 } else {
645 // Color cube: 16 + 36*r + 6*g + b where r,g,b are 0-5
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;
650 }
651 *bg_code = *fg_code; // Same logic for background
652}

References ERROR_INVALID_PARAM, and SET_ERRNO.

◆ rgb_to_ansi_bg()

char * rgb_to_ansi_bg ( int  r,
int  g,
int  b 
)

#include <image.h>

Convert RGB to ANSI background color code.

Parameters
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
Returns
Allocated ANSI color code string (caller must free), or NULL on error

Converts RGB color values to ANSI background color escape sequence. Returns a string containing the ANSI escape sequence for background color (e.g., "\033[48;2;255;0;0m" for red background).

Note
Returns NULL on error (memory allocation failure).
ANSI string must be freed by caller using free().
ANSI sequence format: ESC[48;2;r;g;bm (truecolor mode).
Color components are clamped to 0-255 range automatically.

Definition at line 622 of file video/image.c.

622 {
623 _Thread_local static char color_code[20]; // \033[48;2;255;255;255m + \0 = 20 bytes max
624 SAFE_SNPRINTF(color_code, sizeof(color_code), "\033[48;2;%d;%d;%dm", r, g, b);
625 return color_code;
626}

References SAFE_SNPRINTF.

◆ rgb_to_ansi_fg()

char * rgb_to_ansi_fg ( int  r,
int  g,
int  b 
)

#include <image.h>

Convert RGB to ANSI foreground color code.

Parameters
rRed component (0-255)
gGreen component (0-255)
bBlue component (0-255)
Returns
Allocated ANSI color code string (caller must free), or NULL on error

Converts RGB color values to ANSI foreground color escape sequence. Returns a string containing the ANSI escape sequence for foreground color (e.g., "\033[38;2;255;0;0m" for red).

Note
Returns NULL on error (memory allocation failure).
ANSI string must be freed by caller using free().
ANSI sequence format: ESC[38;2;r;g;bm (truecolor mode).
Color components are clamped to 0-255 range automatically.

Definition at line 616 of file video/image.c.

616 {
617 _Thread_local static char color_code[20]; // \033[38;2;255;255;255m + \0 = 20 bytes max
618 SAFE_SNPRINTF(color_code, sizeof(color_code), "\033[38;2;%d;%d;%dm", r, g, b);
619 return color_code;
620}

References SAFE_SNPRINTF.

◆ set_color_quality_mode()

void set_color_quality_mode ( bool  high_quality)

#include <ascii_simd.h>

Set color quality mode.

Parameters
high_qualitytrue for 24-bit truecolor, false for 256-color

Controls tradeoff between color fidelity and rendering speed.

◆ simd_caches_destroy_all()

void simd_caches_destroy_all ( void  )

#include <common.h>

Destroy all SIMD caches.

Cleans up all caches and frees associated memory.

Referenced by server_main().

◆ str_append_bytes()

void str_append_bytes ( Str s,
const void *  src,
size_t  n 
)

#include <ascii_simd.h>

Append bytes to string buffer.

Parameters
sString buffer structure
srcSource data
nNumber of bytes to append

Definition at line 130 of file ascii_simd.c.

130 {
131 str_reserve(s, s->len + n);
132 memcpy(s->data + s->len, src, n);
133 s->len += n;
134}
void str_reserve(Str *s, size_t need)
Reserve space in string buffer.
Definition ascii_simd.c:120
size_t len
Definition ascii_simd.h:146
char * data
Definition ascii_simd.h:145

References Str::data, Str::len, and str_reserve().

Referenced by str_printf().

◆ str_append_c()

void str_append_c ( Str s,
char  c 
)

#include <ascii_simd.h>

Append character to string buffer.

Parameters
sString buffer structure
cCharacter to append

Definition at line 136 of file ascii_simd.c.

136 {
137 str_reserve(s, s->len + 1);
138 s->data[s->len++] = c;
139}

References Str::data, Str::len, and str_reserve().

◆ str_free()

void str_free ( Str s)

#include <ascii_simd.h>

Free string buffer.

Parameters
sString buffer structure

Definition at line 114 of file ascii_simd.c.

114 {
115 SAFE_FREE(s->data);
116 s->data = NULL;
117 s->len = s->cap = 0;
118}
size_t cap
Definition ascii_simd.h:147

References Str::cap, Str::data, Str::len, and SAFE_FREE.

◆ str_init()

void str_init ( Str s)

#include <ascii_simd.h>

Initialize string buffer.

Parameters
sString buffer structure

Definition at line 108 of file ascii_simd.c.

108 {
109 s->data = NULL;
110 s->len = 0;
111 s->cap = 0;
112}

References Str::cap, Str::data, and Str::len.

◆ str_printf()

void str_printf ( Str s,
const char *  fmt,
  ... 
)

#include <ascii_simd.h>

Append formatted string to buffer.

Parameters
sString buffer structure
fmtFormat string
...Variable arguments

Definition at line 141 of file ascii_simd.c.

141 {
142 va_list ap;
143 va_start(ap, fmt);
144 char stackbuf[256];
145 int n = vsnprintf(stackbuf, sizeof(stackbuf), fmt, ap);
146 va_end(ap);
147 if (n < 0)
148 return;
149 if ((size_t)n < sizeof(stackbuf)) {
150 str_append_bytes(s, stackbuf, (size_t)n);
151 return;
152 }
153 char *heap;
154 heap = SAFE_MALLOC((size_t)n + 1, char *);
155 va_start(ap, fmt);
156 (void)vsnprintf(heap, (size_t)n + 1, fmt, ap);
157 va_end(ap);
158 str_append_bytes(s, heap, (size_t)n);
159 SAFE_FREE(heap);
160}
void str_append_bytes(Str *s, const void *src, size_t n)
Append bytes to string buffer.
Definition ascii_simd.c:130

References SAFE_FREE, SAFE_MALLOC, and str_append_bytes().

◆ str_reserve()

void str_reserve ( Str s,
size_t  need 
)

#include <ascii_simd.h>

Reserve space in string buffer.

Parameters
sString buffer structure
needMinimum bytes needed

Definition at line 120 of file ascii_simd.c.

120 {
121 if (need <= s->cap)
122 return;
123 size_t ncap = s->cap ? s->cap : 4096;
124 while (ncap < need)
125 ncap = (ncap * 3) / 2 + 64;
126 s->data = SAFE_REALLOC(s->data, ncap, char *);
127 s->cap = ncap;
128}

References Str::cap, Str::data, and SAFE_REALLOC.

Referenced by str_append_bytes(), and str_append_c().

◆ write_rgb_triplet()

size_t write_rgb_triplet ( uint8_t  value,
char *  dst 
)

#include <common.h>

Write decimal RGB triplet using dec3 cache.

Parameters
valueRGB component value (0-255)
dstDestination buffer pointer
Returns
Number of bytes written
Note
Defined in ascii_simd.c to avoid circular dependencies.

Definition at line 26 of file ascii_simd.c.

26 {
27 const dec3_t *d = &g_dec3_cache.dec3_table[value];
28 memcpy(dst, d->s, d->len);
29 return d->len;
30}

References global_dec3_cache_t::dec3_table, g_dec3_cache, dec3_t::len, and dec3_t::s.

◆ write_row_rep_from_arrays_enhanced()

size_t write_row_rep_from_arrays_enhanced ( const uint8_t fg_r,
const uint8_t fg_g,
const uint8_t fg_b,
const uint8_t bg_r,
const uint8_t bg_g,
const uint8_t bg_b,
const uint8_t fg_idx,
const uint8_t bg_idx,
const char *  ascii_chars,
int  width,
char *  dst,
size_t  cap,
bool  is_truecolor 
)

#include <ascii_simd.h>

Enhanced REP compression writer.

Parameters
fg_rForeground red array
fg_gForeground green array
fg_bForeground blue array
bg_rBackground red array
bg_gBackground green array
bg_bBackground blue array
fg_idxForeground color indices
bg_idxBackground color indices
ascii_charsCharacter palette
widthImage width in pixels
dstOutput buffer
capOutput buffer capacity
is_truecolorUse truecolor mode
Returns
Number of bytes written

Used by scalar fallbacks and declared in NEON implementation.

Variable Documentation

◆ access_count

_Atomic uint32_t utf8_palette_cache_s::access_count

Total access count (atomic)

Definition at line 111 of file video/simd/common.h.

Referenced by __attribute__().

◆ ascii_palette

char ascii_palette[]
extern

#include <ascii.h>

Default ASCII palette characters.

Default character palette for ASCII art conversion. Contains characters ordered by luminance (dark to light) for mapping pixel brightness to ASCII characters.

Note
External variable (defined in ascii.c).
Default palette: " .:-=+*#%@$" (common ASCII art palette).

◆ avx2_time

double simd_benchmark_t::avx2_time

◆ best_method

const char* simd_benchmark_t::best_method

◆ byte_len

◆ cache

utf8_char_t utf8_palette_cache_s::cache[256]

256-entry cache for direct luminance lookup (monochrome)

Definition at line 103 of file video/simd/common.h.

Referenced by image_print_16color(), image_print_16color_dithered(), image_print_16color_dithered_with_background(), and image_print_color().

◆ cache64

utf8_char_t utf8_palette_cache_s::cache64[64]

64-entry cache for SIMD color lookup

Definition at line 104 of file video/simd/common.h.

Referenced by image_print().

◆ cached_score

double utf8_palette_cache_s::cached_score

Last calculated eviction score

Definition at line 116 of file video/simd/common.h.

◆ cap

size_t Str::cap

Total bytes allocated

Definition at line 147 of file ascii_simd.h.

Referenced by str_free(), str_init(), and str_reserve().

◆ cBB

int RLEState::cBB

Current background RGB

Definition at line 213 of file ascii_simd.h.

◆ cBG

int RLEState::cBG

Definition at line 213 of file ascii_simd.h.

◆ cBR

int RLEState::cBR

Definition at line 213 of file ascii_simd.h.

◆ cFB

int RLEState::cFB

Current foreground RGB

Definition at line 212 of file ascii_simd.h.

◆ cFG

int RLEState::cFG

Definition at line 212 of file ascii_simd.h.

◆ cFR

int RLEState::cFR

Definition at line 212 of file ascii_simd.h.

◆ char_index_ramp

uint8_t utf8_palette_cache_s::char_index_ramp[64]

Character indices for vqtbl4q_u8 lookup

Definition at line 105 of file video/simd/common.h.

Referenced by image_print(), image_print_16color(), image_print_16color_dithered(), and image_print_16color_dithered_with_background().

◆ creation_time

uint64_t utf8_palette_cache_s::creation_time

When cache was created (immutable)

Definition at line 112 of file video/simd/common.h.

◆ data

char* Str::data

Pointer to allocated buffer

Definition at line 145 of file ascii_simd.h.

Referenced by str_append_bytes(), str_append_c(), str_free(), str_init(), and str_reserve().

◆ dec3

dec3_t dec3[256]
extern

#include <ansi_fast.h>

Access to internal decimal lookup table for testing.

◆ dec3_initialized

bool global_dec3_cache_t::dec3_initialized

Whether cache is initialized

Definition at line 99 of file ascii_simd.h.

Referenced by init_dec3().

◆ dec3_table

◆ g_dec3_cache

global_dec3_cache_t g_dec3_cache
extern

#include <ascii_simd.h>

Global decimal cache instance.

Definition at line 23 of file ascii_simd.c.

23{.dec3_initialized = false};

Referenced by append_sgr_truecolor_bg(), append_sgr_truecolor_fg(), append_sgr_truecolor_fg_bg(), append_truecolor_bg(), append_truecolor_fg(), append_truecolor_fg_bg(), init_dec3(), and write_rgb_triplet().

◆ g_default_luminance_palette

char g_default_luminance_palette[256]
extern

#include <ascii_simd.h>

Default luminance palette (256 characters)

Definition at line 33 of file ascii_simd.c.

Referenced by benchmark_simd_color_conversion_with_source(), and init_default_luminance_palette().

◆ h

int ImageRGB::h

Image height in pixels

Definition at line 227 of file ascii_simd.h.

Referenced by alloc_image().

◆ heap_index

size_t utf8_palette_cache_s::heap_index

Min-heap eviction management (protected by write lock) Position in min-heap (for O(log n) updates)

Definition at line 115 of file video/simd/common.h.

◆ hh

UT_hash_handle utf8_palette_cache_s::hh

uthash handle (required for hash table operations)

Definition at line 119 of file video/simd/common.h.

◆ is_valid

bool utf8_palette_cache_s::is_valid

Whether cache is valid

Definition at line 107 of file video/simd/common.h.

◆ key

uint32_t utf8_palette_cache_s::key

Hash key for uthash lookup

Definition at line 102 of file video/simd/common.h.

Referenced by __attribute__().

◆ last_access_time

_Atomic uint64_t utf8_palette_cache_s::last_access_time

Thread-safe eviction tracking Nanoseconds since epoch (atomic)

Definition at line 110 of file video/simd/common.h.

Referenced by __attribute__().

◆ len [1/2]

◆ len [2/2]

size_t Str::len

Number of bytes currently used

Definition at line 146 of file ascii_simd.h.

Referenced by str_append_bytes(), str_append_c(), str_free(), and str_init().

◆ neon_time

double simd_benchmark_t::neon_time

◆ palette_hash

char utf8_palette_cache_s::palette_hash[64]

Hash of palette for cache validation

Definition at line 106 of file video/simd/common.h.

◆ pixels

uint8_t* ImageRGB::pixels

RGB data: w * h * 3 bytes

Definition at line 228 of file ascii_simd.h.

Referenced by alloc_image().

◆ runLen

int RLEState::runLen

Current run length

Definition at line 214 of file ascii_simd.h.

◆ s

◆ scalar_time

double simd_benchmark_t::scalar_time

◆ seeded

int RLEState::seeded

Whether state is initialized

Definition at line 215 of file ascii_simd.h.

◆ speedup_best

double simd_benchmark_t::speedup_best

◆ sse2_time

double simd_benchmark_t::sse2_time

◆ ssse3_time

double simd_benchmark_t::ssse3_time

◆ sve_time

double simd_benchmark_t::sve_time

SVE implementation time (seconds)

Definition at line 277 of file ascii_simd.h.

Referenced by benchmark_simd_color_conversion_with_source(), benchmark_simd_conversion(), and benchmark_simd_conversion_with_source().

◆ utf8_bytes

◆ w

int ImageRGB::w

Image width in pixels

Definition at line 226 of file ascii_simd.h.

Referenced by alloc_image().