FastLED 3.9.15
Loading...
Searching...
No Matches
ease.cpp.hpp
Go to the documentation of this file.
1#ifndef FASTLED_INTERNAL
2#define FASTLED_INTERNAL
3#endif
4
5#include "fl/system/fastled.h"
6
7#include "fl/math/ease.h"
8#include "lib8tion.h" // This is the problematic header that's hard to include
9
10#include "fl/math/math.h"
11#include "fl/math/intmap.h"
12#include "fl/math/sin32.h"
13#include "fl/stl/int.h"
14#include "fl/math/math.h"
15#include "fl/stl/weak_ptr.h"
16#include "fl/stl/flat_map.h"
17#include "fl/stl/align.h"
18#include "fl/math/fixed_point.h"
19
20namespace fl {
21
22// Static PROGMEM lookup table for gamma 2.8 correction (8-bit -> 16-bit).
23// Generated by: round(pow(i / 255.0, 2.8) * 65535.0) for i in 0..255.
24// Uses flash storage on AVR/ESP, zero heap allocation on all platforms.
26 0, 0, 0, 0, 1, 1, 2, 3, 4, 6,
27 8, 10, 13, 16, 19, 24, 28, 33, 39, 46,
28 53, 60, 69, 78, 88, 98, 110, 122, 135, 149,
29 164, 179, 196, 214, 232, 252, 273, 295, 317, 341,
30 366, 393, 420, 449, 478, 510, 542, 575, 610, 647,
31 684, 723, 764, 806, 849, 894, 940, 988, 1037, 1088,
32 1140, 1194, 1250, 1307, 1366, 1427, 1489, 1553, 1619, 1686,
33 1756, 1827, 1900, 1975, 2051, 2130, 2210, 2293, 2377, 2463,
34 2552, 2642, 2734, 2829, 2925, 3024, 3124, 3227, 3332, 3439,
35 3548, 3660, 3774, 3890, 4008, 4128, 4251, 4376, 4504, 4634,
36 4766, 4901, 5038, 5177, 5319, 5464, 5611, 5760, 5912, 6067,
37 6224, 6384, 6546, 6711, 6879, 7049, 7222, 7397, 7576, 7757,
38 7941, 8128, 8317, 8509, 8704, 8902, 9103, 9307, 9514, 9723,
39 9936, 10151, 10370, 10591, 10816, 11043, 11274, 11507, 11744, 11984,
40 12227, 12473, 12722, 12975, 13230, 13489, 13751, 14017, 14285, 14557,
41 14833, 15111, 15393, 15678, 15967, 16259, 16554, 16853, 17155, 17461,
42 17770, 18083, 18399, 18719, 19042, 19369, 19700, 20034, 20372, 20713,
43 21058, 21407, 21759, 22115, 22475, 22838, 23206, 23577, 23952, 24330,
44 24713, 25099, 25489, 25884, 26282, 26683, 27089, 27499, 27913, 28330,
45 28752, 29178, 29608, 30041, 30479, 30921, 31367, 31818, 32272, 32730,
46 33193, 33660, 34131, 34606, 35085, 35569, 36057, 36549, 37046, 37547,
47 38052, 38561, 39075, 39593, 40116, 40643, 41175, 41711, 42251, 42796,
48 43346, 43899, 44458, 45021, 45588, 46161, 46737, 47319, 47905, 48495,
49 49091, 49691, 50295, 50905, 51519, 52138, 52761, 53390, 54023, 54661,
50 55303, 55951, 56604, 57261, 57923, 58590, 59262, 59939, 60621, 61308,
51 62000, 62697, 63399, 64106, 64818, 65535};
52
56
57// 8-bit easing functions
59 // Simple quadratic ease-in: i^2 scaled to 8-bit range
60 // Using scale8(i, i) which computes (i * i) / 255
61 return scale8(i, i);
62}
63
65 constexpr u16 MAX = 0xFF; // 255
66 constexpr u16 HALF = (MAX + 1) >> 1; // 128
67 constexpr u16 DENOM = MAX; // divisor for scaling
68 constexpr u16 ROUND = DENOM >> 1; // for rounding
69
70 if (i < HALF) {
71 // first half: y = 2·(i/MAX)² → y_i = 2·i² / MAX
72 u32 t = i;
73 u32 num = 2 * t * t + ROUND; // 2*i², +half for rounding
74 return u8(num / DENOM);
75 } else {
76 // second half: y = 1 − 2·(1−i/MAX)²
77 // → y_i = MAX − (2·(MAX−i)² / MAX)
78 u32 d = MAX - i;
79 u32 num = 2 * d * d + ROUND; // 2*(MAX−i)², +half for rounding
80 return u8(MAX - (num / DENOM));
81 }
82}
83
85 constexpr u16 MAX = 0xFF; // 255
86 constexpr u16 HALF = (MAX + 1) >> 1; // 128
87 constexpr u32 DENOM = (u32)MAX * MAX; // 255*255 = 65025
88 constexpr u32 ROUND = DENOM >> 1; // for rounding
89
90 if (i < HALF) {
91 // first half: y = 4·(i/MAX)³ → y_i = 4·i³ / MAX²
92 u32 ii = i;
93 u32 cube = ii * ii * ii; // i³
94 u32 num = 4 * cube + ROUND; // 4·i³, +half denom for rounding
95 return u8(num / DENOM);
96 } else {
97 // second half: y = 1 − ((−2·t+2)³)/2
98 // where t = i/MAX; equivalently:
99 // y_i = MAX − (4·(MAX−i)³ / MAX²)
100 u32 d = MAX - i;
101 u32 cube = d * d * d; // (MAX−i)³
102 u32 num = 4 * cube + ROUND;
103 return u8(MAX - (num / DENOM));
104 }
105}
106
108 // ease-out is the inverse of ease-in: 1 - (1-t)²
109 // For 8-bit: y = MAX - (MAX-i)² / MAX
110 constexpr u16 MAX = 0xFF;
111 u32 d = MAX - i; // (MAX - i)
112 u32 num = d * d + (MAX >> 1); // (MAX-i)² + rounding
113 return u8(MAX - (num / MAX));
114}
115
117 // Simple cubic ease-in: i³ scaled to 8-bit range
118 // y = i³ / MAX²
119 constexpr u16 MAX = 0xFF;
120 constexpr u32 DENOM = (u32)MAX * MAX;
121 constexpr u32 ROUND = DENOM >> 1;
122
123 u32 ii = i;
124 u32 cube = ii * ii * ii; // i³
125 u32 num = cube + ROUND;
126 return u8(num / DENOM);
127}
128
130 // ease-out cubic: 1 - (1-t)³
131 // For 8-bit: y = MAX - (MAX-i)³ / MAX²
132 constexpr u16 MAX = 0xFF;
133 constexpr u32 DENOM = (u32)MAX * MAX;
134 constexpr u32 ROUND = DENOM >> 1;
135
136 u32 d = MAX - i; // (MAX - i)
137 u32 cube = d * d * d; // (MAX-i)³
138 u32 num = cube + ROUND;
139 return u8(MAX - (num / DENOM));
140}
141
143
144 static const u8 easeInSineTable[256] FL_PROGMEM = {
145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
146 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4,
147 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8,
148 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14,
149 15, 16, 16, 17, 17, 18, 18, 19, 20, 20, 21, 21, 22, 23,
150 23, 24, 25, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 33,
151 33, 34, 35, 36, 37, 37, 38, 39, 40, 41, 42, 42, 43, 44,
152 45, 46, 47, 48, 49, 50, 51, 52, 52, 53, 54, 55, 56, 57,
153 58, 59, 60, 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72,
154 73, 74, 75, 76, 77, 79, 80, 81, 82, 83, 84, 86, 87, 88,
155 89, 90, 91, 93, 94, 95, 96, 98, 99, 100, 101, 103, 104, 105,
156 106, 108, 109, 110, 112, 113, 114, 115, 117, 118, 119, 121, 122, 123,
157 125, 126, 127, 129, 130, 132, 133, 134, 136, 137, 139, 140, 141, 143,
158 144, 146, 147, 148, 150, 151, 153, 154, 156, 157, 159, 160, 161, 163,
159 164, 166, 167, 169, 170, 172, 173, 175, 176, 178, 179, 181, 182, 184,
160 185, 187, 188, 190, 191, 193, 194, 196, 197, 199, 200, 202, 204, 205,
161 207, 208, 210, 211, 213, 214, 216, 217, 219, 221, 222, 224, 225, 227,
162 228, 230, 231, 233, 235, 236, 238, 239, 241, 242, 244, 246, 247, 249,
163 250, 252, 253, 255};
164
165 // ease-in sine: 1 - cos(t * π/2)
166 // Handle boundary conditions explicitly
167 return FL_PGM_READ_BYTE_NEAR(&easeInSineTable[i]);
168}
169
171 // ease-out sine: sin(t * π/2)
172 // Delegate to 16-bit version for consistency and accuracy
173 // Scale 8-bit input to 16-bit range, call 16-bit function, scale result back
174 u16 input16 = map8_to_16(i);
175 u16 result16 = easeOutSine16(input16);
176 return map16_to_8(result16);
177}
178
180 // ease-in-out sine: -(cos(π*t) - 1) / 2
181 // Delegate to 16-bit version for consistency and accuracy
182 // Scale 8-bit input to 16-bit range, call 16-bit function, scale result back
183 u16 input16 = map8_to_16(i);
184 u16 result16 = easeInOutSine16(input16);
185 return map16_to_8(result16);
186}
187
188// 16-bit easing functions
189u16 easeInQuad16(u16 i) {
190 // Simple quadratic ease-in: i^2 scaled to 16-bit range
191 // Using scale16(i, i) which computes (i * i) / 65535
192 return scale16(i, i);
193}
194
196 // 16-bit quadratic ease-in / ease-out function
197 constexpr u32 MAX = 0xFFFF; // 65535
198 constexpr u32 HALF = (MAX + 1) >> 1; // 32768
199 constexpr u32 DENOM = MAX; // divisor
200 constexpr u32 ROUND = DENOM >> 1; // for rounding
201
202 if (x < HALF) {
203 // first half: y = 2·(x/MAX)² → y_i = 2·x² / MAX
204 fl::u64 xi = x;
205 fl::u64 num = 2 * xi * xi + ROUND; // 2*x², +half for rounding
206 return u16(num / DENOM);
207 } else {
208 // second half: y = 1 − 2·(1−x/MAX)² → y_i = MAX − (2·(MAX−x)² / MAX)
209 fl::u64 d = MAX - x;
210 fl::u64 num = 2 * d * d + ROUND; // 2*(MAX−x)², +half for rounding
211 return u16(MAX - (num / DENOM));
212 }
213}
214
216 const u32 MAX = 0xFFFF; // 65535
217 const u32 HALF = (MAX + 1) >> 1; // 32768
218 const fl::u64 M2 = (fl::u64)MAX * MAX; // 65535² = 4 294 836 225
219
220 if (x < HALF) {
221 // first half: y = 4·(x/MAX)³ → y_i = 4·x³ / MAX²
222 fl::u64 xi = x;
223 fl::u64 cube = xi * xi * xi; // x³
224 // add M2/2 for rounding
225 fl::u64 num = 4 * cube + (M2 >> 1);
226 return (u16)(num / M2);
227 } else {
228 // second half: y = 1 − ((2·(1−x/MAX))³)/2
229 // → y_i = MAX − (4·(MAX−x)³ / MAX²)
230 fl::u64 d = MAX - x;
231 fl::u64 cube = d * d * d; // (MAX−x)³
232 fl::u64 num = 4 * cube + (M2 >> 1);
233 return (u16)(MAX - (num / M2));
234 }
235}
236
237u16 easeOutQuad16(u16 i) {
238 // ease-out quadratic: 1 - (1-t)²
239 // For 16-bit: y = MAX - (MAX-i)² / MAX
240 constexpr u32 MAX = 0xFFFF; // 65535
241 constexpr u32 ROUND = MAX >> 1; // for rounding
242
243 fl::u64 d = MAX - i; // (MAX - i)
244 fl::u64 num = d * d + ROUND; // (MAX-i)² + rounding
245 return u16(MAX - (num / MAX));
246}
247
248u16 easeInCubic16(u16 i) {
249 // Simple cubic ease-in: i³ scaled to 16-bit range
250 // y = i³ / MAX²
251 constexpr u32 MAX = 0xFFFF; // 65535
252 constexpr fl::u64 DENOM = (fl::u64)MAX * MAX; // 65535²
253 constexpr fl::u64 ROUND = DENOM >> 1; // for rounding
254
255 fl::u64 ii = i;
256 fl::u64 cube = ii * ii * ii; // i³
257 fl::u64 num = cube + ROUND;
258 return u16(num / DENOM);
259}
260
261u16 easeOutCubic16(u16 i) {
262 // ease-out cubic: 1 - (1-t)³
263 // For 16-bit: y = MAX - (MAX-i)³ / MAX²
264 constexpr u32 MAX = 0xFFFF; // 65535
265 constexpr fl::u64 DENOM = (fl::u64)MAX * MAX; // 65535²
266 constexpr fl::u64 ROUND = DENOM >> 1; // for rounding
267
268 fl::u64 d = MAX - i; // (MAX - i)
269 fl::u64 cube = d * d * d; // (MAX-i)³
270 fl::u64 num = cube + ROUND;
271 return u16(MAX - (num / DENOM));
272}
273
274u16 easeInSine16(u16 i) {
275 // ease-in sine: 1 - cos(t * π/2)
276 // Handle boundary conditions explicitly
277 if (i == 0)
278 return 0;
279 // Remove the hard-coded boundary for 65535 and let math handle it
280
281 // For 16-bit: use cos32 for efficiency and accuracy
282 // Map i from [0,65535] to [0,4194304] in cos32 space (zero to quarter wave)
283 // Formula: 1 - cos(t * π/2) where t goes from 0 to 1
284 // sin32/cos32 quarter cycle is 16777216/4 = 4194304
285 u32 angle = ((fl::u64)i * 4194304ULL) / 65535ULL;
286 i32 cos_result = fl::cos32(angle);
287
288 // Convert cos32 output and apply easing formula: 1 - cos(t * π/2)
289 // cos32 output range is [-2147418112, 2147418112]
290 // At t=0: cos(0) = 2147418112, result should be 0
291 // At t=1: cos(π/2) = 0, result should be 65535
292
293 const fl::i64 MAX_COS32 = 2147418112LL;
294
295 // Calculate: (MAX_COS32 - cos_result) and scale to [0, 65535]
296 fl::i64 adjusted = MAX_COS32 - (fl::i64)cos_result;
297
298 // Scale from [0, 2147418112] to [0, 65535]
299 fl::u64 result = (fl::u64)adjusted * 65535ULL + (MAX_COS32 >> 1); // Add half for rounding
300 u16 final_result = (u16)(result / (fl::u64)MAX_COS32);
301
302 return final_result;
303}
304
305u16 easeOutSine16(u16 i) {
306 // ease-out sine: sin(t * π/2)
307 // Handle boundary conditions explicitly
308 if (i == 0)
309 return 0;
310 if (i == 65535)
311 return 65535;
312
313 // For 16-bit: use sin32 for efficiency and accuracy
314 // Map i from [0,65535] to [0,4194304] in sin32 space (zero to quarter wave)
315 // Formula: sin(t * π/2) where t goes from 0 to 1
316 // sin32 quarter cycle is 16777216/4 = 4194304
317 u32 angle = ((fl::u64)i * 4194304ULL) / 65535ULL;
318 i32 sin_result = fl::sin32(angle);
319
320 // Convert sin32 output range [-2147418112, 2147418112] to [0, 65535]
321 // sin32 output is in range -32767*65536 to +32767*65536
322 // For ease-out sine, we only use positive portion [0, 2147418112] -> [0, 65535]
323 return (u16)((fl::u64)sin_result * 65535ULL / 2147418112ULL);
324}
325
326u16 easeInOutSine16(u16 i) {
327 // ease-in-out sine: -(cos(π*t) - 1) / 2
328 // Handle boundary conditions explicitly
329 if (i == 0)
330 return 0;
331 if (i == 65535)
332 return 65535;
333
334 // For 16-bit: use cos32 for efficiency and accuracy
335 // Map i from [0,65535] to [0,8388608] in cos32 space (0 to half wave)
336 // Formula: (1 - cos(π*t)) / 2 where t goes from 0 to 1
337 // sin32/cos32 half cycle is 16777216/2 = 8388608
338 u32 angle = ((fl::u64)i * 8388608ULL) / 65535ULL;
339 i32 cos_result = fl::cos32(angle);
340
341 // Convert cos32 output and apply easing formula: (1 - cos(π*t)) / 2
342 // cos32 output range is [-2147418112, 2147418112]
343 // We want: (2147418112 - cos_result) / 2, then scale to [0, 65535]
344 fl::i64 adjusted = (2147418112LL - (fl::i64)cos_result) / 2;
345 return (u16)((fl::u64)adjusted * 65535ULL / 2147418112ULL);
346}
347
348// --- Gamma8 implementation ---
349
350// Fixed-point key for gamma cache: unsigned 4.12 (range [0, 15.999], 1/4096 resolution).
352
353class Gamma8Impl : public Gamma8 {
354public:
355 explicit Gamma8Impl(float gamma) {
356 // i=0 is mathematically exact regardless of gamma: pow(0, any) = 0.
357 // The s8x24::pow short-circuit covers exact 0 input, but we set it
358 // here directly anyway because (a) the loop below avoids the
359 // round-trip math and (b) `mLut` is otherwise uninitialized.
360 // i=255 used to need a workaround for the log2(1+t) endpoint
361 // residual; that snap is now handled inside s8x24::pow itself
362 // (see #2969).
363 mLut[0] = 0;
364 // Compute the 256-entry u16 gamma LUT in fixed-point so we don't
365 // pull `__ieee754_pow` (libm, ~2.7 KB) into release builds — the
366 // double-precision pow chain dominates the top-9 bytes attributed
367 // to libm in the post-#2908 ESP32-S3 NEOPIXEL Blink audit
368 // (see #2886 / #2910).
369 //
370 // `s8x24` is 8-integer + 24-fractional bits (same 32-bit storage as
371 // s16x16, but 256× the sub-LSB resolution). Both log2_fp/exp2_fp use
372 // the same 4-term minimax polynomial, but s8x24 carries the full
373 // 24-bit intermediate precision end-to-end instead of truncating
374 // back to 16 frac bits at each stage — bringing the runtime output
375 // within ~1 LSB of true float pow() at the u16 output. Combined
376 // with the special-case for gamma=2.8 in Gamma8::getOrCreate(),
377 // this closes the divergence with the precomputed GAMMA_2_8_LUT
378 // (see #2963 audit + ucs7604 "default gamma 2.8" subcase).
379 //
380 // Bit budget check: max intermediate is exp*log2_fp(1/255) =
381 // 16 * -7.994 ≈ -127.9, fits in s8x24's signed [-128, 128) range.
382 // (GammaKey caps user gamma at 16.)
383 //
384 // libm-free: log2_fp/exp2_fp are pure integer-polynomial impls.
385 // The only float kept is the one-shot s8x24(gamma) constructor
386 // call (pulls __mulsf3 / __fixsfsi helpers, both << 100 B).
387 const fl::s8x24 gamma_fp(gamma);
388 constexpr fl::s8x24 inv_255_fp(1.0f / 255.0f);
389 for (int i = 1; i < 256; ++i) {
390 const fl::s8x24 x = static_cast<i32>(i) * inv_255_fp; // (0, 1]
391 const fl::s8x24 r = fl::s8x24::pow(x, gamma_fp); // (0, 1]
392 // r.raw() is the s8x24 raw with FRAC_BITS=24, range [0, 2^24].
393 // Scale to u16 [0, 65535] with round-half-up. 24+16 bit
394 // multiplication needs a u64 intermediate:
395 // result = ((u64)raw * 65535 + (1<<23)) >> 24
396 const fl::u64 scaled =
397 (static_cast<fl::u64>(static_cast<fl::u32>(r.raw())) * 65535ull
398 + (1ull << 23)) >> 24;
399 mLut[i] = static_cast<u16>(scaled > 65535u ? 65535u : scaled);
400 }
401 }
402
403 // Construct a Gamma8Impl by copying a precomputed PROGMEM LUT directly
404 // into mLut. Used by Gamma8::getOrCreate(2.8f) to alias GAMMA_2_8_LUT,
405 // guaranteeing bit-identical output with fl::gamma_2_8() and skipping
406 // the runtime pow chain entirely. The `from_progmem_lut_tag` tag
407 // disambiguates from the float ctor; we can't add a ctor taking just
408 // `const u16*` because that would silently bind to integer-literal
409 // gammas (`Gamma8::getOrCreate(2)` would be a footgun).
411 Gamma8Impl(from_progmem_lut_tag, const u16* progmem_lut) {
412 for (int i = 0; i < 256; ++i) {
413 mLut[i] = FL_PGM_READ_WORD_ALIGNED(&progmem_lut[i]);
414 }
415 }
416
418 fl::span<u16> output) const override {
419 const int n =
420 input.size() < output.size() ? input.size() : output.size();
421 for (int i = 0; i < n; ++i) {
422 output[i] = mLut[input[i]];
423 }
424 }
425
427 fl::span<u16> output) const override {
428 const int n =
429 input.size() < output.size() ? input.size() : output.size();
430 for (int i = 0; i < n; ++i) {
431 output[i] = lerpLut(input[i]);
432 }
433 }
434
436 fl::span<fl::ufixed_point<8, 8>> output) const override {
437 using FP = fl::ufixed_point<8, 8>;
438 const int n =
439 input.size() < output.size() ? input.size() : output.size();
440 for (int i = 0; i < n; ++i) {
441 output[i] = FP::from_raw(lerpLut(input[i]));
442 }
443 }
444
445private:
446 u16 lerpLut(const fl::ufixed_point<8, 8>& fp) const {
447 u16 raw = fp.raw();
448 u8 idx = static_cast<u8>(raw >> 8); // integer part (LUT index)
449 u8 frac = static_cast<u8>(raw & 0xFF); // fractional part (0-255)
450 u16 a = mLut[idx];
451 u16 b = (idx < 255) ? mLut[idx + 1] : mLut[idx];
452 i32 diff = static_cast<i32>(b) - static_cast<i32>(a);
453 return static_cast<u16>(a + ((diff * frac) >> 8));
454 }
455
456 FL_ALIGNAS(64) u16 mLut[256];
457};
458
460 // Clamp gamma to the cache-key domain BEFORE deriving the key and
461 // before feeding it to s8x24. `GammaKey` is `ufixed_point<4, 12>`,
462 // so its representable range is [0, ~16). A negative input would
463 // wrap unsigned in the GammaKey ctor; an input ≥ 16 would either
464 // saturate or wrap. Clamping up-front also keeps the s8x24 bit
465 // budget safe: `exp * log2_fp(1/255) ≈ -7.994 * gamma` must stay
466 // within s8x24's signed [-128, 128) integer range — gamma ≤ 16
467 // gives -127.9, right at the edge.
468 constexpr float kGammaMax = 15.99975f; // just under 4.12 ufixed max
469 const float gamma_clamped = (gamma < 0.0f) ? 0.0f
470 : (gamma > kGammaMax) ? kGammaMax
471 : gamma;
472 GammaKey key(gamma_clamped);
473
474 // Fast path: gamma 2.8 is the canonical default (every legacy
475 // `addLeds<NEOPIXEL>` flow, every WS281x chipset). We have a
476 // precomputed `GAMMA_2_8_LUT` already in .rodata (used by the
477 // free function `fl::gamma_2_8`). Construct a Gamma8 instance
478 // that copies that table directly instead of recomputing 256
479 // fixed-point pow operations (which would diverge from the
480 // precomputed values by ~30-50 LSB). Cached via weak_ptr to
481 // match the documented `getOrCreate` lifetime contract — the
482 // instance disappears once all callers drop their shared_ptr,
483 // and the next call rebuilds it.
484 constexpr GammaKey k28(2.8f);
485 if (key == k28) {
486 static fl::weak_ptr<const Gamma8> s28_weak;
487 if (auto existing = s28_weak.lock()) {
488 return existing;
489 }
492 s28_weak = ptr;
493 return ptr;
494 }
495
496 // Slow path: arbitrary gamma. Single-entry weak_ptr cache. Users
497 // who switch gamma values at runtime pay one extra `Gamma8Impl`
498 // reconstruction per switch (256 fixed-point pow operations +
499 // ~512 B alloc). See #2928 / #2886.
500 static GammaKey sCachedKey{};
501 static fl::weak_ptr<const Gamma8> sCachedPtr;
502 static bool sCacheValid = false;
503
504 if (sCacheValid && key == sCachedKey) {
505 if (auto existing = sCachedPtr.lock()) {
506 return existing;
507 }
508 }
509
510 // Use the clamped gamma so the LUT we build matches the cache key
511 // (otherwise out-of-range inputs would keep missing the cache).
513 fl::make_shared<Gamma8Impl>(gamma_clamped);
514 sCachedKey = key;
515 sCachedPtr = ptr;
516 sCacheValid = true;
517 return ptr;
518}
519
520} // namespace fl
Alignment macros and utilities for FastLED.
static fl::shared_ptr< const Gamma8 > getOrCreate(float gamma) FL_NOEXCEPT
Definition ease.cpp.hpp:459
u16 lerpLut(const fl::ufixed_point< 8, 8 > &fp) const
Definition ease.cpp.hpp:446
Gamma8Impl(from_progmem_lut_tag, const u16 *progmem_lut)
Definition ease.cpp.hpp:411
FL_ALIGNAS(64) u16 mLut[256]
void convert(fl::span< const u8 > input, fl::span< u16 > output) const override
Definition ease.cpp.hpp:417
void convert(fl::span< const fl::ufixed_point< 8, 8 > > input, fl::span< u16 > output) const override
Definition ease.cpp.hpp:426
void convert(fl::span< const fl::ufixed_point< 8, 8 > > input, fl::span< fl::ufixed_point< 8, 8 > > output) const override
Definition ease.cpp.hpp:435
Gamma8Impl(float gamma)
Definition ease.cpp.hpp:355
FASTLED_FORCE_INLINE constexpr RawType raw() const FL_NOEXCEPT
static FASTLED_FORCE_INLINE s8x24 pow(s8x24 base, s8x24 exp) FL_NOEXCEPT
Definition s8x24.h:216
constexpr i32 raw() const FL_NOEXCEPT
Definition s8x24.h:59
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
shared_ptr< T > lock() const FL_NOEXCEPT
Definition weak_ptr.h:140
#define MAX(a, b)
Definition coder.h:60
#define FL_ALIGN_PROGMEM(N)
Force N-byte alignment for platforms with unaligned access or cache-line optimization.
#define FL_PGM_READ_BYTE_NEAR(x)
Read a byte (8-bit) from PROGMEM memory.
#define FL_PGM_READ_WORD_ALIGNED(addr)
#define FL_PROGMEM
PROGMEM keyword for storage.
Integer mapping functions between different integer sizes.
Internal FastLED header for implementation files.
u16 map8_to_16(u8 x) FL_NOEXCEPT
Definition intmap.h:48
u8 map16_to_8(u16 x) FL_NOEXCEPT
Definition intmap.h:66
Fast, efficient 8-bit math functions specifically designed for high-performance LED programming.
unsigned char u8
Definition stdint.h:131
u8 easeOutSine8(u8 i)
8-bit sine ease-out function Takes an input value 0-255 and returns an eased value 0-255 Smooth sinus...
Definition ease.cpp.hpp:170
constexpr int type_rank< T >::value
FASTLED_FORCE_INLINE i32 cos32(u32 angle) FL_NOEXCEPT
Definition sin32.h:81
FASTLED_FORCE_INLINE i32 sin32(u32 angle) FL_NOEXCEPT
Definition sin32.h:59
fl::ufixed_point< 4, 12 > GammaKey
Definition ease.cpp.hpp:351
u8 easeInQuad8(u8 i)
8-bit quadratic ease-in function Takes an input value 0-255 and returns an eased value 0-255 The curv...
Definition ease.cpp.hpp:58
u8 easeInOutSine8(u8 i)
8-bit sine ease-in/ease-out function Takes an input value 0-255 and returns an eased value 0-255 Smoo...
Definition ease.cpp.hpp:179
u16 easeInQuad16(u16 i)
16-bit quadratic ease-in function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:189
u8 easeInCubic8(u8 i)
8-bit cubic ease-in function Takes an input value 0-255 and returns an eased value 0-255 More pronoun...
Definition ease.cpp.hpp:116
const u16 GAMMA_2_8_LUT[256]
u8 easeInSine8(u8 i)
8-bit sine ease-in function Takes an input value 0-255 and returns an eased value 0-255 Smooth sinuso...
Definition ease.cpp.hpp:142
fl::i64 i64
Definition s16x16x4.h:222
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
u8 easeOutCubic8(u8 i)
8-bit cubic ease-out function Takes an input value 0-255 and returns an eased value 0-255 More pronou...
Definition ease.cpp.hpp:129
u16 easeOutQuad16(u16 i)
16-bit quadratic ease-out function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:237
u16 easeInCubic16(u16 i)
16-bit cubic ease-in function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:248
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
u16 easeInSine16(u16 i)
16-bit sine ease-in function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:274
u16 gamma_2_8(u8 value)
Definition ease.cpp.hpp:53
u8 easeInOutQuad8(u8 i)
8-bit quadratic ease-in/ease-out function Takes an input value 0-255 and returns an eased value 0-255...
Definition ease.cpp.hpp:64
u16 easeInOutSine16(u16 i)
16-bit sine ease-in/ease-out function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:326
constexpr u32 gamma(float g) FL_NOEXCEPT
Definition gamma_lut.h:36
u16 easeOutCubic16(u16 i)
16-bit cubic ease-out function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:261
u16 easeOutSine16(u16 i)
16-bit sine ease-out function Takes an input value 0-65535 and returns an eased value 0-65535
Definition ease.cpp.hpp:305
u16 easeInOutCubic16(u16 x)
16-bit cubic ease-in/ease-out function Takes an input value 0-65535 and returns an eased value 0-6553...
Definition ease.cpp.hpp:215
fl::u64 u64
Definition s16x16x4.h:221
u8 easeOutQuad8(u8 i)
8-bit quadratic ease-out function Takes an input value 0-255 and returns an eased value 0-255 The cur...
Definition ease.cpp.hpp:107
ufixed_integer< IntBits, FracBits > ufixed_point
u8 easeInOutCubic8(u8 i)
8-bit cubic ease-in/ease-out function Takes an input value 0-255 and returns an eased value 0-255 Mor...
Definition ease.cpp.hpp:84
u16 easeInOutQuad16(u16 x)
16-bit quadratic ease-in/ease-out function Takes an input value 0-65535 and returns an eased value 0-...
Definition ease.cpp.hpp:195
Base definition for an LED controller.
Definition crgb.hpp:179