FastLED 3.9.15
Loading...
Searching...
No Matches
software_decoder.cpp.hpp
Go to the documentation of this file.
1#include "software_decoder.h"
2#include "fl/stl/unique_ptr.h"
3#include "fl/stl/string.h"
4#include "fl/stl/cstring.h" // for fl::memset() and fl::memcpy()
5#include "fl/stl/noexcept.h"
6
7namespace fl {
8namespace third_party {
9
10// Initialize static bitmap callbacks
15 nullptr, // set_opaque (optional)
16 nullptr, // test_opaque (optional)
17 nullptr, // modified (optional)
18 nullptr // get_rowspan (optional)
19};
20
22 : gif_(nullptr)
23 , stream_(nullptr)
24 , currentFrame_(nullptr)
25 , ready_(false)
26 , hasError_(false)
27 , dataComplete_(false)
29 , endOfStream_(false) {
30 (void)format; // Unused - libnsgif always outputs RGBA8888
31}
32
36
38 if (!stream) {
39 setError("Invalid stream provided");
40 return false;
41 }
42
43 stream_ = stream;
44
45 if (!initializeDecoder()) {
46 return false;
47 }
48
49 return loadMoreData();
50}
51
54 stream_ = nullptr;
55 currentFrame_ = nullptr;
56 ready_ = false;
57 hasError_ = false;
58 dataComplete_ = false;
60 endOfStream_ = false;
61 errorMessage_.clear();
62 dataBuffer_.clear();
63}
64
66 if (msg && hasError_) {
67 *msg = errorMessage_;
68 }
69 return hasError_;
70}
71
73 if (hasError_) {
75 }
76
77 if (!ready_) {
78 setError("Decoder not ready");
80 }
81
82 if (endOfStream_) {
84 }
85
86 // Try to decode current frame
87 nsgif_bitmap_t* bitmap = nullptr;
89
90 switch (result) {
91 case NSGIF_OK:
93 if (!currentFrame_) {
94 setError("Failed to convert bitmap to frame");
96 }
99
101 if (!dataComplete_) {
102 // Try to load more data
103 if (loadMoreData()) {
104 return decode(); // Retry
105 }
106 }
107 endOfStream_ = true;
109
111 endOfStream_ = true;
113
114 case NSGIF_ERR_OOM:
115 case NSGIF_ERR_DATA:
120 default:
121 setError(fl::string("GIF decode error: ") + nsgif_strerror(result));
123 }
124}
125
127 if (currentFrame_) {
128 return *currentFrame_;
129 }
130 return fl::Frame(0); // Empty frame
131}
132
134 if (hasError_ || !ready_) {
135 return false;
136 }
137
138 if (endOfStream_) {
139 return false;
140 }
141
142 // Check if we have more frames available
143 const nsgif_info_t* info = nsgif_get_info(gif_);
144 if (!info) {
145 return false;
146 }
147
149}
150
152 if (!ready_ || hasError_) {
153 return 0;
154 }
155
156 const nsgif_info_t* info = nsgif_get_info(gif_);
157 return info ? info->frame_count : 0;
158}
159
160bool SoftwareGifDecoder::seek(fl::u32 frameIndex) FL_NOEXCEPT {
161 if (!ready_ || hasError_) {
162 return false;
163 }
164
165 const nsgif_info_t* info = nsgif_get_info(gif_);
166 if (!info || frameIndex >= info->frame_count) {
167 return false;
168 }
169
170 currentFrameIndex_ = frameIndex;
171 endOfStream_ = false;
172 return true;
173}
174
176 if (!ready_ || hasError_) {
177 return 0;
178 }
179
180 const nsgif_info_t* info = nsgif_get_info(gif_);
181 return info ? static_cast<fl::u16>(info->width) : 0;
182}
183
185 if (!ready_ || hasError_) {
186 return 0;
187 }
188
189 const nsgif_info_t* info = nsgif_get_info(gif_);
190 return info ? static_cast<fl::u16>(info->height) : 0;
191}
192
194 return getFrameCount() > 1;
195}
196
198 if (!ready_ || hasError_) {
199 return 0;
200 }
201
202 const nsgif_info_t* info = nsgif_get_info(gif_);
203 return info ? static_cast<fl::u32>(info->loop_max) : 0;
204}
205
207 // Determine bitmap format based on output format
208 // Use R8G8B8A8 explicitly to ensure byte order: 0xRR, 0xGG, 0xBB, 0xAA
210
212 if (result != NSGIF_OK) {
213 setError(fl::string("Failed to create GIF decoder: ") + nsgif_strerror(result));
214 return false;
215 }
216
217 ready_ = true;
218 return true;
219}
220
222 if (gif_) {
224 gif_ = nullptr;
225 }
226 ready_ = false;
227}
228
230 hasError_ = true;
231 errorMessage_ = message;
232 ready_ = false;
233}
234
236 if (!stream_ || !gif_) {
237 return false;
238 }
239
240 // Read available data from stream in chunks and accumulate
241 const fl::size bufferSize = 4096; // Read in chunks
242 fl::u8 buffer[bufferSize];
243 fl::size bytesRead = stream_->read(buffer, bufferSize);
244
245 if (bytesRead == 0) {
246 // No more data available, mark as complete
248 dataComplete_ = true;
249 return false;
250 }
251
252 // Append new data to accumulated buffer
253 // libnsgif requires ALL data to be provided in each call to nsgif_data_scan
254 fl::size oldSize = dataBuffer_.size();
255 dataBuffer_.resize(oldSize + bytesRead);
256 fl::memcpy(dataBuffer_.data() + oldSize, buffer, bytesRead);
257
258 // Feed ALL accumulated data to libnsgif
260
261 // Check if we've read less than requested (likely end of stream)
262 if (bytesRead < bufferSize) {
264 dataComplete_ = true;
265 }
266
268 setError(fl::string("GIF data scan error: ") + nsgif_strerror(result));
269 return false;
270 }
271
272 // Frame count is available via nsgif_get_info() if needed in the future
273
274 return true;
275}
276
278 if (!bitmap) {
279 setError("convertBitmapToFrame called with null bitmap");
280 return nullptr;
281 }
282
283 GifBitmap* gifBitmap = static_cast<GifBitmap*>(bitmap);
284
285 // Validate bitmap data
286 if (!gifBitmap->pixels || gifBitmap->width == 0 || gifBitmap->height == 0) {
287 setError("GIF bitmap has invalid data or dimensions");
288 return nullptr;
289 }
290
291 fl::u8* rawData = gifBitmap->pixels.get();
292
293 // Since libnsgif outputs RGBA8888, we need to handle the conversion properly
294 // The Frame constructor expects the format we specify (outputFormat_)
295 // but libnsgif always gives us RGBA8888 data
296
297 // Create Frame with RGBA8888 format since that's what libnsgif provides
298 // The Frame constructor will handle conversion to outputFormat_ via convertPixelsToRgb
299 auto frame = fl::make_shared<fl::Frame>(
300 rawData,
301 gifBitmap->width,
302 gifBitmap->height,
303 fl::PixelFormat::RGBA8888, // What libnsgif actually provides
304 currentFrameIndex_ // Use frame index as timestamp
305 );
306
307 if (!frame || !frame->isValid()) {
308 setError("Failed to create valid Frame from GIF bitmap");
309 return nullptr;
310 }
311
312 return frame;
313}
314
315// Static callback implementations
317 // Create a GifBitmap wrapper
318 // Note: In environments without exceptions, new will return nullptr on failure
319 GifBitmap* bitmap = new GifBitmap(static_cast<fl::u16>(width), static_cast<fl::u16>(height), 4); // 4 bytes per pixel for RGBA
320 return static_cast<nsgif_bitmap_t*>(bitmap);
321}
322
324 if (bitmap) {
325 delete static_cast<GifBitmap*>(bitmap);
326 }
327}
328
330 if (!bitmap) {
331 return nullptr;
332 }
333
334 GifBitmap* gifBitmap = static_cast<GifBitmap*>(bitmap);
335 return gifBitmap->pixels.get();
336}
337
338} // namespace third_party
339} // namespace fl
fl::u32 getFrameCount() const FL_NOEXCEPT override
fl::Frame getCurrentFrame() FL_NOEXCEPT override
static fl::u8 * bitmapGetBuffer(nsgif_bitmap_t *bitmap) FL_NOEXCEPT
void setError(const fl::string &message) FL_NOEXCEPT
fl::DecodeResult decode() FL_NOEXCEPT override
bool hasMoreFrames() const FL_NOEXCEPT override
bool seek(fl::u32 frameIndex) FL_NOEXCEPT override
static nsgif_bitmap_t * bitmapCreate(int width, int height) FL_NOEXCEPT
fl::shared_ptr< fl::Frame > currentFrame_
static void bitmapDestroy(nsgif_bitmap_t *bitmap) FL_NOEXCEPT
bool hasError(fl::string *msg=nullptr) const FL_NOEXCEPT override
static nsgif_bitmap_cb_vt bitmapCallbacks_
fl::shared_ptr< fl::Frame > convertBitmapToFrame(nsgif_bitmap_t *bitmap) FL_NOEXCEPT
SoftwareGifDecoder(fl::PixelFormat format=fl::PixelFormat::RGB888) FL_NOEXCEPT
bool begin(fl::filebuf_ptr stream) FL_NOEXCEPT override
pointer get() const FL_NOEXCEPT
Definition unique_ptr.h:97
unsigned char u8
Definition coder.h:132
@ NSGIF_BITMAP_FMT_R8G8B8A8
Bite-wise RGBA: Byte order: 0xRR, 0xGG, 0xBB, 0xAA.
Definition nsgif.hpp:119
nsgif_error
LibNSGIF return codes.
Definition nsgif.hpp:58
@ NSGIF_ERR_DATA_COMPLETE
Can't supply more data after calling nsgif_data_complete.
Definition nsgif.hpp:92
@ NSGIF_ERR_OOM
Out of memory error.
Definition nsgif.hpp:67
@ NSGIF_ERR_BAD_FRAME
Frame number is not valid.
Definition nsgif.hpp:77
@ NSGIF_ERR_END_OF_DATA
Unexpected end of GIF source data.
Definition nsgif.hpp:87
@ NSGIF_ERR_ANIMATION_END
Indicates an animation is complete, and nsgif_reset must be called to restart the animation from the ...
Definition nsgif.hpp:103
@ NSGIF_ERR_DATA_FRAME
GIF source data contained an error in a frame.
Definition nsgif.hpp:82
@ NSGIF_ERR_DATA
GIF source data is invalid, and no frames are recoverable.
Definition nsgif.hpp:72
@ NSGIF_ERR_FRAME_DISPLAY
The current frame cannot be displayed.
Definition nsgif.hpp:97
@ NSGIF_OK
Success.
Definition nsgif.hpp:62
nsgif_error nsgif_data_scan(nsgif_t *gif, fl::size size, const fl::u8 *data) FL_NOEXCEPT
Scan the source image data.
Definition gif.cpp.hpp:1616
nsgif_error nsgif_frame_decode(nsgif_t *gif, fl::u32 frame, nsgif_bitmap_t **bitmap) FL_NOEXCEPT
Decodes a GIF frame.
Definition gif.cpp.hpp:1953
nsgif_error nsgif_create(const nsgif_bitmap_cb_vt *bitmap_vt, nsgif_bitmap_fmt_t bitmap_fmt, nsgif_t **gif_out) FL_NOEXCEPT
Create the NSGIF object.
Definition gif.cpp.hpp:1493
const nsgif_info_t * nsgif_get_info(const nsgif_t *gif) FL_NOEXCEPT
Get information about a GIF from an nsgif_t object.
Definition gif.cpp.hpp:1990
void nsgif_data_complete(nsgif_t *gif) FL_NOEXCEPT
Tell libnsgif that all the gif data has been provided.
Definition gif.cpp.hpp:1772
const char * nsgif_strerror(nsgif_error err) FL_NOEXCEPT
Convert an error code to a string.
Definition gif.cpp.hpp:2045
void nsgif_bitmap_t
Client bitmap type.
Definition nsgif.hpp:175
void nsgif_destroy(nsgif_t *gif) FL_NOEXCEPT
Free a NSGIF object.
Definition gif.cpp.hpp:1395
struct fl::third_party::nsgif_info nsgif_info_t
Information about a GIF.
enum fl::third_party::nsgif_bitmap_fmt nsgif_bitmap_fmt_t
NSGIF nsgif_bitmap_t pixel format.
int loop_max
number of times to play animation (zero means loop forever)
Definition nsgif.hpp:390
fl::u32 frame_count
number of frames decoded
Definition nsgif.hpp:388
fl::u32 width
width of GIF (may increase during decoding)
Definition nsgif.hpp:384
fl::u32 height
height of GIF (may increase during decoding)
Definition nsgif.hpp:386
Bitmap callbacks function table.
Definition nsgif.hpp:178
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
u8 u8 height
Definition blur.h:186
u8 width
Definition blur.h:186
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
DecodeResult
Definition idecoder.h:18
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
fl::unique_ptr< fl::u8[]> pixels