ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
digital_rain.c File Reference

Matrix-style digital rain effect implementation. More...

Go to the source code of this file.

Macros

#define M_PI   3.14159265358979323846
 
#define SQRT_2   1.4142135623730951
 
#define SQRT_5   2.23606797749979
 

Functions

digital_rain_t * digital_rain_init (int num_columns, int num_rows)
 
void digital_rain_destroy (digital_rain_t *rain)
 
void digital_rain_reset (digital_rain_t *rain)
 
void digital_rain_set_fall_speed (digital_rain_t *rain, float speed)
 
void digital_rain_set_raindrop_length (digital_rain_t *rain, float length)
 
void digital_rain_set_color (digital_rain_t *rain, uint8_t r, uint8_t g, uint8_t b)
 
void digital_rain_set_color_from_filter (digital_rain_t *rain, color_filter_t filter)
 
char * digital_rain_apply (digital_rain_t *rain, const char *frame, float delta_time)
 

Detailed Description

Matrix-style digital rain effect implementation.

Definition in file digital_rain.c.

Macro Definition Documentation

◆ M_PI

#define M_PI   3.14159265358979323846

Definition at line 22 of file digital_rain.c.

◆ SQRT_2

#define SQRT_2   1.4142135623730951

Definition at line 25 of file digital_rain.c.

◆ SQRT_5

#define SQRT_5   2.23606797749979

Definition at line 26 of file digital_rain.c.

Function Documentation

◆ digital_rain_apply()

char * digital_rain_apply ( digital_rain_t *  rain,
const char *  frame,
float  delta_time 
)

Definition at line 369 of file digital_rain.c.

369 {
370 if (!rain || !frame) {
371 log_error("digital_rain_apply: NULL parameter");
372 return NULL;
373 }
374
375 // Update time
376 rain->time += delta_time * rain->animation_speed;
377 float sim_time = rain->time;
378
379 // Update rainbow color if rainbow mode is enabled
380 if (rain->rainbow_mode) {
381 color_filter_calculate_rainbow(sim_time, &rain->color_r, &rain->color_g, &rain->color_b);
382 }
383
384 // Allocate output buffer (input size + overhead for ANSI codes)
385 // Estimate: each character might need up to 20 bytes for ANSI codes
386 size_t input_len = strlen(frame);
387 size_t output_capacity = input_len * 20 + 1024;
388 char *output = SAFE_MALLOC(output_capacity, char *);
389 if (!output) {
390 log_error("digital_rain_apply: failed to allocate output buffer");
391 return NULL;
392 }
393
394 const char *src = frame;
395 char *dst = output;
396 size_t remaining = output_capacity;
397
398 int col = 0;
399 int row = 0;
400
401 while (*src && remaining > 100) {
402 // Try to parse and modify ANSI color sequences
403 if (*src == '\033') {
404 int r, g, b;
405 bool is_foreground;
406 const char *after = parse_ansi_color(src, &r, &g, &b, &is_foreground);
407
408 if (after) {
409 // Found a color sequence - calculate brightness for this position
410 float brightness = get_rain_brightness(rain, col, row, sim_time);
411 float brightness_below = get_rain_brightness(rain, col, row + 1, sim_time);
412 bool is_cursor = brightness > brightness_below;
413
414 // Blend with previous brightness for smooth transitions
415 if (!rain->first_frame && row < rain->num_rows && col < rain->num_columns) {
416 size_t idx = (size_t)row * (size_t)rain->num_columns + (size_t)col;
417 float prev_brightness = rain->previous_brightness[idx];
418 brightness = prev_brightness + (brightness - prev_brightness) * rain->brightness_decay;
419 rain->previous_brightness[idx] = brightness;
420 } else if (row < rain->num_rows && col < rain->num_columns) {
421 size_t idx = (size_t)row * (size_t)rain->num_columns + (size_t)col;
422 rain->previous_brightness[idx] = brightness;
423 }
424
425 // Generate modulated color
426 char ansi_buf[32];
427 int ansi_len =
428 generate_modulated_color(ansi_buf, sizeof(ansi_buf), r, g, b, brightness, is_foreground, is_cursor);
429
430 if ((size_t)ansi_len < remaining) {
431 memcpy(dst, ansi_buf, (size_t)ansi_len);
432 dst += ansi_len;
433 remaining -= (size_t)ansi_len;
434 }
435
436 src = after;
437 continue;
438 } else {
439 // Not a color sequence - copy as-is
440 const char *after_skip = skip_ansi_sequence(src);
441 size_t ansi_len = (size_t)(after_skip - src);
442 if (ansi_len < remaining) {
443 memcpy(dst, src, ansi_len);
444 dst += ansi_len;
445 remaining -= ansi_len;
446 src = after_skip;
447 } else {
448 break;
449 }
450 continue;
451 }
452 }
453
454 // Handle newline
455 if (*src == '\n') {
456 *dst++ = *src++;
457 remaining--;
458 row++;
459 col = 0;
460 continue;
461 }
462
463 // Copy regular characters - inject color for non-colored characters
464 if (remaining > 0) {
465 // Calculate brightness for this position
466 float brightness = get_rain_brightness(rain, col, row, sim_time);
467 float brightness_below = get_rain_brightness(rain, col, row + 1, sim_time);
468 bool is_cursor = brightness > brightness_below;
469
470 // Blend with previous brightness
471 if (!rain->first_frame && row < rain->num_rows && col < rain->num_columns) {
472 size_t idx = (size_t)row * (size_t)rain->num_columns + (size_t)col;
473 float prev_brightness = rain->previous_brightness[idx];
474 brightness = prev_brightness + (brightness - prev_brightness) * rain->brightness_decay;
475 rain->previous_brightness[idx] = brightness;
476 } else if (row < rain->num_rows && col < rain->num_columns) {
477 size_t idx = (size_t)row * (size_t)rain->num_columns + (size_t)col;
478 rain->previous_brightness[idx] = brightness;
479 }
480
481 // For characters without explicit color codes, use the rain's default color
482 char ansi_buf[32];
483 int ansi_len = generate_modulated_color(ansi_buf, sizeof(ansi_buf), rain->color_r, rain->color_g, rain->color_b,
484 brightness, true, is_cursor);
485
486 if ((size_t)ansi_len < remaining) {
487 memcpy(dst, ansi_buf, (size_t)ansi_len);
488 dst += ansi_len;
489 remaining -= (size_t)ansi_len;
490 }
491
492 // Decode UTF-8 character to get byte length
493 uint32_t codepoint;
494 int utf8_len = utf8_decode((const uint8_t *)src, &codepoint);
495 if (utf8_len < 0) {
496 // Invalid UTF-8, treat as single byte
497 utf8_len = 1;
498 }
499
500 // Copy all bytes of the UTF-8 character
501 for (int i = 0; i < utf8_len && *src && remaining > 0; i++) {
502 *dst++ = *src++;
503 remaining--;
504 }
505
506 // Only increment column once per character (not per byte)
507 col++;
508 } else {
509 break;
510 }
511 }
512
513 // Null-terminate
514 if (remaining > 0) {
515 *dst = '\0';
516 } else {
517 output[output_capacity - 1] = '\0';
518 }
519
520 rain->first_frame = false;
521 return output;
522}
void color_filter_calculate_rainbow(float time, uint8_t *r, uint8_t *g, uint8_t *b)
int utf8_decode(const uint8_t *s, uint32_t *codepoint)
Definition utf8.c:18

References color_filter_calculate_rainbow(), and utf8_decode().

Referenced by mirror_convert_frame(), and session_display_convert_to_ascii().

◆ digital_rain_destroy()

void digital_rain_destroy ( digital_rain_t *  rain)

Definition at line 154 of file digital_rain.c.

154 {
155 if (!rain) {
156 return;
157 }
158
159 SAFE_FREE(rain->columns);
160 SAFE_FREE(rain->previous_brightness);
161 SAFE_FREE(rain);
162}

Referenced by mirror_cleanup(), mirror_convert_frame(), and session_display_destroy().

◆ digital_rain_init()

digital_rain_t * digital_rain_init ( int  num_columns,
int  num_rows 
)

Definition at line 96 of file digital_rain.c.

96 {
97 if (num_columns <= 0 || num_rows <= 0) {
98 log_error("digital_rain_init: invalid dimensions %dx%d", num_columns, num_rows);
99 return NULL;
100 }
101
102 digital_rain_t *rain = SAFE_CALLOC(1, sizeof(digital_rain_t), digital_rain_t *);
103 if (!rain) {
104 log_error("digital_rain_init: failed to allocate context");
105 return NULL;
106 }
107
108 rain->num_columns = num_columns;
109 rain->num_rows = num_rows;
110
111 // Allocate column state array
112 rain->columns = SAFE_CALLOC((size_t)num_columns, sizeof(digital_rain_column_t), digital_rain_column_t *);
113 if (!rain->columns) {
114 log_error("digital_rain_init: failed to allocate column array");
115 SAFE_FREE(rain);
116 return NULL;
117 }
118
119 // Allocate previous brightness array
120 size_t grid_size = (size_t)num_columns * (size_t)num_rows;
121 rain->previous_brightness = SAFE_CALLOC(grid_size, sizeof(float), float *);
122 if (!rain->previous_brightness) {
123 log_error("digital_rain_init: failed to allocate brightness array");
124 SAFE_FREE(rain->columns);
125 SAFE_FREE(rain);
126 return NULL;
127 }
128
129 // Initialize per-column random offsets
130 for (int col = 0; col < num_columns; col++) {
131 digital_rain_column_t *column = &rain->columns[col];
132 column->time_offset = random_float((float)col, 0.0f) * 1000.0f;
133 column->speed_multiplier = random_float((float)col + 0.1f, 0.0f) * 0.5f + 0.5f;
134 column->phase_offset = random_float((float)col + 0.2f, 0.0f) * (float)M_PI * 2.0f;
135 }
136
137 // Set default parameters
138 rain->fall_speed = DIGITAL_RAIN_DEFAULT_FALL_SPEED;
139 rain->raindrop_length = DIGITAL_RAIN_DEFAULT_RAINDROP_LENGTH;
140 rain->brightness_decay = DIGITAL_RAIN_DEFAULT_BRIGHTNESS_DECAY;
141 rain->animation_speed = DIGITAL_RAIN_DEFAULT_ANIMATION_SPEED;
142 rain->color_r = DIGITAL_RAIN_DEFAULT_COLOR_R;
143 rain->color_g = DIGITAL_RAIN_DEFAULT_COLOR_G;
144 rain->color_b = DIGITAL_RAIN_DEFAULT_COLOR_B;
145 rain->cursor_brightness = DIGITAL_RAIN_DEFAULT_CURSOR_BRIGHTNESS;
146 rain->rainbow_mode = false;
147 rain->first_frame = true;
148 rain->time = 0.0f;
149
150 log_info("Digital rain initialized: %dx%d grid", num_columns, num_rows);
151 return rain;
152}
#define M_PI

References M_PI.

Referenced by mirror_convert_frame(), and session_display_create().

◆ digital_rain_reset()

void digital_rain_reset ( digital_rain_t *  rain)

Definition at line 164 of file digital_rain.c.

164 {
165 if (!rain) {
166 return;
167 }
168
169 rain->time = 0.0f;
170 rain->first_frame = true;
171
172 // Clear previous brightness
173 size_t grid_size = (size_t)rain->num_columns * (size_t)rain->num_rows;
174 memset(rain->previous_brightness, 0, grid_size * sizeof(float));
175}

◆ digital_rain_set_color()

void digital_rain_set_color ( digital_rain_t *  rain,
uint8_t  r,
uint8_t  g,
uint8_t  b 
)

Definition at line 193 of file digital_rain.c.

193 {
194 if (rain) {
195 rain->color_r = r;
196 rain->color_g = g;
197 rain->color_b = b;
198 }
199}

Referenced by digital_rain_set_color_from_filter().

◆ digital_rain_set_color_from_filter()

void digital_rain_set_color_from_filter ( digital_rain_t *  rain,
color_filter_t  filter 
)

Definition at line 201 of file digital_rain.c.

201 {
202 if (!rain) {
203 return;
204 }
205
206 // If no filter is set, use default Matrix green
207 if (filter == COLOR_FILTER_NONE) {
208 rain->rainbow_mode = false;
209 digital_rain_set_color(rain, DIGITAL_RAIN_DEFAULT_COLOR_R, DIGITAL_RAIN_DEFAULT_COLOR_G,
210 DIGITAL_RAIN_DEFAULT_COLOR_B);
211 return;
212 }
213
214 // Rainbow mode: enable dynamic color cycling
215 if (filter == COLOR_FILTER_RAINBOW) {
216 rain->rainbow_mode = true;
217 // Set initial color to red (will be overridden during rendering)
218 digital_rain_set_color(rain, 255, 0, 0);
219 return;
220 }
221
222 // Get color from filter metadata
223 rain->rainbow_mode = false;
224 const color_filter_def_t *filter_def = color_filter_get_metadata(filter);
225 if (filter_def) {
226 digital_rain_set_color(rain, filter_def->r, filter_def->g, filter_def->b);
227 }
228}
const color_filter_def_t * color_filter_get_metadata(color_filter_t filter)
void digital_rain_set_color(digital_rain_t *rain, uint8_t r, uint8_t g, uint8_t b)

References color_filter_get_metadata(), and digital_rain_set_color().

Referenced by mirror_convert_frame(), and session_display_create().

◆ digital_rain_set_fall_speed()

void digital_rain_set_fall_speed ( digital_rain_t *  rain,
float  speed 
)

Definition at line 181 of file digital_rain.c.

181 {
182 if (rain) {
183 rain->fall_speed = speed;
184 }
185}

◆ digital_rain_set_raindrop_length()

void digital_rain_set_raindrop_length ( digital_rain_t *  rain,
float  length 
)

Definition at line 187 of file digital_rain.c.

187 {
188 if (rain) {
189 rain->raindrop_length = length;
190 }
191}