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/stdint.h"
11
12#include "fl/int.h"
13#include "fl/geometry.h"
14#include "fl/grid.h"
15#include "fl/hash_map.h"
16#include "fl/map.h"
17#include "fl/namespace.h"
18#include "fl/span.h"
19#include "fl/tile2x2.h"
20#include "fl/xymap.h"
21
23struct CRGB;
25
26#ifndef FASTLED_RASTER_SPARSE_INLINED_COUNT
27#define FASTLED_RASTER_SPARSE_INLINED_COUNT 128
28#endif
29
30namespace fl {
31
32class XYMap;
33class Gradient;
34class Tile2x2_u8;
35class Leds;
36
37// A raster of u8 values. This is a sparse raster, meaning that it will
38// only store the values that are set.
40 public:
41 XYRasterU8Sparse() = default;
49
51 mSparseGrid.clear();
52 mCache.clear();
53 return *this;
54 }
55
56 XYRasterU8Sparse &clear() { return reset(); }
57
58 // Rasterizes point with a value For best visual results, you'll want to
59 // rasterize tile2x2 tiles, which are generated for you by the XYPathRenderer
60 // to represent sub pixel / neightbor splatting positions along a path.
61 // TODO: Bring the math from XYPathRenderer::at_subpixel(float alpha)
62 // into a general purpose function.
63 void rasterize(const vec2<u16> &pt, u8 value) {
64 // Turn it into a Tile2x2_u8 tile and see if we can cache it.
65 write(pt, value);
66 }
67
68 void setSize(u16 width, u16 height) {
70 }
71
72 void setBounds(const rect<u16> &bounds) {
74 mAbsoluteBoundsSet = true;
75 }
76
79
80 iterator begin() { return mSparseGrid.begin(); }
81 const_iterator begin() const { return mSparseGrid.begin(); }
82 iterator end() { return mSparseGrid.end(); }
83 const_iterator end() const { return mSparseGrid.end(); }
84 fl::size size() const { return mSparseGrid.size(); }
85 bool empty() const { return mSparseGrid.empty(); }
86
87 void rasterize(const span<const Tile2x2_u8> &tiles);
88 void rasterize(const Tile2x2_u8 &tile) { rasterize_internal(tile); }
89
90 void rasterize_internal(const Tile2x2_u8 &tile,
91 const rect<u16> *optional_bounds = nullptr);
92
93 // Renders the subpixel tiles to the raster. Any previous data is
94 // cleared. Memory will only be allocated if the size of the raster
95 // increased. void rasterize(const Slice<const Tile2x2_u8> &tiles);
96 // u8 &at(u16 x, u16 y) { return mGrid.at(x, y); }
97 // const u8 &at(u16 x, u16 y) const { return mGrid.at(x,
98 // y); }
99
100 pair<bool, u8> at(u16 x, u16 y) const {
101 const u8 *val = mSparseGrid.find_value(vec2<u16>(x, y));
102 if (val != nullptr) {
103 return {true, *val};
104 }
105 return {false, 0};
106 }
107
109 if (mAbsoluteBoundsSet) {
110 return mAbsoluteBounds;
111 }
112 return bounds_pixels();
113 }
114
116 u16 min_x = 0;
117 bool min_x_set = false;
118 u16 min_y = 0;
119 bool min_y_set = false;
120 u16 max_x = 0;
121 bool max_x_set = false;
122 u16 max_y = 0;
123 bool max_y_set = false;
124 for (const auto &it : mSparseGrid) {
125 const vec2<u16> &pt = it.first;
126 if (!min_x_set || pt.x < min_x) {
127 min_x = pt.x;
128 min_x_set = true;
129 }
130 if (!min_y_set || pt.y < min_y) {
131 min_y = pt.y;
132 min_y_set = true;
133 }
134 if (!max_x_set || pt.x > max_x) {
135 max_x = pt.x;
136 max_x_set = true;
137 }
138 if (!max_y_set || pt.y > max_y) {
139 max_y = pt.y;
140 max_y_set = true;
141 }
142 }
143 return rect<u16>(min_x, min_y, max_x + 1, max_y + 1);
144 }
145
146 // Warning! - SLOW.
147 u16 width() const { return bounds().width(); }
148 u16 height() const { return bounds().height(); }
149
150 void draw(const CRGB &color, const XYMap &xymap, CRGB *out);
151 void draw(const CRGB &color, Leds *leds);
152
153 void drawGradient(const Gradient &gradient, const XYMap &xymap, CRGB *out);
154 void drawGradient(const Gradient &gradient, Leds *leds);
155
156 // Inlined, yet customizable drawing access. This will only send you
157 // pixels that are within the bounds of the XYMap.
158 template <typename XYVisitor>
159 void draw(const XYMap &xymap, XYVisitor &visitor) {
160 for (const auto &it : mSparseGrid) {
161 auto pt = it.first;
162 if (!xymap.has(pt.x, pt.y)) {
163 continue;
164 }
165 u32 index = xymap(pt.x, pt.y);
166 u8 value = it.second;
167 if (value > 0) { // Something wrote here.
168 visitor.draw(pt, index, value);
169 }
170 }
171 }
172
173 static const int kMaxCacheSize = 8; // Max size for tiny cache.
174
175 void write(const vec2<u16> &pt, u8 value) {
176 // FASTLED_WARN("write: " << pt.x << "," << pt.y << " value: " <<
177 // value); mSparseGrid.insert(pt, value);
178
179 u8 **cached = mCache.find_value(pt);
180 if (cached) {
181 u8 *val = *cached;
182 if (*val < value) {
183 *val = value;
184 }
185 return;
186 }
187 if (mCache.size() <= kMaxCacheSize) {
188 // cache it.
189 u8 *v = mSparseGrid.find_value(pt);
190 if (v == nullptr) {
191 // FASTLED_WARN("write: " << pt.x << "," << pt.y << " value: "
192 // << value);
193 if (mSparseGrid.needs_rehash()) {
194 // mSparseGrid is about to rehash, so we need to clear the
195 // small cache because it shares pointers.
196 mCache.clear();
197 }
198
199 mSparseGrid.insert(pt, value);
200 return;
201 }
202 mCache.insert(pt, v);
203 if (*v < value) {
204 *v = value;
205 }
206 return;
207 } else {
208 // overflow, clear cache and write directly.
209 mCache.clear();
210 mSparseGrid.insert(pt, value);
211 return;
212 }
213 }
214
215 private:
216 using Key = vec2<u16>;
217 using Value = u8;
224 // Small cache for the last N writes to help performance.
228 bool mAbsoluteBoundsSet = false;
229};
230
231} // namespace fl
232
233namespace fl {
234
235// A raster of CRGB values. This is a sparse raster, meaning that it will
236// only store the values that are set.
238 public:
247
249 mSparseGrid.clear();
250 mCache.clear();
251 return *this;
252 }
253
255
256 // Rasterizes point with a CRGB color value
257 void rasterize(const vec2<u16> &pt, const CRGB &color) {
258 write(pt, color);
259 }
260
261 void setSize(u16 width, u16 height) {
263 }
264
267 mAbsoluteBoundsSet = true;
268 }
269
272
273 iterator begin() { return mSparseGrid.begin(); }
274 const_iterator begin() const { return mSparseGrid.begin(); }
275 iterator end() { return mSparseGrid.end(); }
276 const_iterator end() const { return mSparseGrid.end(); }
277 fl::size size() const { return mSparseGrid.size(); }
278 bool empty() const { return mSparseGrid.empty(); }
279
280 pair<bool, CRGB> at(u16 x, u16 y) const {
281 const CRGB *val = mSparseGrid.find_value(vec2<u16>(x, y));
282 if (val != nullptr) {
283 return {true, *val};
284 }
285 return {false, CRGB::Black};
286 }
287
289 if (mAbsoluteBoundsSet) {
290 return mAbsoluteBounds;
291 }
292 return bounds_pixels();
293 }
294
296 int min_x = 0;
297 bool min_x_set = false;
298 int min_y = 0;
299 bool min_y_set = false;
300 int max_x = 0;
301 bool max_x_set = false;
302 int max_y = 0;
303 bool max_y_set = false;
304 for (const auto &it : mSparseGrid) {
305 const vec2<u16> &pt = it.first;
306 if (!min_x_set || pt.x < min_x) {
307 min_x = pt.x;
308 min_x_set = true;
309 }
310 if (!min_y_set || pt.y < min_y) {
311 min_y = pt.y;
312 min_y_set = true;
313 }
314 if (!max_x_set || pt.x > max_x) {
315 max_x = pt.x;
316 max_x_set = true;
317 }
318 if (!max_y_set || pt.y > max_y) {
319 max_y = pt.y;
320 max_y_set = true;
321 }
322 }
323 return rect<u16>(min_x, min_y, max_x + 1, max_y + 1);
324 }
325
326 // Warning! - SLOW.
327 u16 width() const { return bounds().width(); }
328 u16 height() const { return bounds().height(); }
329
330 void draw(const XYMap &xymap, CRGB *out);
331 void draw(Leds *leds);
332
333 // Inlined, yet customizable drawing access. This will only send you
334 // pixels that are within the bounds of the XYMap.
335 template <typename XYVisitor>
336 void draw(const XYMap &xymap, XYVisitor &visitor) {
337 for (const auto &it : mSparseGrid) {
338 auto pt = it.first;
339 if (!xymap.has(pt.x, pt.y)) {
340 continue;
341 }
342 u32 index = xymap(pt.x, pt.y);
343 const CRGB &color = it.second;
344 // Only draw non-black pixels (since black represents "no data")
345 if (color.r != 0 || color.g != 0 || color.b != 0) {
346 visitor.draw(pt, index, color);
347 }
348 }
349 }
350
351 static const int kMaxCacheSize = 8; // Max size for tiny cache.
352
353 void write(const vec2<u16> &pt, const CRGB &color) {
354 CRGB **cached = mCache.find_value(pt);
355 if (cached) {
356 CRGB *val = *cached;
357 // For CRGB, we'll replace the existing color (blend could be added later)
358 *val = color;
359 return;
360 }
361 if (mCache.size() <= kMaxCacheSize) {
362 // cache it.
363 CRGB *v = mSparseGrid.find_value(pt);
364 if (v == nullptr) {
365 if (mSparseGrid.needs_rehash()) {
366 // mSparseGrid is about to rehash, so we need to clear the
367 // small cache because it shares pointers.
368 mCache.clear();
369 }
370 mSparseGrid.insert(pt, color);
371 return;
372 }
373 mCache.insert(pt, v);
374 *v = color;
375 return;
376 } else {
377 // overflow, clear cache and write directly.
378 mCache.clear();
379 mSparseGrid.insert(pt, color);
380 return;
381 }
382 }
383
384 private:
385 using Key = vec2<u16>;
386 using Value = CRGB;
393 // Small cache for the last N writes to help performance.
397 bool mAbsoluteBoundsSet = false;
398};
399
400} // namespace fl
CRGB leds[NUM_LEDS]
int y
Definition simple.h:93
int x
Definition simple.h:92
XYMap xymap(WIDTH, HEIGHT, SERPENTINE)
fl::HashMap< vec2< u16 >, CRGB >::const_iterator const_iterator
fl::HashMap< vec2< u16 >, CRGB >::iterator iterator
void write(const vec2< u16 > &pt, const CRGB &color)
rect< u16 > bounds_pixels() const
static const int kMaxCacheSize
const_iterator begin() const
fl::rect< u16 > mAbsoluteBounds
HashMap< vec2< u16 >, CRGB *, FastHashKey, EqualToKey, kMaxCacheSize > mCache
XYRasterSparse_CRGB(XYRasterSparse_CRGB &&)=default
XYRasterSparse_CRGB & clear()
pair< bool, CRGB > at(u16 x, u16 y) const
void draw(const XYMap &xymap, CRGB *out)
XYRasterSparse_CRGB & operator=(XYRasterSparse_CRGB &)=default
void draw(const XYMap &xymap, XYVisitor &visitor)
XYRasterSparse_CRGB(u16 width, u16 height)
void rasterize(const vec2< u16 > &pt, const CRGB &color)
fl::size size() const
XYRasterSparse_CRGB & reset()
FastHash< Key > FastHashKey
void setSize(u16 width, u16 height)
XYRasterSparse_CRGB(const XYRasterSparse_CRGB &)=default
XYRasterSparse_CRGB & operator=(XYRasterSparse_CRGB &&)=default
rect< u16 > bounds() const
EqualTo< Key > EqualToKey
const_iterator end() const
fl::HashMap< Key, Value, HashKey, EqualToKey, FASTLED_HASHMAP_INLINED_COUNT > HashMapLarge
void setBounds(const rect< u16 > &bounds)
XYRasterU8Sparse(const XYRasterU8Sparse &)=default
void rasterize(const vec2< u16 > &pt, u8 value)
fl::HashMap< Key, Value, HashKey, EqualToKey, FASTLED_HASHMAP_INLINED_COUNT > HashMapLarge
fl::size size() const
XYRasterU8Sparse & operator=(const XYRasterU8Sparse &)=default
void setBounds(const rect< u16 > &bounds)
void draw(const XYMap &xymap, XYVisitor &visitor)
XYRasterU8Sparse & clear()
XYRasterU8Sparse(int width, int height)
pair< bool, u8 > at(u16 x, u16 y) const
XYRasterU8Sparse()=default
fl::HashMap< vec2< u16 >, u8 >::const_iterator const_iterator
void rasterize(const Tile2x2_u8 &tile)
const_iterator begin() const
static const int kMaxCacheSize
XYRasterU8Sparse(XYRasterU8Sparse &&)=default
fl::rect< u16 > mAbsoluteBounds
rect< u16 > bounds() const
EqualTo< Key > EqualToKey
fl::HashMap< vec2< u16 >, u8 >::iterator iterator
rect< u16 > bounds_pixels() const
const_iterator end() const
FastHash< Key > FastHashKey
void setSize(u16 width, u16 height)
XYRasterU8Sparse & reset()
void draw(const CRGB &color, const XYMap &xymap, CRGB *out)
HashMapLarge mSparseGrid
void drawGradient(const Gradient &gradient, const XYMap &xymap, CRGB *out)
XYRasterU8Sparse & operator=(XYRasterU8Sparse &&)=default
HashMap< 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)
#define FASTLED_HASHMAP_INLINED_COUNT
Definition hash_map.h:54
#define FASTLED_NAMESPACE_END
Definition namespace.h:23
#define FASTLED_NAMESPACE_BEGIN
Definition namespace.h:22
Implements the FastLED namespace macros.
unsigned char u8
Definition int.h:17
Slice< T > span
Definition span.h:8
IMPORTANT!
Definition crgb.h:20
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Definition crgb.h:567
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:86
Definition pair.h:9
u16 height() const
Definition geometry.h:435
u16 width() const
Definition geometry.h:433
value_type y
Definition geometry.h:191
value_type x
Definition geometry.h:190