FastLED 3.9.15
Loading...
Searching...
No Matches
driver.cpp.hpp
Go to the documentation of this file.
1#include "driver.h"
2#include "fl/stl/chrono.h"
3#include "fl/log/log.h"
4#include "fl/stl/stdio.h"
5#include "fl/stl/string.h"
6#include "fl/stl/cstring.h" // for fl::memset() and fl::memcpy()
7#include "fl/stl/noexcept.h"
8
9namespace fl {
10namespace third_party {
11
17
21
23 if (!stream) {
24 setError("Invalid stream provided");
25 return false;
26 }
27
28 input_stream_ = stream;
31 progress_ = 0.0f;
32 error_message_.clear();
33
34 // Read stream data
35 if (!readStreamData()) {
36 return false;
37 }
38
39 // Initialize decoder
40 if (!initializeDecoder()) {
41 return false;
42 }
43
45 return true;
46}
47
49 if (!input_stream_) {
50 setError("No input stream");
51 return false;
52 }
53
54 // Read all data from stream
55 fl::vector<fl::u8> temp_buffer;
56 temp_buffer.reserve(4096);
57
58 fl::u8 chunk[256];
59 while (true) {
60 fl::size bytes_read = input_stream_->read(chunk, sizeof(chunk));
61 if (bytes_read == 0) break;
62
63 fl::size old_size = temp_buffer.size();
64 temp_buffer.resize(old_size + bytes_read);
65 fl::memcpy(temp_buffer.data() + old_size, chunk, bytes_read);
66 }
67
68 input_size_ = temp_buffer.size();
69 if (input_size_ == 0) {
70 setError("Empty input stream");
71 return false;
72 }
73
75 fl::memcpy(input_buffer_.get(), temp_buffer.data(), input_size_);
76
77 // Set up embedded state for input
78 embedded_tjpg_.array_data = input_buffer_.get();
79 embedded_tjpg_.array_index = 0;
80 embedded_tjpg_.array_size = input_size_;
81
82 return true;
83}
84
86 // Create JDEC instance in our workspace
87 JDEC* jdec = reinterpret_cast<JDEC*>(embedded_tjpg_.workspace);
88
89 // Calculate working memory pool (after JDEC struct)
90 fl::size jdec_size = sizeof(JDEC);
91 fl::u8* pool = embedded_tjpg_.workspace + jdec_size;
92 fl::size pool_size = sizeof(embedded_tjpg_.workspace) - jdec_size;
93
94 // Prepare decoder with our input callback
95 JRESULT res = jd_prepare(jdec, inputCallback, pool, pool_size, &embedded_tjpg_);
96
97 if (res != JDR_OK) {
98 char err_str[32];
99 fl::snprintf(err_str, sizeof(err_str), "jd_prepare failed: %d", (int)res);
100 setError(err_str);
101 return false;
102 }
103
104 // Get image dimensions
105 fl::u16 width = jdec->width;
106 fl::u16 height = jdec->height;
107
108
109 // Apply scaling based on jpg_scale setting
110 if (embedded_tjpg_.jpg_scale > 1) {
111 width /= embedded_tjpg_.jpg_scale;
112 height /= embedded_tjpg_.jpg_scale;
113 }
114
115
116 // Allocate frame buffer
118
119 // Create frame object
121
122 if (!current_frame_->isValid()) {
123 setError("Failed to create frame");
124 return false;
125 }
126
127 // Initialize progressive state if needed
128 if (use_progressive_) {
129 fl::memcpy(&progressive_state_.base, jdec, sizeof(JDEC));
130 progressive_state_.current_mcu_x = 0;
131 progressive_state_.current_mcu_y = 0;
132 progressive_state_.mcus_processed = 0;
133 progressive_state_.total_mcus = (jdec->width / jdec->msx / 8) *
134 (jdec->height / jdec->msy / 8);
135 }
136
137 return true;
138}
139
142 return false;
143 }
144
147 }
148
149 startTick();
150
151 if (use_progressive_) {
152 // Progressive decoding with time budget
153 fl::u8 more_data_needed = 0;
154 fl::u8 processing_complete = 0;
155
159 embedded_tjpg_.jpg_scale,
160 progressive_config_.max_mcus_per_tick,
161 &more_data_needed,
162 &processing_complete
163 );
164
165 if (processing_complete) {
167 progress_ = 1.0f;
168 return false;
169 }
170
171 if (res == (JRESULT)JDR_SUSPEND) {
172 // Time budget exceeded, more work remains
173 // Update progress
174 if (progressive_state_.total_mcus > 0) {
175 progress_ = (float)progressive_state_.mcus_processed /
176 (float)progressive_state_.total_mcus;
177 }
178 return true; // More work to do
179 }
180
181 if (res != JDR_OK) {
182 char err_str[32];
183 fl::snprintf(err_str, sizeof(err_str), "Progressive decode error: %d", (int)res);
184 setError(err_str);
185 return false;
186 }
187
188 return true; // Continue processing
189 } else {
190 // Non-progressive decoding (all at once)
191 JDEC* jdec = reinterpret_cast<JDEC*>(embedded_tjpg_.workspace);
192
193 JRESULT res = jd_decomp(jdec, outputCallback, embedded_tjpg_.jpg_scale);
194
195 if (res == JDR_OK) {
196 // Check if any pixels were actually set by sampling the first pixel
197 CRGB* pixels = current_frame_->rgb().data();
198 fl::u8 first_pixel_sum = 0;
199 if (pixels) {
200 first_pixel_sum = pixels[0].r + pixels[0].g + pixels[0].b;
201 }
202
203 char debug_str[64];
204 fl::snprintf(debug_str, sizeof(debug_str), "JPEG decode OK, first_pixel_sum=%d", first_pixel_sum);
205
206 // If no pixels were set, this indicates the output callback wasn't called
207 if (first_pixel_sum == 0) {
208 setError("JPEG decode succeeded but output callback was not called");
209 return false;
210 }
211
213 progress_ = 1.0f;
214 return false; // Complete
215 } else {
216 char err_str[32];
217 fl::snprintf(err_str, sizeof(err_str), "Decode error: %d", (int)res);
218 setError(err_str);
219 return false;
220 }
221 }
222}
223
225 input_stream_.reset();
226 input_buffer_.reset();
227 frame_buffer_.reset();
228 current_frame_.reset();
230 progress_ = 0.0f;
231}
232
234 if (state_ == State::Error) {
235 if (msg) {
236 *msg = error_message_;
237 }
238 return true;
239 }
240 return false;
241}
242
244 if (current_frame_) {
245 return *current_frame_;
246 }
247 return Frame(0);
248}
249
250
254
258
260 if (use_progressive_ && progressive_state_.total_mcus > 0) {
261 fl::u16 mcu_height = progressive_state_.base.msy * 8;
262 return progressive_state_.current_mcu_y * mcu_height;
263 }
264 return 0;
265}
266
268 return embedded_tjpg_.array_index;
269}
270
278
282
287
289 return (fl::millis() - start_time_ms_) >= progressive_config_.max_time_per_tick_ms;
290}
291
296
297// Static input callback - reads from embedded state
298fl::size TJpgInstanceDecoder::inputCallback(JDEC* jd, fl::u8* buff, fl::size nbyte) FL_NOEXCEPT {
299 EmbeddedTJpgState* state = reinterpret_cast<EmbeddedTJpgState*>(jd->device);
300
301 if (!state || !state->array_data) {
302 return 0;
303 }
304
305 fl::size remaining = state->array_size - state->array_index;
306 fl::size to_read = (nbyte < remaining) ? nbyte : remaining;
307
308 if (buff) {
309 fl::memcpy(buff, state->array_data + state->array_index, to_read);
310 }
311
312 state->array_index += to_read;
313 return to_read;
314}
315
316// Static output callback - writes to frame buffer
318
319 EmbeddedTJpgState* state = reinterpret_cast<EmbeddedTJpgState*>(jd->device);
320
321 if (!state || !state->decoder_instance) {
322 return 0;
323 }
324
325 TJpgInstanceDecoder* decoder = state->decoder_instance;
326
327 if (!decoder->current_frame_) {
328 return 0;
329 }
330
331 fl::u16 frame_width = decoder->current_frame_->getWidth();
332 fl::u16 frame_height = decoder->current_frame_->getHeight();
333
334 // Calculate rectangle dimensions
335 fl::u16 x = rect->left;
336 fl::u16 y = rect->top;
337 fl::u16 w = rect->right - rect->left + 1; // JRECT is inclusive
338 fl::u16 h = rect->bottom - rect->top + 1; // JRECT is inclusive
339
340
341 // Handle zero-size rectangle by treating it as full frame for small images
342 if (w == 0 && h == 0 && frame_width <= 8 && frame_height <= 8) {
343 x = 0;
344 y = 0;
345 w = frame_width;
346 h = frame_height;
347 }
348
349 // Bounds check
350 if (x >= frame_width || y >= frame_height ||
351 x + w > frame_width || y + h > frame_height) {
352 return 0;
353 }
354
355 // Get pointer to frame's RGB buffer
356 CRGB* frame_pixels = decoder->current_frame_->rgb().data();
357 if (!frame_pixels) {
358 return 0;
359 }
360
361 // Copy pixels (already in RGB888 format since JD_FORMAT=0)
362 fl::u8* rgb_data = reinterpret_cast<fl::u8*>(bitmap);
363
364 for (fl::u16 row = 0; row < h; ++row) {
365 for (fl::u16 col = 0; col < w; ++col) {
366 fl::u16 src_idx = (row * w + col) * 3; // RGB888 is 3 bytes per pixel
367
368 // Calculate destination position
369 int pixel_x = x + col;
370 int pixel_y = y + row;
371 int frame_idx = pixel_y * frame_width + pixel_x;
372
373 // Get RGB888 values directly
374 fl::u8 r = rgb_data[src_idx + 0];
375 fl::u8 g = rgb_data[src_idx + 1];
376 fl::u8 b = rgb_data[src_idx + 2];
377
378 // Set pixel in frame buffer
379 frame_pixels[frame_idx] = CRGB(r, g, b);
380 }
381 }
382
383 return 1; // Success
384}
385
386// Factory function
390
391} // namespace third_party
392} // namespace fl
TestState state
FastLED chrono implementation - duration types for time measurements.
fl::size getBytesPerPixel() const FL_NOEXCEPT
bool beginDecodingStream(fl::filebuf_ptr stream, PixelFormat format) FL_NOEXCEPT
static fl::size inputCallback(JDEC *jd, fl::u8 *buff, fl::size nbyte) FL_NOEXCEPT
struct fl::third_party::TJpgInstanceDecoder::EmbeddedTJpgState embedded_tjpg_
JDEC_Progressive progressive_state_
Definition driver.h:67
fl::unique_ptr< fl::u8[]> frame_buffer_
Definition driver.h:76
fl::unique_ptr< fl::u8[]> input_buffer_
Definition driver.h:86
Frame getCurrentFrame() const FL_NOEXCEPT
fl::u16 getDecodedRows() const FL_NOEXCEPT
Frame getPartialFrame() const FL_NOEXCEPT
fl::shared_ptr< Frame > current_frame_
Definition driver.h:75
bool hasPartialImage() const FL_NOEXCEPT
fl::size getBytesProcessed() const FL_NOEXCEPT
bool hasError(fl::string *msg=nullptr) const FL_NOEXCEPT
bool shouldYield() const FL_NOEXCEPT
TJpgProgressiveConfig progressive_config_
Definition driver.h:71
void allocateFrameBuffer(fl::u16 width, fl::u16 height) FL_NOEXCEPT
void setError(const fl::string &msg) FL_NOEXCEPT
static int outputCallback(JDEC *jd, void *bitmap, JRECT *rect) FL_NOEXCEPT
fl::size size() const FL_NOEXCEPT
T * data() FL_NOEXCEPT
Definition vector.h:619
void reserve(fl::size n) FL_NOEXCEPT
Definition vector.h:591
void resize(fl::size n) FL_NOEXCEPT
Definition vector.h:593
Centralized logging categories for FastLED hardware interfaces and subsystems.
unsigned char u8
Definition coder.h:132
JRESULT jd_decomp(JDEC *jd, int(*outfunc)(JDEC *, void *, JRECT *), uint8_t scale) FL_NOEXCEPT
fl::shared_ptr< TJpgInstanceDecoder > TJpgInstanceDecoderPtr
Definition driver.h:23
TJpgInstanceDecoderPtr createTJpgInstanceDecoder() FL_NOEXCEPT
JRESULT jd_prepare(JDEC *jd, size_t(*infunc)(JDEC *, uint8_t *, size_t), void *pool, size_t sz_pool, void *dev) FL_NOEXCEPT
JRESULT jd_decomp_progressive(JDEC_Progressive *jpd, int(*outfunc)(JDEC *, void *, JRECT *), uint8_t scale, uint16_t max_mcus_per_call, uint8_t *more_data_needed, uint8_t *processing_complete) FL_NOEXCEPT
uint16_t height
Definition tjpgd.h:61
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
fl::CRGB CRGB
Definition video.h:15
u8 u8 height
Definition blur.h:186
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
int snprintf(char *buffer, fl::size size, const char *format, const Args &... args) FL_NOEXCEPT
Snprintf-like formatting function that writes to a buffer.
Definition stdio.h:666
u8 width
Definition blur.h:186
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
fl::shared_ptr< filebuf > filebuf_ptr
Definition idecoder.h:15
fl::u8 getBytesPerPixel(PixelFormat format)
Definition pixel.h:15
fl::string format(const char *fmt)
Format with no arguments.
Definition format.h:439
PixelFormat
Definition pixel.h:7
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
Representation of an 8-bit RGB pixel (Red, Green, Blue)
Definition crgb.h:38