FastLED 3.9.15
Loading...
Searching...
No Matches
downscale.cpp
Go to the documentation of this file.
1
2// BETA - NOT TESTED!!!
3// VIBE CODED WITH AI
4
5#include "fl/downscale.h"
6
7#include "crgb.h"
8#include "fl/assert.h"
9#include "fl/math_macros.h"
10#include "fl/xymap.h"
11
12#pragma GCC diagnostic push
13#pragma GCC diagnostic ignored "-Wshift-count-overflow"
14
15namespace fl {
16
17void downscaleHalf(const CRGB *src, uint16_t srcWidth, uint16_t srcHeight,
18 CRGB *dst) {
19 uint16_t dstWidth = srcWidth / 2;
20 uint16_t dstHeight = srcHeight / 2;
21
22 for (uint16_t y = 0; y < dstHeight; ++y) {
23 for (uint16_t x = 0; x < dstWidth; ++x) {
24 // Map to top-left of the 2x2 block in source
25 uint16_t srcX = x * 2;
26 uint16_t srcY = y * 2;
27
28 // Fetch 2x2 block
29 const CRGB &p00 = src[(srcY)*srcWidth + (srcX)];
30 const CRGB &p10 = src[(srcY)*srcWidth + (srcX + 1)];
31 const CRGB &p01 = src[(srcY + 1) * srcWidth + (srcX)];
32 const CRGB &p11 = src[(srcY + 1) * srcWidth + (srcX + 1)];
33
34 // Average each color channel
35 uint16_t r =
36 (p00.r + p10.r + p01.r + p11.r + 2) / 4; // +2 for rounding
37 uint16_t g = (p00.g + p10.g + p01.g + p11.g + 2) / 4;
38 uint16_t b = (p00.b + p10.b + p01.b + p11.b + 2) / 4;
39
40 // Store result
41 dst[y * dstWidth + x] = CRGB((uint8_t)r, (uint8_t)g, (uint8_t)b);
42 }
43 }
44}
45
46void downscaleHalf(const CRGB *src, const XYMap &srcXY, CRGB *dst,
47 const XYMap &dstXY) {
48 uint16_t dstWidth = dstXY.getWidth();
49 uint16_t dstHeight = dstXY.getHeight();
50
51 FASTLED_ASSERT(srcXY.getWidth() == dstXY.getWidth() * 2,
52 "Source width must be double the destination width");
53 FASTLED_ASSERT(srcXY.getHeight() == dstXY.getHeight() * 2,
54 "Source height must be double the destination height");
55
56 for (uint16_t y = 0; y < dstHeight; ++y) {
57 for (uint16_t x = 0; x < dstWidth; ++x) {
58 // Map to top-left of the 2x2 block in source
59 uint16_t srcX = x * 2;
60 uint16_t srcY = y * 2;
61
62 // Fetch 2x2 block
63 const CRGB &p00 = src[srcXY.mapToIndex(srcX, srcY)];
64 const CRGB &p10 = src[srcXY.mapToIndex(srcX + 1, srcY)];
65 const CRGB &p01 = src[srcXY.mapToIndex(srcX, srcY + 1)];
66 const CRGB &p11 = src[srcXY.mapToIndex(srcX + 1, srcY + 1)];
67
68 // Average each color channel
69 uint16_t r =
70 (p00.r + p10.r + p01.r + p11.r + 2) / 4; // +2 for rounding
71 uint16_t g = (p00.g + p10.g + p01.g + p11.g + 2) / 4;
72 uint16_t b = (p00.b + p10.b + p01.b + p11.b + 2) / 4;
73
74 // Store result
75 dst[dstXY.mapToIndex(x, y)] =
76 CRGB((uint8_t)r, (uint8_t)g, (uint8_t)b);
77 }
78 }
79}
80
81void downscaleArbitrary(const CRGB *src, const XYMap &srcXY, CRGB *dst,
82 const XYMap &dstXY) {
83 const uint16_t srcWidth = srcXY.getWidth();
84 const uint16_t srcHeight = srcXY.getHeight();
85 const uint16_t dstWidth = dstXY.getWidth();
86 const uint16_t dstHeight = dstXY.getHeight();
87
88 const uint32_t FP_ONE = 256; // Q8.8 fixed-point multiplier
89
90 FASTLED_ASSERT(dstWidth <= srcWidth,
91 "Destination width must be <= source width");
92 FASTLED_ASSERT(dstHeight <= srcHeight,
93 "Destination height must be <= source height");
94
95 for (uint16_t dy = 0; dy < dstHeight; ++dy) {
96 // Fractional boundaries in Q8.8
97 uint32_t dstY0 = (dy * srcHeight * FP_ONE) / dstHeight;
98 uint32_t dstY1 = ((dy + 1) * srcHeight * FP_ONE) / dstHeight;
99
100 for (uint16_t dx = 0; dx < dstWidth; ++dx) {
101 uint32_t dstX0 = (dx * srcWidth * FP_ONE) / dstWidth;
102 uint32_t dstX1 = ((dx + 1) * srcWidth * FP_ONE) / dstWidth;
103
104 uint64_t rSum = 0, gSum = 0, bSum = 0;
105 uint32_t totalWeight = 0;
106
107 // Find covered source pixels
108 uint16_t srcY_start = dstY0 / FP_ONE;
109 uint16_t srcY_end = (dstY1 + FP_ONE - 1) / FP_ONE; // ceil
110
111 uint16_t srcX_start = dstX0 / FP_ONE;
112 uint16_t srcX_end = (dstX1 + FP_ONE - 1) / FP_ONE; // ceil
113
114 for (uint16_t sy = srcY_start; sy < srcY_end; ++sy) {
115 // Calculate vertical overlap in Q8.8
116 uint32_t sy0 = sy * FP_ONE;
117 uint32_t sy1 = (sy + 1) * FP_ONE;
118 uint32_t y_overlap = MIN(dstY1, sy1) - MAX(dstY0, sy0);
119 if (y_overlap == 0)
120 continue;
121
122 for (uint16_t sx = srcX_start; sx < srcX_end; ++sx) {
123 uint32_t sx0 = sx * FP_ONE;
124 uint32_t sx1 = (sx + 1) * FP_ONE;
125 uint32_t x_overlap = MIN(dstX1, sx1) - MAX(dstX0, sx0);
126 if (x_overlap == 0)
127 continue;
128
129 uint32_t weight = (x_overlap * y_overlap + (FP_ONE >> 1)) >>
130 8; // Q8.8 * Q8.8 → Q16.16 → Q8.8
131
132 const CRGB &p = src[srcXY.mapToIndex(sx, sy)];
133 rSum += p.r * weight;
134 gSum += p.g * weight;
135 bSum += p.b * weight;
136 totalWeight += weight;
137 }
138 }
139
140 // Final division, rounding
141 uint8_t r =
142 totalWeight ? (rSum + (totalWeight >> 1)) / totalWeight : 0;
143 uint8_t g =
144 totalWeight ? (gSum + (totalWeight >> 1)) / totalWeight : 0;
145 uint8_t b =
146 totalWeight ? (bSum + (totalWeight >> 1)) / totalWeight : 0;
147
148 dst[dstXY.mapToIndex(dx, dy)] = CRGB(r, g, b);
149 }
150 }
151}
152
153void downscale(const CRGB *src, const XYMap &srcXY, CRGB *dst,
154 const XYMap &dstXY) {
155 uint16_t srcWidth = srcXY.getWidth();
156 uint16_t srcHeight = srcXY.getHeight();
157 uint16_t dstWidth = dstXY.getWidth();
158 uint16_t dstHeight = dstXY.getHeight();
159
160 FASTLED_ASSERT(dstWidth <= srcWidth,
161 "Destination width must be <= source width");
162 FASTLED_ASSERT(dstHeight <= srcHeight,
163 "Destination height must be <= source height");
164 const bool destination_is_half_of_source =
165 (dstWidth * 2 == srcWidth) && (dstHeight * 2 == srcHeight);
166 // Attempt to use the downscaleHalf function if the destination is half the
167 // size of the source.
168 if (destination_is_half_of_source) {
169 const bool both_rectangles = (srcXY.getType() == XYMap::kLineByLine) &&
170 (dstXY.getType() == XYMap::kLineByLine);
171 if (both_rectangles) {
172 // If both source and destination are rectangular, we can use the
173 // optimized version
174 downscaleHalf(src, srcWidth, srcHeight, dst);
175 } else {
176 // Otherwise, we need to use the mapped version
177 downscaleHalf(src, srcXY, dst, dstXY);
178 }
179 return;
180 }
181
182 downscaleArbitrary(src, srcXY, dst, dstXY);
183}
184
185} // namespace fl
186
187#pragma GCC diagnostic pop
uint32_t x[NUM_LAYERS]
Definition Fire2023.ino:82
uint32_t y[NUM_LAYERS]
Definition Fire2023.ino:83
XyMapType getType() const
Definition xymap.cpp:130
uint16_t getWidth() const
Definition xymap.cpp:124
uint16_t mapToIndex(const uint16_t &x, const uint16_t &y) const
Definition xymap.cpp:97
@ kLineByLine
Definition xymap.h:45
uint16_t getHeight() const
Definition xymap.cpp:126
Defines the red, green, and blue (RGB) pixel struct.
#define MIN(a, b)
Definition math_macros.h:15
#define MAX(a, b)
Definition math_macros.h:11
void downscale(const CRGB *src, const XYMap &srcXY, CRGB *dst, const XYMap &dstXY)
void downscaleArbitrary(const CRGB *src, const XYMap &srcXY, CRGB *dst, const XYMap &dstXY)
Definition downscale.cpp:81
void downscaleHalf(const CRGB *src, uint16_t srcWidth, uint16_t srcHeight, CRGB *dst)
Definition downscale.cpp:17
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16
static FASTLED_NAMESPACE_BEGIN uint8_t const p[]
Definition noise.cpp:30
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:55