ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
render.h File Reference

Go to the source code of this file.

Data Structures

struct  ascii_temporal_cell_t
 For ascii stabilizer, this is the live record that ties cells, width, height, configuration together. The owning code creates it, updates it while work is active, and releases its handles and pointed-to storage before freeing the record. More...
struct  ascii_stabilizer_t
struct  ascii_frame_t
 When code needs to work with ascii frame, it keeps output, output_target, width, source_height here instead of scattering it across globals. Initialization establishes the handles and sizes; teardown must release any allocation, lock, thread, or callback context recorded in those fields. More...

Macros

#define ASCII_INLINE_SCRATCH_COLUMNS   512
#define ASCII_SCRATCH_PLANES   6
#define DECLARE_ASCII_PROCESSOR(isa)
#define DECLARE_HALF_PROCESSOR(isa)

Enumerations

enum  ascii_color_t { ASCII_COLOR_NONE , ASCII_COLOR_16 , ASCII_COLOR_256 , ASCII_COLOR_TRUE }

Functions

asciichat_error_t ascii_stabilizer_prepare (ascii_stabilizer_t *state, size_t width, size_t height, uint64_t configuration)
void ascii_stabilizer_reset (ascii_stabilizer_t *state)
ascii_stabilizer_t * ascii_stabilizer_set_target (ascii_stabilizer_t *state)
uint32_t ascii_stabilizer_choose (ascii_temporal_cell_t *cell, const uint8_t samples[16], uint32_t candidate, uint64_t candidate_error, uint64_t previous_error, uint32_t color, uint64_t elapsed_ns, uint32_t *stabilized_color)
char * ascii_render (const image_t *image, const char *palette, ascii_color_t color, bool background, bool dither, const simd_cells_t *ops)
bool ascii_glyph_palette_supported (const char *palette)
char * ascii_render_glyph_match (const image_t *image, ascii_color_t color, const simd_cells_t *ops, const char *palette)
char * ascii_render_glyph_match_mode (const image_t *image, ascii_color_t color, const simd_cells_t *ops, const char *palette, bool background)
char * ascii_render_halfblocks (const uint8_t *rgb, int width, int height, int stride_bytes, ascii_color_t color, const simd_cells_t *ops)
asciichat_error_t ascii_frame_init (ascii_frame_t *frame, const image_t *image, const char *palette)
void ascii_frame_emit_row (ascii_frame_t *frame, ascii_color_t mode, bool background, bool more_rows, const simd_cells_t *ops)
char * ascii_frame_finish (ascii_frame_t *frame)
char * render_ascii_cells_sse2 (const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_ssse3 (const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_avx2 (const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_neon (const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_sve (const image_t *, const char *, ascii_color_t, bool)
asciichat_error_t ascii_half_frame_init (ascii_frame_t *frame, const uint8_t *rgb, int width, int height, int *stride, ascii_color_t mode)
void ascii_frame_emit_halfrow (ascii_frame_t *frame, ascii_color_t mode, bool more_rows, const simd_cells_t *ops)
char * render_halfblocks_sse2 (const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_ssse3 (const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_avx2 (const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_neon (const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_sve (const uint8_t *, int, int, int, ascii_color_t)
outbuf_t * ascii_render_set_output_target (outbuf_t *target)

Macro Definition Documentation

◆ ASCII_INLINE_SCRATCH_COLUMNS

#define ASCII_INLINE_SCRATCH_COLUMNS   512

Definition at line 4 of file include/ascii-chat/video/ascii/render.h.

Referenced by ascii_frame_t::_Alignas().

◆ ASCII_SCRATCH_PLANES

#define ASCII_SCRATCH_PLANES   6

Definition at line 5 of file include/ascii-chat/video/ascii/render.h.

Referenced by ascii_frame_t::_Alignas().

◆ DECLARE_ASCII_PROCESSOR

#define DECLARE_ASCII_PROCESSOR ( isa)
Value:
char *render_ascii_cells_##isa(const image_t *, const char *, ascii_color_t, bool)
SIMD-friendly storage for one RGB pixel. r, g, and b hold the color channels and padding keeps the la...

Definition at line 91 of file include/ascii-chat/video/ascii/render.h.

◆ DECLARE_HALF_PROCESSOR

#define DECLARE_HALF_PROCESSOR ( isa)
Value:
char *render_halfblocks_##isa(const uint8_t *, int, int, int, ascii_color_t)
unsigned char uint8_t
Definition common.h:60

Definition at line 102 of file include/ascii-chat/video/ascii/render.h.

Enumeration Type Documentation

◆ ascii_color_t

Shared terminal-cell contract for every ISA and scalar fallback. Palettes use complete UTF-8 glyphs from cache64. Runs never cross rows. Background mode sets a contrasting foreground. Colored rows finish with SGR reset before newline; monochrome rows carry no SGR or erase sequences. The returned buffer is owned by the caller and freed with SAFE_FREE. ISA entry points select their vector stages; callers must check CPU support.

Enumerator
ASCII_COLOR_NONE 
ASCII_COLOR_16 
ASCII_COLOR_256 
ASCII_COLOR_TRUE 

Definition at line 18 of file include/ascii-chat/video/ascii/render.h.

Function Documentation

◆ ascii_frame_emit_halfrow()

void ascii_frame_emit_halfrow ( ascii_frame_t * frame,
ascii_color_t mode,
bool more_rows,
const simd_cells_t * ops )

Definition at line 453 of file lib/video/ascii/render.c.

453 {
454 if (frame->stabilizer) {
455 size_t top_row = frame->row * 2;
456 bool has_bottom = top_row + 1 < frame->source_height;
457 for (size_t x = 0; x < frame->width; ++x) {
458 uint32_t *pixels[] = {&frame->packed[x], &frame->index[x]};
459 for (size_t half = 0; half < (has_bottom ? 2u : 1u); ++half) {
460 uint32_t color = *pixels[half];
461 uint8_t luma = (uint8_t)((76u * (color >> 16) + 150u * ((color >> 8) & 255) + 29u * (color & 255)) >> 8);
462 uint8_t samples[16];
463 memset(samples, luma, sizeof(samples));
464 ascii_temporal_cell_t *cell = &frame->stabilizer->cells[(top_row + half) * frame->width + x];
465 ascii_stabilizer_choose(cell, samples, 0, 0, UINT64_MAX, color, frame->stabilizer->elapsed_ns, pixels[half]);
466 }
467 if (!has_bottom)
468 frame->index[x] = frame->packed[x];
469 }
470 if (mode == ASCII_COLOR_TRUE) {
471 memcpy(frame->fg, frame->packed, frame->width * sizeof(uint32_t));
472 memcpy(frame->bg, frame->index, frame->width * sizeof(uint32_t));
473 } else if (mode != ASCII_COLOR_NONE) {
474 ops->quantize(frame->packed, frame->fg, frame->width, mode == ASCII_COLOR_16 ? 16 : 256);
475 ops->quantize(frame->index, frame->bg, frame->width, mode == ASCII_COLOR_16 ? 16 : 256);
476 }
477 ops->halfblock(frame->packed, frame->index, frame->glyph, frame->fg, frame->bg, frame->width, (unsigned)mode,
478 frame->table);
479 }
480 emit_prepared_row(&frame->output, mode, ops, frame->glyph, frame->fg, frame->bg, frame->width);
481 emit_row_end(&frame->output, mode != ASCII_COLOR_NONE, more_rows);
482 ++frame->row;
483}
unsigned int uint32_t
Definition common.h:62
uint32_t ascii_stabilizer_choose(ascii_temporal_cell_t *cell, const uint8_t samples[16], uint32_t candidate, uint64_t candidate_error, uint64_t previous_error, uint32_t color, uint64_t elapsed_ns, uint32_t *stabilized_color)
void emit_row_end(outbuf_t *out, bool colored, bool newline)
For ascii stabilizer, this is the live record that ties cells, width, height, configuration together....
void(* halfblock)(const uint32_t *top, const uint32_t *bottom, uint32_t *glyph, uint32_t *fg, uint32_t *bg, size_t count, unsigned mode, const uint32_t keys[5])
Definition cells.h:15
void(* quantize)(const uint32_t *rgb, uint32_t *out, size_t count, unsigned colors)
Definition cells.h:14

References ASCII_COLOR_16, ASCII_COLOR_NONE, ASCII_COLOR_TRUE, ascii_stabilizer_choose(), ascii_frame_t::bg, ascii_stabilizer_t::cells, ascii_stabilizer_t::elapsed_ns, emit_row_end(), ascii_frame_t::fg, ascii_frame_t::glyph, simd_cells_t::halfblock, ascii_frame_t::index, ascii_frame_t::output, ascii_frame_t::packed, simd_cells_t::quantize, ascii_frame_t::row, ascii_frame_t::source_height, ascii_frame_t::stabilizer, ascii_frame_t::table, and ascii_frame_t::width.

Referenced by ascii_render_halfblocks().

◆ ascii_frame_emit_row()

void ascii_frame_emit_row ( ascii_frame_t * frame,
ascii_color_t mode,
bool background,
bool more_rows,
const simd_cells_t * ops )

Definition at line 340 of file lib/video/ascii/render.c.

341 {
342 if (frame->stabilizer && !frame->temporal_preselected) {
343 const uint64_t elapsed = frame->stabilizer->elapsed_ns;
344 const bool recent = elapsed <= ASCII_TEMPORAL_MAX_INTERVAL_NS;
345 const unsigned weight = recent ? (unsigned)(elapsed * 256 / (elapsed + ASCII_TEMPORAL_COLOR_TIME_NS)) : 256;
346 for (size_t x = 0; x < frame->width; ++x) {
347 ascii_temporal_cell_t *cell = &frame->stabilizer->cells[frame->row * frame->width + x];
348 uint8_t luma = frame->luma[x];
349 uint32_t glyph = frame->glyph[x];
350 uint32_t color = frame->packed[x];
351 if (cell->valid && recent && abs((int)luma - (int)cell->samples[0]) <= ASCII_TEMPORAL_MOTION_LIMIT) {
352 if (cell->glyph != glyph) {
353 // Only nearby brightness bins can win the hysteresis test. Searching
354 // outward avoids scanning the entire 64-entry palette for every cell.
355 unsigned bin = luma >> 2;
356 int best_delta = (int)luma - ((int)bin * 4 + 2);
357 uint64_t best_error = (uint64_t)(best_delta * best_delta);
358 uint64_t margin = best_error / 8 + 128;
359 for (unsigned distance = 1; distance < 64; ++distance) {
360 bool found = false;
361 for (int direction = -1; direction <= 1; direction += 2) {
362 int index = (int)bin + direction * (int)distance;
363 if (index < 0 || index >= 64 || frame->table[index] != cell->glyph)
364 continue;
365 int delta = (int)luma - (index * 4 + 2);
366 uint64_t error = (uint64_t)(delta * delta);
367 if (error <= best_error || error - best_error <= margin)
368 glyph = cell->glyph;
369 found = true;
370 }
371 // Bins farther than this cannot fall within the error margin.
372 int nearest_delta = (int)distance * 4 - 3;
373 if (found || (uint64_t)(nearest_delta * nearest_delta) > best_error + margin)
374 break;
375 }
376 }
377 color = temporal_blend_color(cell->color, color, weight);
378 }
379 cell->samples[0] = luma;
380 cell->glyph = frame->glyph[x] = glyph;
381 cell->color = frame->packed[x] = color;
382 cell->valid = true;
383 if (background)
384 frame->luma[x] =
385 (uint8_t)((77u * (color >> 16) + 150u * ((color >> 8) & 255) + 29u * (color & 255) + 128u) >> 8);
386 }
387 if (!frame->dithered && (mode == ASCII_COLOR_16 || mode == ASCII_COLOR_256))
388 ops->quantize(frame->packed, frame->index, frame->width, mode == ASCII_COLOR_16 ? 16 : 256);
389 }
390 for (size_t x = 0; x < frame->width; ++x) {
391 frame->fg[x] = frame->bg[x] = UINT32_MAX;
392 if (mode == ASCII_COLOR_NONE)
393 continue;
394 uint32_t value = mode == ASCII_COLOR_TRUE ? frame->packed[x] : frame->index[x];
395 uint32_t contrast = frame->luma[x] < 128 ? (mode == ASCII_COLOR_TRUE ? 0xffffffu : 15u) : 0;
396 if (mode == ASCII_COLOR_16) {
397 uint8_t r, g, b;
398 get_16color_rgb((uint8_t)value, &r, &g, &b);
399 contrast = (77 * r + 150 * g + 29 * b) / 256 < 127 ? 15u : 0;
400 }
401 frame->fg[x] = background ? contrast : value;
402 frame->bg[x] = background ? value : UINT32_MAX;
403 }
404 emit_prepared_row(&frame->output, mode, ops, frame->glyph, frame->fg, frame->bg, frame->width);
405 emit_row_end(&frame->output, mode != ASCII_COLOR_NONE, more_rows);
406 ++frame->row;
407}
void get_16color_rgb(uint8_t color_index, uint8_t *r, uint8_t *g, uint8_t *b)
Definition ansi.c:397
unsigned long long uint64_t
Definition common.h:63
#define ASCII_TEMPORAL_COLOR_TIME_NS
#define ASCII_TEMPORAL_MAX_INTERVAL_NS
#define ASCII_TEMPORAL_MOTION_LIMIT
static bool size_t index
Definition queries.h:8

References ASCII_COLOR_16, ASCII_COLOR_256, ASCII_COLOR_NONE, ASCII_COLOR_TRUE, ASCII_TEMPORAL_COLOR_TIME_NS, ASCII_TEMPORAL_MAX_INTERVAL_NS, ASCII_TEMPORAL_MOTION_LIMIT, ascii_frame_t::bg, ascii_stabilizer_t::cells, ascii_temporal_cell_t::color, ascii_frame_t::dithered, ascii_stabilizer_t::elapsed_ns, emit_row_end(), ascii_frame_t::fg, get_16color_rgb(), ascii_frame_t::glyph, ascii_temporal_cell_t::glyph, ascii_frame_t::index, index, ascii_frame_t::luma, ascii_frame_t::output, ascii_frame_t::packed, simd_cells_t::quantize, ascii_frame_t::row, ascii_temporal_cell_t::samples, ascii_frame_t::stabilizer, ascii_frame_t::table, ascii_frame_t::temporal_preselected, ascii_temporal_cell_t::valid, and ascii_frame_t::width.

Referenced by ascii_frame_t::_Alignas(), ascii_render(), and ascii_render_glyph_match_mode().

◆ ascii_frame_finish()

char * ascii_frame_finish ( ascii_frame_t * frame)

Definition at line 408 of file lib/video/ascii/render.c.

408 {
409 if (frame->scratch != frame->inline_scratch)
410 release_scratch(frame->scratch);
411 ob_term(&frame->output);
412 if (frame->output_target)
413 *frame->output_target = frame->output;
414 return frame->output.buf;
415}
void ob_term(outbuf_t *ob)
Append null terminator to buffer.
char * buf
Buffer pointer (allocated, owned by caller, must be freed).

References outbuf_t::buf, ob_term(), ascii_frame_t::output, ascii_frame_t::output_target, and ascii_frame_t::scratch.

Referenced by ascii_frame_t::_Alignas(), ascii_render(), ascii_render_glyph_match_mode(), and ascii_render_halfblocks().

◆ ascii_frame_init()

asciichat_error_t ascii_frame_init ( ascii_frame_t * frame,
const image_t * image,
const char * palette )

Definition at line 305 of file lib/video/ascii/render.c.

305 {
306 ASCII_OTEL_SCOPE(ASCII_FRAME_INIT);
307 outbuf_t *target = g_output_target;
308 memset(frame, 0, sizeof(*frame));
309 if (!image || !image->pixels || image->w <= 0 || image->h <= 0 || !palette || !palette[0])
310 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid image or palette");
311 size_t bytes, pixels, capacity, rows;
312 if (checked_size_mul((size_t)image->w, (size_t)image->h, &pixels) != ASCIICHAT_OK ||
313 checked_size_mul(pixels, 48u, &capacity) != ASCIICHAT_OK ||
314 checked_size_mul((size_t)image->h, 5u, &rows) != ASCIICHAT_OK ||
315 checked_size_add(capacity, rows, &capacity) != ASCIICHAT_OK ||
316 checked_size_add(capacity, 1u, &capacity) != ASCIICHAT_OK ||
317 checked_size_mul((size_t)image->w, 6 * sizeof(uint32_t), &bytes) != ASCIICHAT_OK)
318 return ERROR_INVALID_PARAM;
320 if (!cache)
321 return ERROR_INVALID_PARAM;
322 prepare_output(frame, capacity, target);
323 frame->width = (size_t)image->w;
324 if (g_stabilizer_target && g_stabilizer_target->cells && g_stabilizer_target->width == (size_t)image->w &&
325 g_stabilizer_target->height == (size_t)image->h)
326 frame->stabilizer = g_stabilizer_target;
327 for (size_t i = 0; i < 64; ++i)
328 memcpy(frame->table + i, cache->cache64[i].utf8_bytes, cache->cache64[i].byte_len);
329 frame->scratch = acquire_scratch(frame->inline_scratch, bytes);
330 if (!frame->scratch)
331 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ASCII scratch workspace");
332 frame->packed = frame->scratch;
333 frame->index = frame->packed + frame->width;
334 frame->glyph = frame->index + frame->width;
335 frame->fg = frame->glyph + frame->width;
336 frame->bg = frame->fg + frame->width;
337 frame->luma = (uint8_t *)(frame->bg + frame->width);
338 return ASCIICHAT_OK;
339}
#define SET_ERRNO(code,...)
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
uint8_t utf8_bytes[4]
utf8_palette_cache_t * get_utf8_palette_cache(const char *ascii_chars)
Get UTF-8 palette cache for a character set.
struct utf8_palette_cache utf8_palette_cache_t
utf8_char_t cache64[64]
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72
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).
Dynamic output buffer (auto-expanding).

References ASCII_OTEL_SCOPE, ASCIICHAT_OK, ascii_frame_t::bg, utf8_char_t::byte_len, utf8_palette_cache::cache64, ERROR_INVALID_PARAM, ERROR_MEMORY, ascii_frame_t::fg, get_utf8_palette_cache(), ascii_frame_t::glyph, image_t::h, ascii_frame_t::index, ascii_frame_t::luma, ascii_frame_t::packed, image_t::pixels, ascii_frame_t::scratch, SET_ERRNO, ascii_frame_t::stabilizer, ascii_frame_t::table, utf8_char_t::utf8_bytes, image_t::w, and ascii_frame_t::width.

Referenced by ascii_frame_t::_Alignas(), ascii_render(), and ascii_render_glyph_match_mode().

◆ ascii_glyph_palette_supported()

bool ascii_glyph_palette_supported ( const char * palette)

Definition at line 540 of file lib/video/ascii/render.c.

540 {
541 if (!palette || !*palette)
542 return false;
543 for (const uint8_t *p = (const uint8_t *)palette; *p;) {
544 size_t length;
545 if (glyph_palette_index(p, &length) < 0)
546 return false;
547 p += length;
548 }
549 return true;
550}

Referenced by acip_render_settings_prepare(), ascii_convert_with_buffers_stabilized(), ascii_render_glyph_match_mode(), mirror_convert_frame(), and validate_glyph_palette().

◆ ascii_half_frame_init()

asciichat_error_t ascii_half_frame_init ( ascii_frame_t * frame,
const uint8_t * rgb,
int width,
int height,
int * stride,
ascii_color_t mode )

Definition at line 417 of file lib/video/ascii/render.c.

418 {
419 ASCII_OTEL_SCOPE(ASCII_HALF_FRAME_INIT);
420 outbuf_t *target = g_output_target;
421 memset(frame, 0, sizeof(*frame));
422 size_t row_bytes, extent, bytes, capacity;
423 if (!rgb || width <= 0 || height <= 0 || mode < ASCII_COLOR_NONE || mode > ASCII_COLOR_TRUE ||
424 checked_size_mul((size_t)width, 3, &row_bytes) != ASCIICHAT_OK || row_bytes > INT_MAX)
425 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid half-block input");
426 if (*stride <= 0)
427 *stride = (int)row_bytes;
428 if ((size_t)*stride < row_bytes || checked_size_mul((size_t)*stride, (size_t)height, &extent) != ASCIICHAT_OK ||
429 checked_size_mul((size_t)width, 6 * sizeof(uint32_t), &bytes) != ASCIICHAT_OK ||
430 checked_size_mul((size_t)width, ((size_t)height + 1) / 2, &capacity) != ASCIICHAT_OK ||
431 checked_size_mul(capacity, 48u, &capacity) != ASCIICHAT_OK ||
432 checked_size_add(capacity, 1u, &capacity) != ASCIICHAT_OK)
433 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid half-block stride or size");
434 prepare_output(frame, capacity, target);
435 frame->width = (size_t)width;
436 frame->source_height = (size_t)height;
437 if (g_stabilizer_target && g_stabilizer_target->cells && g_stabilizer_target->width == (size_t)width &&
438 g_stabilizer_target->height == (size_t)height)
439 frame->stabilizer = g_stabilizer_target;
440 frame->scratch = acquire_scratch(frame->inline_scratch, bytes);
441 if (!frame->scratch)
442 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate ASCII scratch workspace");
443 frame->packed = frame->scratch;
444 frame->index = frame->packed + width;
445 frame->glyph = frame->index + width;
446 frame->fg = frame->glyph + width;
447 frame->bg = frame->fg + width;
448 const char blocks[5][4] = {"\xe2\x96\x91", "\xe2\x96\x92", "\xe2\x96\x93", "\xe2\x96\x88", "\xe2\x96\x80"};
449 for (size_t i = 0; i < 5; ++i)
450 memcpy(frame->table + i, blocks[i], 3);
451 return ASCIICHAT_OK;
452}
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23

References ASCII_COLOR_TRUE, ASCII_OTEL_SCOPE, ASCIICHAT_OK, ascii_frame_t::bg, ERROR_INVALID_PARAM, ERROR_MEMORY, ascii_frame_t::fg, ascii_frame_t::glyph, height, ascii_frame_t::index, ascii_frame_t::packed, ascii_frame_t::scratch, SET_ERRNO, ascii_frame_t::source_height, ascii_frame_t::stabilizer, ascii_frame_t::table, ascii_frame_t::width, and width.

Referenced by ascii_render_halfblocks().

◆ ascii_render()

char * ascii_render ( const image_t * image,
const char * palette,
ascii_color_t color,
bool background,
bool dither,
const simd_cells_t * ops )

Definition at line 197 of file lib/video/ascii/render.c.

198 {
199 if (!ops || mode < ASCII_COLOR_NONE || mode > ASCII_COLOR_TRUE) {
200 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid rendering backend or color mode");
201 return NULL;
202 }
203 if (!dither) {
204#if SIMD_SUPPORT_SSE2
205 if (ops == &simd_cells_sse2)
206 return render_ascii_cells_sse2(image, palette, mode, background);
207#endif
208#if SIMD_SUPPORT_SSSE3
209 if (ops == &simd_cells_ssse3)
210 return render_ascii_cells_ssse3(image, palette, mode, background);
211#endif
212#if SIMD_SUPPORT_AVX2
213 if (ops == &simd_cells_avx2)
214 return render_ascii_cells_avx2(image, palette, mode, background);
215#endif
216#if SIMD_SUPPORT_NEON
217 if (ops == &simd_cells_neon)
218 return render_ascii_cells_neon(image, palette, mode, background);
219#endif
220#if SIMD_SUPPORT_SVE
221 if (ops == &simd_cells_sve)
222 return render_ascii_cells_sve(image, palette, mode, background);
223#endif
224 }
225 ascii_frame_t frame;
226 if (ascii_frame_init(&frame, image, palette) != ASCIICHAT_OK)
227 return NULL;
228 rgb_error_t *errors = NULL;
229 if (mode == ASCII_COLOR_16 && dither)
230 errors = SAFE_CALLOC((size_t)image->w * (size_t)image->h, sizeof(rgb_error_t), rgb_error_t *);
231 frame.dithered = errors != NULL;
232 for (int y = 0; y < image->h; ++y) {
233 const uint8_t *source = (const uint8_t *)(image->pixels + (size_t)y * frame.width);
234 ops->luminance(source, frame.luma, frame.width);
235 ops->pack(source, frame.packed, frame.width);
236 ops->lookup(frame.luma, frame.table, frame.glyph, frame.width);
237 if (mode == ASCII_COLOR_16 || mode == ASCII_COLOR_256) {
238 if (errors) {
239 // Floyd-Steinberg error propagation is ordered; the other stages remain SIMD.
240 for (int x = 0; x < image->w; ++x) {
241 uint32_t pixel = frame.packed[x];
242 frame.index[x] = rgb_to_16color_dithered((int)(pixel >> 16), (int)((pixel >> 8) & 255), (int)(pixel & 255), x,
243 y, image->w, image->h, errors);
244 }
245 } else
246 ops->quantize(frame.packed, frame.index, frame.width, mode == ASCII_COLOR_16 ? 16 : 256);
247 }
248 ascii_frame_emit_row(&frame, mode, background, y + 1 < image->h, ops);
249 }
250 SAFE_FREE(errors);
251 return ascii_frame_finish(&frame);
252}
const simd_cells_t simd_cells_sve
const simd_cells_t simd_cells_sse2
const simd_cells_t simd_cells_avx2
const simd_cells_t simd_cells_ssse3
const simd_cells_t simd_cells_neon
audio_visualization_source_t source
Definition fft.c:24
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)
Definition ansi.c:428
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
char * render_ascii_cells_sve(const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_sse2(const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_ssse3(const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_neon(const image_t *, const char *, ascii_color_t, bool)
char * render_ascii_cells_avx2(const image_t *, const char *, ascii_color_t, bool)
char * ascii_frame_finish(ascii_frame_t *frame)
void ascii_frame_emit_row(ascii_frame_t *frame, ascii_color_t mode, bool background, bool more_rows, const simd_cells_t *ops)
asciichat_error_t ascii_frame_init(ascii_frame_t *frame, const image_t *image, const char *palette)
When code needs to work with ascii frame, it keeps output, output_target, width, source_height here i...
void(* pack)(const uint8_t *rgb, uint32_t *out, size_t count)
Definition cells.h:12
simd_luminance_fn luminance
Definition cells.h:11
void(* lookup)(const uint8_t *luma, const uint32_t table[64], uint32_t *out, size_t count)
Definition cells.h:13

References ASCII_COLOR_16, ASCII_COLOR_256, ASCII_COLOR_TRUE, ascii_frame_emit_row(), ascii_frame_finish(), ascii_frame_init(), ASCIICHAT_OK, ascii_frame_t::dithered, ERROR_INVALID_PARAM, ascii_frame_t::glyph, image_t::h, ascii_frame_t::index, simd_cells_t::lookup, ascii_frame_t::luma, simd_cells_t::luminance, simd_cells_t::pack, ascii_frame_t::packed, image_t::pixels, simd_cells_t::quantize, render_ascii_cells_avx2(), render_ascii_cells_neon(), render_ascii_cells_sse2(), render_ascii_cells_ssse3(), render_ascii_cells_sve(), rgb_to_16color_dithered(), SAFE_CALLOC, SAFE_FREE, SET_ERRNO, simd_cells_avx2, simd_cells_neon, simd_cells_sse2, simd_cells_ssse3, simd_cells_sve, source, ascii_frame_t::table, image_t::w, and ascii_frame_t::width.

Referenced by image_print(), image_print_16color(), image_print_16color_dithered(), image_print_16color_dithered_with_background(), image_print_256color(), image_print_color(), image_print_color_background(), image_print_color_simd(), image_print_color_utf8(), and image_print_with_capabilities().

◆ ascii_render_glyph_match()

char * ascii_render_glyph_match ( const image_t * image,
ascii_color_t color,
const simd_cells_t * ops,
const char * palette )

Definition at line 584 of file lib/video/ascii/render.c.

584 {
585 return ascii_render_glyph_match_mode(image, mode, ops, palette, false);
586}
char * ascii_render_glyph_match_mode(const image_t *image, ascii_color_t mode, const simd_cells_t *ops, const char *palette, bool background)

References ascii_render_glyph_match_mode().

◆ ascii_render_glyph_match_mode()

char * ascii_render_glyph_match_mode ( const image_t * image,
ascii_color_t color,
const simd_cells_t * ops,
const char * palette,
bool background )

Definition at line 588 of file lib/video/ascii/render.c.

589 {
590 if (!image || !image->pixels || !ops || image->w <= 0 || image->h <= 0 || image->w % 8 || image->h % 8 ||
591 mode < ASCII_COLOR_NONE || mode > ASCII_COLOR_TRUE) {
592 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid glyph matching input");
593 return NULL;
594 }
595 bool allowed[GLYPH_COUNT] = {false};
596 int candidates[GLYPH_COUNT];
597 // Restrict the atlas to unique palette entries before scoring any cells.
598 size_t candidate_count = 0;
599 int (*shape_correlation)(const int16_t[16], const int16_t[16]) =
601 if (palette) {
602 if (!ascii_glyph_palette_supported(palette)) {
603 SET_ERRNO(ERROR_INVALID_PARAM, "Glyph palette contains an unsupported character");
604 return NULL;
605 }
606 for (const uint8_t *p = (const uint8_t *)palette; *p;) {
607 size_t length;
608 int index = glyph_palette_index(p, &length);
609 if (!allowed[index]) {
610 allowed[index] = true;
611 candidates[candidate_count++] = index;
612 }
613 p += length;
614 }
615 } else {
616 for (int i = 0; i < GLYPH_ASCII_COUNT; ++i) {
617 allowed[i] = true;
618 candidates[candidate_count++] = i;
619 }
620 }
621 // Prepare coverage and its sufficient statistics once per frame.
623 // Precompute each mask's brightness and centered shape for correlation.
624 for (size_t c = 0; c < candidate_count; ++c) {
625 int g = candidates[c];
626 glyph_candidate_t *glyph = &glyphs[c];
627 glyph->key = glyph_key(g);
628 uint8_t block_mask[16];
629 const uint8_t *mask = g < GLYPH_ASCII_COUNT ? glyph_masks[g] : block_mask;
630 if (g >= GLYPH_ASCII_COUNT)
631 glyph_block_mask(g - GLYPH_ASCII_COUNT, block_mask);
632 glyph->sum = glyph->energy = 0;
633 for (int i = 0; i < 16; ++i) {
634 glyph->mask[i] = mask[i];
635 glyph->sum += mask[i];
636 }
637 int mean = glyph->sum / 16;
638 glyph->tone = glyph->sum * 255 / (g < GLYPH_ASCII_COUNT ? GLYPH_MASK_MAX_SUM : 16 * 255);
639 for (int i = 0; i < 16; ++i) {
640 glyph->centered[i] = (int16_t)((int)mask[i] - mean);
641 glyph->energy += glyph->centered[i] * glyph->centered[i];
642 }
643 }
644 image_t cells = *image;
645 cells.w /= 8;
646 cells.h /= 8;
647 ascii_frame_t frame;
648 if (ascii_frame_init(&frame, &cells, " .") != ASCIICHAT_OK)
649 return NULL;
650 frame.temporal_preselected = true;
651 for (int y = 0; y < cells.h; ++y) {
652 for (int x = 0; x < cells.w; ++x) {
653 int16_t samples[3][16] = {{0}};
654 int sums[3] = {0};
655 uint8_t patch[16];
656 int sample_mean = 0;
657 for (int sy = 0; sy < 4; ++sy) {
658 const rgb_pixel_t *top = image->pixels + (size_t)(y * 8 + sy * 2) * (size_t)image->w + (size_t)(x * 8);
659 const rgb_pixel_t *bottom = top + image->w;
660 for (int sx = 0; sx < 4; ++sx) {
661 const rgb_pixel_t a = top[sx * 2], b = top[sx * 2 + 1];
662 const rgb_pixel_t c = bottom[sx * 2], d = bottom[sx * 2 + 1];
663 int i = sy * 4 + sx;
664 int red = (a.r + b.r + c.r + d.r) / 4;
665 int green = (a.g + b.g + c.g + d.g) / 4;
666 int blue = (a.b + b.b + c.b + d.b) / 4;
667 patch[i] = (uint8_t)((77 * red + 150 * green + 29 * blue) >> 8);
668 samples[0][i] = (int16_t)red;
669 samples[1][i] = (int16_t)green;
670 samples[2][i] = (int16_t)blue;
671 sample_mean += patch[i];
672 for (int channel = 0; channel < 3; ++channel)
673 sums[channel] += samples[channel][i];
674 }
675 }
676 // Compare local shape independently of the patch's average brightness.
677 sample_mean /= 16;
678 int16_t centered_samples[16];
679 int sample_energy = 0;
680 for (int i = 0; i < 16; ++i) {
681 int centered = (int)patch[i] - sample_mean;
682 centered_samples[i] = (int16_t)centered;
683 sample_energy += centered * centered;
684 }
685 size_t best = 0, previous_index = 0;
686 uint64_t best_error = UINT64_MAX;
687 uint64_t previous_error = UINT64_MAX;
688 ascii_temporal_cell_t *previous = frame.stabilizer ? &frame.stabilizer->cells[(size_t)y * frame.width + x] : NULL;
689 for (size_t c = 0; c < candidate_count; ++c) {
690 bool is_previous = previous && previous->valid && previous->glyph == glyphs[c].key;
691 int tone_delta = sample_mean - glyphs[c].tone;
692 int tone_error = 16 * tone_delta * tone_delta;
693 // Shape error cannot lower the score below the brightness error.
694 if (!is_previous && (uint64_t)tone_error > best_error)
695 continue;
696 int dot = shape_correlation(centered_samples, glyphs[c].centered);
697 uint64_t error = (uint64_t)(tone_error + sample_energy + glyphs[c].energy - 2 * dot);
698 if (is_previous) {
699 previous_error = error;
700 previous_index = c;
701 }
702 if (error < best_error) {
703 best_error = error;
704 best = c;
705 }
706 }
707 uint32_t rgb = glyph_foreground(&glyphs[best], samples, sums);
708 uint32_t bg_rgb =
709 ((uint32_t)(sums[0] / 128) << 16) | ((uint32_t)(sums[1] / 128) << 8) | (uint32_t)(sums[2] / 128);
710 uint32_t selected = glyphs[best].key;
711 if (previous) {
712 ascii_temporal_cell_t old = *previous;
713 unsigned motion = 0;
714 unsigned color_motion = 0;
715 for (int i = 0; i < 16; ++i)
716 motion += (unsigned)abs((int)patch[i] - (int)old.samples[i]);
717 if (old.valid && mode != ASCII_COLOR_NONE) {
718 for (int channel = 0; channel < 3; ++channel)
719 for (int i = 0; i < 16; ++i)
720 color_motion += (unsigned)abs(samples[channel][i] - (int)old.rgb_samples[channel][i]);
721 color_motion /= 48;
722 }
723 uint64_t elapsed = frame.stabilizer->elapsed_ns;
724 uint64_t effective_elapsed = color_motion > ASCII_TEMPORAL_MOTION_LIMIT ? UINT64_MAX : elapsed;
725 selected = ascii_stabilizer_choose(previous, patch, selected, best_error, previous_error, rgb,
726 effective_elapsed, &rgb);
727 if (selected != glyphs[best].key) {
728 *previous = old;
729 selected = ascii_stabilizer_choose(previous, patch, glyphs[best].key, best_error, previous_error,
730 glyph_foreground(&glyphs[previous_index], samples, sums),
731 effective_elapsed, &rgb);
732 }
733 if (old.valid && motion / 16 <= ASCII_TEMPORAL_MOTION_LIMIT && color_motion <= ASCII_TEMPORAL_MOTION_LIMIT &&
735 unsigned weight = (unsigned)(elapsed * 256 / (elapsed + ASCII_TEMPORAL_COLOR_TIME_NS));
736 bg_rgb = temporal_blend_color(old.background, bg_rgb, weight);
737 }
738 for (int channel = 0; channel < 3; ++channel)
739 for (int i = 0; i < 16; ++i)
740 previous->rgb_samples[channel][i] = (uint8_t)samples[channel][i];
741 previous->background = bg_rgb;
742 }
743 frame.glyph[x] = selected;
744 frame.packed[x] = rgb;
745 frame.index[x] = bg_rgb;
746 frame.luma[x] = (uint8_t)((77 * ((rgb >> 16) & 255) + 150 * ((rgb >> 8) & 255) + 29 * (rgb & 255)) >> 8);
747 }
748 if (background && mode != ASCII_COLOR_NONE) {
749 if (mode == ASCII_COLOR_TRUE) {
750 memcpy(frame.fg, frame.packed, frame.width * sizeof(uint32_t));
751 memcpy(frame.bg, frame.index, frame.width * sizeof(uint32_t));
752 } else {
753 ops->quantize(frame.packed, frame.fg, frame.width, mode == ASCII_COLOR_16 ? 16 : 256);
754 ops->quantize(frame.index, frame.bg, frame.width, mode == ASCII_COLOR_16 ? 16 : 256);
755 }
756 emit_prepared_row(&frame.output, mode, ops, frame.glyph, frame.fg, frame.bg, frame.width);
757 emit_row_end(&frame.output, true, y + 1 < cells.h);
758 ++frame.row;
759 } else {
760 if (mode == ASCII_COLOR_16 || mode == ASCII_COLOR_256)
761 ops->quantize(frame.packed, frame.index, frame.width, mode == ASCII_COLOR_16 ? 16 : 256);
762 ascii_frame_emit_row(&frame, mode, false, y + 1 < cells.h, ops);
763 }
764 }
765 return ascii_frame_finish(&frame);
766}
int glyph_shape_correlation_scalar(const int16_t samples[16], const int16_t mask[16])
Definition cells.c:4
char cells[(512 *256 *2)]
Definition fft.c:21
#define GLYPH_MASK_MAX_SUM
#define GLYPH_COUNT
#define GLYPH_ASCII_COUNT
bool ascii_glyph_palette_supported(const char *palette)
uint8_t b
Blue color component (0-255).
uint8_t g
Green color component (0-255).
uint8_t r
Red color component (0-255).
int(* glyph_shape_correlation)(const int16_t samples[16], const int16_t mask[16])
Definition cells.h:20

References ASCII_COLOR_16, ASCII_COLOR_256, ASCII_COLOR_NONE, ASCII_COLOR_TRUE, ascii_frame_emit_row(), ascii_frame_finish(), ascii_frame_init(), ascii_glyph_palette_supported(), ascii_stabilizer_choose(), ASCII_TEMPORAL_COLOR_TIME_NS, ASCII_TEMPORAL_MAX_INTERVAL_NS, ASCII_TEMPORAL_MOTION_LIMIT, ASCIICHAT_OK, rgb_pixel_t::b, ascii_temporal_cell_t::background, ascii_frame_t::bg, ascii_stabilizer_t::cells, cells, glyph_candidate_t::centered, ascii_stabilizer_t::elapsed_ns, emit_row_end(), glyph_candidate_t::energy, ERROR_INVALID_PARAM, ascii_frame_t::fg, rgb_pixel_t::g, ascii_frame_t::glyph, GLYPH_ASCII_COUNT, GLYPH_COUNT, GLYPH_MASK_MAX_SUM, simd_cells_t::glyph_shape_correlation, glyph_shape_correlation_scalar(), image_t::h, ascii_frame_t::index, index, glyph_candidate_t::key, ascii_frame_t::luma, glyph_candidate_t::mask, ascii_frame_t::output, ascii_frame_t::packed, image_t::pixels, simd_cells_t::quantize, rgb_pixel_t::r, ascii_temporal_cell_t::rgb_samples, ascii_frame_t::row, ascii_temporal_cell_t::samples, SET_ERRNO, ascii_frame_t::stabilizer, glyph_candidate_t::sum, ascii_frame_t::temporal_preselected, glyph_candidate_t::tone, ascii_temporal_cell_t::valid, image_t::w, and ascii_frame_t::width.

Referenced by ascii_convert_with_buffers_stabilized(), and ascii_render_glyph_match().

◆ ascii_render_halfblocks()

char * ascii_render_halfblocks ( const uint8_t * rgb,
int width,
int height,
int stride_bytes,
ascii_color_t color,
const simd_cells_t * ops )

Two source rows per terminal row; the supplied backend processes both rows.

Definition at line 254 of file lib/video/ascii/render.c.

255 {
256#if SIMD_SUPPORT_SSE2
257 if (ops == &simd_cells_sse2)
258 return render_halfblocks_sse2(rgb, width, height, stride, mode);
259#endif
260#if SIMD_SUPPORT_SSSE3
261 if (ops == &simd_cells_ssse3)
262 return render_halfblocks_ssse3(rgb, width, height, stride, mode);
263#endif
264#if SIMD_SUPPORT_AVX2
265 if (ops == &simd_cells_avx2)
266 return render_halfblocks_avx2(rgb, width, height, stride, mode);
267#endif
268#if SIMD_SUPPORT_NEON
269 if (ops == &simd_cells_neon)
270 return render_halfblocks_neon(rgb, width, height, stride, mode);
271#endif
272#if SIMD_SUPPORT_SVE
273 if (ops == &simd_cells_sve)
274 return render_halfblocks_sve(rgb, width, height, stride, mode);
275#endif
276
277 if (width <= 0 || height <= 0)
278 return platform_strdup("");
279 if (!ops) {
280 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid half-block backend");
281 return NULL;
282 }
283 ascii_frame_t frame;
284 if (ascii_half_frame_init(&frame, rgb, width, height, &stride, mode) != ASCIICHAT_OK)
285 return NULL;
286 for (size_t y = 0; y < (size_t)height; y += 2) {
287 const uint8_t *top = rgb + y * (size_t)stride;
288 const uint8_t *bottom = y + 1 < (size_t)height ? top + stride : top;
289 ops->pack(top, frame.packed, frame.width);
290 ops->pack(bottom, frame.index, frame.width);
291 if (mode == ASCII_COLOR_TRUE) {
292 memcpy(frame.fg, frame.packed, frame.width * sizeof(uint32_t));
293 memcpy(frame.bg, frame.index, frame.width * sizeof(uint32_t));
294 } else if (mode != ASCII_COLOR_NONE) {
295 ops->quantize(frame.packed, frame.fg, frame.width, mode == ASCII_COLOR_16 ? 16 : 256);
296 ops->quantize(frame.index, frame.bg, frame.width, mode == ASCII_COLOR_16 ? 16 : 256);
297 }
298 ops->halfblock(frame.packed, frame.index, frame.glyph, frame.fg, frame.bg, frame.width, (unsigned)mode,
299 frame.table);
300 ascii_frame_emit_halfrow(&frame, mode, y + 2 < (size_t)height, ops);
301 }
302 return ascii_frame_finish(&frame);
303}
char * render_halfblocks_sse2(const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_ssse3(const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_avx2(const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_sve(const uint8_t *, int, int, int, ascii_color_t)
char * render_halfblocks_neon(const uint8_t *, int, int, int, ascii_color_t)
asciichat_error_t ascii_half_frame_init(ascii_frame_t *frame, const uint8_t *rgb, int width, int height, int *stride, ascii_color_t mode)
void ascii_frame_emit_halfrow(ascii_frame_t *frame, ascii_color_t mode, bool more_rows, const simd_cells_t *ops)

References ASCII_COLOR_16, ASCII_COLOR_NONE, ASCII_COLOR_TRUE, ascii_frame_emit_halfrow(), ascii_frame_finish(), ascii_half_frame_init(), ASCIICHAT_OK, ascii_frame_t::bg, ERROR_INVALID_PARAM, ascii_frame_t::fg, ascii_frame_t::glyph, simd_cells_t::halfblock, height, ascii_frame_t::index, simd_cells_t::pack, ascii_frame_t::packed, simd_cells_t::quantize, render_halfblocks_avx2(), render_halfblocks_neon(), render_halfblocks_sse2(), render_halfblocks_ssse3(), render_halfblocks_sve(), SET_ERRNO, simd_cells_avx2, simd_cells_neon, simd_cells_sse2, simd_cells_ssse3, simd_cells_sve, ascii_frame_t::table, ascii_frame_t::width, and width.

Referenced by ascii_convert(), image_print_with_capabilities(), rgb_to_16color_halfblocks_scalar(), rgb_to_256color_halfblocks_scalar(), rgb_to_halfblocks_scalar(), and rgb_to_truecolor_halfblocks_scalar().

◆ ascii_render_set_output_target()

outbuf_t * ascii_render_set_output_target ( outbuf_t * target)

Internal one-shot output target, consumed by the next frame initializer. Returns the prior target so capability conversion can restore its scope.

Definition at line 126 of file lib/video/ascii/render.c.

126 {
127 outbuf_t *previous = g_output_target;
128 g_output_target = target;
129 return previous;
130}

Referenced by ascii_convert_with_buffers_stabilized().

◆ ascii_stabilizer_choose()

uint32_t ascii_stabilizer_choose ( ascii_temporal_cell_t * cell,
const uint8_t samples[16],
uint32_t candidate,
uint64_t candidate_error,
uint64_t previous_error,
uint32_t color,
uint64_t elapsed_ns,
uint32_t * stabilized_color )

Choose a glyph using current and previous candidate errors and patch motion.

Definition at line 97 of file lib/video/ascii/render.c.

99 {
100 unsigned motion = 0;
101 if (cell->valid) {
102 for (size_t i = 0; i < 16; ++i)
103 motion += (unsigned)abs((int)samples[i] - (int)cell->samples[i]);
104 motion /= 16;
105 }
106 uint32_t result = candidate;
107 uint32_t output_color = color;
108 if (cell->valid && motion <= ASCII_TEMPORAL_MOTION_LIMIT && elapsed_ns <= ASCII_TEMPORAL_MAX_INTERVAL_NS) {
109 uint64_t margin = candidate_error / 8 + 128;
110 if (previous_error != UINT64_MAX &&
111 (previous_error <= candidate_error || previous_error - candidate_error <= margin))
112 result = cell->glyph;
113 // Weight depends on elapsed time, avoiding a different response at each FPS.
114 unsigned weight = (unsigned)(elapsed_ns * 256 / (elapsed_ns + ASCII_TEMPORAL_COLOR_TIME_NS));
115 output_color = temporal_blend_color(cell->color, color, weight);
116 }
117 memcpy(cell->samples, samples, sizeof(cell->samples));
118 cell->glyph = result;
119 cell->color = output_color;
120 cell->valid = true;
121 if (stabilized_color)
122 *stabilized_color = output_color;
123 return result;
124}

References ASCII_TEMPORAL_COLOR_TIME_NS, ASCII_TEMPORAL_MAX_INTERVAL_NS, ASCII_TEMPORAL_MOTION_LIMIT, ascii_temporal_cell_t::color, ascii_temporal_cell_t::glyph, ascii_temporal_cell_t::samples, and ascii_temporal_cell_t::valid.

Referenced by ascii_frame_emit_halfrow(), and ascii_render_glyph_match_mode().

◆ ascii_stabilizer_prepare()

asciichat_error_t ascii_stabilizer_prepare ( ascii_stabilizer_t * state,
size_t width,
size_t height,
uint64_t configuration )

Prepare history for a viewport and rendering contract, resetting it on change.

Definition at line 67 of file lib/video/ascii/render.c.

68 {
69 if (!state || !width || !height || width > SIZE_MAX / height ||
70 width * height > SIZE_MAX / sizeof(ascii_temporal_cell_t))
71 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid stabilization viewport");
72 if (state->cells && state->width == width && state->height == height && state->configuration == configuration)
73 return ASCIICHAT_OK;
75 if (!cells)
76 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate stabilization history");
77 SAFE_FREE(state->cells);
78 state->cells = cells;
79 state->width = width;
80 state->height = height;
81 state->configuration = configuration;
82 state->last_timestamp_ns = 0;
83 state->elapsed_ns = UINT64_MAX;
84 return ASCIICHAT_OK;
85}

References ASCIICHAT_OK, cells, ERROR_INVALID_PARAM, ERROR_MEMORY, height, SAFE_CALLOC, SAFE_FREE, SET_ERRNO, and width.

Referenced by ascii_convert_with_buffers_stabilized().

◆ ascii_stabilizer_reset()

void ascii_stabilizer_reset ( ascii_stabilizer_t * state)

Definition at line 60 of file lib/video/ascii/render.c.

60 {
61 if (!state)
62 return;
63 SAFE_FREE(state->cells);
64 memset(state, 0, sizeof(*state));
65}

References SAFE_FREE.

Referenced by mirror_convert_frame(), session_display_destroy(), session_display_reset_stabilizer(), session_host_destroy(), and session_render_reset_stabilizer().

◆ ascii_stabilizer_set_target()

ascii_stabilizer_t * ascii_stabilizer_set_target ( ascii_stabilizer_t * state)

Select history for the current thread until the previous target is restored.

Definition at line 54 of file lib/video/ascii/render.c.

54 {
55 ascii_stabilizer_t *previous = g_stabilizer_target;
56 g_stabilizer_target = state;
57 return previous;
58}

Referenced by ascii_convert_with_buffers_stabilized().

◆ render_ascii_cells_avx2()

char * render_ascii_cells_avx2 ( const image_t * ,
const char * ,
ascii_color_t ,
bool  )

Referenced by ascii_render().

◆ render_ascii_cells_neon()

char * render_ascii_cells_neon ( const image_t * ,
const char * ,
ascii_color_t ,
bool  )

Referenced by ascii_render().

◆ render_ascii_cells_sse2()

char * render_ascii_cells_sse2 ( const image_t * ,
const char * ,
ascii_color_t ,
bool  )

Referenced by ascii_render().

◆ render_ascii_cells_ssse3()

char * render_ascii_cells_ssse3 ( const image_t * ,
const char * ,
ascii_color_t ,
bool  )

Referenced by ascii_render().

◆ render_ascii_cells_sve()

char * render_ascii_cells_sve ( const image_t * ,
const char * ,
ascii_color_t ,
bool  )

References height, and width.

Referenced by ascii_render().

◆ render_halfblocks_avx2()

char * render_halfblocks_avx2 ( const uint8_t * ,
int ,
int ,
int ,
ascii_color_t  )

Referenced by ascii_render_halfblocks().

◆ render_halfblocks_neon()

char * render_halfblocks_neon ( const uint8_t * ,
int ,
int ,
int ,
ascii_color_t  )

Referenced by ascii_render_halfblocks().

◆ render_halfblocks_sse2()

char * render_halfblocks_sse2 ( const uint8_t * ,
int ,
int ,
int ,
ascii_color_t  )

Referenced by ascii_render_halfblocks().

◆ render_halfblocks_ssse3()

char * render_halfblocks_ssse3 ( const uint8_t * ,
int ,
int ,
int ,
ascii_color_t  )

Referenced by ascii_render_halfblocks().

◆ render_halfblocks_sve()

char * render_halfblocks_sve ( const uint8_t * ,
int ,
int ,
int ,
ascii_color_t  )

Referenced by ascii_render_halfblocks().