FastLED 3.9.15
Loading...
Searching...
No Matches
five_bit_hd_gamma.cpp.hpp
Go to the documentation of this file.
2#include "fl/stl/align.h"
3#include "fl/math/ease.h"
4#include "fl/system/fastled.h"
5
7
8namespace fl {
9
10namespace {
11namespace five_bit_impl {
12
13// ix/31 * 255/65536 * 256 scaling factors, valid for indexes 1..31.
14// Uses flash storage on AVR/ESP, zero heap allocation on all platforms.
15FL_ALIGN_PROGMEM(4) static constexpr u32 BRIGHT_SCALE[32] FL_PROGMEM = {
16 0, 2023680, 1011840, 674560, 505920, 404736, 337280, 289097,
17 252960, 224853, 202368, 183971, 168640, 155668, 144549, 134912,
18 126480, 119040, 112427, 106509, 101184, 96366, 91985, 87986,
19 84320, 80947, 77834, 74951, 72274, 69782, 67456, 65280};
20
21// Inlined gamma lookup with alignment enforced by aligned_ptr type.
22// Avoids cross-TU function call overhead of gamma_2_8() in hot span loops.
26
27// Branchless scale16by8: (i * (1 + scale)) >> 8.
28// Eliminates the if(scale==0) branch from the standard scale16by8.
29// The caller is responsible for skipping the call when scale==0.
30FL_ALWAYS_INLINE u16 scale16by8_nozero(u16 i, u16 scale_plus_one) {
31 return static_cast<u16>((static_cast<u32>(i) * scale_plus_one) >> 8);
32}
33
34// Core per-pixel transform, fully inlined. All uniform values are
35// precomputed by the caller and passed in to avoid per-pixel branches.
37 u16 r16, u16 g16, u16 b16, u8 brightness,
38 // Precomputed: (1 + brightness), or 0 if brightness==0xff (skip scaling)
39 u16 bright_p1, bool apply_brightness,
40 CRGB *out, u8 *out_power_5bit) {
41
42 // All-zero fast path (rare but worth checking — writes are cheap).
43 if ((r16 | g16 | b16) == 0) {
44 *out = CRGB(0, 0, 0);
45 *out_power_5bit = (brightness <= 31) ? brightness : 31;
46 return;
47 }
48
49 // Apply brightness: branchless multiply, skip only if brightness==0xff.
50 if (apply_brightness) {
51 r16 = scale16by8_nozero(r16, bright_p1);
52 g16 = scale16by8_nozero(g16, bright_p1);
53 b16 = scale16by8_nozero(b16, bright_p1);
54 }
55
56 // max3 via branchless ternary (compiler emits cmov on x86, csel on ARM).
57 u16 scale = r16;
58 if (g16 > scale) scale = g16;
59 if (b16 > scale) scale = b16;
60
61 if (scale == 0) {
62 *out = CRGB(0, 0, 0);
63 *out_power_5bit = 0;
64 return;
65 }
66
67 // 5-bit quantized scale at or above maximum value.
68 scale = (scale + (2047 - (scale >> 5))) >> 11;
69
70 u32 scalef = FL_PGM_READ_DWORD_ALIGNED(&BRIGHT_SCALE[scale]);
71 u8 r8 = static_cast<u8>((r16 * scalef + 0x808000) >> 24);
72 u8 g8 = static_cast<u8>((g16 * scalef + 0x808000) >> 24);
73 u8 b8 = static_cast<u8>((b16 * scalef + 0x808000) >> 24);
74
75 *out = CRGB(r8, g8, b8);
76 *out_power_5bit = static_cast<u8>(scale);
77}
78
79} // namespace five_bit_impl
80} // anonymous namespace
81
84 fl::span<const CRGB> colors, CRGB colors_scale, u8 global_brightness,
85 fl::span<CRGB> out_colors, fl::span<u8> out_power_5bit) {
86
87 u16 n = static_cast<u16>(colors.size());
88 if (out_colors.size() < n) n = static_cast<u16>(out_colors.size());
89 if (out_power_5bit.size() < n) n = static_cast<u16>(out_power_5bit.size());
90
91 // Brightness==0: zero everything and return immediately.
92 if (global_brightness == 0) {
93 for (u16 i = 0; i < n; ++i) {
94 out_colors[i] = CRGB(0, 0, 0);
95 out_power_5bit[i] = 0;
96 }
97 return;
98 }
99
101
102 // Precompute color-scale multipliers once (avoid per-pixel branches).
103 const bool apply_r_scale = (colors_scale.r != 0xff);
104 const bool apply_g_scale = (colors_scale.g != 0xff);
105 const bool apply_b_scale = (colors_scale.b != 0xff);
106 const u16 rscale_p1 = 1u + static_cast<u16>(colors_scale.r);
107 const u16 gscale_p1 = 1u + static_cast<u16>(colors_scale.g);
108 const u16 bscale_p1 = 1u + static_cast<u16>(colors_scale.b);
109
110 // Precompute brightness multiplier once.
111 const bool apply_brightness = (global_brightness != 0xff);
112 const u16 bright_p1 = 1u + static_cast<u16>(global_brightness);
113
114 for (u16 i = 0; i < n; ++i) {
115 const CRGB &c = colors[i];
116
117 u16 r16 = five_bit_impl::gamma_lut_read(glut, c.r);
118 u16 g16 = five_bit_impl::gamma_lut_read(glut, c.g);
119 u16 b16 = five_bit_impl::gamma_lut_read(glut, c.b);
120
121 // Color-scale: branchless multiply, guarded by precomputed bools.
122 if (apply_r_scale) r16 = five_bit_impl::scale16by8_nozero(r16, rscale_p1);
123 if (apply_g_scale) g16 = five_bit_impl::scale16by8_nozero(g16, gscale_p1);
124 if (apply_b_scale) b16 = five_bit_impl::scale16by8_nozero(b16, bscale_p1);
125
126 five_bit_impl::five_bit_pixel(r16, g16, b16, global_brightness,
127 bright_p1, apply_brightness,
128 &out_colors[i], &out_power_5bit[i]);
129 }
130}
131
134 fl::span<const CRGB> colors, CRGB colors_scale, u8 global_brightness,
135 fl::span<CRGBA5> out) {
136
137 u16 n = static_cast<u16>(colors.size());
138 if (out.size() < n) n = static_cast<u16>(out.size());
139
140 // Brightness==0: zero everything and return immediately.
141 if (global_brightness == 0) {
142 for (u16 i = 0; i < n; ++i) {
143 out[i].color = CRGB(0, 0, 0);
144 out[i].brightness_5bit = 0;
145 }
146 return;
147 }
148
150
151 // Precompute color-scale multipliers once (avoid per-pixel branches).
152 const bool apply_r_scale = (colors_scale.r != 0xff);
153 const bool apply_g_scale = (colors_scale.g != 0xff);
154 const bool apply_b_scale = (colors_scale.b != 0xff);
155 const u16 rscale_p1 = 1u + static_cast<u16>(colors_scale.r);
156 const u16 gscale_p1 = 1u + static_cast<u16>(colors_scale.g);
157 const u16 bscale_p1 = 1u + static_cast<u16>(colors_scale.b);
158
159 // Precompute brightness multiplier once.
160 const bool apply_brightness = (global_brightness != 0xff);
161 const u16 bright_p1 = 1u + static_cast<u16>(global_brightness);
162
163 for (u16 i = 0; i < n; ++i) {
164 const CRGB &c = colors[i];
165
166 u16 r16 = five_bit_impl::gamma_lut_read(glut, c.r);
167 u16 g16 = five_bit_impl::gamma_lut_read(glut, c.g);
168 u16 b16 = five_bit_impl::gamma_lut_read(glut, c.b);
169
170 // Color-scale: branchless multiply, guarded by precomputed bools.
171 if (apply_r_scale) r16 = five_bit_impl::scale16by8_nozero(r16, rscale_p1);
172 if (apply_g_scale) g16 = five_bit_impl::scale16by8_nozero(g16, gscale_p1);
173 if (apply_b_scale) b16 = five_bit_impl::scale16by8_nozero(b16, bscale_p1);
174
175 five_bit_impl::five_bit_pixel(r16, g16, b16, global_brightness,
176 bright_p1, apply_brightness,
177 &out[i].color, &out[i].brightness_5bit);
178 }
179}
180
181} // namespace fl
182
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
fl::UISlider scale("Scale", 4,.1, 4,.1)
Alignment macros and utilities for FastLED.
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
#define constexpr
Declares that it is possible to evaluate a value at compile time, introduced in C++11.
Definition cpp_compat.h:15
#define FL_ALIGN_PROGMEM(N)
Force N-byte alignment for platforms with unaligned access or cache-line optimization.
#define FL_PGM_READ_DWORD_ALIGNED(addr)
#define FL_PGM_READ_WORD_ALIGNED(addr)
#define FL_PROGMEM
PROGMEM keyword for storage.
Declares functions for five-bit gamma correction.
Internal FastLED header for implementation files.
fl::CRGB CRGB
Definition crgb.h:25
FL_ALWAYS_INLINE void five_bit_pixel(u16 r16, u16 g16, u16 b16, u8 brightness, u16 bright_p1, bool apply_brightness, CRGB *out, u8 *out_power_5bit)
FL_ALWAYS_INLINE u16 gamma_lut_read(aligned_ptr< const u16, 64 > lut, u8 idx)
FL_ALWAYS_INLINE u16 scale16by8_nozero(u16 i, u16 scale_plus_one)
unsigned char u8
Definition stdint.h:131
fl::CRGB CRGB
Definition video.h:15
const u16 GAMMA_2_8_LUT[256]
FL_OPTIMIZE_FUNCTION void five_bit_hd_gamma_bitshift(fl::span< const CRGB > colors, CRGB colors_scale, u8 global_brightness, fl::span< CRGB > out_colors, fl::span< u8 > out_power_5bit)
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_OPTIMIZATION_LEVEL_O3_BEGIN
#define FL_ALWAYS_INLINE
#define FL_OPTIMIZATION_LEVEL_O3_END
#define FL_OPTIMIZE_FUNCTION
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38