forked from FastLED/FastLED
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathpixel_iterator.h
More file actions
193 lines (166 loc) · 8.5 KB
/
pixel_iterator.h
File metadata and controls
193 lines (166 loc) · 8.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/// @file pixel_iterator.h
/// Non-templated low level pixel data writing class
#pragma once
#include "fl/stdint.h"
#include "fl/string.h"
#include "rgbw.h"
#include "crgb.h"
namespace fl {
#ifndef FASTLED_PIXEL_ITERATOR_HAS_APA102_HD
// Takes more memory, so disable by default.
#define FASTLED_PIXEL_ITERATOR_HAS_APA102_HD 0
#endif
// Due to to the template nature of the PixelController class, the only way we can make
// it a concrete polymorphic class is to manually bind the functions and make our own
// vtable. The PixelControllerVtable is cheaper than doing fl::function<>.
template<typename PixelControllerT>
struct PixelControllerVtable {
static void loadAndScaleRGBW(void* pixel_controller, Rgbw rgbw, uint8_t* b0_out, uint8_t* b1_out, uint8_t* b2_out, uint8_t* b3_out) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->loadAndScaleRGBW(rgbw, b0_out, b1_out, b2_out, b3_out);
}
static void loadAndScaleRGB(void* pixel_controller, uint8_t* r_out, uint8_t* g_out, uint8_t* b_out) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->loadAndScaleRGB(r_out, g_out, b_out);
}
#if FASTLED_PIXEL_ITERATOR_HAS_APA102_HD
static void loadAndScale_APA102_HD(void* pixel_controller, uint8_t* b0_out, uint8_t* b1_out, uint8_t* b2_out, uint8_t* brightness_out) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->loadAndScale_APA102_HD(b0_out, b1_out, b2_out, brightness_out);
}
#endif
static void loadAndScale_WS2816_HD(void* pixel_controller, uint16_t *s0_out, uint16_t* s1_out, uint16_t* s2_out) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->loadAndScale_WS2816_HD(s0_out, s1_out, s2_out);
}
static void stepDithering(void* pixel_controller) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->stepDithering();
}
static void advanceData(void* pixel_controller) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->advanceData();
}
static int size(void* pixel_controller) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
return pc->size();
}
static bool has(void* pixel_controller, int n) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
return pc->has(n);
}
// function for getHdScale
#if FASTLED_HD_COLOR_MIXING
static void getHdScale(void* pixel_controller, uint8_t* c0, uint8_t* c1, uint8_t* c2, uint8_t* brightness) {
PixelControllerT* pc = static_cast<PixelControllerT*>(pixel_controller);
pc->getHdScale(c0, c1, c2, brightness);
}
#endif
};
typedef void (*loadAndScaleRGBWFunction)(void* pixel_controller, Rgbw rgbw, uint8_t* b0_out, uint8_t* b1_out, uint8_t* b2_out, uint8_t* b3_out);
typedef void (*loadAndScaleRGBFunction)(void* pixel_controller, uint8_t* r_out, uint8_t* g_out, uint8_t* b_out);
#if FASTLED_PIXEL_ITERATOR_HAS_APA102_HD
typedef void (*loadAndScale_APA102_HDFunction)(void* pixel_controller, uint8_t* b0_out, uint8_t* b1_out, uint8_t* b2_out, uint8_t* brightness_out);
#endif
typedef void (*loadAndScale_WS2816_HDFunction)(void* pixel_controller, uint16_t* b0_out, uint16_t* b1_out, uint16_t* b2_out);
typedef void (*stepDitheringFunction)(void* pixel_controller);
typedef void (*advanceDataFunction)(void* pixel_controller);
typedef int (*sizeFunction)(void* pixel_controller);
typedef bool (*hasFunction)(void* pixel_controller, int n);
typedef uint8_t (*globalBrightness)(void* pixel_controller);
typedef void (*getHdScaleFunction)(void* pixel_controller, uint8_t* c0, uint8_t* c1, uint8_t* c2, uint8_t* brightness);
// PixelIterator is turns a PixelController<> into a concrete object that can be used to iterate
// over pixels and transform them into driver data. See PixelController<>::as_iterator() for how
// to create a PixelIterator.
// Note: This is designed for micro-controllers with a lot of memory. DO NOT use this in the core library
// as a PixelIterator consumes a *lot* more instruction data than an instance of PixelController<RGB_ORDER>.
// This iterator is designed for code in src/platforms/**.
class PixelIterator {
public:
template<typename PixelControllerT>
PixelIterator(PixelControllerT* pc, Rgbw rgbw)
: mPixelController(pc), mRgbw(rgbw) {
// Manually build up a vtable.
// Wait... what? Stupid nerds trying to show off how smart they are...
// Why not just use a virtual function?!
//
// Before you think this then you should know that the alternative straight
// forward way is to have a virtual interface class that PixelController inherits from.
// ...and that was already tried. And if you try to do this yourself
// this then let me tell you what is going to happen...
//
// EVERY SINGLE PLATFORM THAT HAS A COMPILED BINARY SIZE CHECK WILL IMMEDIATELY
// FAIL AS THE BINARY BLOWS UP BY 10-30%!!! It doesn't matter if only one PixelController
// with a vtable is used, gcc seems not to de-virtualize the calls. And we really care
// about binary size since FastLED needs to run on those tiny little microcontrollers like
// the Attiny85 (and family) which are in the sub $1 range used for commercial products.
//
// So to satisfy these tight memory requirements we make the dynamic dispatch used in PixelIterator
// an optional zero-cost abstraction which doesn't affect the binary size for platforms that
// don't use it. So that's why we are using this manual construction of the vtable that is built
// up using template magic. If your platform has lots of memory then you'll gladly trade
// a sliver of memory for the convenience of having a concrete implementation of
// PixelController that you can use without having to make all your driver code a template.
//
// Btw, this pattern in C++ is called the "type-erasure pattern". It allows non virtual
// polymorphism by leveraging the C++ template system to ensure type safety.
typedef PixelControllerVtable<PixelControllerT> Vtable;
mLoadAndScaleRGBW = &Vtable::loadAndScaleRGBW;
mLoadAndScaleRGB = &Vtable::loadAndScaleRGB;
#if FASTLED_PIXEL_ITERATOR_HAS_APA102_HD
mLoadAndScale_APA102_HD = &Vtable::loadAndScale_APA102_HD;
#endif
mLoadAndScale_WS2816_HD = &Vtable::loadAndScale_WS2816_HD;
mStepDithering = &Vtable::stepDithering;
mAdvanceData = &Vtable::advanceData;
mSize = &Vtable::size;
mHas = &Vtable::has;
#if FASTLED_HD_COLOR_MIXING
mGetHdScale = &Vtable::getHdScale;
#endif
}
bool has(int n) { return mHas(mPixelController, n); }
void loadAndScaleRGBW(uint8_t *b0_out, uint8_t *b1_out, uint8_t *b2_out, uint8_t *w_out) {
mLoadAndScaleRGBW(mPixelController, mRgbw, b0_out, b1_out, b2_out, w_out);
}
void loadAndScaleRGB(uint8_t *r_out, uint8_t *g_out, uint8_t *b_out) {
mLoadAndScaleRGB(mPixelController, r_out, g_out, b_out);
}
#if FASTLED_PIXEL_ITERATOR_HAS_APA102_HD
void loadAndScale_APA102_HD(uint8_t *b0_out, uint8_t *b1_out, uint8_t *b2_out, uint8_t *brightness_out) {
mLoadAndScale_APA102_HD(mPixelController, b0_out, b1_out, b2_out, brightness_out);
}
#endif
void loadAndScale_WS2816_HD(uint16_t *s0_out, uint16_t *s1_out, uint16_t *s2_out) {
mLoadAndScale_WS2816_HD(mPixelController, s0_out, s1_out, s2_out);
}
void stepDithering() { mStepDithering(mPixelController); }
void advanceData() { mAdvanceData(mPixelController); }
int size() { return mSize(mPixelController); }
void set_rgbw(Rgbw rgbw) { mRgbw = rgbw; }
Rgbw get_rgbw() const { return mRgbw; }
#if FASTLED_HD_COLOR_MIXING
void getHdScale(uint8_t* c0, uint8_t* c1, uint8_t* c2, uint8_t* brightness) {
mGetHdScale(mPixelController, c0, c1, c2, brightness);
}
#endif
private:
// vtable emulation
void* mPixelController = nullptr;
Rgbw mRgbw;
loadAndScaleRGBWFunction mLoadAndScaleRGBW = nullptr;
loadAndScaleRGBFunction mLoadAndScaleRGB = nullptr;
#if FASTLED_PIXEL_ITERATOR_HAS_APA102_HD
loadAndScale_APA102_HDFunction mLoadAndScale_APA102_HD = nullptr;
#endif
loadAndScale_WS2816_HDFunction mLoadAndScale_WS2816_HD = nullptr;
stepDitheringFunction mStepDithering = nullptr;
advanceDataFunction mAdvanceData = nullptr;
sizeFunction mSize = nullptr;
hasFunction mHas = nullptr;
#if FASTLED_HD_COLOR_MIXING
getHdScaleFunction mGetHdScale = nullptr;
#endif
};
} // namespace
using PixelIterator = fl::PixelIterator;