src/third_party/libnsgif/README.md
Pulled from: https://www.netsurf-browser.org/projects/libnsgif/
fl/codec/idecoder.h with base decoder interfacefl::third_party.c files need conversion to .cppfl/codec/gif.cppfl::third_party::SoftwareGifDecoder class in software_decoder.hGif::createDecoder() function to return shared_ptr<IDecoder>GifConfig integration requiredFastLED mandates that all third-party libraries be wrapped in the fl::third_party namespace to:
// Before: Global namespace collision risk
struct nsgif { /* ... */ };
// After: Safely namespaced
namespace fl {
namespace third_party {
struct nsgif { /* ... */ };
}
}
The fl::third_party namespace makes it immediately clear:
fl::third_party comes from external sources// Third-party code stays isolated
namespace fl::third_party {
struct nsgif { /* Original C API */ };
nsgif_error nsgif_create(nsgif **gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
// Software decoder implementation within third-party namespace
class SoftwareGifDecoder : public fl::IDecoder {
nsgif* decoder_; // Direct access to nsgif within same namespace
// IDecoder interface implementation for animated GIFs
};
}
// FastLED provides clean factory interface
namespace fl {
// GIF decoder factory (following MPEG1 pattern for multi-frame formats)
class Gif {
public:
// Create a GIF decoder for the current platform
static fl::shared_ptr<IDecoder> createDecoder(const GifConfig& config, fl::string* error_message = nullptr);
// Check if GIF decoding is supported on this platform
static bool isSupported();
};
}
The libnsgif implementation will demonstrate this pattern:
// File: nsgif.h
namespace fl {
namespace third_party {
// Original libnsgif API preserved
typedef struct nsgif nsgif;
typedef enum {
NSGIF_OK,
NSGIF_INSUFFICIENT_MEMORY,
NSGIF_DATA_ERROR,
// ... other error codes
} nsgif_error;
nsgif_error nsgif_create(nsgif **gif, nsgif_bitmap_cb_vt *bitmap_callbacks);
nsgif_error nsgif_data_scan(nsgif *gif, size_t size, const uint8_t *data);
nsgif_error nsgif_frame_decode(nsgif *gif, uint32_t frame, nsgif_bitmap_t **bitmap);
}
}
// File: fl/codec/gif.h
namespace fl {
// GIF-specific configuration
struct GifConfig {
enum FrameMode { SingleFrame, Streaming };
FrameMode mode = Streaming;
PixelFormat format = PixelFormat::RGB888;
bool looping = true;
fl::u16 maxWidth = 1920;
fl::u16 maxHeight = 1080;
fl::u8 bufferFrames = 3; // For smooth animation
};
// GIF decoder factory
class Gif {
public:
// Create a GIF decoder for the current platform
static fl::shared_ptr<IDecoder> createDecoder(const GifConfig& config, fl::string* error_message = nullptr);
// Check if GIF decoding is supported on this platform
static bool isSupported();
};
// Software GIF decoder implementation (in fl::third_party namespace)
namespace third_party {
class SoftwareGifDecoder : public IDecoder {
nsgif* decoder_; // Direct access to nsgif within same namespace
// Complete IDecoder interface implementation for animated GIFs
};
}
}
When adding new third-party libraries to FastLED:
fl::third_party namespace - Non-negotiable requirementThis architecture ensures FastLED remains maintainable, extensible, and free from the complexities often introduced by direct third-party library integration.
C++ Conversion
gif.c → gif.cpplzw.c → lzw.cppNamespace Wrapping
namespace fl {
namespace third_party {
// All libnsgif code wrapped here
}
}
Dependency Cleanup
Bridge Implementation (src/third_party/libnsgif/software_decoder.h)
namespace fl::third_party {
class SoftwareGifDecoder : public fl::IDecoder {
nsgif* decoder_;
fl::GifConfig config_;
// Complete IDecoder interface implementation
bool begin(fl::ByteStreamPtr stream) override;
DecodeResult decode() override;
Frame getCurrentFrame() override;
bool hasMoreFrames() const override;
fl::u32 getFrameCount() const override;
bool seek(fl::u32 frameIndex) override;
};
}
IDecoder interface implementation in third-party namespaceFactory Class Implementation (src/fl/codec/gif.cpp)
namespace fl {
fl::shared_ptr<IDecoder> Gif::createDecoder(const GifConfig& config, fl::string* error_message) {
// Create and return fl::third_party::SoftwareGifDecoder instance
return fl::make_shared<fl::third_party::SoftwareGifDecoder>(config);
}
bool Gif::isSupported() {
return true; // libnsgif is always available
}
}
Configuration Mapping
GifConfig::looping → GIF animation loop handlingGifConfig::format → pixel format conversionGifConfig::maxWidth/Height → size validationGifConfig::mode → SingleFrame vs Streaming animationGifConfig::bufferFrames → animation frame bufferingMemory Management
fl::scoped_array and buffer management implementedFactory Class Implementation
Gif::createDecoder() factory function implementedGif::isSupported() platform detection functionfl/codec/gif.hIDecoder Interface Implementation
begin() - Initialize with ByteStream for streaming GIF datadecode() - Decode next frame with proper state managementgetCurrentFrame() - Return current decoded framehasMoreFrames() - Check for additional animation framesgetFrameCount() - Total frame count for animationsseek() - Jump to specific frame indexError Handling
nsgif_error codes mapped to FastLED DecodeResult enumhasError()Advanced Features
fl::scoped_array and proper buffer managementThe GIF decoder will provide comprehensive animated GIF support using the IDecoder interface:
#include "fl/codec/gif.h"
// Create GIF decoder for animated content
fl::GifConfig config;
config.format = fl::PixelFormat::RGB888;
config.mode = fl::GifConfig::Streaming; // For animations
config.looping = true;
fl::string error;
auto decoder = fl::Gif::createDecoder(config, &error);
if (!decoder) {
printf("GIF decoder creation failed: %s\n", error.c_str());
return;
}
// Initialize with GIF data stream
auto stream = fl::ByteStream::fromSpan(gif_data_span);
if (!decoder->begin(stream)) {
printf("Failed to initialize GIF decoder\n");
return;
}
// Decode animation frames
while (decoder->hasMoreFrames()) {
auto result = decoder->decode();
if (result == fl::DecodeResult::Success) {
fl::Frame frame = decoder->getCurrentFrame();
// Display or process the frame
displayFrame(frame);
// Get current frame info
fl::u32 currentIndex = decoder->getCurrentFrameIndex();
printf("Displaying frame %u of %u\n", currentIndex, decoder->getFrameCount());
} else if (result == fl::DecodeResult::EndOfStream) {
// Animation complete
break;
} else if (result == fl::DecodeResult::Error) {
fl::string error;
decoder->hasError(&error);
printf("Decode error: %s\n", error.c_str());
break;
}
}
// Cleanup
decoder->end();
// Jump to specific frame
fl::u32 targetFrame = 10;
if (decoder->seek(targetFrame)) {
auto result = decoder->decode();
if (result == fl::DecodeResult::Success) {
fl::Frame frame = decoder->getCurrentFrame();
// Process frame 10
}
}
// Custom configuration for memory-constrained environments
fl::GifConfig config;
config.format = fl::PixelFormat::RGB565; // 16-bit color to save memory
config.maxWidth = 800; // Size limits
config.maxHeight = 600;
config.bufferFrames = 2; // Minimal buffering
config.mode = fl::GifConfig::SingleFrame; // Just decode one frame
// Check if GIF decoding is available
if (fl::Gif::isSupported()) {
// GIF decoding available
} else {
// Platform doesn't support GIF
}
The GIF decoder integrates seamlessly with FastLED's codec architecture:
IDecoder interface for consistent APIfl::PixelFormat for output format specificationFrame and FramePtr typesDecodeResultfl::ByteStreamPtr for streaming input datahasMoreFrames() and seek()Planned test framework: tests/codec_gif.cpp
TEST_CASE("GIF libnsgif decoder initialization") {
// Test NsGifDecoder specific initialization
// Verify callback setup and memory allocation
// Check namespace wrapping
}
TEST_CASE("GIF valid file decoding") {
// Test with minimal valid GIF data
// Verify frame dimensions and pixel data
// Check memory allocation patterns
}
TEST_CASE("GIF animation frame handling") {
// Test multi-frame GIF decoding
// Verify frame count detection
// Check frame-by-frame decoding accuracy
}
TEST_CASE("GIF transparency and disposal") {
// Test transparent GIF handling
// Verify background disposal methods
// Check alpha channel preservation
}
TEST_CASE("GIF pixel format conversion") {
// Test RGB888, RGB565, RGBA8888 output formats
// Verify color palette conversion accuracy
// Check byte ordering and alignment
}
TEST_CASE("GIF interlaced decoding") {
// Test interlaced GIF support
// Verify progressive rendering capability
// Performance benchmarks for different settings
}
TEST_CASE("GIF large animation handling") {
// Test maxWidth/maxHeight enforcement
// Memory usage validation with many frames
// Animation timing accuracy
}
TEST_CASE("GIF malformed data") {
// Truncated files, invalid headers
// Corrupted frame data recovery
// Memory leak detection
}
TEST_CASE("GIF stress testing") {
// Multiple decode operations
// Concurrent decoder instances
// Large animation sequences
}
⏳ Status: Test suite to be created
tests/codec_gif.cpp following existing codec test patternssrc/fl/codec/idecoder.h ✅ - Base IDecoder interface and DecodeResult enumsrc/fl/codec/gif.h ⏳ - Public API entry point with factory functionssrc/fl/codec/gif.cpp ⏳ - Factory implementation bridging to third-party decodersrc/third_party/libnsgif/software_decoder.h ⏳ - SoftwareGifDecoder implementationsrc/third_party/libnsgif/software_decoder.cpp ⏳ - Decoder implementation detailsfl::Gif::createDecoder() - Factory function returning shared_ptr<IDecoder>fl::Gif::isSupported() - Platform detection functionfl::GifConfig - Configuration structure for animated GIFsfl::third_party::SoftwareGifDecoder - IDecoder implementation class in third-party namespaceFrame, PixelFormat, and ByteStream systemsIDecoder interface support for animation streamingLibnsgif is a decoding library for GIF image format, originally developed for the NetSurf web browser. Key features include:
The libnsgif integration is PLANNED and represents the next step in FastLED's codec expansion. Once complete, the GIF decoder will provide:
The implementation will follow the established third-party integration pattern used successfully with TJpg_Decoder, ensuring consistent architecture and maintainability across FastLED's codec ecosystem.