FastLED 3.9.15
Loading...
Searching...
No Matches
upscale.cpp
Go to the documentation of this file.
1
4
5#include "fl/stdint.h"
6
7#include "crgb.h"
8#include "fl/namespace.h"
9#include "fl/upscale.h"
10#include "fl/xymap.h"
11
12namespace fl {
13
14u8 bilinearInterpolate(u8 v00, u8 v10, u8 v01, u8 v11,
15 u16 dx, u16 dy);
16
18 u8 v11, u8 dx, u8 dy);
19
20void upscaleRectangular(const CRGB *input, CRGB *output, u16 inputWidth,
21 u16 inputHeight, u16 outputWidth, u16 outputHeight) {
22 const u16 scale_factor = 256; // Using 8 bits for the fractional part
23
24 for (u16 y = 0; y < outputHeight; y++) {
25 for (u16 x = 0; x < outputWidth; x++) {
26 // Calculate the corresponding position in the input grid
27 u32 fx = ((u32)x * (inputWidth - 1) * scale_factor) /
28 (outputWidth - 1);
29 u32 fy = ((u32)y * (inputHeight - 1) * scale_factor) /
30 (outputHeight - 1);
31
32 u16 ix = fx / scale_factor; // Integer part of x
33 u16 iy = fy / scale_factor; // Integer part of y
34 u16 dx = fx % scale_factor; // Fractional part of x
35 u16 dy = fy % scale_factor; // Fractional part of y
36
37 u16 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
38 u16 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
39
40 // Direct array access - no XY mapping overhead
41 u16 i00 = iy * inputWidth + ix;
42 u16 i10 = iy * inputWidth + ix1;
43 u16 i01 = iy1 * inputWidth + ix;
44 u16 i11 = iy1 * inputWidth + ix1;
45
46 CRGB c00 = input[i00];
47 CRGB c10 = input[i10];
48 CRGB c01 = input[i01];
49 CRGB c11 = input[i11];
50
52 result.r = bilinearInterpolate(c00.r, c10.r, c01.r, c11.r, dx, dy);
53 result.g = bilinearInterpolate(c00.g, c10.g, c01.g, c11.g, dx, dy);
54 result.b = bilinearInterpolate(c00.b, c10.b, c01.b, c11.b, dx, dy);
55
56 // Direct array access - no XY mapping overhead
57 u16 idx = y * outputWidth + x;
58 output[idx] = result;
59 }
60 }
61}
62
63void upscaleRectangularPowerOf2(const CRGB *input, CRGB *output, u8 inputWidth,
64 u8 inputHeight, u8 outputWidth, u8 outputHeight) {
65 for (u8 y = 0; y < outputHeight; y++) {
66 for (u8 x = 0; x < outputWidth; x++) {
67 // Use 8-bit fixed-point arithmetic with 8 fractional bits
68 // (scale factor of 256)
69 u16 fx = ((u16)x * (inputWidth - 1) * 256) / (outputWidth - 1);
70 u16 fy = ((u16)y * (inputHeight - 1) * 256) / (outputHeight - 1);
71
72 u8 ix = fx >> 8; // Integer part
73 u8 iy = fy >> 8;
74 u8 dx = fx & 0xFF; // Fractional part
75 u8 dy = fy & 0xFF;
76
77 u8 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
78 u8 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
79
80 // Direct array access - no XY mapping overhead
81 u16 i00 = iy * inputWidth + ix;
82 u16 i10 = iy * inputWidth + ix1;
83 u16 i01 = iy1 * inputWidth + ix;
84 u16 i11 = iy1 * inputWidth + ix1;
85
86 CRGB c00 = input[i00];
87 CRGB c10 = input[i10];
88 CRGB c01 = input[i01];
89 CRGB c11 = input[i11];
90
92 result.r =
93 bilinearInterpolatePowerOf2(c00.r, c10.r, c01.r, c11.r, dx, dy);
94 result.g =
95 bilinearInterpolatePowerOf2(c00.g, c10.g, c01.g, c11.g, dx, dy);
96 result.b =
97 bilinearInterpolatePowerOf2(c00.b, c10.b, c01.b, c11.b, dx, dy);
98
99 // Direct array access - no XY mapping overhead
100 u16 idx = y * outputWidth + x;
101 output[idx] = result;
102 }
103 }
104}
105
106void upscaleArbitrary(const CRGB *input, CRGB *output, u16 inputWidth,
107 u16 inputHeight, const XYMap& xyMap) {
108 u16 n = xyMap.getTotal();
109 u16 outputWidth = xyMap.getWidth();
110 u16 outputHeight = xyMap.getHeight();
111 const u16 scale_factor = 256; // Using 8 bits for the fractional part
112
113 for (u16 y = 0; y < outputHeight; y++) {
114 for (u16 x = 0; x < outputWidth; x++) {
115 // Calculate the corresponding position in the input grid
116 u32 fx = ((u32)x * (inputWidth - 1) * scale_factor) /
117 (outputWidth - 1);
118 u32 fy = ((u32)y * (inputHeight - 1) * scale_factor) /
119 (outputHeight - 1);
120
121 u16 ix = fx / scale_factor; // Integer part of x
122 u16 iy = fy / scale_factor; // Integer part of y
123 u16 dx = fx % scale_factor; // Fractional part of x
124 u16 dy = fy % scale_factor; // Fractional part of y
125
126 u16 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
127 u16 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
128
129 u16 i00 = iy * inputWidth + ix;
130 u16 i10 = iy * inputWidth + ix1;
131 u16 i01 = iy1 * inputWidth + ix;
132 u16 i11 = iy1 * inputWidth + ix1;
133
134 CRGB c00 = input[i00];
135 CRGB c10 = input[i10];
136 CRGB c01 = input[i01];
137 CRGB c11 = input[i11];
138
139 CRGB result;
140 result.r = bilinearInterpolate(c00.r, c10.r, c01.r, c11.r, dx, dy);
141 result.g = bilinearInterpolate(c00.g, c10.g, c01.g, c11.g, dx, dy);
142 result.b = bilinearInterpolate(c00.b, c10.b, c01.b, c11.b, dx, dy);
143
144 u16 idx = xyMap.mapToIndex(x, y);
145 if (idx < n) {
146 output[idx] = result;
147 }
148 }
149 }
150}
151u8 bilinearInterpolate(u8 v00, u8 v10, u8 v01, u8 v11,
152 u16 dx, u16 dy) {
153 u16 dx_inv = 256 - dx;
154 u16 dy_inv = 256 - dy;
155
156 u32 w00 = (u32)dx_inv * dy_inv;
157 u32 w10 = (u32)dx * dy_inv;
158 u32 w01 = (u32)dx_inv * dy;
159 u32 w11 = (u32)dx * dy;
160
161 u32 sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
162
163 // Normalize the result by dividing by 65536 (shift right by 16 bits),
164 // with rounding
165 u8 result = (u8)((sum + 32768) >> 16);
166
167 return result;
168}
169
170void upscalePowerOf2(const CRGB *input, CRGB *output, u8 inputWidth,
171 u8 inputHeight, const XYMap& xyMap) {
172 u8 width = xyMap.getWidth();
173 u8 height = xyMap.getHeight();
174 if (width != xyMap.getWidth() || height != xyMap.getHeight()) {
175 // xyMap has width and height that do not fit in an u16.
176 return;
177 }
178 u16 n = xyMap.getTotal();
179
180 for (u8 y = 0; y < height; y++) {
181 for (u8 x = 0; x < width; x++) {
182 // Use 8-bit fixed-point arithmetic with 8 fractional bits
183 // (scale factor of 256)
184 u16 fx = ((u16)x * (inputWidth - 1) * 256) / (width - 1);
185 u16 fy =
186 ((u16)y * (inputHeight - 1) * 256) / (height - 1);
187
188 u8 ix = fx >> 8; // Integer part
189 u8 iy = fy >> 8;
190 u8 dx = fx & 0xFF; // Fractional part
191 u8 dy = fy & 0xFF;
192
193 u8 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
194 u8 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
195
196 u16 i00 = iy * inputWidth + ix;
197 u16 i10 = iy * inputWidth + ix1;
198 u16 i01 = iy1 * inputWidth + ix;
199 u16 i11 = iy1 * inputWidth + ix1;
200
201 CRGB c00 = input[i00];
202 CRGB c10 = input[i10];
203 CRGB c01 = input[i01];
204 CRGB c11 = input[i11];
205
206 CRGB result;
207 result.r =
208 bilinearInterpolatePowerOf2(c00.r, c10.r, c01.r, c11.r, dx, dy);
209 result.g =
210 bilinearInterpolatePowerOf2(c00.g, c10.g, c01.g, c11.g, dx, dy);
211 result.b =
212 bilinearInterpolatePowerOf2(c00.b, c10.b, c01.b, c11.b, dx, dy);
213
214 u16 idx = xyMap.mapToIndex(x, y);
215 if (idx < n) {
216 output[idx] = result;
217 }
218 }
219 }
220}
221
223 u8 v11, u8 dx, u8 dy) {
224 u16 dx_inv = 256 - dx; // 0 to 256
225 u16 dy_inv = 256 - dy; // 0 to 256
226
227 // Scale down weights to fit into u16
228 u16 w00 = (dx_inv * dy_inv) >> 8; // Max value 256
229 u16 w10 = (dx * dy_inv) >> 8;
230 u16 w01 = (dx_inv * dy) >> 8;
231 u16 w11 = (dx * dy) >> 8;
232
233 // Sum of weights should be approximately 256
234 u16 weight_sum = w00 + w10 + w01 + w11;
235
236 // Compute the weighted sum of pixel values
237 u16 sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
238
239 // Normalize the result
240 u8 result = (sum + (weight_sum >> 1)) / weight_sum;
241
242 return result;
243}
244
245// Floating-point version of bilinear interpolation
246u8 upscaleFloat(u8 v00, u8 v10, u8 v01,
247 u8 v11, float dx, float dy) {
248 float dx_inv = 1.0f - dx;
249 float dy_inv = 1.0f - dy;
250
251 // Calculate the weights for each corner
252 float w00 = dx_inv * dy_inv;
253 float w10 = dx * dy_inv;
254 float w01 = dx_inv * dy;
255 float w11 = dx * dy;
256
257 // Compute the weighted sum
258 float sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
259
260 // Clamp the result to [0, 255] and round
261 u8 result = static_cast<u8>(sum + 0.5f);
262
263 return result;
264}
265
266// Floating-point version for arbitrary grid sizes
267void upscaleArbitraryFloat(const CRGB *input, CRGB *output, u16 inputWidth,
268 u16 inputHeight, const XYMap& xyMap) {
269 u16 n = xyMap.getTotal();
270 u16 outputWidth = xyMap.getWidth();
271 u16 outputHeight = xyMap.getHeight();
272
273 for (u16 y = 0; y < outputHeight; y++) {
274 for (u16 x = 0; x < outputWidth; x++) {
275 // Map output pixel to input grid position
276 float fx =
277 static_cast<float>(x) * (inputWidth - 1) / (outputWidth - 1);
278 float fy =
279 static_cast<float>(y) * (inputHeight - 1) / (outputHeight - 1);
280
281 u16 ix = static_cast<u16>(fx);
282 u16 iy = static_cast<u16>(fy);
283 float dx = fx - ix;
284 float dy = fy - iy;
285
286 u16 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
287 u16 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
288
289 u16 i00 = iy * inputWidth + ix;
290 u16 i10 = iy * inputWidth + ix1;
291 u16 i01 = iy1 * inputWidth + ix;
292 u16 i11 = iy1 * inputWidth + ix1;
293
294 CRGB c00 = input[i00];
295 CRGB c10 = input[i10];
296 CRGB c01 = input[i01];
297 CRGB c11 = input[i11];
298
299 CRGB result;
300 result.r =
301 upscaleFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
302 result.g =
303 upscaleFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
304 result.b =
305 upscaleFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
306
307 u16 idx = xyMap.mapToIndex(x, y);
308 if (idx < n) {
309 output[idx] = result;
310 }
311 }
312 }
313}
314
315// Floating-point version for power-of-two grid sizes
316void upscaleFloat(const CRGB *input, CRGB *output, u8 inputWidth,
317 u8 inputHeight, const XYMap& xyMap) {
318 u8 outputWidth = xyMap.getWidth();
319 u8 outputHeight = xyMap.getHeight();
320 if (outputWidth != xyMap.getWidth() || outputHeight != xyMap.getHeight()) {
321 // xyMap has width and height that do not fit in a u8.
322 return;
323 }
324 u16 n = xyMap.getTotal();
325
326 for (u8 y = 0; y < outputHeight; y++) {
327 for (u8 x = 0; x < outputWidth; x++) {
328 // Map output pixel to input grid position
329 float fx =
330 static_cast<float>(x) * (inputWidth - 1) / (outputWidth - 1);
331 float fy =
332 static_cast<float>(y) * (inputHeight - 1) / (outputHeight - 1);
333
334 u8 ix = static_cast<u8>(fx);
335 u8 iy = static_cast<u8>(fy);
336 float dx = fx - ix;
337 float dy = fy - iy;
338
339 u8 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
340 u8 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
341
342 u16 i00 = iy * inputWidth + ix;
343 u16 i10 = iy * inputWidth + ix1;
344 u16 i01 = iy1 * inputWidth + ix;
345 u16 i11 = iy1 * inputWidth + ix1;
346
347 CRGB c00 = input[i00];
348 CRGB c10 = input[i10];
349 CRGB c01 = input[i01];
350 CRGB c11 = input[i11];
351
352 CRGB result;
353 result.r =
354 upscaleFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
355 result.g =
356 upscaleFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
357 result.b =
358 upscaleFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
359
360 u16 idx = xyMap.mapToIndex(x, y);
361 if (idx < n) {
362 output[idx] = result;
363 }
364 }
365 }
366}
367
368} // namespace fl
int y
Definition simple.h:93
int x
Definition simple.h:92
fl::XYMap xyMap
Definition ColorBoost.h:61
Result type for promise operations.
Defines the red, green, and blue (RGB) pixel struct.
Implements the FastLED namespace macros.
unsigned char u8
Definition int.h:17
void upscaleRectangular(const CRGB *input, CRGB *output, u16 inputWidth, u16 inputHeight, u16 outputWidth, u16 outputHeight)
Optimized upscale for rectangular/line-by-line XY maps.
Definition upscale.cpp:20
void upscaleRectangularPowerOf2(const CRGB *input, CRGB *output, u8 inputWidth, u8 inputHeight, u8 outputWidth, u8 outputHeight)
Optimized upscale for rectangular/line-by-line XY maps (power-of-2 version).
Definition upscale.cpp:63
u8 bilinearInterpolatePowerOf2(u8 v00, u8 v10, u8 v01, u8 v11, u8 dx, u8 dy)
Definition upscale.cpp:222
void upscaleArbitraryFloat(const CRGB *input, CRGB *output, u16 inputWidth, u16 inputHeight, const XYMap &xyMap)
Definition upscale.cpp:267
void upscaleArbitrary(const CRGB *input, CRGB *output, u16 inputWidth, u16 inputHeight, const XYMap &xyMap)
Performs bilinear interpolation for upscaling an image.
Definition upscale.cpp:106
u8 bilinearInterpolate(u8 v00, u8 v10, u8 v01, u8 v11, u16 dx, u16 dy)
Definition upscale.cpp:151
u8 upscaleFloat(u8 v00, u8 v10, u8 v01, u8 v11, float dx, float dy)
Definition upscale.cpp:246
void upscalePowerOf2(const CRGB *input, CRGB *output, u8 inputWidth, u8 inputHeight, const XYMap &xyMap)
Performs bilinear interpolation for upscaling an image.
Definition upscale.cpp:170
IMPORTANT!
Definition crgb.h:20
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:86