| 167 | } |
| 168 | |
| 169 | void upscalePowerOf2(const CRGB *input, CRGB *output, u8 inputWidth, |
| 170 | u8 inputHeight, const XYMap& xyMap) { |
| 171 | u8 width = xyMap.getWidth(); |
| 172 | u8 height = xyMap.getHeight(); |
| 173 | if (width != xyMap.getWidth() || height != xyMap.getHeight()) { |
| 174 | // xyMap has width and height that do not fit in an u16. |
| 175 | return; |
| 176 | } |
| 177 | u16 n = xyMap.getTotal(); |
| 178 | |
| 179 | for (u8 y = 0; y < height; y++) { |
| 180 | for (u8 x = 0; x < width; x++) { |
| 181 | // Use 8-bit fixed-point arithmetic with 8 fractional bits |
| 182 | // (scale factor of 256) |
| 183 | u16 fx = ((u16)x * (inputWidth - 1) * 256) / (width - 1); |
| 184 | u16 fy = |
| 185 | ((u16)y * (inputHeight - 1) * 256) / (height - 1); |
| 186 | |
| 187 | u8 ix = fx >> 8; // Integer part |
| 188 | u8 iy = fy >> 8; |
| 189 | u8 dx = fx & 0xFF; // Fractional part |
| 190 | u8 dy = fy & 0xFF; |
| 191 | |
| 192 | u8 ix1 = (ix + 1 < inputWidth) ? ix + 1 : ix; |
| 193 | u8 iy1 = (iy + 1 < inputHeight) ? iy + 1 : iy; |
| 194 | |
| 195 | u16 i00 = iy * inputWidth + ix; |
| 196 | u16 i10 = iy * inputWidth + ix1; |
| 197 | u16 i01 = iy1 * inputWidth + ix; |
| 198 | u16 i11 = iy1 * inputWidth + ix1; |
| 199 | |
| 200 | CRGB c00 = input[i00]; |
| 201 | CRGB c10 = input[i10]; |
| 202 | CRGB c01 = input[i01]; |
| 203 | CRGB c11 = input[i11]; |
| 204 | |
| 205 | CRGB result; |
| 206 | result.r = |
| 207 | bilinearInterpolatePowerOf2(c00.r, c10.r, c01.r, c11.r, dx, dy); |
| 208 | result.g = |
| 209 | bilinearInterpolatePowerOf2(c00.g, c10.g, c01.g, c11.g, dx, dy); |
| 210 | result.b = |
| 211 | bilinearInterpolatePowerOf2(c00.b, c10.b, c01.b, c11.b, dx, dy); |
| 212 | |
| 213 | u16 idx = xyMap.mapToIndex(x, y); |
| 214 | if (idx < n) { |
| 215 | output[idx] = result; |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | u8 bilinearInterpolatePowerOf2(u8 v00, u8 v10, u8 v01, |
| 222 | u8 v11, u8 dx, u8 dy) { |
no test coverage detected