ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
video/rgba/image.c
Go to the documentation of this file.
1
6
7#include <stdbool.h>
8#include <stddef.h>
9#include <stdint.h>
10#include <limits.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <math.h>
15#ifndef _WIN32
16#include <unistd.h>
17#endif
18
19#include <ascii-chat/common.h>
26
29#include <ascii-chat/buffer.h> // For buffer domain allocation functions
33
34// NOTE: luminance_palette is now passed as parameter to functions instead of using global cache
35
36// ansi_fast functions are declared in ansi_fast.h (already included)
37
38image_t *image_new(size_t width, size_t height) {
40 return NULL;
41 size_t pixels_size;
42 if (checked_size_mul3(width, height, sizeof(rgb_pixel_t), &pixels_size) != ASCIICHAT_OK ||
43 pixels_size > IMAGE_MAX_PIXELS_SIZE)
44 return NULL;
45 const size_t offset = (sizeof(image_t) + BUFFER_DEFAULT_ALIGNMENT - 1) & ~(size_t)(BUFFER_DEFAULT_ALIGNMENT - 1);
46 size_t total;
47 if (checked_size_add(offset, pixels_size, &total) != ASCIICHAT_OK)
48 return NULL;
49 image_t *image = buffer_alloc(NULL, total);
50 if (!image)
51 return NULL;
52 image->w = (int)width;
53 image->h = (int)height;
54 image->pixels = (rgb_pixel_t *)((uint8_t *)image + offset);
55 return image;
56}
57
58void image_destroy(image_t *image) {
59 buffer_release(image);
60}
61
63 if (!p || !p->pixels) {
64 SET_ERRNO(ERROR_INVALID_PARAM, "p or p->pixels is NULL");
65 return;
66 }
67 // Check for integer overflow before multiplication.
68 unsigned long w_ul = (unsigned long)p->w;
69 unsigned long h_ul = (unsigned long)p->h;
70 if (w_ul > 0 && h_ul > ULONG_MAX / w_ul) {
71 SET_ERRNO(ERROR_INVALID_PARAM, "dimensions overflow: %d x %d", p->w, p->h);
72 return;
73 }
74 unsigned long pixel_count = w_ul * h_ul;
75 if (pixel_count > ULONG_MAX / sizeof(rgb_pixel_t)) {
76 SET_ERRNO(ERROR_INVALID_PARAM, "buffer size overflow");
77 return;
78 }
79 size_t clear_size = pixel_count * sizeof(rgb_pixel_t);
80 SAFE_MEMSET(p->pixels, clear_size, 0, clear_size);
81}
82
84 if (!source) {
85 SET_ERRNO(ERROR_INVALID_PARAM, "source is NULL");
86 return NULL;
87 }
88
89 // Create new image with same dimensions
90 image_t *copy = image_new((size_t)source->w, (size_t)source->h);
91 if (!copy) {
92 return NULL;
93 }
94
95 // Copy pixel data from source to copy
96 if (source->pixels && copy->pixels) {
97 size_t pixel_count = (size_t)source->w * (size_t)source->h;
98 size_t pixels_size = pixel_count * sizeof(rgb_pixel_t);
99 memcpy(copy->pixels, source->pixels, pixels_size);
100 }
101
102 return copy;
103}
104
105inline rgb_pixel_t *image_pixel(image_t *p, const int x, const int y) {
106 // Add bounds checking to prevent buffer overflow on invalid coordinates
107 if (!p || !p->pixels || x < 0 || x >= p->w || y < 0 || y >= p->h) {
108 return NULL;
109 }
110 return &p->pixels[x + y * p->w];
111}
112
113void image_resize(const image_t *s, image_t *d) {
114 if (!s || !d) {
115 SET_ERRNO(ERROR_INVALID_PARAM, "s or d is NULL");
116 return;
117 }
118
120}
121
122// Optimized interpolation function with better integer arithmetic and memory
123// access
125 if (!source || !dest || !source->pixels || !dest->pixels) {
126 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
127 return;
128 }
129
130 const int src_w = source->w;
131 const int src_h = source->h;
132 const int dst_w = dest->w;
133 const int dst_h = dest->h;
134
135 // Handle edge cases
136 if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
137 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid image dimensions for resize: src=%dx%d dst=%dx%d", src_w, src_h, dst_w,
138 dst_h);
139 return;
140 }
141
142 // Defensive checks to prevent invalid shift (UBSan protection)
143 if (src_w < 1 || src_h < 1 || dst_w < 1 || dst_h < 1) {
144 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid dimensions detected after first check");
145 return;
146 }
147
148 // Use fixed-point arithmetic for better performance
149 // Use uint64_t intermediate to prevent overflow when shifting large dimensions
150 const uint32_t x_ratio = (uint32_t)((((uint64_t)src_w << 16) / (uint64_t)dst_w) + 1);
151 const uint32_t y_ratio = (uint32_t)((((uint64_t)src_h << 16) / (uint64_t)dst_h) + 1);
152
153 const rgb_pixel_t *src_pixels = source->pixels;
154 rgb_pixel_t *dst_pixels = dest->pixels;
155
156 for (int y = 0; y < dst_h; y++) {
157 const uint32_t src_y = ((uint32_t)(unsigned int)y * y_ratio) >> 16;
158 // Explicitly clamp to valid range [0, src_h-1]
159 const uint32_t safe_src_y = src_y >= (uint32_t)(unsigned int)src_h ? (uint32_t)(src_h - 1) : src_y;
160
161 // Bounds check: ensure safe_src_y is valid
162 if (safe_src_y >= (uint32_t)(unsigned int)src_h) {
163 SET_ERRNO(ERROR_INVALID_PARAM, "safe_src_y out of bounds: %u >= %d", safe_src_y, src_h);
164 return;
165 }
166
167 const rgb_pixel_t *src_row = src_pixels + (safe_src_y * (size_t)src_w);
168
169 rgb_pixel_t *dst_row = dst_pixels + ((size_t)y * (size_t)dst_w);
170
171 for (int x = 0; x < dst_w; x++) {
172 const uint32_t src_x = ((uint32_t)(unsigned int)x * x_ratio) >> 16;
173 // Explicitly clamp to valid range [0, src_w-1]
174 const uint32_t safe_src_x = src_x >= (uint32_t)(unsigned int)src_w ? (uint32_t)(src_w - 1) : src_x;
175
176 // Bounds check: ensure safe_src_x is valid
177 if (safe_src_x >= (uint32_t)(unsigned int)src_w) {
178 SET_ERRNO(ERROR_INVALID_PARAM, "safe_src_x out of bounds: %u >= %d", safe_src_x, src_w);
179 return;
180 }
181
182 dst_row[x] = src_row[safe_src_x];
183 }
184 }
185}
186
187// Note: luminance_palette is now handled by ascii_simd.c via g_ascii_cache.luminance_palette
188
189void precalc_rgb_palettes(const float red, const float green, const float blue) {
190 // Validate input parameters to prevent overflow
191 // Luminance weights should typically be in range 0.0-1.0
192 // But allow slightly larger values for brightness adjustment
193 const float max_weight = 255.0f; // Maximum value that won't overflow when multiplied by 255
194 const float min_weight = -255.0f; // Allow negative for color correction
195
196 // Note: isfinite() checks are skipped in Release builds due to -ffinite-math-only flag
197 // which disables NaN/infinity support for performance. Range checks below are sufficient.
198#if !defined(NDEBUG) && !defined(__FAST_MATH__)
199 if (!isfinite(red) || !isfinite(green) || !isfinite(blue)) {
200 log_error("Invalid weight values (non-finite): red=%f, green=%f, blue=%f", red, green, blue);
201 SET_ERRNO(ERROR_INVALID_PARAM, "non-finite weight values");
202 return;
203 }
204#endif
205
206 if (red < min_weight || red > max_weight || green < min_weight || green > max_weight || blue < min_weight ||
207 blue > max_weight) {
208 log_warn("Weight values out of expected range: red=%f, green=%f, blue=%f (clamping to safe range)", red, green,
209 blue);
210 }
211
212 // A corrupted startup value must not turn into a black/invalid palette.
213 // Restore the canonical luminance weights instead of clamping it to +/-255.
214 const bool invalid_weight = red < min_weight || red > max_weight || green < min_weight || green > max_weight ||
215 blue < min_weight || blue > max_weight;
216 const float effective_red = invalid_weight ? 0.2989f : red;
217 const float effective_green = invalid_weight ? 0.5866f : green;
218 const float effective_blue = invalid_weight ? 0.1145f : blue;
219
220 // Clamp weights to safe range to prevent overflow
221 const float safe_red =
222 (effective_red < min_weight) ? min_weight : ((effective_red > max_weight) ? max_weight : effective_red);
223 const float safe_green =
224 (effective_green < min_weight) ? min_weight : ((effective_green > max_weight) ? max_weight : effective_green);
225 const float safe_blue =
226 (effective_blue < min_weight) ? min_weight : ((effective_blue > max_weight) ? max_weight : effective_blue);
227
228 const unsigned short max_ushort = 65535;
229 const unsigned short min_ushort = 0;
230
231 for (int n = 0; n < ASCII_LUMINANCE_LEVELS; ++n) {
232 // Compute with float, then clamp to unsigned short range
233 float red_val = (float)n * safe_red;
234 float green_val = (float)n * safe_green;
235 float blue_val = (float)n * safe_blue;
236
237 // Clamp to unsigned short range before assignment
238 if (red_val < (float)min_ushort) {
239 red_val = (float)min_ushort;
240 } else if (red_val > (float)max_ushort) {
241 red_val = (float)max_ushort;
242 }
243
244 if (green_val < (float)min_ushort) {
245 green_val = (float)min_ushort;
246 } else if (green_val > (float)max_ushort) {
247 green_val = (float)max_ushort;
248 }
249
250 if (blue_val < (float)min_ushort) {
251 blue_val = (float)min_ushort;
252 } else if (blue_val > (float)max_ushort) {
253 blue_val = (float)max_ushort;
254 }
255
256 RED[n] = (unsigned short)red_val;
257 GREEN[n] = (unsigned short)green_val;
258 BLUE[n] = (unsigned short)blue_val;
259 GRAY[n] = (unsigned short)n;
260 }
261}
ANSI escape sequence utilities and fast color code generation.
#define SET_ERRNO(code,...)
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
#define BUFFER_DEFAULT_ALIGNMENT
Definition buffer.h:26
#define buffer_release(data)
Definition buffer.h:91
⚙️ Common definitions, error codes, macros, and types shared throughout the application
audio_visualization_source_t source
Definition fft.c:24
unsigned int height
Definition fft.c:23
unsigned int width
Definition fft.c:23
unsigned int uint32_t
Definition common.h:62
#define SAFE_MEMSET(dest, dest_size, ch, count)
Definition common.h:484
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
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.
image_t * image_new_copy(const image_t *source)
Create a copy of an existing image.
void precalc_rgb_palettes(const float red, const float green, const float blue)
Precalculate RGB palettes with color adjustment.
void image_resize_interpolation(const image_t *source, image_t *dest)
Resize image using bilinear interpolation.
#define ASCII_LUMINANCE_LEVELS
Number of luminance levels supported (256).
Definition ascii.h:440
#define IMAGE_MAX_PIXELS_SIZE
Maximum pixel data size in bytes.
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.
void image_destroy(image_t *image)
Destroy an image allocated with image_new().
image_t * image_new(size_t width, size_t height)
Create a new image with standard allocation.
🔢 Mathematical Utility Functions
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
✅ Safe Integer Arithmetic and Overflow Detection
🔤 String Manipulation and Shell Escaping Utilities
SIMD-friendly storage for one RGB pixel. r, g, and b hold the color channels and padding keeps the la...
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).
asciichat_error_t image_validate_dimensions(size_t width, size_t height)
Validate image dimensions (non-zero, within limits).
Definition util/image.c:100
🖼️ Safe overflow-checked buffer size calculations for images and video frames
SIMD-optimized ASCII conversion interface.
rgb_pixel_t * image_pixel(image_t *p, const int x, const int y)