mirror of
https://github.com/wled/WLED.git
synced 2025-07-19 16:56:35 +00:00
Merge branch 'main' into aws-queue-0_16
This commit is contained in:
commit
7c05914e5a
@ -24,29 +24,30 @@
|
|||||||
// risk to running the build directly on the host.
|
// risk to running the build directly on the host.
|
||||||
// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"],
|
// "runArgs": ["--privileged", "-v", "/dev/bus/usb:/dev/bus/usb", "--group-add", "dialout"],
|
||||||
|
|
||||||
// Set *default* container specific settings.json values on container create.
|
"customizations": {
|
||||||
"settings": {
|
"vscode": {
|
||||||
"terminal.integrated.shell.linux": "/bin/bash",
|
"settings": {
|
||||||
"python.pythonPath": "/usr/local/bin/python",
|
"terminal.integrated.shell.linux": "/bin/bash",
|
||||||
"python.linting.enabled": true,
|
"python.pythonPath": "/usr/local/bin/python",
|
||||||
"python.linting.pylintEnabled": true,
|
"python.linting.enabled": true,
|
||||||
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
"python.linting.pylintEnabled": true,
|
||||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
||||||
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||||
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
|
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
||||||
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
|
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
|
||||||
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
|
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
|
||||||
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
|
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
|
||||||
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
|
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
|
||||||
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
|
"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.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
// "forwardPorts": [],
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
3
.github/workflows/nightly.yml
vendored
3
.github/workflows/nightly.yml
vendored
@ -37,4 +37,5 @@ jobs:
|
|||||||
prerelease: true
|
prerelease: true
|
||||||
body: ${{ steps.changelog.outputs.changelog }}
|
body: ${{ steps.changelog.outputs.changelog }}
|
||||||
files: |
|
files: |
|
||||||
./*.bin
|
*.bin
|
||||||
|
*.bin.gz
|
||||||
|
@ -238,6 +238,13 @@ lib_deps_compat =
|
|||||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||||
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
|
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.0
|
||||||
|
|
||||||
|
[esp32_all_variants]
|
||||||
|
lib_deps =
|
||||||
|
willmmiles/AsyncTCP @ 1.3.1
|
||||||
|
bitbank2/AnimatedGIF@^1.4.7
|
||||||
|
https://github.com/Aircoookie/GifDecoder#bc3af18
|
||||||
|
build_flags =
|
||||||
|
-D WLED_ENABLE_GIF
|
||||||
|
|
||||||
[esp32]
|
[esp32]
|
||||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
|
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
|
||||||
@ -251,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
|
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
|
||||||
-D LOROL_LITTLEFS
|
-D LOROL_LITTLEFS
|
||||||
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
; -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
|
tiny_partitions = tools/WLED_ESP32_2MB_noOTA.csv
|
||||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||||
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
|
extended_partitions = tools/WLED_ESP32_4MB_700k_FS.csv
|
||||||
@ -259,7 +268,7 @@ large_partitions = tools/WLED_ESP32_8MB.csv
|
|||||||
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
|
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/lorol/LITTLEFS.git
|
https://github.com/lorol/LITTLEFS.git
|
||||||
willmmiles/AsyncTCP @ 1.3.1
|
${esp32_all_variants.lib_deps}
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
# additional build flags for audioreactive
|
# additional build flags for audioreactive
|
||||||
AR_build_flags = -D USERMOD_AUDIOREACTIVE
|
AR_build_flags = -D USERMOD_AUDIOREACTIVE
|
||||||
@ -282,9 +291,10 @@ build_flags = -g
|
|||||||
-DARDUINO_ARCH_ESP32 -DESP32
|
-DARDUINO_ARCH_ESP32 -DESP32
|
||||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
-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
|
-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}
|
||||||
-D WLED_ENABLE_DMX_INPUT
|
-D WLED_ENABLE_DMX_INPUT
|
||||||
lib_deps =
|
lib_deps =
|
||||||
willmmiles/AsyncTCP @ 1.3.1
|
${esp32_all_variants.lib_deps}
|
||||||
https://github.com/someweisguy/esp_dmx.git#47db25d
|
https://github.com/someweisguy/esp_dmx.git#47db25d
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||||
@ -303,8 +313,9 @@ build_flags = -g
|
|||||||
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
|
-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:
|
;; 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
|
;; ARDUINO_USB_CDC_ON_BOOT
|
||||||
|
${esp32_all_variants.build_flags}
|
||||||
lib_deps =
|
lib_deps =
|
||||||
willmmiles/AsyncTCP @ 1.3.1
|
${esp32_all_variants.lib_deps}
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||||
|
|
||||||
@ -321,8 +332,9 @@ build_flags = -g
|
|||||||
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
|
-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:
|
;; 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
|
;; ARDUINO_USB_CDC_ON_BOOT
|
||||||
|
${esp32_all_variants.build_flags}
|
||||||
lib_deps =
|
lib_deps =
|
||||||
willmmiles/AsyncTCP @ 1.3.1
|
${esp32_all_variants.lib_deps}
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||||
board_build.flash_mode = qio
|
board_build.flash_mode = qio
|
||||||
@ -341,8 +353,9 @@ build_flags = -g
|
|||||||
-DCO
|
-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:
|
;; 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
|
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
|
||||||
|
${esp32_all_variants.build_flags}
|
||||||
lib_deps =
|
lib_deps =
|
||||||
willmmiles/AsyncTCP @ 1.3.1
|
${esp32_all_variants.lib_deps}
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
|
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
|
||||||
|
|
||||||
|
@ -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:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile
|
# pip-compile
|
||||||
|
@ -4450,6 +4450,24 @@ uint16_t mode_washing_machine(void) {
|
|||||||
static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!;;!";
|
static const char _data_FX_MODE_WASHING_MACHINE[] PROGMEM = "Washing Machine@!,!;;!";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Image effect
|
||||||
|
Draws a .gif image from filesystem on the matrix/strip
|
||||||
|
*/
|
||||||
|
uint16_t mode_image(void) {
|
||||||
|
#ifndef WLED_ENABLE_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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
static const char _data_FX_MODE_IMAGE[] PROGMEM = "Image@!,;;;12;sx=128";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Blends random colors across palette
|
Blends random colors across palette
|
||||||
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
|
Modified, originally by Mark Kriegsman https://gist.github.com/kriegsman/1f7ccbbfa492a73c015e
|
||||||
@ -7734,7 +7752,9 @@ void WS2812FX::setupEffectData() {
|
|||||||
addEffect(FX_MODE_TWO_DOTS, &mode_two_dots, _data_FX_MODE_TWO_DOTS);
|
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_FAIRYTWINKLE, &mode_fairytwinkle, _data_FX_MODE_FAIRYTWINKLE);
|
||||||
addEffect(FX_MODE_RUNNING_DUAL, &mode_running_dual, _data_FX_MODE_RUNNING_DUAL);
|
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_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_WIPE, &mode_tricolor_wipe, _data_FX_MODE_TRICOLOR_WIPE);
|
||||||
addEffect(FX_MODE_TRICOLOR_FADE, &mode_tricolor_fade, _data_FX_MODE_TRICOLOR_FADE);
|
addEffect(FX_MODE_TRICOLOR_FADE, &mode_tricolor_fade, _data_FX_MODE_TRICOLOR_FADE);
|
||||||
|
@ -184,7 +184,7 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
|
|||||||
#define FX_MODE_TWO_DOTS 50
|
#define FX_MODE_TWO_DOTS 50
|
||||||
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
|
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
|
||||||
#define FX_MODE_RUNNING_DUAL 52
|
#define FX_MODE_RUNNING_DUAL 52
|
||||||
// #define FX_MODE_HALLOWEEN 53 // removed in 0.14!
|
#define FX_MODE_IMAGE 53
|
||||||
#define FX_MODE_TRICOLOR_CHASE 54
|
#define FX_MODE_TRICOLOR_CHASE 54
|
||||||
#define FX_MODE_TRICOLOR_WIPE 55
|
#define FX_MODE_TRICOLOR_WIPE 55
|
||||||
#define FX_MODE_TRICOLOR_FADE 56
|
#define FX_MODE_TRICOLOR_FADE 56
|
||||||
|
@ -199,6 +199,9 @@ void Segment::resetIfRequired() {
|
|||||||
if (data && _dataLen > 0) memset(data, 0, _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
|
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;
|
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
|
||||||
reset = false;
|
reset = false;
|
||||||
|
#ifdef WLED_ENABLE_GIF
|
||||||
|
endImagePlayback(this);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||||
|
@ -128,7 +128,7 @@
|
|||||||
<div style="padding: 8px 0;" id="btns">
|
<div style="padding: 8px 0;" id="btns">
|
||||||
<button class="btn btn-xs" title="File editor" type="button" id="edit" onclick="window.location.href=getURL('/edit')"><i class="icons btn-icon"></i></button>
|
<button class="btn btn-xs" title="File editor" type="button" id="edit" onclick="window.location.href=getURL('/edit')"><i class="icons btn-icon"></i></button>
|
||||||
<button class="btn btn-xs" title="Pixel Magic Tool" type="button" id="pxmb" onclick="window.location.href=getURL('/pxmagic.htm')"><i class="icons btn-icon"></i></button>
|
<button class="btn btn-xs" title="Pixel Magic Tool" type="button" id="pxmb" onclick="window.location.href=getURL('/pxmagic.htm')"><i class="icons btn-icon"></i></button>
|
||||||
<button class="btn btn-xs" title="Add custom palette" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon"></i></button>
|
<button class="btn btn-xs" title="Add custom palette" type="button" id="adPal" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon"></i></button>
|
||||||
<button class="btn btn-xs" title="Remove last custom palette" type="button" id="rmPal" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon"></i></button>
|
<button class="btn btn-xs" title="Remove last custom palette" type="button" id="rmPal" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<p class="labels hd" id="pall"><i class="icons sel-icon" onclick="tglHex()"></i> Color palette</p>
|
<p class="labels hd" id="pall"><i class="icons sel-icon" onclick="tglHex()"></i> Color palette</p>
|
||||||
@ -273,20 +273,20 @@
|
|||||||
<option value="1">Fairy Dust</option>
|
<option value="1">Fairy Dust</option>
|
||||||
<option value="2">Swipe right</option>
|
<option value="2">Swipe right</option>
|
||||||
<option value="3">Swipe left</option>
|
<option value="3">Swipe left</option>
|
||||||
<option value="4">Push right</option>
|
<option value="16">Push right</option>
|
||||||
<option value="5">Push left</option>
|
<option value="17">Push left</option>
|
||||||
<option value="6">Pinch-out</option>
|
<option value="4">Pinch-out</option>
|
||||||
<option value="7">Inside-out</option>
|
<option value="5">Inside-out</option>
|
||||||
<option value="8" data-type="2D">Swipe up</option>
|
<option value="6" data-type="2D">Swipe up</option>
|
||||||
<option value="9" data-type="2D">Swipe down</option>
|
<option value="7" data-type="2D">Swipe down</option>
|
||||||
<option value="10" data-type="2D">Open V</option>
|
<option value="8" data-type="2D">Open H</option>
|
||||||
<option value="11" data-type="2D">Open H</option>
|
<option value="9" data-type="2D">Open V</option>
|
||||||
<option value="12" data-type="2D">Push up</option>
|
<option value="18" data-type="2D">Push up</option>
|
||||||
<option value="13" data-type="2D">Push down</option>
|
<option value="19" data-type="2D">Push down</option>
|
||||||
<option value="14" data-type="2D">Push TL</option>
|
<option value="20" data-type="2D">Push TL</option>
|
||||||
<option value="15" data-type="2D">Push TR</option>
|
<option value="21" data-type="2D">Push TR</option>
|
||||||
<option value="16" data-type="2D">Push BR</option>
|
<option value="22" data-type="2D">Push BR</option>
|
||||||
<option value="17" data-type="2D">Push BL</option>
|
<option value="23" data-type="2D">Push BL</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p id="ledmap" class="hide"></p>
|
<p id="ledmap" class="hide"></p>
|
||||||
|
@ -219,6 +219,19 @@ void onHueConnect(void* arg, AsyncClient* client);
|
|||||||
void sendHuePoll();
|
void sendHuePoll();
|
||||||
void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
|
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
|
||||||
|
#ifdef WLED_ENABLE_GIF
|
||||||
|
bool fileSeekCallback(unsigned long position);
|
||||||
|
unsigned long filePositionCallback(void);
|
||||||
|
int fileReadCallback(void);
|
||||||
|
int fileReadBlockCallback(void * buffer, int numberOfBytes);
|
||||||
|
int fileSizeCallback(void);
|
||||||
|
byte renderImageToSegment(Segment &seg);
|
||||||
|
void endImagePlayback(Segment* seg);
|
||||||
|
#endif
|
||||||
|
|
||||||
//improv.cpp
|
//improv.cpp
|
||||||
enum ImprovRPCType {
|
enum ImprovRPCType {
|
||||||
Command_Wifi = 0x01,
|
Command_Wifi = 0x01,
|
||||||
|
144
wled00/image_loader.cpp
Normal file
144
wled00/image_loader.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#include "wled.h"
|
||||||
|
|
||||||
|
#ifdef WLED_ENABLE_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
|
Loading…
x
Reference in New Issue
Block a user