From 3ad56ea103d794082b3475fd22591a5a7787e63d Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Sun, 10 Mar 2024 14:34:15 +0100
Subject: [PATCH 01/12] GIF testing
---
wled00/fcn_declare.h | 3 ++-
wled00/image_loader.cpp | 58 +++++++++++++++++++++--------------------
2 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index 59be67624..72918d1a2 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -5,7 +5,6 @@
* All globally accessible functions are declared here
*/
-#include "FX.h"
//alexa.cpp
#ifndef WLED_DISABLE_ALEXA
@@ -127,6 +126,8 @@ void onHueConnect(void* arg, AsyncClient* client);
void sendHuePoll();
void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
+#include "FX.h" // must be below colors.cpp declarations (potentially due to duplicate declarations of e.g. color_blend)
+
//image_loader.cpp
#ifndef WLED_DISABLE_GIF
bool fileSeekCallback(unsigned long position);
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 500fbc2dd..4a8052400 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -5,7 +5,7 @@
File file;
char lastFilename[34] = "/";
-GifDecoder<320,320,12,true>* decoder;
+GifDecoder<32,32,12> decoder;
bool gifDecodeFailed = false;
long lastFrameDisplayTime = 0, currentFrameDelay = 0;
@@ -38,7 +38,7 @@ bool openGif(const char *filename) {
Segment* activeSeg;
uint16_t gifWidth, gifHeight;
-uint16_t fillPixX, fillPixY;
+//uint16_t fillPixX, fillPixY;
void screenClearCallback(void) {
activeSeg->fill(0);
@@ -66,6 +66,7 @@ void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t
#define IMAGE_ERROR_DECODER_ALLOC 5
#define IMAGE_ERROR_GIF_DECODE 6
#define IMAGE_ERROR_FRAME_DECODE 7
+#define IMAGE_ERROR_WAITING 254
#define IMAGE_ERROR_PREV 255
// renders an image (.gif only; .bmp and .fseq to be added soon) from FS to a segment
@@ -84,47 +85,48 @@ byte renderImageToSegment(Segment &seg) {
if (file) file.close();
openGif(lastFilename);
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
- if (!decoder) decoder = new GifDecoder<320,320,12,true>();
- if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
- decoder->setScreenClearCallback(screenClearCallback);
- decoder->setUpdateScreenCallback(updateScreenCallback);
- decoder->setDrawPixelCallback(drawPixelCallback);
- decoder->setFileSeekCallback(fileSeekCallback);
- decoder->setFilePositionCallback(filePositionCallback);
- decoder->setFileReadCallback(fileReadCallback);
- decoder->setFileReadBlockCallback(fileReadBlockCallback);
- decoder->setFileSizeCallback(fileSizeCallback);
+ //if (!decoder) decoder = new GifDecoder<32,32,12>();
+ //if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
+ decoder.setScreenClearCallback(screenClearCallback);
+ decoder.setUpdateScreenCallback(updateScreenCallback);
+ decoder.setDrawPixelCallback(drawPixelCallback);
+ decoder.setFileSeekCallback(fileSeekCallback);
+ decoder.setFilePositionCallback(filePositionCallback);
+ decoder.setFileReadCallback(fileReadCallback);
+ decoder.setFileReadBlockCallback(fileReadBlockCallback);
+ decoder.setFileSizeCallback(fileSizeCallback);
Serial.println("Starting decoding");
- if(decoder->startDecoding() < 0) { gifDecodeFailed = true; return IMAGE_ERROR_GIF_DECODE; }
+ if(decoder.startDecoding() < 0) { gifDecodeFailed = true; return IMAGE_ERROR_GIF_DECODE; }
Serial.println("Decoding started");
}
if (gifDecodeFailed) return IMAGE_ERROR_PREV;
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
- if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
+ //if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
// speed 0 = half speed, 128 = normal, 255 = full FX FPS
// TODO: 0 = 4x slow, 64 = 2x slow, 128 = normal, 192 = 2x fast, 255 = 4x fast
uint32_t wait = currentFrameDelay * 2 - seg.speed * currentFrameDelay / 128;
- if((millis() - lastFrameDisplayTime) >= wait) {
- decoder->getSize(&gifWidth, &gifHeight);
- fillPixX = (seg.width()+(gifWidth-1)) / gifWidth;
- fillPixY = (seg.height()+(gifHeight-1)) / gifHeight;
- int result = decoder->decodeFrame(false);
- if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; }
- long lastFrameDelay = currentFrameDelay;
- currentFrameDelay = decoder->getFrameDelay_ms();
- long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate
- currentFrameDelay -= tooSlowBy;
- lastFrameDisplayTime = millis();
- }
- return true;
+ if((millis() - lastFrameDisplayTime) < wait) return IMAGE_ERROR_WAITING;
+
+ decoder.getSize(&gifWidth, &gifHeight);
+ //fillPixX = (seg.width()+(gifWidth-1)) / gifWidth;
+ //fillPixY = (seg.height()+(gifHeight-1)) / gifHeight;
+ int result = decoder.decodeFrame(false);
+ if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; }
+ //long lastFrameDelay = currentFrameDelay;
+ currentFrameDelay = decoder.getFrameDelay_ms();
+ //long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate
+ //currentFrameDelay -= tooSlowBy; // TODO this is broken
+ lastFrameDisplayTime = millis();
+
+ return IMAGE_ERROR_NONE;
}
void endImagePlayback() {
if (file) file.close();
- delete decoder;
+ //delete decoder;
gifDecodeFailed = false;
activeSeg = nullptr;
lastFilename[0] = '\0';
From b2afac891415a65c5267755a351844903aba8e9d Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Sun, 10 Mar 2024 21:36:13 +0100
Subject: [PATCH 02/12] GIFs work again in principle
---
platformio.ini | 2 +-
wled00/FX.cpp | 11 +++++++----
wled00/FX_fcn.cpp | 1 +
wled00/fcn_declare.h | 1 +
wled00/image_loader.cpp | 13 +++++++++----
5 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index 66bd09957..73d82c3a7 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -221,7 +221,7 @@ lib_deps =
https://github.com/lorol/LITTLEFS.git
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
bitbank2/AnimatedGIF@^1.4.7
- pixelmatix/GifDecoder@^1.1.0
+ https://github.com/Aircoookie/GifDecoder#e76f58f
${env.lib_deps}
# additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 681f3d107..e8c59babe 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -4470,7 +4470,12 @@ static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!
Draws a .gif image from filesystem on the matrix/strip
*/
uint16_t mode_image(void) {
- renderImageToSegment(SEGMENT);
+ //Serial.println(renderImageToSegment(SEGMENT));
+ int status = renderImageToSegment(SEGMENT);
+ if (status != 0 && status != 254 && status != 255) {
+ Serial.print("GIF renderer return: ");
+ Serial.println(status);
+ }
return FRAMETIME;
}
static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128";
@@ -7973,7 +7978,7 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
addEffect(FX_MODE_FAIRYTWINKLE, &mode_fairytwinkle, _data_FX_MODE_FAIRYTWINKLE);
addEffect(FX_MODE_RUNNING_DUAL, &mode_running_dual, _data_FX_MODE_RUNNING_DUAL);
-
+ addEffect(FX_MODE_IMAGE, &mode_image, _data_FX_MODE_IMAGE);
addEffect(FX_MODE_TRICOLOR_CHASE, &mode_tricolor_chase, _data_FX_MODE_TRICOLOR_CHASE);
addEffect(FX_MODE_TRICOLOR_WIPE, &mode_tricolor_wipe, _data_FX_MODE_TRICOLOR_WIPE);
addEffect(FX_MODE_TRICOLOR_FADE, &mode_tricolor_fade, _data_FX_MODE_TRICOLOR_FADE);
@@ -8035,8 +8040,6 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_DANCING_SHADOWS, &mode_dancing_shadows, _data_FX_MODE_DANCING_SHADOWS);
addEffect(FX_MODE_WASHING_MACHINE, &mode_washing_machine, _data_FX_MODE_WASHING_MACHINE);
- addEffect(FX_MODE_IMAGE, &mode_image, _data_FX_MODE_IMAGE);
-
addEffect(FX_MODE_BLENDS, &mode_blends, _data_FX_MODE_BLENDS);
addEffect(FX_MODE_TV_SIMULATOR, &mode_tv_simulator, _data_FX_MODE_TV_SIMULATOR);
addEffect(FX_MODE_DYNAMIC_SMOOTH, &mode_dynamic_smooth, _data_FX_MODE_DYNAMIC_SMOOTH);
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 42e98452f..94fdb3006 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -199,6 +199,7 @@ void Segment::resetIfRequired() {
if (data && _dataLen > 0) memset(data, 0, _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false;
+ endImagePlayback(this);
}
CRGBPalette16 IRAM_ATTR &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index 72918d1a2..4619e640a 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -136,6 +136,7 @@ int fileReadCallback(void);
int fileReadBlockCallback(void * buffer, int numberOfBytes);
int fileSizeCallback(void);
byte renderImageToSegment(Segment &seg);
+void endImagePlayback(Segment* seg);
#endif
//improv.cpp
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 4a8052400..44fb7f7c8 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -5,7 +5,7 @@
File file;
char lastFilename[34] = "/";
-GifDecoder<32,32,12> decoder;
+GifDecoder<32,32,12,true> decoder;
bool gifDecodeFailed = false;
long lastFrameDisplayTime = 0, currentFrameDelay = 0;
@@ -85,7 +85,7 @@ byte renderImageToSegment(Segment &seg) {
if (file) file.close();
openGif(lastFilename);
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
- //if (!decoder) decoder = new GifDecoder<32,32,12>();
+ //if (!decoder) decoder = new GifDecoder<32,32,12,true>();
//if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
decoder.setScreenClearCallback(screenClearCallback);
decoder.setUpdateScreenCallback(updateScreenCallback);
@@ -95,6 +95,7 @@ byte renderImageToSegment(Segment &seg) {
decoder.setFileReadCallback(fileReadCallback);
decoder.setFileReadBlockCallback(fileReadBlockCallback);
decoder.setFileSizeCallback(fileSizeCallback);
+ decoder.alloc(); // TODO only if not already allocated
Serial.println("Starting decoding");
if(decoder.startDecoding() < 0) { gifDecodeFailed = true; return IMAGE_ERROR_GIF_DECODE; }
Serial.println("Decoding started");
@@ -124,12 +125,16 @@ byte renderImageToSegment(Segment &seg) {
return IMAGE_ERROR_NONE;
}
-void endImagePlayback() {
+void endImagePlayback(Segment *seg) {
+ Serial.println("Image playback end called");
+ if (!activeSeg || activeSeg != seg) return;
if (file) file.close();
//delete decoder;
+ decoder.dealloc();
gifDecodeFailed = false;
activeSeg = nullptr;
- lastFilename[0] = '\0';
+ lastFilename[1] = '\0';
+ Serial.println("Image playback ended");
}
#endif
\ No newline at end of file
From 3e60d3d96e9ebbea6232fca0b0e10fe714f9b008 Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Sun, 17 Mar 2024 22:24:55 +0100
Subject: [PATCH 03/12] Working GIF support
---
wled00/FX.cpp | 11 +++++------
wled00/image_loader.cpp | 28 +++++++++++++++-------------
2 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index e8c59babe..48866224e 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -4470,12 +4470,11 @@ static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!
Draws a .gif image from filesystem on the matrix/strip
*/
uint16_t mode_image(void) {
- //Serial.println(renderImageToSegment(SEGMENT));
- int status = renderImageToSegment(SEGMENT);
- if (status != 0 && status != 254 && status != 255) {
- Serial.print("GIF renderer return: ");
- Serial.println(status);
- }
+ renderImageToSegment(SEGMENT);
+ // if (status != 0 && status != 254 && status != 255) {
+ // Serial.print("GIF renderer return: ");
+ // Serial.println(status);
+ // }
return FRAMETIME;
}
static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128";
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 44fb7f7c8..9ad4662e6 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -3,11 +3,15 @@
#include "GifDecoder.h"
#include "wled.h"
+/*
+ * Functions to render images from filesystem to segments, used by the "Image" effect
+ */
+
File file;
char lastFilename[34] = "/";
-GifDecoder<32,32,12,true> decoder;
+GifDecoder<320,320,12,true> decoder;
bool gifDecodeFailed = false;
-long lastFrameDisplayTime = 0, currentFrameDelay = 0;
+unsigned long lastFrameDisplayTime = 0, currentFrameDelay = 0;
bool fileSeekCallback(unsigned long position) {
return file.seek(position);
@@ -38,7 +42,6 @@ bool openGif(const char *filename) {
Segment* activeSeg;
uint16_t gifWidth, gifHeight;
-//uint16_t fillPixX, fillPixY;
void screenClearCallback(void) {
activeSeg->fill(0);
@@ -72,6 +75,8 @@ void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t
// renders an image (.gif only; .bmp and .fseq to be added soon) from FS to a segment
byte renderImageToSegment(Segment &seg) {
if (!seg.name) return IMAGE_ERROR_NO_NAME;
+ // disable during effect transition, causes flickering, multiple allocations and depending on image, part of old FX remaining
+ if (seg.mode != seg.currentMode()) return IMAGE_ERROR_WAITING;
if (activeSeg && activeSeg != &seg) return IMAGE_ERROR_SEG_LIMIT; // only one segment at a time
activeSeg = &seg;
@@ -85,8 +90,6 @@ byte renderImageToSegment(Segment &seg) {
if (file) file.close();
openGif(lastFilename);
if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
- //if (!decoder) decoder = new GifDecoder<32,32,12,true>();
- //if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
decoder.setScreenClearCallback(screenClearCallback);
decoder.setUpdateScreenCallback(updateScreenCallback);
decoder.setDrawPixelCallback(drawPixelCallback);
@@ -95,7 +98,7 @@ byte renderImageToSegment(Segment &seg) {
decoder.setFileReadCallback(fileReadCallback);
decoder.setFileReadBlockCallback(fileReadBlockCallback);
decoder.setFileSizeCallback(fileSizeCallback);
- decoder.alloc(); // TODO only if not already allocated
+ decoder.alloc();
Serial.println("Starting decoding");
if(decoder.startDecoding() < 0) { gifDecodeFailed = true; return IMAGE_ERROR_GIF_DECODE; }
Serial.println("Decoding started");
@@ -109,17 +112,17 @@ byte renderImageToSegment(Segment &seg) {
// TODO: 0 = 4x slow, 64 = 2x slow, 128 = normal, 192 = 2x fast, 255 = 4x fast
uint32_t wait = currentFrameDelay * 2 - seg.speed * currentFrameDelay / 128;
- if((millis() - lastFrameDisplayTime) < wait) return IMAGE_ERROR_WAITING;
+ // TODO consider handling this on FX level with a different frametime, but that would cause slow gifs to speed up during transitions
+ if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING;
decoder.getSize(&gifWidth, &gifHeight);
- //fillPixX = (seg.width()+(gifWidth-1)) / gifWidth;
- //fillPixY = (seg.height()+(gifHeight-1)) / gifHeight;
+
int result = decoder.decodeFrame(false);
if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; }
- //long lastFrameDelay = currentFrameDelay;
+
currentFrameDelay = decoder.getFrameDelay_ms();
- //long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate
- //currentFrameDelay -= tooSlowBy; // TODO this is broken
+ unsigned long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate
+ currentFrameDelay = tooSlowBy > currentFrameDelay ? 0 : currentFrameDelay - tooSlowBy;
lastFrameDisplayTime = millis();
return IMAGE_ERROR_NONE;
@@ -129,7 +132,6 @@ void endImagePlayback(Segment *seg) {
Serial.println("Image playback end called");
if (!activeSeg || activeSeg != seg) return;
if (file) file.close();
- //delete decoder;
decoder.dealloc();
gifDecodeFailed = false;
activeSeg = nullptr;
From 247de600afd3d06918c7c9873e63a756e970ad2b Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Sun, 17 Mar 2024 22:57:15 +0100
Subject: [PATCH 04/12] Fix missing GIF enable macros
---
platformio.ini | 2 +-
wled00/FX.cpp | 6 +++++-
wled00/FX_fcn.cpp | 2 ++
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index 73d82c3a7..c08c632a4 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -221,7 +221,7 @@ lib_deps =
https://github.com/lorol/LITTLEFS.git
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
bitbank2/AnimatedGIF@^1.4.7
- https://github.com/Aircoookie/GifDecoder#e76f58f
+ https://github.com/Aircoookie/GifDecoder#bc3af18
${env.lib_deps}
# additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 48866224e..5543b54ba 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -4470,12 +4470,16 @@ static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!
Draws a .gif image from filesystem on the matrix/strip
*/
uint16_t mode_image(void) {
+ #ifdef WLED_DISABLE_GIF
+ return mode_static();
+ #else
renderImageToSegment(SEGMENT);
+ return FRAMETIME;
+ #endif
// if (status != 0 && status != 254 && status != 255) {
// Serial.print("GIF renderer return: ");
// Serial.println(status);
// }
- return FRAMETIME;
}
static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128";
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 94fdb3006..7a2c00574 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -199,7 +199,9 @@ void Segment::resetIfRequired() {
if (data && _dataLen > 0) memset(data, 0, _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false;
+ #ifndef WLED_DISABLE_GIF
endImagePlayback(this);
+ #endif
}
CRGBPalette16 IRAM_ATTR &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
From d4ba603cf745de2f88a39e7cf13a8967073ab64b Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Tue, 19 Mar 2024 22:39:33 +0100
Subject: [PATCH 05/12] Update pio version
---
requirements.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements.txt b/requirements.txt
index 666122aa2..ee70cd689 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.12
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile
From 7c03f716a8eb2b167ae337e3263ed088827c7661 Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Tue, 19 Mar 2024 22:53:43 +0100
Subject: [PATCH 06/12] Include gif library for all esp32 variants
---
platformio.ini | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index 2c7d1442a..00df6666c 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -238,6 +238,11 @@ lib_deps_compat =
https://github.com/blazoncek/QuickESPNow.git#optional-debug
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
+[esp32_all_variants]
+lib_deps =
+ https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ bitbank2/AnimatedGIF@^1.4.7
+ https://github.com/Aircoookie/GifDecoder#bc3af18
[esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
@@ -259,9 +264,7 @@ large_partitions = tools/WLED_ESP32_8MB.csv
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
lib_deps =
https://github.com/lorol/LITTLEFS.git
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
- bitbank2/AnimatedGIF@^1.4.7
- https://github.com/Aircoookie/GifDecoder#bc3af18
+ ${esp32_all_variants.lib_deps}
${env.lib_deps}
# additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE
@@ -284,7 +287,7 @@ build_flags = -g
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
lib_deps =
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ ${esp32_all_variants.lib_deps}
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
@@ -304,7 +307,7 @@ build_flags = -g
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ ${esp32_all_variants.lib_deps}
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
@@ -323,7 +326,7 @@ build_flags = -g
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ ${esp32_all_variants.lib_deps}
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
@@ -343,7 +346,7 @@ build_flags = -g
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
lib_deps =
- https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
+ ${esp32_all_variants.lib_deps}
${env.lib_deps}
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
From e852df3179641ef9fe411608d69090aa1c2a5f4a Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Sat, 13 Apr 2024 20:06:33 +0200
Subject: [PATCH 07/12] Proper debug statements
---
wled00/image_loader.cpp | 144 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)
create mode 100644 wled00/image_loader.cpp
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
new file mode 100644
index 000000000..31a84e660
--- /dev/null
+++ b/wled00/image_loader.cpp
@@ -0,0 +1,144 @@
+#include "wled.h"
+
+#ifndef WLED_DISABLE_GIF
+
+#include "GifDecoder.h"
+
+
+/*
+ * Functions to render images from filesystem to segments, used by the "Image" effect
+ */
+
+File file;
+char lastFilename[34] = "/";
+GifDecoder<320,320,12,true> decoder;
+bool gifDecodeFailed = false;
+unsigned long lastFrameDisplayTime = 0, currentFrameDelay = 0;
+
+bool fileSeekCallback(unsigned long position) {
+ return file.seek(position);
+}
+
+unsigned long filePositionCallback(void) {
+ return file.position();
+}
+
+int fileReadCallback(void) {
+ return file.read();
+}
+
+int fileReadBlockCallback(void * buffer, int numberOfBytes) {
+ return file.read((uint8_t*)buffer, numberOfBytes);
+}
+
+int fileSizeCallback(void) {
+ return file.size();
+}
+
+bool openGif(const char *filename) {
+ file = WLED_FS.open(filename, "r");
+
+ if (!file) return false;
+ return true;
+}
+
+Segment* activeSeg;
+uint16_t gifWidth, gifHeight;
+
+void screenClearCallback(void) {
+ activeSeg->fill(0);
+}
+
+void updateScreenCallback(void) {}
+
+void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t blue) {
+ // simple nearest-neighbor scaling
+ int16_t outY = y * activeSeg->height() / gifHeight;
+ int16_t outX = x * activeSeg->width() / gifWidth;
+ // set multiple pixels if upscaling
+ for (int16_t i = 0; i < (activeSeg->width()+(gifWidth-1)) / gifWidth; i++) {
+ for (int16_t j = 0; j < (activeSeg->height()+(gifHeight-1)) / gifHeight; j++) {
+ activeSeg->setPixelColorXY(outX + i, outY + j, gamma8(red), gamma8(green), gamma8(blue));
+ }
+ }
+}
+
+#define IMAGE_ERROR_NONE 0
+#define IMAGE_ERROR_NO_NAME 1
+#define IMAGE_ERROR_SEG_LIMIT 2
+#define IMAGE_ERROR_UNSUPPORTED_FORMAT 3
+#define IMAGE_ERROR_FILE_MISSING 4
+#define IMAGE_ERROR_DECODER_ALLOC 5
+#define IMAGE_ERROR_GIF_DECODE 6
+#define IMAGE_ERROR_FRAME_DECODE 7
+#define IMAGE_ERROR_WAITING 254
+#define IMAGE_ERROR_PREV 255
+
+// renders an image (.gif only; .bmp and .fseq to be added soon) from FS to a segment
+byte renderImageToSegment(Segment &seg) {
+ if (!seg.name) return IMAGE_ERROR_NO_NAME;
+ // disable during effect transition, causes flickering, multiple allocations and depending on image, part of old FX remaining
+ if (seg.mode != seg.currentMode()) return IMAGE_ERROR_WAITING;
+ if (activeSeg && activeSeg != &seg) return IMAGE_ERROR_SEG_LIMIT; // only one segment at a time
+ activeSeg = &seg;
+
+ if (strncmp(lastFilename +1, seg.name, 32) != 0) { // segment name changed, load new image
+ strncpy(lastFilename +1, seg.name, 32);
+ gifDecodeFailed = false;
+ if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) {
+ gifDecodeFailed = true;
+ return IMAGE_ERROR_UNSUPPORTED_FORMAT;
+ }
+ if (file) file.close();
+ openGif(lastFilename);
+ if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
+ decoder.setScreenClearCallback(screenClearCallback);
+ decoder.setUpdateScreenCallback(updateScreenCallback);
+ decoder.setDrawPixelCallback(drawPixelCallback);
+ decoder.setFileSeekCallback(fileSeekCallback);
+ decoder.setFilePositionCallback(filePositionCallback);
+ decoder.setFileReadCallback(fileReadCallback);
+ decoder.setFileReadBlockCallback(fileReadBlockCallback);
+ decoder.setFileSizeCallback(fileSizeCallback);
+ decoder.alloc();
+ DEBUG_PRINTLN(F("Starting decoding"));
+ if(decoder.startDecoding() < 0) { gifDecodeFailed = true; return IMAGE_ERROR_GIF_DECODE; }
+ DEBUG_PRINTLN(F("Decoding started"));
+ }
+
+ if (gifDecodeFailed) return IMAGE_ERROR_PREV;
+ if (!file) { gifDecodeFailed = true; return IMAGE_ERROR_FILE_MISSING; }
+ //if (!decoder) { gifDecodeFailed = true; return IMAGE_ERROR_DECODER_ALLOC; }
+
+ // speed 0 = half speed, 128 = normal, 255 = full FX FPS
+ // TODO: 0 = 4x slow, 64 = 2x slow, 128 = normal, 192 = 2x fast, 255 = 4x fast
+ uint32_t wait = currentFrameDelay * 2 - seg.speed * currentFrameDelay / 128;
+
+ // TODO consider handling this on FX level with a different frametime, but that would cause slow gifs to speed up during transitions
+ if (millis() - lastFrameDisplayTime < wait) return IMAGE_ERROR_WAITING;
+
+ decoder.getSize(&gifWidth, &gifHeight);
+
+ int result = decoder.decodeFrame(false);
+ if (result < 0) { gifDecodeFailed = true; return IMAGE_ERROR_FRAME_DECODE; }
+
+ currentFrameDelay = decoder.getFrameDelay_ms();
+ unsigned long tooSlowBy = (millis() - lastFrameDisplayTime) - wait; // if last frame was longer than intended, compensate
+ currentFrameDelay = tooSlowBy > currentFrameDelay ? 0 : currentFrameDelay - tooSlowBy;
+ lastFrameDisplayTime = millis();
+
+ return IMAGE_ERROR_NONE;
+}
+
+void endImagePlayback(Segment *seg) {
+ DEBUG_PRINTLN(F("Image playback end called"));
+ if (!activeSeg || activeSeg != seg) return;
+ if (file) file.close();
+ decoder.dealloc();
+ gifDecodeFailed = false;
+ activeSeg = nullptr;
+ lastFilename[1] = '\0';
+ DEBUG_PRINTLN(F("Image playback ended"));
+}
+
+#endif
\ No newline at end of file
From 56b8af86d70b1de414794e70c4875028fdc71967 Mon Sep 17 00:00:00 2001
From: Will Tatam
Date: Tue, 14 Jan 2025 18:40:41 +0000
Subject: [PATCH 08/12] Swap to WLED_ENABLE_GIF
---
wled00/FX.cpp | 4 +++-
wled00/FX_fcn.cpp | 2 +-
wled00/fcn_declare.h | 2 +-
wled00/image_loader.cpp | 2 +-
4 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 081009f4e..22c33356b 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -4484,7 +4484,7 @@ static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!
Draws a .gif image from filesystem on the matrix/strip
*/
uint16_t mode_image(void) {
- #ifdef WLED_DISABLE_GIF
+ #ifndef WLED_ENABLE_GIF
return mode_static();
#else
renderImageToSegment(SEGMENT);
@@ -7755,7 +7755,9 @@ void WS2812FX::setupEffectData() {
addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
addEffect(FX_MODE_FAIRYTWINKLE, &mode_fairytwinkle, _data_FX_MODE_FAIRYTWINKLE);
addEffect(FX_MODE_RUNNING_DUAL, &mode_running_dual, _data_FX_MODE_RUNNING_DUAL);
+ #ifdef WLED_ENABLE_GIF
addEffect(FX_MODE_IMAGE, &mode_image, _data_FX_MODE_IMAGE);
+ #endif
addEffect(FX_MODE_TRICOLOR_CHASE, &mode_tricolor_chase, _data_FX_MODE_TRICOLOR_CHASE);
addEffect(FX_MODE_TRICOLOR_WIPE, &mode_tricolor_wipe, _data_FX_MODE_TRICOLOR_WIPE);
addEffect(FX_MODE_TRICOLOR_FADE, &mode_tricolor_fade, _data_FX_MODE_TRICOLOR_FADE);
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index af277718d..97bb99c9a 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -195,7 +195,7 @@ void Segment::resetIfRequired() {
if (data && _dataLen > 0) memset(data, 0, _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
reset = false;
- #ifndef WLED_DISABLE_GIF
+ #ifdef WLED_ENABLE_GIF
endImagePlayback(this);
#endif
}
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index bd45a2f30..cab5941e8 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -217,7 +217,7 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
#include "FX.h" // must be below colors.cpp declarations (potentially due to duplicate declarations of e.g. color_blend)
//image_loader.cpp
-#ifndef WLED_DISABLE_GIF
+#ifdef WLED_ENABLE_GIF
bool fileSeekCallback(unsigned long position);
unsigned long filePositionCallback(void);
int fileReadCallback(void);
diff --git a/wled00/image_loader.cpp b/wled00/image_loader.cpp
index 31a84e660..966505794 100644
--- a/wled00/image_loader.cpp
+++ b/wled00/image_loader.cpp
@@ -1,6 +1,6 @@
#include "wled.h"
-#ifndef WLED_DISABLE_GIF
+#ifdef WLED_ENABLE_GIF
#include "GifDecoder.h"
From 53c1856038a273906fe5d3b253600f3cf2596250 Mon Sep 17 00:00:00 2001
From: Will Tatam
Date: Tue, 14 Jan 2025 18:47:06 +0000
Subject: [PATCH 09/12] Enable GIF support for all esp32 builds
---
platformio.ini | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/platformio.ini b/platformio.ini
index 00df6666c..b11343b16 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -243,6 +243,8 @@ lib_deps =
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
bitbank2/AnimatedGIF@^1.4.7
https://github.com/Aircoookie/GifDecoder#bc3af18
+build_flags =
+ -D WLED_ENABLE_GIF
[esp32]
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
@@ -256,6 +258,8 @@ build_flags = -g
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
+ ${esp32_all_variants.build_flags}
+
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
@@ -286,6 +290,7 @@ build_flags = -g
-DARDUINO_ARCH_ESP32 -DESP32
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
+ ${esp32_all_variants.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
@@ -306,6 +311,7 @@ build_flags = -g
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
+ ${esp32_all_variants.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
@@ -325,6 +331,7 @@ build_flags = -g
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
+ ${esp32_all_variants.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
@@ -345,6 +352,7 @@ build_flags = -g
-DCO
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
+ ${esp32_all_variants.build_flags}
lib_deps =
${esp32_all_variants.lib_deps}
${env.lib_deps}
From c92dbb10ac79058c96d8bfd7863b83bc63249f78 Mon Sep 17 00:00:00 2001
From: Malachi Soord
Date: Wed, 25 Dec 2024 21:31:47 +0100
Subject: [PATCH 10/12] Use proper devcontainers schema for vscode
customisations
---
.devcontainer/devcontainer.json | 43 +++++++++++++++++----------------
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 241acd79d..588641824 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -24,29 +24,30 @@
// risk to running the build directly on the host.
// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"],
- // Set *default* container specific settings.json values on container create.
- "settings": {
- "terminal.integrated.shell.linux": "/bin/bash",
- "python.pythonPath": "/usr/local/bin/python",
- "python.linting.enabled": true,
- "python.linting.pylintEnabled": true,
- "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
- "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
- "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
- "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
- "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
- "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
- "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
- "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
- "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.shell.linux": "/bin/bash",
+ "python.pythonPath": "/usr/local/bin/python",
+ "python.linting.enabled": true,
+ "python.linting.pylintEnabled": true,
+ "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
+ "python.formatting.blackPath": "/usr/local/py-utils/bin/black",
+ "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
+ "python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
+ "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
+ "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
+ "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
+ "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
+ "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
+ },
+ "extensions": [
+ "ms-python.python",
+ "platformio.platformio-ide"
+ ]
+ }
},
- // Add the IDs of extensions you want installed when the container is created.
- "extensions": [
- "ms-python.python",
- "platformio.platformio-ide"
- ],
-
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
From e7c0ce794b76ec1286a920534be2ff222f4d78c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Kristan?=
Date: Fri, 24 Jan 2025 10:10:14 +0100
Subject: [PATCH 11/12] Merge conflict fix - updated blending style constants
---
wled00/data/index.htm | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/wled00/data/index.htm b/wled00/data/index.htm
index 6027eeb51..aa06b5122 100644
--- a/wled00/data/index.htm
+++ b/wled00/data/index.htm
@@ -128,7 +128,7 @@
-
+
Color palette
@@ -273,20 +273,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From a778ff01f613a92974ba04447adaa0e2490d7e6b Mon Sep 17 00:00:00 2001
From: "Christian W. Zuckschwerdt"
Date: Fri, 24 Jan 2025 23:17:01 +0100
Subject: [PATCH 12/12] Nightly release - Add bin.gz artifacts
---
.github/workflows/nightly.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 138730058..a5c80f22d 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -37,4 +37,5 @@ jobs:
prerelease: true
body: ${{ steps.changelog.outputs.changelog }}
files: |
- ./*.bin
\ No newline at end of file
+ *.bin
+ *.bin.gz