FastLED 3.9.15
Loading...
Searching...
No Matches
jpeg.cpp.hpp
Go to the documentation of this file.
1#include "fl/codec/jpeg.h"
2#include "fl/stl/unique_ptr.h" // For make_unique
3// IWYU pragma: begin_keep
5// IWYU pragma: end_keep
6#include "fl/stl/utility.h"
7#include "fl/stl/vector.h"
8#include "fl/fx/frame.h"
9#include "fl/stl/stdio.h"
10#include "fl/log/log.h"
12#include "fl/codec/pixel.h"
13#include "fl/stl/chrono.h"
14#include "fl/stl/noexcept.h"
15
16namespace fl {
17
19// JpegInfo Implementation
21
22JpegInfo::JpegInfo(fl::u16 w, fl::u16 h, fl::u8 comp)
23 : width(w), height(h), components(comp)
24 , is_grayscale(comp == 1), is_valid(true) {}
25
27// JpegConfig Implementation
29
32
34// JpegDecoder::Impl - PIMPL Implementation
36
38private:
43 float mProgress;
46
47 void setError(const fl::string& message) {
48 mHasError = true;
49 mErrorMessage = message;
51 }
52
53 fl::u8 getScale() const {
54 switch (mConfig.quality) {
55 case JpegConfig::Quality::Low: return 3; // 1/8 scale
56 case JpegConfig::Quality::Medium: return 2; // 1/4 scale
57 case JpegConfig::Quality::High: return 0; // Full scale (1:1)
58 }
59 return 0;
60 }
61
62public:
63 explicit Impl(const JpegConfig& config)
65 , mProgress(0.0f), mHasError(false) {
67 }
68
69 ~Impl() FL_NOEXCEPT = default;
70
71 bool begin(fl::filebuf_ptr stream) {
72 if (!mDriver) {
73 setError("Driver not initialized");
74 return false;
75 }
76
78 mHasError = false;
79 mErrorMessage.clear();
80 mProgress = 0.0f;
81
82 // Configure progressive settings
84 mDriverconfig.max_mcus_per_tick = progressive_mConfig.max_mcus_per_tick;
85 mDriverconfig.max_time_per_tick_ms = progressive_mConfig.max_time_per_tick_ms;
86 mDriver->setProgressiveConfig(mDriverconfig);
87
88 // Set scale based on quality setting
89 mDriver->setScale(getScale());
90
91 if (!mDriver->beginDecodingStream(stream, mConfig.format)) {
92 fl::string err;
93 if (mDriver->hasError(&err)) {
94 setError(err);
95 } else {
96 setError("Failed to begin JPEG decoding");
97 }
98 return false;
99 }
100
102 return true;
103 }
104
105 void end() {
106 if (mDriver) {
107 mDriver->endDecoding();
108 }
110 }
111
116
117 bool hasError(fl::string* msg = nullptr) const {
118 if (msg && mHasError) {
119 *msg = mErrorMessage;
120 }
121 return mHasError;
122 }
123
126 return DecodeResult::Error;
127 }
128
131 }
132
133 while (processChunk()) {
134 if (should_yield && (*should_yield)()) {
135 // Caller requested yield - return current state
137 }
138 }
139
141 }
142
144 return mDriver ? mDriver->getCurrentFrame() : Frame(0);
145 }
146
147 bool hasMoreFrames() const { return false; } // JPEG is single frame
148
150 progressive_mConfig = config;
151 if (mDriver) {
153 mDriverconfig.max_mcus_per_tick = config.max_mcus_per_tick;
154 mDriverconfig.max_time_per_tick_ms = config.max_time_per_tick_ms;
155 mDriver->setProgressiveConfig(mDriverconfig);
156 }
157 }
158
161 return false;
162 }
163
166 }
167
168 if (!mDriver) {
169 setError("Driver not available");
170 return false;
171 }
172
173 bool more_work = mDriver->processChunk();
174
175 auto mDriverstate = mDriver->getState();
176 switch (mDriverstate) {
180 mProgress = mDriver->getProgress();
181 break;
184 mProgress = 1.0f;
185 return false;
187 fl::string err;
188 if (mDriver->hasError(&err)) {
189 setError(err);
190 } else {
191 setError("JPEG decoding failed");
192 }
193 return false;
194 }
195 }
196
197 return more_work;
198 }
199
200 float getProgress() const { return mProgress; }
201 bool hasPartialImage() const { return mDriver ? mDriver->hasPartialImage() : false; }
202 Frame getPartialFrame() { return mDriver ? mDriver->getPartialFrame() : Frame(0); }
203 fl::u16 getDecodedRows() const { return mDriver ? mDriver->getDecodedRows() : 0; }
204 bool feedData(fl::span<const fl::u8> data) { (void)data; return false; } // Not implemented
205 bool needsMoreData() const { return false; } // Not implemented
206 fl::size getBytesProcessed() const { return mDriver ? mDriver->getBytesProcessed() : 0; }
209};
210
212// JpegDecoder Implementation
214
216 : mImpl(fl::make_unique<Impl>(config)) {}
217
219
221 return mImpl->begin(stream);
222}
223
225 mImpl->end();
226}
227
229 return mImpl->isReady();
230}
231
233 return mImpl->hasError(msg);
234}
235
237 return mImpl->decode(fl::nullopt); // No callback - process to completion
238}
239
241 return mImpl->decode(should_yield);
242}
243
245 return mImpl->getCurrentFrame();
246}
247
249 return mImpl->hasMoreFrames();
250}
251
253 mImpl->setProgressiveConfig(config);
254}
255
257 return mImpl->getProgressiveConfig();
258}
259
261 return mImpl->getProgress();
262}
263
265 return mImpl->hasPartialImage();
266}
267
269 return mImpl->getPartialFrame();
270}
271
273 return mImpl->getDecodedRows();
274}
275
277 return mImpl->feedData(data);
278}
279
281 return mImpl->needsMoreData();
282}
283
285 return mImpl->getBytesProcessed();
286}
287
289 return mImpl->getState();
290}
291
293// Jpeg Static Methods Implementation
295
296bool Jpeg::decode(const JpegConfig& config, fl::span<const fl::u8> data, Frame* frame, fl::string* error_message) {
297 if (!frame) {
298 if (error_message) {
299 *error_message = "Frame pointer is null";
300 }
301 return false;
302 }
303
304 if (!frame->isFromCodec() || frame->getWidth() == 0 || frame->getHeight() == 0) {
305 if (error_message) {
306 *error_message = "Target frame must be created with proper dimensions for in-place decoding";
307 }
308 return false;
309 }
310
311 auto decoder = createDecoder(config);
312 auto stream = fl::make_shared<fl::memorybuf>(data.size());
313 stream->write(data);
314
315 if (!decoder->begin(stream)) {
316 if (error_message) {
317 decoder->hasError(error_message);
318 }
319 return false;
320 }
321
322 DecodeResult result = decoder->decode();
324 if (error_message) {
325 decoder->hasError(error_message);
326 }
327 return false;
328 }
329
330 Frame decoded = decoder->getCurrentFrame();
331
332 if (frame->getWidth() != decoded.getWidth() || frame->getHeight() != decoded.getHeight()) {
333 if (error_message) {
334 *error_message = "Target frame dimensions do not match decoded image dimensions";
335 }
336 return false;
337 }
338
339 frame->copy(decoded);
340 return true;
341}
342
343FramePtr Jpeg::decode(const JpegConfig& config, fl::span<const fl::u8> data, fl::string* error_message) {
344 auto decoder = createDecoder(config);
345 auto stream = fl::make_shared<fl::memorybuf>(data.size());
346 stream->write(data);
347
348 if (!decoder->begin(stream)) {
349 if (error_message) {
350 decoder->hasError(error_message);
351 }
352 return nullptr;
353 }
354
355 DecodeResult result = decoder->decode();
357 if (error_message) {
358 decoder->hasError(error_message);
359 }
360 return nullptr;
361 }
362
363 Frame frame = decoder->getCurrentFrame();
364 return frame.isValid() ? fl::make_shared<Frame>(frame) : nullptr;
365}
366
367FramePtr Jpeg::decode(fl::span<const fl::u8> data, fl::string* error_message) {
368 JpegConfig config; // Uses defaults
369 return decode(config, data, error_message);
370}
371
375
377 return true; // TJpg decoder is always supported
378}
379
381 const JpegConfig& config,
383 Frame* frame,
384 fl::u32 timeout_ms,
385 float* mProgressout,
386 fl::string* error_message) {
387
388 if (!frame) {
389 if (error_message) {
390 *error_message = "Frame pointer is null";
391 }
392 return false;
393 }
394
395 auto decoder = createDecoder(config);
396 auto stream = fl::make_shared<fl::memorybuf>(data.size());
397 stream->write(data);
398
399 if (!decoder->begin(stream)) {
400 if (error_message) {
401 decoder->hasError(error_message);
402 }
403 return false;
404 }
405
406 fl::u32 start_time = fl::millis();
407 fl::u32 deadline = start_time + timeout_ms;
408
409 // Use callback-based decode with time budget check
410 DecodeResult result = decoder->decode(fl::function<bool()>([&]() {
411 if (mProgressout) {
412 *mProgressout = decoder->getProgress();
413 }
414 return fl::millis() >= deadline; // Yield if time budget exceeded
415 }));
416
418 Frame decoded = decoder->getCurrentFrame();
419
420 if (frame->getWidth() != decoded.getWidth() || frame->getHeight() != decoded.getHeight()) {
421 if (error_message) {
422 *error_message = "Target frame dimensions do not match decoded image dimensions";
423 }
424 return false;
425 }
426
427 frame->copy(decoded);
428 return true;
429 } else if (result == DecodeResult::Error || decoder->hasError()) {
430 if (error_message) {
431 decoder->hasError(error_message);
432 }
433 return false;
434 }
435
436 // Partial completion due to timeout
437 if (mProgressout) {
438 *mProgressout = decoder->getProgress();
439 }
440 return false; // Not complete yet
441}
442
444 const JpegConfig& config,
445 fl::filebuf_ptr input_stream,
446 Frame* frame,
447 fl::u32 max_time_per_chunk_ms,
448 fl::function<bool(float)> mProgresscallback) {
449
450 if (!frame || !input_stream) {
451 return false;
452 }
453
454 auto decoder = createDecoder(config);
455
456 ProgressiveConfig prog_config;
457 prog_config.max_time_per_tick_ms = max_time_per_chunk_ms;
458 decoder->setProgressiveConfig(prog_config);
459
460 if (!decoder->begin(input_stream)) {
461 return false;
462 }
463
464 // Use callback-based decode with progress notifications
465 fl::optional<fl::function<bool()>> yield_func;
466 if (mProgresscallback) {
467 yield_func = fl::function<bool()>([&]() {
468 float progress = decoder->getProgress();
469 return !mProgresscallback(progress); // Yield if callback returns false
470 });
471 }
472 DecodeResult result = decoder->decode(yield_func);
473
475 Frame decoded = decoder->getCurrentFrame();
476
477 if (frame->getWidth() != decoded.getWidth() || frame->getHeight() != decoded.getHeight()) {
478 return false;
479 }
480
481 frame->copy(decoded);
482 return true;
483 }
484
485 return false;
486}
487
489 (void)data;
490 (void)error_message;
491 // TODO: Implement JPEG header parsing
492 return JpegInfo();
493}
494
495} // namespace fl
FastLED chrono implementation - duration types for time measurements.
bool isFromCodec() const
Definition frame.h:61
fl::u16 getWidth() const
Definition frame.h:59
fl::u16 getHeight() const
Definition frame.h:60
void copy(const Frame &other)
Definition frame.h:78
bool isValid() const
static bool decode(const JpegConfig &config, fl::span< const fl::u8 > data, Frame *frame, fl::string *error_message=nullptr)
Definition jpeg.cpp.hpp:296
static JpegDecoderPtr createDecoder(const JpegConfig &config)
Definition jpeg.cpp.hpp:372
static bool isSupported()
Definition jpeg.cpp.hpp:376
static bool decodeWithTimeout(const JpegConfig &config, fl::span< const fl::u8 > data, Frame *frame, fl::u32 timeout_ms, float *progress_out=nullptr, fl::string *error_message=nullptr)
Definition jpeg.cpp.hpp:380
static bool decodeStream(const JpegConfig &config, fl::filebuf_ptr input_stream, Frame *frame, fl::u32 max_time_per_chunk_ms=4, fl::function< bool(float)> progress_callback={})
Definition jpeg.cpp.hpp:443
static JpegInfo parseInfo(fl::span< const fl::u8 > data, fl::string *error_message=nullptr)
Definition jpeg.cpp.hpp:488
DecodeResult decode(fl::optional< fl::function< bool()> > should_yield)
Definition jpeg.cpp.hpp:124
bool needsMoreData() const
Definition jpeg.cpp.hpp:205
ProgressiveConfig getProgressiveConfig() const
Definition jpeg.cpp.hpp:208
Impl(const JpegConfig &config)
Definition jpeg.cpp.hpp:63
bool feedData(fl::span< const fl::u8 > data)
Definition jpeg.cpp.hpp:204
void setError(const fl::string &message)
Definition jpeg.cpp.hpp:47
bool hasMoreFrames() const
Definition jpeg.cpp.hpp:147
fl::string mErrorMessage
Definition jpeg.cpp.hpp:44
float getProgress() const
Definition jpeg.cpp.hpp:200
bool begin(fl::filebuf_ptr stream)
Definition jpeg.cpp.hpp:71
~Impl() FL_NOEXCEPT=default
fl::u16 getDecodedRows() const
Definition jpeg.cpp.hpp:203
bool hasError(fl::string *msg=nullptr) const
Definition jpeg.cpp.hpp:117
JpegDecoder::State mState
Definition jpeg.cpp.hpp:42
void setProgressiveConfig(const ProgressiveConfig &config)
Definition jpeg.cpp.hpp:149
ProgressiveConfig progressive_mConfig
Definition jpeg.cpp.hpp:41
JpegDecoder::State getState() const
Definition jpeg.cpp.hpp:207
fl::third_party::TJpgInstanceDecoderPtr mDriver
Definition jpeg.cpp.hpp:39
bool hasPartialImage() const
Definition jpeg.cpp.hpp:201
fl::u8 getScale() const
Definition jpeg.cpp.hpp:53
fl::size getBytesProcessed() const
Definition jpeg.cpp.hpp:206
bool isReady() const
Definition jpeg.cpp.hpp:112
fl::unique_ptr< Impl > mImpl
Definition jpeg.h:99
bool hasPartialImage() const
Definition jpeg.cpp.hpp:264
bool begin(fl::filebuf_ptr stream) override
Definition jpeg.cpp.hpp:220
void end() override
Definition jpeg.cpp.hpp:224
~JpegDecoder() FL_NOEXCEPT override
JpegDecoder(const JpegConfig &config)
Definition jpeg.cpp.hpp:215
bool needsMoreData() const
Definition jpeg.cpp.hpp:280
bool hasMoreFrames() const override
Definition jpeg.cpp.hpp:248
void setProgressiveConfig(const ProgressiveConfig &config)
Definition jpeg.cpp.hpp:252
Frame getPartialFrame()
Definition jpeg.cpp.hpp:268
ProgressiveConfig getProgressiveConfig() const
Definition jpeg.cpp.hpp:256
bool feedData(fl::span< const fl::u8 > data)
Definition jpeg.cpp.hpp:276
DecodeResult decode() override
Definition jpeg.cpp.hpp:236
bool hasError(fl::string *msg=nullptr) const override
Definition jpeg.cpp.hpp:232
Frame getCurrentFrame() override
Definition jpeg.cpp.hpp:244
float getProgress() const
Definition jpeg.cpp.hpp:260
bool isReady() const override
Definition jpeg.cpp.hpp:228
fl::u16 getDecodedRows() const
Definition jpeg.cpp.hpp:272
State getState() const
Definition jpeg.cpp.hpp:288
fl::size getBytesProcessed() const
Definition jpeg.cpp.hpp:284
constexpr fl::size size() const FL_NOEXCEPT
Definition span.h:458
Centralized logging categories for FastLED hardware interfaces and subsystems.
unsigned char u8
Definition s16x16x4.h:132
fl::shared_ptr< TJpgInstanceDecoder > TJpgInstanceDecoderPtr
Definition driver.h:23
TJpgInstanceDecoderPtr createTJpgInstanceDecoder() FL_NOEXCEPT
fl::u32 millis()
Universal millisecond timer - returns milliseconds since system startup.
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
Definition unique_ptr.h:261
Optional< T > optional
Definition optional.h:16
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
fl::shared_ptr< filebuf > filebuf_ptr
Definition idecoder.h:15
constexpr nullopt_t nullopt
Definition optional.h:13
DecodeResult
Definition idecoder.h:18
fl::shared_ptr< JpegDecoder > JpegDecoderPtr
Definition jpeg.h:11
PixelFormat
Definition pixel.h:7
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
PixelFormat format
Definition jpeg.h:31
JpegConfig() FL_NOEXCEPT=default
Quality quality
Definition jpeg.h:30
fl::u16 width
Definition jpeg.h:15
bool is_valid
Definition jpeg.h:20
JpegInfo() FL_NOEXCEPT=default
fl::u16 height
Definition jpeg.h:16
fl::u8 components
Definition jpeg.h:17
bool is_grayscale
Definition jpeg.h:19
fl::u16 max_mcus_per_tick
Definition jpeg.h:39
fl::u32 max_time_per_tick_ms
Definition jpeg.h:40