|
FastLED 3.10.6
|
Temporal binary dithering, as one self-contained, testable unit (#4672).
Temporal dithering recovers fractional brightness precision lost to integer quantization by varying pixel values across frames. At refresh rates above ~50Hz, human vision integrates these variations, perceiving the true fractional brightness.
THE PROBLEM: Integer scaling causes color shifts at low brightness. For example: CRGB(100, 60, 20) at 20% brightness -> RGB(19, 11, 3) Each channel loses different fractional precision, distorting the color.
THE SOLUTION: Add frame-varying noise BEFORE scaling, causing different rounding outcomes: Frame 1: scale8(100+0, 51) = 19 Frame 2: scale8(100+3, 51) = 20 <- noise pushed over threshold Your eye averages these to perceive the correct fractional brightness.
THE ALGORITHM:
VIRTUAL BITS: 8-frame cycle at 400Hz = 50Hz complete cycle -> +3 "virtual" bits Result: 8-bit hardware provides 11-bit perceived precision (0-2047 levels)
DISABLE FOR:
NOTE: This is NOT gamma correction. Dithering is pure temporal averaging to recover quantization precision.
The state is two caller-owned arrays, indexed by source channel:
d[3]: the offset added to the current pixel.e[3]: the toggle range. Stored one less than the 256/scale + 1 it is computed from, so stepping is the single subtraction d = e - d. PixelController owns them as its public d/e members because hand-written drivers (AVR clockless asm, the M0 and Teensy structs) read them by name; this file owns everything that is done to them.Two policies share one static interface:
fl::BinaryDither – the algorithm.fl::NoDither – init clears, step does nothing, apply is the identity. Every call compiles to nothing (or to the zeroing that keeps the asm drivers defined). fl::Dither is the one selected by NO_DITHERING. Definition in file binary_dither.h.
#include "fl/stl/int.h"#include "fl/stl/compiler_control.h"#include "fl/stl/noexcept.h"#include "fl/math/math8.h"#include "fl/math/scale8.h"
Include dependency graph for binary_dither.h:
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Classes | |
| struct | fl::BinaryDither |
Temporal binary dithering over caller-owned d[3]/e[3]. More... | |
| struct | fl::NoDither |
| The disabled policy: same interface, no work. More... | |
Namespaces | |
| namespace | fl |
| Base definition for an LED controller. | |
Macros | |
| #define | MAX_LIKELY_UPDATE_RATE_HZ 400 |
| Predicted max update rate, in Hertz. | |
| #define | MIN_ACCEPTABLE_DITHER_RATE_HZ 50 |
| Minimum acceptable dithering rate, in Hertz. | |
| #define | RECOMMENDED_VIRTUAL_BITS |
| Set "virtual bits" of dithering to the highest level that is not likely to cause excessive flickering at low brightness levels + low update rates. | |
| #define | UPDATES_PER_FULL_DITHER_CYCLE (MAX_LIKELY_UPDATE_RATE_HZ / MIN_ACCEPTABLE_DITHER_RATE_HZ) |
| The number of updates in a single dither cycle. | |
| #define | VIRTUAL_BITS RECOMMENDED_VIRTUAL_BITS |
| Alias for RECOMMENDED_VIRTUAL_BITS. | |
Typedefs | |
| typedef BinaryDither | fl::Dither |
| The policy this build uses. | |