FastLED 3.9.3
Loading...
Searching...
No Matches
bilinear_expansion.cpp
1
5
6#include <stdint.h>
7
8#include "bilinear_expansion.h"
9#include "crgb.h"
10#include "namespace.h"
11#include "xymap.h"
12
13
14FASTLED_NAMESPACE_BEGIN
15
16
17uint8_t bilinearInterpolate(uint8_t v00, uint8_t v10, uint8_t v01,
18 uint8_t v11, uint16_t dx, uint16_t dy);
19
20uint8_t bilinearInterpolatePowerOf2(uint8_t v00, uint8_t v10, uint8_t v01,
21 uint8_t v11, uint8_t dx, uint8_t dy);
22
23
24void bilinearExpandArbitrary(const CRGB *input, CRGB *output, uint16_t inputWidth,
25 uint16_t inputHeight, XYMap xyMap) {
26 uint16_t n = xyMap.getTotal();
27 uint16_t outputWidth = xyMap.getWidth();
28 uint16_t outputHeight = xyMap.getHeight();
29 const uint16_t scale_factor = 256; // Using 8 bits for the fractional part
30
31 for (uint16_t y = 0; y < outputHeight; y++) {
32 for (uint16_t x = 0; x < outputWidth; x++) {
33 // Calculate the corresponding position in the input grid
34 uint32_t fx = ((uint32_t)x * (inputWidth - 1) * scale_factor) /
35 (outputWidth - 1);
36 uint32_t fy = ((uint32_t)y * (inputHeight - 1) * scale_factor) /
37 (outputHeight - 1);
38
39 uint16_t ix = fx / scale_factor; // Integer part of x
40 uint16_t iy = fy / scale_factor; // Integer part of y
41 uint16_t dx = fx % scale_factor; // Fractional part of x
42 uint16_t dy = fy % scale_factor; // Fractional part of y
43
44 uint16_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
45 uint16_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
46
47 uint16_t i00 = iy * inputWidth + ix;
48 uint16_t i10 = iy * inputWidth + ix1;
49 uint16_t i01 = iy1 * inputWidth + ix;
50 uint16_t i11 = iy1 * inputWidth + ix1;
51
52 CRGB c00 = input[i00];
53 CRGB c10 = input[i10];
54 CRGB c01 = input[i01];
55 CRGB c11 = input[i11];
56
57 CRGB result;
58 result.r = bilinearInterpolate(c00.r, c10.r, c01.r, c11.r, dx, dy);
59 result.g = bilinearInterpolate(c00.g, c10.g, c01.g, c11.g, dx, dy);
60 result.b = bilinearInterpolate(c00.b, c10.b, c01.b, c11.b, dx, dy);
61
62 uint16_t idx = xyMap.mapToIndex(x, y);
63 if (idx < n) {
64 output[idx] = result;
65 }
66 }
67 }
68}
69uint8_t bilinearInterpolate(uint8_t v00, uint8_t v10, uint8_t v01, uint8_t v11,
70 uint16_t dx, uint16_t dy) {
71 uint16_t dx_inv = 256 - dx;
72 uint16_t dy_inv = 256 - dy;
73
74 uint32_t w00 = (uint32_t)dx_inv * dy_inv;
75 uint32_t w10 = (uint32_t)dx * dy_inv;
76 uint32_t w01 = (uint32_t)dx_inv * dy;
77 uint32_t w11 = (uint32_t)dx * dy;
78
79 uint32_t sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
80
81 // Normalize the result by dividing by 65536 (shift right by 16 bits),
82 // with rounding
83 uint8_t result = (uint8_t)((sum + 32768) >> 16);
84
85 return result;
86}
87
88void bilinearExpandPowerOf2(const CRGB *input, CRGB *output, uint8_t inputWidth, uint8_t inputHeight, XYMap xyMap) {
89 uint8_t width = xyMap.getWidth();
90 uint8_t height = xyMap.getHeight();
91 if (width != xyMap.getWidth() || height != xyMap.getHeight()) {
92 // xyMap has width and height that do not fit in an uint16_t.
93 return;
94 }
95 uint16_t n = xyMap.getTotal();
96
97 for (uint8_t y = 0; y < height; y++) {
98 for (uint8_t x = 0; x < width; x++) {
99 // Use 8-bit fixed-point arithmetic with 8 fractional bits
100 // (scale factor of 256)
101 uint16_t fx = ((uint16_t)x * (inputWidth - 1) * 256) / (width - 1);
102 uint16_t fy =
103 ((uint16_t)y * (inputHeight - 1) * 256) / (height - 1);
104
105 uint8_t ix = fx >> 8; // Integer part
106 uint8_t iy = fy >> 8;
107 uint8_t dx = fx & 0xFF; // Fractional part
108 uint8_t dy = fy & 0xFF;
109
110 uint8_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
111 uint8_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
112
113 uint16_t i00 = iy * inputWidth + ix;
114 uint16_t i10 = iy * inputWidth + ix1;
115 uint16_t i01 = iy1 * inputWidth + ix;
116 uint16_t i11 = iy1 * inputWidth + ix1;
117
118 CRGB c00 = input[i00];
119 CRGB c10 = input[i10];
120 CRGB c01 = input[i01];
121 CRGB c11 = input[i11];
122
123 CRGB result;
124 result.r = bilinearInterpolatePowerOf2(c00.r, c10.r, c01.r, c11.r, dx, dy);
125 result.g = bilinearInterpolatePowerOf2(c00.g, c10.g, c01.g, c11.g, dx, dy);
126 result.b = bilinearInterpolatePowerOf2(c00.b, c10.b, c01.b, c11.b, dx, dy);
127
128 uint16_t idx = xyMap.mapToIndex(x, y);
129 if (idx < n) {
130 output[idx] = result;
131 }
132 }
133 }
134}
135
136uint8_t bilinearInterpolatePowerOf2(uint8_t v00, uint8_t v10, uint8_t v01,
137 uint8_t v11, uint8_t dx, uint8_t dy) {
138 uint16_t dx_inv = 256 - dx; // 0 to 256
139 uint16_t dy_inv = 256 - dy; // 0 to 256
140
141 // Scale down weights to fit into uint16_t
142 uint16_t w00 = (dx_inv * dy_inv) >> 8; // Max value 256
143 uint16_t w10 = (dx * dy_inv) >> 8;
144 uint16_t w01 = (dx_inv * dy) >> 8;
145 uint16_t w11 = (dx * dy) >> 8;
146
147 // Sum of weights should be approximately 256
148 uint16_t weight_sum = w00 + w10 + w01 + w11;
149
150 // Compute the weighted sum of pixel values
151 uint16_t sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
152
153 // Normalize the result
154 uint8_t result = (sum + (weight_sum >> 1)) / weight_sum;
155
156 return result;
157}
158
159
164
165#include "bilinear_expansion.h"
166#include "crgb.h"
167#include "namespace.h"
168#include "xymap.h"
169#include <stdint.h>
170
171FASTLED_NAMESPACE_BEGIN
172
173// Floating-point version of bilinear interpolation
174uint8_t bilinearInterpolateFloat(uint8_t v00, uint8_t v10, uint8_t v01,
175 uint8_t v11, float dx, float dy) {
176 float dx_inv = 1.0f - dx;
177 float dy_inv = 1.0f - dy;
178
179 // Calculate the weights for each corner
180 float w00 = dx_inv * dy_inv;
181 float w10 = dx * dy_inv;
182 float w01 = dx_inv * dy;
183 float w11 = dx * dy;
184
185 // Compute the weighted sum
186 float sum = v00 * w00 + v10 * w10 + v01 * w01 + v11 * w11;
187
188 // Clamp the result to [0, 255] and round
189 uint8_t result = static_cast<uint8_t>(sum + 0.5f);
190
191 return result;
192}
193
194// Floating-point version for arbitrary grid sizes
195void bilinearExpandArbitraryFloat(const CRGB *input, CRGB *output,
196 uint16_t inputWidth, uint16_t inputHeight,
197 XYMap xyMap) {
198 uint16_t n = xyMap.getTotal();
199 uint16_t outputWidth = xyMap.getWidth();
200 uint16_t outputHeight = xyMap.getHeight();
201
202 for (uint16_t y = 0; y < outputHeight; y++) {
203 for (uint16_t x = 0; x < outputWidth; x++) {
204 // Map output pixel to input grid position
205 float fx = static_cast<float>(x) * (inputWidth - 1) / (outputWidth - 1);
206 float fy = static_cast<float>(y) * (inputHeight - 1) / (outputHeight - 1);
207
208 uint16_t ix = static_cast<uint16_t>(fx);
209 uint16_t iy = static_cast<uint16_t>(fy);
210 float dx = fx - ix;
211 float dy = fy - iy;
212
213 uint16_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
214 uint16_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
215
216 uint16_t i00 = iy * inputWidth + ix;
217 uint16_t i10 = iy * inputWidth + ix1;
218 uint16_t i01 = iy1 * inputWidth + ix;
219 uint16_t i11 = iy1 * inputWidth + ix1;
220
221 CRGB c00 = input[i00];
222 CRGB c10 = input[i10];
223 CRGB c01 = input[i01];
224 CRGB c11 = input[i11];
225
226 CRGB result;
227 result.r = bilinearInterpolateFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
228 result.g = bilinearInterpolateFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
229 result.b = bilinearInterpolateFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
230
231 uint16_t idx = xyMap.mapToIndex(x, y);
232 if (idx < n) {
233 output[idx] = result;
234 }
235 }
236 }
237}
238
239// Floating-point version for power-of-two grid sizes
240void bilinearExpandFloat(const CRGB *input, CRGB *output,
241 uint8_t inputWidth, uint8_t inputHeight,
242 XYMap xyMap) {
243 uint8_t outputWidth = xyMap.getWidth();
244 uint8_t outputHeight = xyMap.getHeight();
245 if (outputWidth != xyMap.getWidth() || outputHeight != xyMap.getHeight()) {
246 // xyMap has width and height that do not fit in a uint8_t.
247 return;
248 }
249 uint16_t n = xyMap.getTotal();
250
251 for (uint8_t y = 0; y < outputHeight; y++) {
252 for (uint8_t x = 0; x < outputWidth; x++) {
253 // Map output pixel to input grid position
254 float fx = static_cast<float>(x) * (inputWidth - 1) / (outputWidth - 1);
255 float fy = static_cast<float>(y) * (inputHeight - 1) / (outputHeight - 1);
256
257 uint8_t ix = static_cast<uint8_t>(fx);
258 uint8_t iy = static_cast<uint8_t>(fy);
259 float dx = fx - ix;
260 float dy = fy - iy;
261
262 uint8_t ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix;
263 uint8_t iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy;
264
265 uint16_t i00 = iy * inputWidth + ix;
266 uint16_t i10 = iy * inputWidth + ix1;
267 uint16_t i01 = iy1 * inputWidth + ix;
268 uint16_t i11 = iy1 * inputWidth + ix1;
269
270 CRGB c00 = input[i00];
271 CRGB c10 = input[i10];
272 CRGB c01 = input[i01];
273 CRGB c11 = input[i11];
274
275 CRGB result;
276 result.r = bilinearInterpolateFloat(c00.r, c10.r, c01.r, c11.r, dx, dy);
277 result.g = bilinearInterpolateFloat(c00.g, c10.g, c01.g, c11.g, dx, dy);
278 result.b = bilinearInterpolateFloat(c00.b, c10.b, c01.b, c11.b, dx, dy);
279
280 uint16_t idx = xyMap.mapToIndex(x, y);
281 if (idx < n) {
282 output[idx] = result;
283 }
284 }
285 }
286}
287
288FASTLED_NAMESPACE_END
289
290
291FASTLED_NAMESPACE_END
Definition xymap.h:39
uint8_t r
Red channel value.
Definition crgb.h:43
uint8_t g
Green channel value.
Definition crgb.h:47
uint8_t b
Blue channel value.
Definition crgb.h:51
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:39