ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
aspect_ratio.c
Go to the documentation of this file.
1
7#include "common.h"
8#include "util/math.h"
9
10#define CHAR_ASPECT 2.0f // terminal cell height ÷ width
11
12// Constants for aspect ratio calculations
13enum {
14 MIN_DIMENSION = 1 // minimum width/height to prevent zero dimensions
15};
16
17// Helper functions for aspect ratio calculations
18static inline ssize_t calc_width_from_height(ssize_t height, ssize_t img_w, ssize_t img_h) {
19 if (img_h == 0) {
20 return MIN_DIMENSION;
21 }
22
23 float width = (float)height * (float)img_w / (float)img_h * CHAR_ASPECT;
24 int result = ROUND(width);
25 return (result > 0) ? result : MIN_DIMENSION;
26}
27
28static inline ssize_t calc_height_from_width(ssize_t width, ssize_t img_w, ssize_t img_h) {
29 if (img_w == 0) {
30 return MIN_DIMENSION;
31 }
32
33 float height = ((float)width / CHAR_ASPECT) * (float)img_h / (float)img_w;
34 int result = ROUND(height);
35 return (result > 0) ? result : MIN_DIMENSION;
36}
37
38static void calculate_fit_dimensions(ssize_t img_w, ssize_t img_h, ssize_t max_w, ssize_t max_h, ssize_t *out_width,
39 ssize_t *out_height) {
40 if (!out_width || !out_height) {
41 log_error("calculate_fit_dimensions: out_width or out_height is NULL");
42 return;
43 }
44
45 // Calculate both possible dimensions
46 ssize_t width_from_height = calc_width_from_height(max_h, img_w, img_h);
47 ssize_t height_from_width = calc_height_from_width(max_w, img_w, img_h);
48
49 // Choose the option that fits within both constraints
50 if (width_from_height <= max_w) {
51 // Height-constrained: use full height, calculated width
52 *out_width = width_from_height;
53 *out_height = max_h;
54 } else {
55 // Width-constrained: use full width, calculated height
56 *out_width = max_w;
57 *out_height = height_from_width;
58 }
59
60 // Final safety check
61 if (*out_width <= 0) {
62 *out_width = MIN_DIMENSION;
63 }
64 if (*out_height <= 0) {
65 *out_height = MIN_DIMENSION;
66 }
67}
68
69void aspect_ratio(const ssize_t img_w, const ssize_t img_h, const ssize_t width, const ssize_t height,
70 const bool stretch, ssize_t *out_width, ssize_t *out_height) {
71 // Input validation
72 if (!out_width || !out_height) {
73 return; // or log error
74 }
75
76 if (img_w <= 0 || img_h <= 0) {
77 // Handle degenerate image dimensions
78 *out_width = MIN_DIMENSION;
79 *out_height = MIN_DIMENSION;
80 return;
81 }
82
83 if (stretch) {
84 // If stretching is enabled, just use the full terminal dimensions
85 *out_width = width;
86 *out_height = height;
87 } else {
88 // Calculate aspect-correct dimensions to fit within the given width/height
89 calculate_fit_dimensions(img_w, img_h, width, height, out_width, out_height);
90 }
91}
92
93// Simple aspect ratio calculation without terminal character correction
94// Used for image resizing where we want pixel-perfect aspect ratio
95void aspect_ratio2(const ssize_t img_w, const ssize_t img_h, const ssize_t target_w, const ssize_t target_h,
96 ssize_t *out_width, ssize_t *out_height) {
97 // Input validation
98 if (!out_width || !out_height) {
99 return;
100 }
101
102 if (img_w <= 0 || img_h <= 0 || target_w <= 0 || target_h <= 0) {
103 // Handle invalid dimensions
104 *out_width = MIN_DIMENSION;
105 *out_height = MIN_DIMENSION;
106 return;
107 }
108
109 // Calculate aspect ratio
110 float img_aspect = (float)img_w / (float)img_h;
111
112 // We want the image to fill at least one dimension of the target box
113 // while maintaining aspect ratio and fitting within the box
114
115 // Calculate dimensions if we scale to fill width
116 ssize_t width_if_fill_width = target_w;
117 ssize_t height_if_fill_width = (ssize_t)((float)target_w / img_aspect);
118
119 // Calculate dimensions if we scale to fill height
120 ssize_t width_if_fill_height = (ssize_t)((float)target_h * img_aspect);
121 ssize_t height_if_fill_height = target_h;
122
123 // Choose the scaling that maximizes usage of the target box
124 // We want to fill at least one dimension completely
125 if (height_if_fill_width <= target_h) {
126 // Filling width fits within height - use it
127 *out_width = width_if_fill_width;
128 *out_height = height_if_fill_width;
129 } else {
130 // Filling width would exceed height, so fill height instead
131 *out_width = width_if_fill_height;
132 *out_height = height_if_fill_height;
133 }
134
135 // Ensure minimum dimensions
136 if (*out_width <= 0) {
137 *out_width = MIN_DIMENSION;
138 }
139 if (*out_height <= 0) {
140 *out_height = MIN_DIMENSION;
141 }
142}
143
144// Calculate the best dimensions to fit an image in a terminal area while preserving aspect ratio
145// Returns the dimensions in characters/pixels (1:1 for our use case with stretch=false)
146void calculate_fit_dimensions_pixel(int img_width, int img_height, int max_width, int max_height, int *out_width,
147 int *out_height) {
148 if (!out_width || !out_height || img_width <= 0 || img_height <= 0) {
149 if (out_width)
150 *out_width = max_width;
151 if (out_height)
152 *out_height = max_height;
153 return;
154 }
155
156 float src_aspect = (float)img_width / (float)img_height;
157
158 // Try filling width
159 int width_if_fill_w = max_width;
160 int height_if_fill_w = (int)(((float)max_width / src_aspect) + 0.5f);
161
162 // Try filling height
163 int width_if_fill_h = (int)(((float)max_height * src_aspect) + 0.5f);
164 int height_if_fill_h = max_height;
165
166 // log_debug("calculate_fit_dimensions: img %dx%d (aspect %.3f), max %dx%d", img_width, img_height, src_aspect,
167 // max_width, max_height);
168 // log_debug(" Fill width: %dx%d, Fill height: %dx%d", width_if_fill_w, height_if_fill_w, width_if_fill_h,
169 // height_if_fill_h);
170
171 // Choose the option that fits
172 if (height_if_fill_w <= max_height) {
173 // Filling width fits
174 // log_debug(" Choosing fill width: %dx%d", width_if_fill_w, height_if_fill_w);
175 *out_width = width_if_fill_w;
176 *out_height = height_if_fill_w;
177 } else {
178 // Fill height instead
179 // log_debug(" Choosing fill height: %dx%d", width_if_fill_h, height_if_fill_h);
180 *out_width = width_if_fill_h;
181 *out_height = height_if_fill_h;
182 }
183
184 // Clamp to bounds
185 if (*out_width > max_width)
186 *out_width = max_width;
187 if (*out_height > max_height)
188 *out_height = max_height;
189 if (*out_width < 1)
190 *out_width = 1;
191 if (*out_height < 1)
192 *out_height = 1;
193
194 // log_debug(" Final output: %dx%d", *out_width, *out_height);
195}
#define CHAR_ASPECT
@ MIN_DIMENSION
#define log_error(...)
Log an ERROR message.
void aspect_ratio2(const ssize_t img_w, const ssize_t img_h, const ssize_t target_w, const ssize_t target_h, ssize_t *out_width, ssize_t *out_height)
Simple aspect ratio calculation without terminal character correction.
void calculate_fit_dimensions_pixel(int img_width, int img_height, int max_width, int max_height, int *out_width, int *out_height)
Calculate fit dimensions for pixel-based images.
#define ROUND(xfloat)
Round floating-point value to nearest integer.
Definition math.h:53
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.
🔢 Mathematical Utility Functions