FastLED 3.9.15
Loading...
Searching...
No Matches
raster_sparse.h
Go to the documentation of this file.
1#pragma once
2
3/*
4A sparse path through an xy grid. When a value is set != 0, it will get stored
5in the sparse grid. The raster will only store the values that are set, and will
6not allocate memory for the entire grid. This is useful for large grids where
7only a small number of pixels are set.
8*/
9
10#include "fl/stl/int.h"
11#include "fl/math/geometry.h"
13#include "fl/stl/span.h"
14#include "fl/math/xymap.h"
15#include "fl/stl/noexcept.h"
16
17#ifndef FASTLED_RASTER_SPARSE_INLINED_COUNT
18#define FASTLED_RASTER_SPARSE_INLINED_COUNT 128
19#endif
20
21namespace fl {
22
23class XYMap;
24class Gradient;
25class Tile2x2_u8;
26class Leds;
27
28// A raster of u8 values. This is a sparse raster, meaning that it will
29// only store the values that are set.
31 public:
39 XYRasterU8Sparse &operator=(const XYRasterU8Sparse &) FL_NOEXCEPT = default;
40
42 mSparseGrid.clear();
43 mCache.clear();
44 return *this;
45 }
46
47 XYRasterU8Sparse &clear() { return reset(); }
48
49 // Rasterizes point with a value For best visual results, you'll want to
50 // rasterize tile2x2 tiles, which are generated for you by the XYPathRenderer
51 // to represent sub pixel / neightbor splatting positions along a path.
52 // TODO: Bring the math from XYPathRenderer::at_subpixel(float alpha)
53 // into a general purpose function.
54 void rasterize(const vec2<u16> &pt, u8 value) {
55 // Turn it into a Tile2x2_u8 tile and see if we can cache it.
56 write(pt, value);
57 }
58
59 void setSize(u16 width, u16 height) {
61 }
62
63 void setBounds(const rect<u16> &bounds) {
65 mAbsoluteBoundsSet = true;
66 }
67
70
71 iterator begin() { return mSparseGrid.begin(); }
72 const_iterator begin() const { return mSparseGrid.begin(); }
73 iterator end() { return mSparseGrid.end(); }
74 const_iterator end() const { return mSparseGrid.end(); }
75 fl::size size() const { return mSparseGrid.size(); }
76 bool empty() const { return mSparseGrid.empty(); }
77
78 void rasterize(const span<const Tile2x2_u8> &tiles);
79 void rasterize(const Tile2x2_u8 &tile) { rasterize_internal(tile); }
80
81 void rasterize_internal(const Tile2x2_u8 &tile,
82 const rect<u16> *optional_bounds = nullptr);
83
84 // Renders the subpixel tiles to the raster. Any previous data is
85 // cleared. Memory will only be allocated if the size of the raster
86 // increased. void rasterize(const span<const Tile2x2_u8> &tiles);
87 // u8 &at(u16 x, u16 y) { return mGrid.at(x, y); }
88 // const u8 &at(u16 x, u16 y) const { return mGrid.at(x,
89 // y); }
90
91 pair<bool, u8> at(u16 x, u16 y) const {
92 const u8 *val = mSparseGrid.find_value(vec2<u16>(x, y));
93 if (val != nullptr) {
94 return {true, *val};
95 }
96 return {false, 0};
97 }
98
99 rect<u16> bounds() const {
100 if (mAbsoluteBoundsSet) {
101 return mAbsoluteBounds;
102 }
103 return bounds_pixels();
104 }
105
107 u16 min_x = 0;
108 bool min_x_set = false;
109 u16 min_y = 0;
110 bool min_y_set = false;
111 u16 max_x = 0;
112 bool max_x_set = false;
113 u16 max_y = 0;
114 bool max_y_set = false;
115 for (const auto &it : mSparseGrid) {
116 const vec2<u16> &pt = it.first;
117 if (!min_x_set || pt.x < min_x) {
118 min_x = pt.x;
119 min_x_set = true;
120 }
121 if (!min_y_set || pt.y < min_y) {
122 min_y = pt.y;
123 min_y_set = true;
124 }
125 if (!max_x_set || pt.x > max_x) {
126 max_x = pt.x;
127 max_x_set = true;
128 }
129 if (!max_y_set || pt.y > max_y) {
130 max_y = pt.y;
131 max_y_set = true;
132 }
133 }
134 return rect<u16>(min_x, min_y, max_x + 1, max_y + 1);
135 }
136
137 // Warning! - SLOW.
138 u16 width() const { return bounds().width(); }
139 u16 height() const { return bounds().height(); }
140
141 void draw(const CRGB &color, const XYMap &xymap, fl::span<CRGB> out);
142 void draw(const CRGB &color, Leds *leds);
143
144 void drawGradient(const Gradient &gradient, const XYMap &xymap, fl::span<CRGB> out);
145 void drawGradient(const Gradient &gradient, Leds *leds);
146
147 // Inlined, yet customizable drawing access. This will only send you
148 // pixels that are within the bounds of the XYMap.
149 template <typename XYVisitor>
150 void draw(const XYMap &xymap, XYVisitor &visitor) {
151 for (const auto &it : mSparseGrid) {
152 auto pt = it.first;
153 if (!xymap.has(pt.x, pt.y)) {
154 continue;
155 }
156 u32 index = xymap(pt.x, pt.y);
157 u8 value = it.second;
158 if (value > 0) { // Something wrote here.
159 visitor.draw(pt, index, value);
160 }
161 }
162 }
163
164 static const int kMaxCacheSize = 8; // Max size for tiny cache.
165
166 void write(const vec2<u16> &pt, u8 value) {
167 // FL_WARN("write: " << pt.x << "," << pt.y << " value: " <<
168 // value); mSparseGrid.insert(pt, value);
169
170 u8 **cached = mCache.find_value(pt);
171 if (cached) {
172 u8 *val = *cached;
173 if (*val < value) {
174 *val = value;
175 }
176 return;
177 }
178 if (mCache.size() <= kMaxCacheSize) {
179 // cache it.
180 u8 *v = mSparseGrid.find_value(pt);
181 if (v == nullptr) {
182 // FL_WARN("write: " << pt.x << "," << pt.y << " value: "
183 // << value);
184 if (mSparseGrid.needs_rehash()) {
185 // mSparseGrid is about to rehash, so we need to clear the
186 // small cache because it shares pointers.
187 mCache.clear();
188 }
189
190 mSparseGrid.insert(pt, value);
191 return;
192 }
193 mCache.insert(pt, v);
194 if (*v < value) {
195 *v = value;
196 }
197 return;
198 } else {
199 // overflow, clear cache and write directly.
200 mCache.clear();
201 mSparseGrid.insert(pt, value);
202 return;
203 }
204 }
205
206 private:
207 using Key = vec2<u16>;
208 using Value = u8;
215 // Small cache for the last N writes to help performance.
219 bool mAbsoluteBoundsSet = false;
220};
221
222} // namespace fl
223
224namespace fl {
225
226// A raster of CRGB values. This is a sparse raster, meaning that it will
227// only store the values that are set.
229 public:
238
240 mSparseGrid.clear();
241 mCache.clear();
242 return *this;
243 }
244
246
247 // Rasterizes point with a CRGB color value
248 void rasterize(const vec2<u16> &pt, const CRGB &color) {
249 write(pt, color);
250 }
251
252 void setSize(u16 width, u16 height) {
254 }
255
258 mAbsoluteBoundsSet = true;
259 }
260
263
264 iterator begin() { return mSparseGrid.begin(); }
265 const_iterator begin() const { return mSparseGrid.begin(); }
266 iterator end() { return mSparseGrid.end(); }
267 const_iterator end() const { return mSparseGrid.end(); }
268 fl::size size() const { return mSparseGrid.size(); }
269 bool empty() const { return mSparseGrid.empty(); }
270
271 pair<bool, CRGB> at(u16 x, u16 y) const {
272 const CRGB *val = mSparseGrid.find_value(vec2<u16>(x, y));
273 if (val != nullptr) {
274 return {true, *val};
275 }
276 return {false, CRGB::Black};
277 }
278
280 if (mAbsoluteBoundsSet) {
281 return mAbsoluteBounds;
282 }
283 return bounds_pixels();
284 }
285
287 u16 min_x = 0;
288 bool min_x_set = false;
289 u16 min_y = 0;
290 bool min_y_set = false;
291 u16 max_x = 0;
292 bool max_x_set = false;
293 u16 max_y = 0;
294 bool max_y_set = false;
295 for (const auto &it : mSparseGrid) {
296 const vec2<u16> &pt = it.first;
297 if (!min_x_set || pt.x < min_x) {
298 min_x = pt.x;
299 min_x_set = true;
300 }
301 if (!min_y_set || pt.y < min_y) {
302 min_y = pt.y;
303 min_y_set = true;
304 }
305 if (!max_x_set || pt.x > max_x) {
306 max_x = pt.x;
307 max_x_set = true;
308 }
309 if (!max_y_set || pt.y > max_y) {
310 max_y = pt.y;
311 max_y_set = true;
312 }
313 }
314 return rect<u16>(min_x, min_y, max_x + 1, max_y + 1);
315 }
316
317 // Warning! - SLOW.
318 u16 width() const { return bounds().width(); }
319 u16 height() const { return bounds().height(); }
320
321 void draw(const XYMap &xymap, fl::span<CRGB> out);
322 void draw(Leds *leds);
323
324 // Inlined, yet customizable drawing access. This will only send you
325 // pixels that are within the bounds of the XYMap.
326 template <typename XYVisitor>
327 void draw(const XYMap &xymap, XYVisitor &visitor) {
328 for (const auto &it : mSparseGrid) {
329 auto pt = it.first;
330 if (!xymap.has(pt.x, pt.y)) {
331 continue;
332 }
333 u32 index = xymap(pt.x, pt.y);
334 const CRGB &color = it.second;
335 // Only draw non-black pixels (since black represents "no data")
336 if (color.r != 0 || color.g != 0 || color.b != 0) {
337 visitor.draw(pt, index, color);
338 }
339 }
340 }
341
342 static const int kMaxCacheSize = 8; // Max size for tiny cache.
343
344 void write(const vec2<u16> &pt, const CRGB &color) {
345 CRGB **cached = mCache.find_value(pt);
346 if (cached) {
347 CRGB *val = *cached;
348 // For CRGB, we'll replace the existing color (blend could be added later)
349 *val = color;
350 return;
351 }
352 if (mCache.size() <= kMaxCacheSize) {
353 // cache it.
354 CRGB *v = mSparseGrid.find_value(pt);
355 if (v == nullptr) {
356 if (mSparseGrid.needs_rehash()) {
357 // mSparseGrid is about to rehash, so we need to clear the
358 // small cache because it shares pointers.
359 mCache.clear();
360 }
361 mSparseGrid.insert(pt, color);
362 return;
363 }
364 mCache.insert(pt, v);
365 *v = color;
366 return;
367 } else {
368 // overflow, clear cache and write directly.
369 mCache.clear();
370 mSparseGrid.insert(pt, color);
371 return;
372 }
373 }
374
375 private:
376 using Key = vec2<u16>;
377 using Value = CRGB;
384 // Small cache for the last N writes to help performance.
388 bool mAbsoluteBoundsSet = false;
389};
390
391// Backwards compatibility typedef
393
394} // namespace fl
fl::CRGB leds[NUM_LEDS]
XYMap xymap
fl::rect< u16 > mAbsoluteBounds
XYRasterSparse_RGB8() FL_NOEXCEPT=default
fl::unordered_map< vec2< u16 >, CRGB >::iterator iterator
unordered_map< vec2< u16 >, CRGB *, FastHashKey, EqualToKey, kMaxCacheSize > mCache
void setSize(u16 width, u16 height)
const_iterator begin() const
fl::size size() const
XYRasterSparse_RGB8 & reset()
void setBounds(const rect< u16 > &bounds)
void write(const vec2< u16 > &pt, const CRGB &color)
rect< u16 > bounds_pixels() const
void draw(const XYMap &xymap, fl::span< CRGB > out)
const_iterator end() const
XYRasterSparse_RGB8 & operator=(XYRasterSparse_RGB8 &&) FL_NOEXCEPT=default
XYRasterSparse_RGB8 & clear()
static const int kMaxCacheSize
fl::unordered_map< vec2< u16 >, CRGB >::const_iterator const_iterator
XYRasterSparse_RGB8(const XYRasterSparse_RGB8 &) FL_NOEXCEPT=default
rect< u16 > bounds() const
void rasterize(const vec2< u16 > &pt, const CRGB &color)
EqualTo< Key > EqualToKey
void draw(const XYMap &xymap, XYVisitor &visitor)
fl::unordered_map< Key, Value, HashKey, EqualToKey, FASTLED_HASHMAP_INLINED_COUNT > HashMapLarge
FastHash< Key > FastHashKey
pair< bool, CRGB > at(u16 x, u16 y) const
void rasterize(const vec2< u16 > &pt, u8 value)
XYRasterU8Sparse() FL_NOEXCEPT=default
fl::size size() const
void setBounds(const rect< u16 > &bounds)
void draw(const XYMap &xymap, XYVisitor &visitor)
XYRasterU8Sparse & clear()
void draw(const CRGB &color, const XYMap &xymap, fl::span< CRGB > out)
pair< bool, u8 > at(u16 x, u16 y) const
XYRasterU8Sparse & operator=(XYRasterU8Sparse &&) FL_NOEXCEPT=default
fl::unordered_map< vec2< u16 >, u8 >::iterator iterator
void rasterize(const Tile2x2_u8 &tile)
const_iterator begin() const
static const int kMaxCacheSize
XYRasterU8Sparse(const XYRasterU8Sparse &) FL_NOEXCEPT=default
fl::rect< u16 > mAbsoluteBounds
rect< u16 > bounds() const
EqualTo< Key > EqualToKey
fl::unordered_map< vec2< u16 >, u8 >::const_iterator const_iterator
void drawGradient(const Gradient &gradient, const XYMap &xymap, fl::span< CRGB > out)
rect< u16 > bounds_pixels() const
const_iterator end() const
FastHash< Key > FastHashKey
void setSize(u16 width, u16 height)
XYRasterU8Sparse & reset()
HashMapLarge mSparseGrid
fl::unordered_map< Key, Value, HashKey, EqualToKey, FASTLED_HASHMAP_INLINED_COUNT > HashMapLarge
unordered_map< vec2< u16 >, u8 *, FastHashKey, EqualToKey, kMaxCacheSize > mCache
void rasterize_internal(const Tile2x2_u8 &tile, const rect< u16 > *optional_bounds=nullptr)
void write(const vec2< u16 > &pt, u8 value)
unsigned char u8
Definition stdint.h:131
constexpr int type_rank< T >::value
XYRasterSparse_RGB8 XYRasterSparse_CRGB
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Definition crgb.h:510
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38
u16 width() const FL_NOEXCEPT
Definition geometry.h:451
u16 height() const FL_NOEXCEPT
Definition geometry.h:453
value_type y
Definition geometry.h:191
value_type x
Definition geometry.h:190
#define FASTLED_HASHMAP_INLINED_COUNT