From 0c8d9d5614a2e4f0013604cfcac8552cf1646279 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Wed, 3 Apr 2024 18:38:06 +0200
Subject: [PATCH 01/14] Mode blending styles - alternative to #3669
---
wled00/FX.h | 28 ++++++++++
wled00/FX_2Dfcn.cpp | 33 +++++++++++-
wled00/FX_fcn.cpp | 123 +++++++++++++++++++++++++++++++++++++-----
wled00/data/index.htm | 20 ++++++-
wled00/data/index.js | 4 ++
wled00/fcn_declare.h | 1 +
wled00/json.cpp | 8 +++
wled00/util.cpp | 7 +++
wled00/wled.h | 3 +-
9 files changed, 212 insertions(+), 15 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index 1089a0b8b..44c5e1549 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -316,6 +316,25 @@
#define MODE_COUNT 187
+
+#define BLEND_STYLE_FADE 0
+#define BLEND_STYLE_FAIRY_DUST 1
+#define BLEND_STYLE_SWIPE_RIGHT 2
+#define BLEND_STYLE_SWIPE_LEFT 3
+#define BLEND_STYLE_PINCH_OUT 4
+#define BLEND_STYLE_INSIDE_OUT 5
+#define BLEND_STYLE_SWIPE_UP 6
+#define BLEND_STYLE_SWIPE_DOWN 7
+#define BLEND_STYLE_OPEN_H 8
+#define BLEND_STYLE_OPEN_V 9
+#define BLEND_STYLE_PUSH_TL 10
+#define BLEND_STYLE_PUSH_TR 11
+#define BLEND_STYLE_PUSH_BR 12
+#define BLEND_STYLE_PUSH_BL 13
+
+#define BLEND_STYLE_COUNT 14
+
+
typedef enum mapping1D2D {
M12_Pixels = 0,
M12_pBar = 1,
@@ -419,6 +438,9 @@ typedef struct Segment {
static uint16_t _lastPaletteBlend; // blend palette according to set Transition Delay in millis()%0xFFFF
#ifndef WLED_DISABLE_MODE_BLEND
static bool _modeBlend; // mode/effect blending semaphore
+ // clipping
+ static uint16_t _clipStart, _clipStop;
+ static uint8_t _clipStartY, _clipStopY;
#endif
// transition data, valid only if transitional==true, holds values during transition (72 bytes)
@@ -578,6 +600,10 @@ typedef struct Segment {
void setPixelColor(float i, uint32_t c, bool aa = true);
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
+ #ifndef WLED_DISABLE_MODE_BLEND
+ inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; };
+ #endif
+ bool isPixelClipped(int i);
uint32_t getPixelColor(int i);
// 1D support functions (some implement 2D as well)
void blur(uint8_t);
@@ -606,6 +632,7 @@ typedef struct Segment {
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
+ bool isPixelXYClipped(int x, int y);
uint32_t getPixelColorXY(uint16_t x, uint16_t y);
// 2D support functions
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); }
@@ -640,6 +667,7 @@ typedef struct Segment {
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); }
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); }
+ inline bool isPixelXYClipped(int x, int y) { return isPixelClipped(x); }
inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); }
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 7aecd2271..7e1419293 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -167,10 +167,41 @@ uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y)
return isActive() ? (x%width) + (y%height) * width : 0;
}
+// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false)
+// if clipping start > stop the clipping range is inverted
+// _modeBlend==true -> old effect during transition
+// _modeBlend==false -> new effect during transition
+bool IRAM_ATTR Segment::isPixelXYClipped(int x, int y) {
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) {
+ const bool invertX = _clipStart > _clipStop;
+ const bool invertY = _clipStartY > _clipStopY;
+ const unsigned startX = invertX ? _clipStop : _clipStart;
+ const unsigned stopX = invertX ? _clipStart : _clipStop;
+ const unsigned startY = invertY ? _clipStopY : _clipStartY;
+ const unsigned stopY = invertY ? _clipStartY : _clipStopY;
+ if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
+ const unsigned width = stopX - startX; // assumes full segment width (faster than virtualWidth())
+ const unsigned len = width * (stopY - startY); // assumes full segment height (faster than virtualHeight())
+ if (len < 2) return false;
+ const unsigned shuffled = hashInt(x + y * width) % len;
+ const unsigned pos = (shuffled * 0xFFFFU) / len;
+ return progress() <= pos;
+ }
+ bool xInside = (x >= startX && x < stopX); if (invertX) xInside = !xInside;
+ bool yInside = (y >= startY && y < stopY); if (invertY) yInside = !yInside;
+ const bool clip = (invertX && invertY) ? !_modeBlend : _modeBlend;
+ if (xInside && yInside) return clip; // covers window & corners (inverted)
+ return !clip;
+ }
+#endif
+ return false;
+}
+
void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
{
if (!isActive()) return; // not active
- if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
+ if (x >= virtualWidth() || y >= virtualHeight() || x < 0 || y < 0 || isPixelXYClipped(x,y)) return; // if pixel would fall out of virtual segment just exit
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index f97268f9b..989809d02 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -84,6 +84,10 @@ uint16_t Segment::_lastPaletteBlend = 0; //in millis (lowest 16 bits only)
#ifndef WLED_DISABLE_MODE_BLEND
bool Segment::_modeBlend = false;
+uint16_t Segment::_clipStart = 0;
+uint16_t Segment::_clipStop = 0;
+uint8_t Segment::_clipStartY = 0;
+uint8_t Segment::_clipStopY = 1;
#endif
// copy constructor
@@ -413,12 +417,17 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
uint32_t prog = progress();
+ uint32_t curBri = useCct ? cct : (on ? opacity : 0);
if (prog < 0xFFFFU) {
- uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
- curBri += (useCct ? _t->_cctT : _t->_briT) * (0xFFFFU - prog);
+ uint8_t tmpBri = useCct ? _t->_cctT : (_t->_segT._optionsT & 0x0004 ? _t->_briT : 0);
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? tmpBri : curBri; // not fade/blend transition, each effect uses its brightness
+#endif
+ curBri *= prog;
+ curBri += tmpBri * (0xFFFFU - prog);
return curBri / 0xFFFFU;
}
- return (useCct ? cct : (on ? opacity : 0));
+ return curBri;
}
uint8_t IRAM_ATTR Segment::currentMode() {
@@ -431,16 +440,23 @@ uint8_t IRAM_ATTR Segment::currentMode() {
uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
if (slot >= NUM_COLORS) slot = 0;
+ uint32_t prog = progress();
+ if (prog == 0xFFFFU) return colors[slot];
#ifndef WLED_DISABLE_MODE_BLEND
- return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
+ if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color
+ return color_blend(_t->_segT._colorT[slot], colors[slot], prog, true);
#else
- return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
+ return color_blend(_t->_colorT[slot], colors[slot], prog, true);
#endif
}
CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal);
uint16_t prog = progress();
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (prog < 0xFFFFU && blendingStyle > BLEND_STYLE_FADE && _modeBlend) targetPalette = _t->_palT; // not fade/blend transition, each effect uses its palette
+ else
+#endif
if (strip.paletteFade && prog < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
@@ -456,9 +472,9 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u
void Segment::handleRandomPalette() {
// is it time to generate a new palette?
if ((millis()/1000U) - _lastPaletteChange > randomPaletteChangeTime) {
- _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
- _lastPaletteChange = millis()/1000U;
- _lastPaletteBlend = (uint16_t)(millis() & 0xFFFF)-512; // starts blending immediately
+ _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
+ _lastPaletteChange = millis()/1000U;
+ _lastPaletteBlend = (uint16_t)(millis() & 0xFFFF)-512; // starts blending immediately
}
// if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls)
@@ -662,6 +678,32 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
return vLength;
}
+// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false)
+// if clipping start > stop the clipping range is inverted
+// _modeBlend==true -> old effect during transition
+// _modeBlend==false -> new effect during transition
+bool IRAM_ATTR Segment::isPixelClipped(int i) {
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) {
+ bool invert = _clipStart > _clipStop;
+ unsigned start = invert ? _clipStop : _clipStart;
+ unsigned stop = invert ? _clipStart : _clipStop;
+ if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
+ unsigned len = stop - start;
+ if (len < 2) return false;
+ unsigned shuffled = hashInt(i) % len;
+ unsigned pos = (shuffled * 0xFFFFU) / len;
+ return progress() <= pos;
+ }
+ const bool iInside = (i >= start && i < stop);
+ if (!invert && iInside) return _modeBlend;
+ if ( invert && !iInside) return _modeBlend;
+ return !_modeBlend;
+ }
+#endif
+ return false;
+}
+
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{
if (!isActive()) return; // not active
@@ -732,6 +774,8 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
#endif
+ if (isPixelClipped(i)) return; // handle clipping on 1D
+
uint16_t len = length();
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
@@ -763,14 +807,16 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
- if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
+ // _modeBlend==true -> old effect
+ if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexMir, tmpCol);
}
indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
- if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
+ // _modeBlend==true -> old effect
+ if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexSet, tmpCol);
}
@@ -1062,7 +1108,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined)
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGBPalette16 curPal;
- curPal = currentPalette(curPal, palette);
+ currentPalette(curPal, palette);
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color));
@@ -1185,8 +1231,59 @@ void WS2812FX::service() {
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
- delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
+ seg.setClippingRect(0, 0); // disable clipping
+ if (modeBlending && seg.mode != tmpMode) {
+ // set clipping rectangle
+ // new mode is run inside clipping area and old mode outside clipping area
+ unsigned p = seg.progress();
+ unsigned w = seg.is2D() ? seg.virtualWidth() : _virtualSegmentLength;
+ unsigned h = seg.virtualHeight();
+ unsigned dw = p * w / 0xFFFFU + 1;
+ unsigned dh = p * h / 0xFFFFU + 1;
+ switch (blendingStyle) {
+ case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
+ seg.setClippingRect(0, w, 0, h);
+ break;
+ case BLEND_STYLE_SWIPE_RIGHT: // left-to-right
+ seg.setClippingRect(0, dw, 0, h);
+ break;
+ case BLEND_STYLE_SWIPE_LEFT: // right-to-left
+ seg.setClippingRect(w - dw, w, 0, h);
+ break;
+ case BLEND_STYLE_PINCH_OUT: // corners
+ seg.setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!!
+ break;
+ case BLEND_STYLE_INSIDE_OUT: // outward
+ seg.setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2);
+ break;
+ case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D)
+ seg.setClippingRect(0, w, 0, dh);
+ break;
+ case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D)
+ seg.setClippingRect(0, w, h - dh, h);
+ break;
+ case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D
+ seg.setClippingRect((w - dw)/2, (w + dw)/2, 0, h);
+ break;
+ case BLEND_STYLE_OPEN_V: // vertical-outward (2D)
+ seg.setClippingRect(0, w, (h - dh)/2, (h + dh)/2);
+ break;
+ case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D)
+ seg.setClippingRect(0, dw, 0, dh);
+ break;
+ case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D)
+ seg.setClippingRect(w - dw, w, 0, dh);
+ break;
+ case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D)
+ seg.setClippingRect(w - dw, w, h - dh, h);
+ break;
+ case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D)
+ seg.setClippingRect(0, dw, h - dh, h);
+ break;
+ }
+ }
+ delay = (*_mode[seg.mode])(); // run new/current mode
if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
@@ -1197,6 +1294,8 @@ void WS2812FX::service() {
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
}
+#else
+ delay = (*_mode[seg.mode])(); // run effect mode
#endif
seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
diff --git a/wled00/data/index.htm b/wled00/data/index.htm
index 4a532abb7..dc1431767 100644
--- a/wled00/data/index.htm
+++ b/wled00/data/index.htm
@@ -265,7 +265,25 @@
- Transition: s
+ Transition: s
+ Blend:
+
+
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 4ad2044ad..1a50b6a3b 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -1426,6 +1426,9 @@ function readState(s,command=false)
tr = s.transition;
gId('tt').value = tr/10;
+ gId('bs').value = s.bs || 0;
+ if (tr===0) gId('bsp').classList.add('hide')
+ else gId('bsp').classList.remove('hide')
populateSegments(s);
var selc=0;
@@ -1682,6 +1685,7 @@ function requestJson(command=null)
var tn = parseInt(t.value*10);
if (tn != tr) command.transition = tn;
}
+ //command.bs = parseInt(gId('bs').value);
req = JSON.stringify(command);
if (req.length > 1340) useWs = false; // do not send very long requests over websocket
if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false; // esp8266 can only handle 500 bytes
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index f1b013e99..9a843dbf4 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -383,6 +383,7 @@ uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
+uint32_t hashInt(uint32_t s);
// RAII guard class for the JSON Buffer lock
// Modeled after std::lock_guard
diff --git a/wled00/json.cpp b/wled00/json.cpp
index fd1527a21..bde6c36c7 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -355,6 +355,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
}
}
+#ifndef WLED_DISABLE_MODE_BLEND
+ blendingStyle = root[F("bs")] | blendingStyle;
+ blendingStyle = constrain(blendingStyle, 0, BLEND_STYLE_COUNT-1);
+#endif
+
// temporary transition (applies only once)
tr = root[F("tt")] | -1;
if (tr >= 0) {
@@ -581,6 +586,9 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root["on"] = (bri > 0);
root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms
+#ifndef WLED_DISABLE_MODE_BLEND
+ root[F("bs")] = blendingStyle;
+#endif
}
if (!forPreset) {
diff --git a/wled00/util.cpp b/wled00/util.cpp
index ad7e4b670..eabd3e383 100644
--- a/wled00/util.cpp
+++ b/wled00/util.cpp
@@ -599,3 +599,10 @@ uint8_t get_random_wheel_index(uint8_t pos) {
}
return r;
}
+
+uint32_t hashInt(uint32_t s) {
+ // borrowed from https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
+ s = ((s >> 16) ^ s) * 0x45d9f3b;
+ s = ((s >> 16) ^ s) * 0x45d9f3b;
+ return (s >> 16) ^ s;
+}
diff --git a/wled00/wled.h b/wled00/wled.h
index 35b99260a..a58e1ceab 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
-#define VERSION 2403280
+#define VERSION 2404030
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -547,6 +547,7 @@ WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random co
// transitions
WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading brightness/color
WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending
+WLED_GLOBAL uint8_t blendingStyle _INIT(0); // effect blending/transitionig style
WLED_GLOBAL bool transitionActive _INIT(false);
WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration
WLED_GLOBAL uint16_t transitionDelayDefault _INIT(750); // default transition time (stored in cfg.json)
From f5199d2b73bb3b516b7d98b4fee8b2af2b688c07 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Wed, 3 Apr 2024 20:55:59 +0200
Subject: [PATCH 02/14] Fix compile.
---
wled00/FX_fcn.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 989809d02..bd5758e31 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -419,9 +419,11 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
uint32_t prog = progress();
uint32_t curBri = useCct ? cct : (on ? opacity : 0);
if (prog < 0xFFFFU) {
- uint8_t tmpBri = useCct ? _t->_cctT : (_t->_segT._optionsT & 0x0004 ? _t->_briT : 0);
#ifndef WLED_DISABLE_MODE_BLEND
+ uint8_t tmpBri = useCct ? _t->_cctT : (_t->_segT._optionsT & 0x0004 ? _t->_briT : 0);
if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? tmpBri : curBri; // not fade/blend transition, each effect uses its brightness
+#else
+ uint8_t tmpBri = useCct ? _t->_cctT : _t->_briT;
#endif
curBri *= prog;
curBri += tmpBri * (0xFFFFU - prog);
From a3a8fa1cef1f54c2d1028a0c3e5a5fcc6ef8235e Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Mon, 8 Apr 2024 16:24:27 +0200
Subject: [PATCH 03/14] Remove conditional fade/blend - transitions always
enabled (use delay 0 to disable) - optimisation in on/off fade - fix for
palette/color blend when blending style is not fade - various tweaks and
optimisations
---
CHANGELOG.md | 3 +
.../stairway-wipe-usermod-v2.h | 2 +-
.../stairway_wipe_basic/wled06_usermod.ino | 111 ------------------
wled00/FX.cpp | 61 +++++-----
wled00/FX.h | 7 +-
wled00/FX_2Dfcn.cpp | 10 +-
wled00/FX_fcn.cpp | 104 ++++++++--------
wled00/bus_manager.cpp | 6 +-
wled00/bus_manager.h | 3 +-
wled00/cfg.cpp | 14 +--
wled00/data/settings_leds.htm | 7 +-
wled00/fcn_declare.h | 1 -
wled00/json.cpp | 6 +-
wled00/led.cpp | 54 +++------
wled00/playlist.cpp | 2 +-
wled00/set.cpp | 9 +-
wled00/udp.cpp | 6 +-
wled00/wled.cpp | 6 +-
wled00/wled.h | 2 -
wled00/wled_eeprom.cpp | 2 +-
wled00/xml.cpp | 5 +-
21 files changed, 135 insertions(+), 286 deletions(-)
delete mode 100644 usermods/stairway_wipe_basic/wled06_usermod.ino
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46f6df2de..0d86c8b2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
## WLED changelog
+#### Build 2404050
+- Blending styles (with help from @tkadauke)
+
#### Build 2403280
- Individual color channel control for JSON API (fixes #3860)
- "col":[int|string|object|array, int|string|object|array, int|string|object|array]
diff --git a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
index f712316b8..707479df1 100644
--- a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
+++ b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
@@ -96,7 +96,7 @@ void setup() {
jsonTransitionOnce = true;
strip.setTransition(0); //no transition
effectCurrent = FX_MODE_COLOR_WIPE;
- resetTimebase(); //make sure wipe starts from beginning
+ strip.resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
Segment& seg = strip.getSegment(0);
diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino
deleted file mode 100644
index c1264ebfb..000000000
--- a/usermods/stairway_wipe_basic/wled06_usermod.ino
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * This file allows you to add own functionality to WLED more easily
- * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
- * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in wled_eeprom.h)
- * bytes 2400+ are currently ununsed, but might be used for future wled features
- */
-
-//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
-
-byte wipeState = 0; //0: inactive 1: wiping 2: solid
-unsigned long timeStaticStart = 0;
-uint16_t previousUserVar0 = 0;
-
-//comment this out if you want the turn off effect to be just fading out instead of reverse wipe
-#define STAIRCASE_WIPE_OFF
-
-//gets called once at boot. Do all initialization that doesn't depend on network here
-void userSetup()
-{
- //setup PIR sensor here, if needed
-}
-
-//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
-void userConnected()
-{
-
-}
-
-//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
-void userLoop()
-{
- //userVar0 (U0 in HTTP API):
- //has to be set to 1 if movement is detected on the PIR that is the same side of the staircase as the ESP8266
- //has to be set to 2 if movement is detected on the PIR that is the opposite side
- //can be set to 0 if no movement is detected. Otherwise LEDs will turn off after a configurable timeout (userVar1 seconds)
-
- if (userVar0 > 0)
- {
- if ((previousUserVar0 == 1 && userVar0 == 2) || (previousUserVar0 == 2 && userVar0 == 1)) wipeState = 3; //turn off if other PIR triggered
- previousUserVar0 = userVar0;
-
- if (wipeState == 0) {
- startWipe();
- wipeState = 1;
- } else if (wipeState == 1) { //wiping
- uint32_t cycleTime = 360 + (255 - effectSpeed)*75; //this is how long one wipe takes (minus 25 ms to make sure we switch in time)
- if (millis() + strip.timebase > (cycleTime - 25)) { //wipe complete
- effectCurrent = FX_MODE_STATIC;
- timeStaticStart = millis();
- colorUpdated(CALL_MODE_NOTIFICATION);
- wipeState = 2;
- }
- } else if (wipeState == 2) { //static
- if (userVar1 > 0) //if U1 is not set, the light will stay on until second PIR or external command is triggered
- {
- if (millis() - timeStaticStart > userVar1*1000) wipeState = 3;
- }
- } else if (wipeState == 3) { //switch to wipe off
- #ifdef STAIRCASE_WIPE_OFF
- effectCurrent = FX_MODE_COLOR_WIPE;
- strip.timebase = 360 + (255 - effectSpeed)*75 - millis(); //make sure wipe starts fully lit
- colorUpdated(CALL_MODE_NOTIFICATION);
- wipeState = 4;
- #else
- turnOff();
- #endif
- } else { //wiping off
- if (millis() + strip.timebase > (725 + (255 - effectSpeed)*150)) turnOff(); //wipe complete
- }
- } else {
- wipeState = 0; //reset for next time
- if (previousUserVar0) {
- #ifdef STAIRCASE_WIPE_OFF
- userVar0 = previousUserVar0;
- wipeState = 3;
- #else
- turnOff();
- #endif
- }
- previousUserVar0 = 0;
- }
-}
-
-void startWipe()
-{
- bri = briLast; //turn on
- transitionDelayTemp = 0; //no transition
- effectCurrent = FX_MODE_COLOR_WIPE;
- resetTimebase(); //make sure wipe starts from beginning
-
- //set wipe direction
- Segment& seg = strip.getSegment(0);
- bool doReverse = (userVar0 == 2);
- seg.setOption(1, doReverse);
-
- colorUpdated(CALL_MODE_NOTIFICATION);
-}
-
-void turnOff()
-{
- #ifdef STAIRCASE_WIPE_OFF
- transitionDelayTemp = 0; //turn off immediately after wipe completed
- #else
- transitionDelayTemp = 4000; //fade out slowly
- #endif
- bri = 0;
- stateUpdated(CALL_MODE_NOTIFICATION);
- wipeState = 0;
- userVar0 = 0;
- previousUserVar0 = 0;
-}
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 14341f5b9..e75e20cd2 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -1211,8 +1211,9 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!"
*/
uint16_t mode_fireworks() {
if (SEGLEN == 1) return mode_static();
- const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
- const uint16_t height = SEGMENT.virtualHeight();
+ const unsigned width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
+ const unsigned height = SEGMENT.virtualHeight();
+ const unsigned dimension = width * height;
if (SEGENV.call == 0) {
SEGENV.aux0 = UINT16_MAX;
@@ -1220,19 +1221,19 @@ uint16_t mode_fireworks() {
}
SEGMENT.fade_out(128);
- bool valid1 = (SEGENV.aux0 < width*height);
- bool valid2 = (SEGENV.aux1 < width*height);
- uint8_t x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte
+ bool valid1 = (SEGENV.aux0 < dimension);
+ bool valid2 = (SEGENV.aux1 < dimension);
+ unsigned x = SEGENV.aux0%width, y = SEGENV.aux0/width; // 2D coordinates stored in upper and lower byte
uint32_t sv1 = 0, sv2 = 0;
if (valid1) sv1 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux0); // get spark color
if (valid2) sv2 = SEGMENT.is2D() ? SEGMENT.getPixelColorXY(x, y) : SEGMENT.getPixelColor(SEGENV.aux1);
- if (!SEGENV.step) SEGMENT.blur(16);
+ if (!SEGENV.step) SEGMENT.blur(dimension > 100 ? 16 : 8);
if (valid1) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv1); else SEGMENT.setPixelColor(SEGENV.aux0, sv1); } // restore spark color after blur
if (valid2) { if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(x, y, sv2); else SEGMENT.setPixelColor(SEGENV.aux1, sv2); } // restore old spark color after blur
- for (int i=0; i> 1)) == 0) {
- uint16_t index = random16(width*height);
+ unsigned index = random16(dimension);
x = index % width;
y = index / width;
uint32_t col = SEGMENT.color_from_palette(random8(), false, false, 0);
@@ -2066,41 +2067,41 @@ uint16_t mode_fire_2012() {
struct virtualStrip {
static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) {
- const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels
+ const unsigned ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels
// Step 1. Cool down every cell a little
- for (int i = 0; i < SEGLEN; i++) {
- uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random8(4);
- uint8_t minTemp = (i 1; k--) {
+ for (unsigned k = SEGLEN -1; k > 1; k--) {
heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
if (random8() <= SEGMENT.intensity) {
- uint8_t y = random8(ignition);
- uint8_t boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math!
+ unsigned y = random8(ignition);
+ unsigned boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math!
heat[y] = qadd8(heat[y], random8(96+2*boost,207+boost));
}
}
// Step 4. Map from heat cells to LED colors
for (int j = 0; j < SEGLEN; j++) {
- SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, NOBLEND));
+ SEGMENT.setPixelColor(indexToVStrip(j, stripNr), ColorFromPalette(SEGPALETTE, MIN(heat[j],240), 255, LINEARBLEND_NOWRAP));
}
}
};
- for (int stripNr=0; stripNr 100 ? 32 : 0);
if (it != SEGENV.step)
SEGENV.step = it;
@@ -4856,9 +4857,9 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef
uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
- const uint16_t cols = SEGMENT.virtualWidth();
- const uint16_t rows = SEGMENT.virtualHeight();
- uint16_t x, y;
+ const unsigned cols = SEGMENT.virtualWidth();
+ const unsigned rows = SEGMENT.virtualHeight();
+ unsigned x, y;
SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails
unsigned long t = strip.now/128; // timebase
@@ -4877,7 +4878,7 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
// central white dot
SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE);
// blur everything a bit
- SEGMENT.blur(16);
+ SEGMENT.blur(sqrt16(cols*rows) > 100 ? 16 : 0);
return FRAMETIME;
} // mode_2DBlackHole()
@@ -6436,8 +6437,8 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B
uint16_t mode_2DWaverly(void) {
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
- const uint16_t cols = SEGMENT.virtualWidth();
- const uint16_t rows = SEGMENT.virtualHeight();
+ const unsigned cols = SEGMENT.virtualWidth();
+ const unsigned rows = SEGMENT.virtualHeight();
um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
@@ -6449,21 +6450,21 @@ uint16_t mode_2DWaverly(void) {
SEGMENT.fadeToBlackBy(SEGMENT.speed);
long t = strip.now / 2;
- for (int i = 0; i < cols; i++) {
- uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2;
+ for (unsigned i = 0; i < cols; i++) {
+ unsigned thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2;
// use audio if available
if (um_data) {
thisVal /= 32; // reduce intensity of inoise8()
thisVal *= volumeSmth;
}
- uint16_t thisMax = map(thisVal, 0, 512, 0, rows);
+ unsigned thisMax = map(thisVal, 0, 512, 0, rows);
- for (int j = 0; j < thisMax; j++) {
+ for (unsigned j = 0; j < thisMax; j++) {
SEGMENT.addPixelColorXY(i, j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND));
SEGMENT.addPixelColorXY((cols - 1) - i, (rows - 1) - j, ColorFromPalette(SEGPALETTE, map(j, 0, thisMax, 250, 0), 255, LINEARBLEND));
}
}
- SEGMENT.blur(16);
+ SEGMENT.blur(sqrt16(cols*rows) > 100 ? 16 : 0);
return FRAMETIME;
} // mode_2DWaverly()
diff --git a/wled00/FX.h b/wled00/FX.h
index 44c5e1549..fd0bb297b 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -601,7 +601,7 @@ typedef struct Segment {
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
#ifndef WLED_DISABLE_MODE_BLEND
- inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; };
+ static inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; };
#endif
bool isPixelClipped(int i);
uint32_t getPixelColor(int i);
@@ -708,9 +708,7 @@ class WS2812FX { // 96 bytes
public:
WS2812FX() :
- paletteFade(0),
paletteBlend(0),
- cctBlending(0),
now(millis()),
timebase(0),
isMatrix(false),
@@ -792,6 +790,7 @@ class WS2812FX { // 96 bytes
addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name), // add effect to the list; defined in FX.cpp
setupEffectData(void); // add default effects to the list; defined in FX.cpp
+ inline void resetTimebase() { timebase = 0U - millis(); }
inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); }
inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); }
inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
@@ -806,7 +805,6 @@ class WS2812FX { // 96 bytes
inline void resume(void) { _suspend = false; } // will resume strip.service() execution
bool
- paletteFade,
checkSegmentAlignment(void),
hasRGBWBus(void),
hasCCTBus(void),
@@ -822,7 +820,6 @@ class WS2812FX { // 96 bytes
uint8_t
paletteBlend,
- cctBlending,
getActiveSegmentsNum(void),
getFirstSelectedSegId(void),
getLastActiveSegmentId(void),
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 7e1419293..a37e5d11f 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -176,10 +176,10 @@ bool IRAM_ATTR Segment::isPixelXYClipped(int x, int y) {
if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) {
const bool invertX = _clipStart > _clipStop;
const bool invertY = _clipStartY > _clipStopY;
- const unsigned startX = invertX ? _clipStop : _clipStart;
- const unsigned stopX = invertX ? _clipStart : _clipStop;
- const unsigned startY = invertY ? _clipStopY : _clipStartY;
- const unsigned stopY = invertY ? _clipStartY : _clipStopY;
+ const int startX = invertX ? _clipStop : _clipStart;
+ const int stopX = invertX ? _clipStart : _clipStop;
+ const int startY = invertY ? _clipStopY : _clipStartY;
+ const int stopY = invertY ? _clipStartY : _clipStopY;
if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
const unsigned width = stopX - startX; // assumes full segment width (faster than virtualWidth())
const unsigned len = width * (stopY - startY); // assumes full segment height (faster than virtualHeight())
@@ -295,7 +295,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel
uint32_t IRAM_ATTR Segment::getPixelColorXY(uint16_t x, uint16_t y) {
if (!isActive()) return 0; // not active
- if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
+ if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 00196e38e..4ef110e07 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -293,21 +293,17 @@ void Segment::startTransition(uint16_t dur) {
_t->_briT = on ? opacity : 0;
_t->_cctT = cct;
#ifndef WLED_DISABLE_MODE_BLEND
- if (modeBlending) {
- swapSegenv(_t->_segT);
- _t->_modeT = mode;
- _t->_segT._dataLenT = 0;
- _t->_segT._dataT = nullptr;
- if (_dataLen > 0 && data) {
- _t->_segT._dataT = (byte *)malloc(_dataLen);
- if (_t->_segT._dataT) {
- //DEBUG_PRINTF_P(PSTR("-- Allocated duplicate data (%d) for %p: %p\n"), _dataLen, this, _t->_segT._dataT);
- memcpy(_t->_segT._dataT, data, _dataLen);
- _t->_segT._dataLenT = _dataLen;
- }
+ swapSegenv(_t->_segT);
+ _t->_modeT = mode;
+ _t->_segT._dataLenT = 0;
+ _t->_segT._dataT = nullptr;
+ if (_dataLen > 0 && data) {
+ _t->_segT._dataT = (byte *)malloc(_dataLen);
+ if (_t->_segT._dataT) {
+ //DEBUG_PRINTF_P(PSTR("-- Allocated duplicate data (%d) for %p: %p\n"), _dataLen, this, _t->_segT._dataT);
+ memcpy(_t->_segT._dataT, data, _dataLen);
+ _t->_segT._dataLenT = _dataLen;
}
- } else {
- for (size_t i=0; i_segT._colorT[i] = colors[i];
}
#else
for (size_t i=0; i_colorT[i] = colors[i];
@@ -435,7 +431,7 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
uint8_t IRAM_ATTR Segment::currentMode() {
#ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress();
- if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
+ if (prog < 0xFFFFU) return _t->_modeT;
#endif
return mode;
}
@@ -445,7 +441,7 @@ uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
uint32_t prog = progress();
if (prog == 0xFFFFU) return colors[slot];
#ifndef WLED_DISABLE_MODE_BLEND
- if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color
+ if (blendingStyle > BLEND_STYLE_FADE && mode != _t->_modeT) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color
return color_blend(_t->_segT._colorT[slot], colors[slot], prog, true);
#else
return color_blend(_t->_colorT[slot], colors[slot], prog, true);
@@ -456,10 +452,10 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u
loadPalette(targetPalette, pal);
uint16_t prog = progress();
#ifndef WLED_DISABLE_MODE_BLEND
- if (prog < 0xFFFFU && blendingStyle > BLEND_STYLE_FADE && _modeBlend) targetPalette = _t->_palT; // not fade/blend transition, each effect uses its palette
+ if (prog < 0xFFFFU && blendingStyle > BLEND_STYLE_FADE && _modeBlend && mode != _t->_modeT) targetPalette = _t->_palT; // not fade/blend transition, each effect uses its palette
else
#endif
- if (strip.paletteFade && prog < 0xFFFFU) {
+ if (prog < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
@@ -473,19 +469,16 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u
// relies on WS2812FX::service() to call it for each frame
void Segment::handleRandomPalette() {
// is it time to generate a new palette?
- if ((uint16_t)(millis() / 1000U) - _lastPaletteChange > randomPaletteChangeTime){
- _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
- _lastPaletteChange = (uint16_t)(millis() / 1000U);
- _lastPaletteBlend = (uint16_t)millis() - 512; // starts blending immediately
+ if ((uint16_t)(millis()/1000U) - _lastPaletteChange > randomPaletteChangeTime) {
+ _newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
+ _lastPaletteChange = (uint16_t)(millis()/1000U);
+ _lastPaletteBlend = (uint16_t)(millis())-512; // starts blending immediately
}
- // if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls)
- if (strip.paletteFade) {
- // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less)
- // in reality there need to be 255 blends to fully blend two entirely different palettes
- if ((uint16_t)((uint16_t)millis() - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update
- _lastPaletteBlend = (uint16_t)millis();
- }
+ // assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less)
+ // in reality there need to be 255 blends to fully blend two entirely different palettes
+ if ((uint16_t)millis() - _lastPaletteBlend < strip.getTransition() >> 7) return; // not yet time to fade, delay the update
+ _lastPaletteBlend = (uint16_t)millis();
nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
}
@@ -549,7 +542,7 @@ bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
}
- if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
+ startTransition(strip.getTransition()); // start transition prior to change
colors[slot] = c;
stateChanged = true; // send UDP/WS broadcast
return true;
@@ -562,21 +555,21 @@ void Segment::setCCT(uint16_t k) {
k = (k - 1900) >> 5;
}
if (cct == k) return;
- if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
+ startTransition(strip.getTransition()); // start transition prior to change
cct = k;
stateChanged = true; // send UDP/WS broadcast
}
void Segment::setOpacity(uint8_t o) {
if (opacity == o) return;
- if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
+ startTransition(strip.getTransition()); // start transition prior to change
opacity = o;
stateChanged = true; // send UDP/WS broadcast
}
void Segment::setOption(uint8_t n, bool val) {
bool prevOn = on;
- if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
+ if (n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
@@ -589,7 +582,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved
if (fx != mode) {
#ifndef WLED_DISABLE_MODE_BLEND
- if (modeBlending) startTransition(strip.getTransition()); // set effect transitions
+ startTransition(strip.getTransition()); // set effect transitions
#endif
mode = fx;
// load default values from effect string
@@ -620,7 +613,7 @@ void Segment::setPalette(uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // custom palettes
if (pal != palette) {
- if (strip.paletteFade) startTransition(strip.getTransition());
+ startTransition(strip.getTransition());
palette = pal;
stateChanged = true; // send UDP/WS broadcast
}
@@ -688,8 +681,8 @@ bool IRAM_ATTR Segment::isPixelClipped(int i) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) {
bool invert = _clipStart > _clipStop;
- unsigned start = invert ? _clipStop : _clipStart;
- unsigned stop = invert ? _clipStart : _clipStop;
+ int start = invert ? _clipStop : _clipStart;
+ int stop = invert ? _clipStart : _clipStop;
if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
unsigned len = stop - start;
if (len < 2) return false;
@@ -888,6 +881,8 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
}
#endif
+ if (isPixelClipped(i)) return 0; // handle clipping on 1D
+
if (reverse) i = virtualLength() - i - 1;
i *= groupLength();
i += start;
@@ -1234,8 +1229,8 @@ void WS2812FX::service() {
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
#ifndef WLED_DISABLE_MODE_BLEND
- seg.setClippingRect(0, 0); // disable clipping
- if (modeBlending && seg.mode != tmpMode) {
+ Segment::setClippingRect(0, 0); // disable clipping (just in case)
+ if (seg.mode != tmpMode) { // could try seg.isInTransition() to allow color and palette to follow blending styles
// set clipping rectangle
// new mode is run inside clipping area and old mode outside clipping area
unsigned p = seg.progress();
@@ -1245,48 +1240,48 @@ void WS2812FX::service() {
unsigned dh = p * h / 0xFFFFU + 1;
switch (blendingStyle) {
case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
- seg.setClippingRect(0, w, 0, h);
+ Segment::setClippingRect(0, w, 0, h);
break;
case BLEND_STYLE_SWIPE_RIGHT: // left-to-right
- seg.setClippingRect(0, dw, 0, h);
+ Segment::setClippingRect(0, dw, 0, h);
break;
case BLEND_STYLE_SWIPE_LEFT: // right-to-left
- seg.setClippingRect(w - dw, w, 0, h);
+ Segment::setClippingRect(w - dw, w, 0, h);
break;
case BLEND_STYLE_PINCH_OUT: // corners
- seg.setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!!
+ Segment::setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!!
break;
case BLEND_STYLE_INSIDE_OUT: // outward
- seg.setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2);
+ Segment::setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2);
break;
case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D)
- seg.setClippingRect(0, w, 0, dh);
+ Segment::setClippingRect(0, w, 0, dh);
break;
case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D)
- seg.setClippingRect(0, w, h - dh, h);
+ Segment::setClippingRect(0, w, h - dh, h);
break;
case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D
- seg.setClippingRect((w - dw)/2, (w + dw)/2, 0, h);
+ Segment::setClippingRect((w - dw)/2, (w + dw)/2, 0, h);
break;
case BLEND_STYLE_OPEN_V: // vertical-outward (2D)
- seg.setClippingRect(0, w, (h - dh)/2, (h + dh)/2);
+ Segment::setClippingRect(0, w, (h - dh)/2, (h + dh)/2);
break;
case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D)
- seg.setClippingRect(0, dw, 0, dh);
+ Segment::setClippingRect(0, dw, 0, dh);
break;
case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D)
- seg.setClippingRect(w - dw, w, 0, dh);
+ Segment::setClippingRect(w - dw, w, 0, dh);
break;
case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D)
- seg.setClippingRect(w - dw, w, h - dh, h);
+ Segment::setClippingRect(w - dw, w, h - dh, h);
break;
case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D)
- seg.setClippingRect(0, dw, h - dh, h);
+ Segment::setClippingRect(0, dw, h - dh, h);
break;
}
}
delay = (*_mode[seg.mode])(); // run new/current mode
- if (modeBlending && seg.mode != tmpMode) {
+ if (seg.mode != tmpMode) { // could try seg.isInTransition() to allow color and palette to follow blending styles
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
@@ -1301,13 +1296,14 @@ void WS2812FX::service() {
#endif
seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
- BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
+ BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
}
seg.next_time = nowUp + delay;
}
_segment_index++;
}
+ Segment::setClippingRect(0, 0); // disable clipping for overlays
_virtualSegmentLength = 0;
_isServicing = false;
_triggered = false;
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 82e81a387..02a76f69d 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -391,14 +391,14 @@ BusPwm::BusPwm(BusConfig &bc)
uint8_t numPins = NUM_PWM_PINS(bc.type);
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
- #ifdef ESP8266
+#ifdef ESP8266
// duty cycle resolution (_depth) can be extracted from this formula: 1MHz > _frequency * 2^_depth
if (_frequency > 1760) _depth = 8;
else if (_frequency > 880) _depth = 9;
else _depth = 10; // WLED_PWM_FREQ <= 880Hz
analogWriteRange((1<<_depth)-1);
analogWriteFreq(_frequency);
- #else
+#else
_ledcStart = pinManager.allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels
deallocatePins(); return;
@@ -408,7 +408,7 @@ BusPwm::BusPwm(BusConfig &bc)
else if (_frequency > 39062) _depth = 10;
else if (_frequency > 19531) _depth = 11;
else _depth = 12; // WLED_PWM_FREQ <= 19531Hz
- #endif
+#endif
for (unsigned i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i];
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index c128f8c09..cdd1e82ff 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -173,10 +173,11 @@ class Bus {
type == TYPE_FW1906 || type == TYPE_WS2805 ) return true;
return false;
}
- static int16_t getCCT() { return _cct; }
+ static inline int16_t getCCT() { return _cct; }
static void setCCT(int16_t cct) {
_cct = cct;
}
+ static inline uint8_t getCCTBlend() { return _cctBlend; }
static void setCCTBlend(uint8_t b) {
if (b > 100) b = 100;
_cctBlend = (b * 127) / 100;
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 530777ab5..01c407f95 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -111,8 +111,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(correctWB, hw_led["cct"]);
CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(cctICused, hw_led[F("ic")]);
- CJSON(strip.cctBlending, hw_led[F("cb")]);
- Bus::setCCTBlend(strip.cctBlending);
+ uint8_t cctBlending = hw_led[F("cb")] | Bus::getCCTBlend();
+ Bus::setCCTBlend(cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
@@ -408,12 +408,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
}
JsonObject light_tr = light["tr"];
- CJSON(fadeTransition, light_tr["mode"]);
- CJSON(modeBlending, light_tr["fx"]);
int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
- strip.setTransition(fadeTransition ? transitionDelayDefault : 0);
- CJSON(strip.paletteFade, light_tr["pal"]);
+ strip.setTransition(transitionDelayDefault);
CJSON(randomPaletteChangeTime, light_tr[F("rpc")]);
CJSON(useHarmonicRandomPalette, light_tr[F("hrp")]);
@@ -777,7 +774,7 @@ void serializeConfig() {
hw_led["cct"] = correctWB;
hw_led[F("cr")] = cctFromRgb;
hw_led[F("ic")] = cctICused;
- hw_led[F("cb")] = strip.cctBlending;
+ hw_led[F("cb")] = Bus::getCCTBlend();
hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
hw_led[F("ld")] = useGlobalLedBuffer;
@@ -894,10 +891,7 @@ void serializeConfig() {
light_gc["val"] = gammaCorrectVal;
JsonObject light_tr = light.createNestedObject("tr");
- light_tr["mode"] = fadeTransition;
- light_tr["fx"] = modeBlending;
light_tr["dur"] = transitionDelayDefault / 100;
- light_tr["pal"] = strip.paletteFade;
light_tr[F("rpc")] = randomPaletteChangeTime;
light_tr[F("hrp")] = useHarmonicRandomPalette;
diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm
index dddedd471..2a5267825 100644
--- a/wled00/data/settings_leds.htm
+++ b/wled00/data/settings_leds.htm
@@ -834,12 +834,7 @@ Swap:
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 4cb707965..1f8a91616 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -677,8 +677,10 @@ function parseInfo(i) {
isM = mw>0 && mh>0;
if (!isM) {
gId("filter2D").classList.add('hide');
+ gId('bs').querySelectorAll('option[data-type="2D"]').forEach((o,i)=>{o.style.display='none';});
} else {
gId("filter2D").classList.remove('hide');
+ gId('bs').querySelectorAll('option[data-type="2D"]').forEach((o,i)=>{o.style.display='';});
}
// if (i.noaudio) {
// gId("filterVol").classList.add("hide");
From 0275bd1d45660ae1630792c835e65fd5a34c40ca Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Fri, 5 Jul 2024 21:23:59 +0200
Subject: [PATCH 07/14] On/Off blending respected
---
wled00/FX_fcn.cpp | 37 ++++++++++++++++++++++++++++++-------
1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 3ebf0c448..af9b3218c 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -433,9 +433,17 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
uint8_t IRAM_ATTR Segment::currentMode() {
#ifndef WLED_DISABLE_MODE_BLEND
unsigned prog = progress();
- if (prog < 0xFFFFU) return _t->_modeT;
-#endif
+ if (prog == 0xFFFFU) return mode;
+ if (blendingStyle > BLEND_STYLE_FADE) {
+ // workaround for on/off transition to respect blending style
+ uint8_t modeT = (bri != briT) && bri ? FX_MODE_STATIC : _t->_modeT; // On/Off transition active (bri!=briT) and final bri>0 : old mode is STATIC
+ uint8_t modeS = (bri != briT) && !bri ? FX_MODE_STATIC : mode; // On/Off transition active (bri!=briT) and final bri==0 : new mode is STATIC
+ return _modeBlend ? modeT : modeS;
+ }
+ return _modeBlend ? _t->_modeT : mode;
+#else
return mode;
+#endif
}
uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
@@ -443,7 +451,12 @@ uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
uint32_t prog = progress();
if (prog == 0xFFFFU) return colors[slot];
#ifndef WLED_DISABLE_MODE_BLEND
- if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color
+ if (blendingStyle > BLEND_STYLE_FADE) {
+ // workaround for on/off transition to respect blending style
+ uint32_t colT = (bri != briT) && bri ? BLACK : _t->_segT._colorT[slot]; // On/Off transition active (bri!=briT) and final bri>0 : old color is BLACK
+ uint32_t colS = (bri != briT) && !bri ? BLACK : colors[slot]; // On/Off transition active (bri!=briT) and final bri==0 : new color is BLACK
+ return _modeBlend ? colT : colS;
+ }
return color_blend(_t->_segT._colorT[slot], colors[slot], prog, true);
#else
return color_blend(_t->_colorT[slot], colors[slot], prog, true);
@@ -559,6 +572,7 @@ bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
}
+ //DEBUG_PRINTF_P(PSTR("- Starting color transition: %d [0x%X]\n"), slot, c);
startTransition(strip.getTransition()); // start transition prior to change
colors[slot] = c;
stateChanged = true; // send UDP/WS broadcast
@@ -572,6 +586,7 @@ void Segment::setCCT(uint16_t k) {
k = (k - 1900) >> 5;
}
if (cct == k) return;
+ //DEBUG_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k);
startTransition(strip.getTransition()); // start transition prior to change
cct = k;
stateChanged = true; // send UDP/WS broadcast
@@ -579,6 +594,7 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) {
if (opacity == o) return;
+ //DEBUG_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o);
startTransition(strip.getTransition()); // start transition prior to change
opacity = o;
stateChanged = true; // send UDP/WS broadcast
@@ -599,6 +615,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved
if (fx != mode) {
#ifndef WLED_DISABLE_MODE_BLEND
+ //DEBUG_PRINTF_P(PSTR("- Starting effect transition: %d\n"), fx);
startTransition(strip.getTransition()); // set effect transitions
#endif
mode = fx;
@@ -630,6 +647,7 @@ void Segment::setPalette(uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // custom palettes
if (pal != palette) {
+ //DEBUG_PRINTF_P(PSTR("- Starting palette transition: %d\n"), pal);
startTransition(strip.getTransition());
palette = pal;
stateChanged = true; // send UDP/WS broadcast
@@ -1373,7 +1391,6 @@ void WS2812FX::service() {
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
#ifndef WLED_DISABLE_MODE_BLEND
- uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
Segment::setClippingRect(0, 0); // disable clipping (just in case)
if (seg.isInTransition()) {
// set clipping rectangle
@@ -1425,14 +1442,20 @@ void WS2812FX::service() {
break;
}
}
- delay = (*_mode[seg.mode])(); // run new/current mode
+ delay = (*_mode[seg.currentMode()])(); // run new/current mode
if (seg.isInTransition()) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
_virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed)
- seg.setCurrentPalette(); // load actual palette
- unsigned d2 = (*_mode[tmpMode])(); // run old mode
+ _colors_t[0] = gamma32(seg.currentColor(0));
+ _colors_t[1] = gamma32(seg.currentColor(1));
+ _colors_t[2] = gamma32(seg.currentColor(2));
+ if (seg.currentPalette() != pal) {
+ seg.setCurrentPalette(); // load actual palette
+ pal = seg.currentPalette();
+ }
+ unsigned d2 = (*_mode[seg.currentMode()])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
From c03422ee3703dd604c909a58997442c0f2c532e4 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Tue, 30 Jul 2024 17:26:50 +0200
Subject: [PATCH 08/14] Push variants
---
wled00/FX.h | 26 ++++++++++--------
wled00/FX_2Dfcn.cpp | 61 ++++++++++++++++++++++++++++++++++++++-----
wled00/FX_fcn.cpp | 30 ++++++++++++++++++---
wled00/data/index.htm | 28 +++++++++++---------
4 files changed, 112 insertions(+), 33 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index 136f8e18b..3dc69e987 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -325,18 +325,22 @@
#define BLEND_STYLE_FAIRY_DUST 1
#define BLEND_STYLE_SWIPE_RIGHT 2
#define BLEND_STYLE_SWIPE_LEFT 3
-#define BLEND_STYLE_PINCH_OUT 4
-#define BLEND_STYLE_INSIDE_OUT 5
-#define BLEND_STYLE_SWIPE_UP 6
-#define BLEND_STYLE_SWIPE_DOWN 7
-#define BLEND_STYLE_OPEN_H 8
-#define BLEND_STYLE_OPEN_V 9
-#define BLEND_STYLE_PUSH_TL 10
-#define BLEND_STYLE_PUSH_TR 11
-#define BLEND_STYLE_PUSH_BR 12
-#define BLEND_STYLE_PUSH_BL 13
+#define BLEND_STYLE_PUSH_RIGHT 4
+#define BLEND_STYLE_PUSH_LEFT 5
+#define BLEND_STYLE_PINCH_OUT 6
+#define BLEND_STYLE_INSIDE_OUT 7
+#define BLEND_STYLE_SWIPE_UP 8
+#define BLEND_STYLE_SWIPE_DOWN 9
+#define BLEND_STYLE_OPEN_H 10
+#define BLEND_STYLE_OPEN_V 11
+#define BLEND_STYLE_PUSH_UP 12
+#define BLEND_STYLE_PUSH_DOWN 13
+#define BLEND_STYLE_PUSH_TL 14
+#define BLEND_STYLE_PUSH_TR 15
+#define BLEND_STYLE_PUSH_BR 16
+#define BLEND_STYLE_PUSH_BL 17
-#define BLEND_STYLE_COUNT 14
+#define BLEND_STYLE_COUNT 18
typedef enum mapping1D2D {
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index a62aa330e..846c78675 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -202,15 +202,39 @@ bool IRAM_ATTR Segment::isPixelXYClipped(int x, int y) {
void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
{
if (!isActive()) return; // not active
- if (x >= virtualWidth() || y >= virtualHeight() || x < 0 || y < 0 || isPixelXYClipped(x,y)) return; // if pixel would fall out of virtual segment just exit
+
+ int vW = virtualWidth();
+ int vH = virtualHeight();
+
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (!_modeBlend &&
+ (blendingStyle == BLEND_STYLE_PUSH_RIGHT ||
+ blendingStyle == BLEND_STYLE_PUSH_LEFT ||
+ blendingStyle == BLEND_STYLE_PUSH_UP ||
+ blendingStyle == BLEND_STYLE_PUSH_DOWN ||
+ blendingStyle == BLEND_STYLE_PUSH_TL ||
+ blendingStyle == BLEND_STYLE_PUSH_TR ||
+ blendingStyle == BLEND_STYLE_PUSH_BR ||
+ blendingStyle == BLEND_STYLE_PUSH_BL)) {
+ unsigned prog = 0xFFFF - progress();
+ unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF;
+ unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF;
+ if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x -= dX;
+ else x += dX;
+ if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY;
+ else y += dY;
+ }
+#endif
+
+ if (x >= vW || y >= vH || x < 0 || y < 0 || isPixelXYClipped(x,y)) return; // if pixel would fall out of virtual segment just exit
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
col = color_fade(col, _bri_t);
}
- if (reverse ) x = virtualWidth() - x - 1;
- if (reverse_y) y = virtualHeight() - y - 1;
+ if (reverse ) x = vW - x - 1;
+ if (reverse_y) y = vH - y - 1;
if (transpose) { unsigned t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= groupLength(); // expand to physical pixels
@@ -294,9 +318,34 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel
uint32_t IRAM_ATTR Segment::getPixelColorXY(int x, int y) {
if (!isActive()) return 0; // not active
- if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit
- if (reverse ) x = virtualWidth() - x - 1;
- if (reverse_y) y = virtualHeight() - y - 1;
+
+ int vW = virtualWidth();
+ int vH = virtualHeight();
+
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (!_modeBlend &&
+ (blendingStyle == BLEND_STYLE_PUSH_RIGHT ||
+ blendingStyle == BLEND_STYLE_PUSH_LEFT ||
+ blendingStyle == BLEND_STYLE_PUSH_UP ||
+ blendingStyle == BLEND_STYLE_PUSH_DOWN ||
+ blendingStyle == BLEND_STYLE_PUSH_TL ||
+ blendingStyle == BLEND_STYLE_PUSH_TR ||
+ blendingStyle == BLEND_STYLE_PUSH_BR ||
+ blendingStyle == BLEND_STYLE_PUSH_BL)) {
+ unsigned prog = 0xFFFF - progress();
+ unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF;
+ unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF;
+ if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x -= dX;
+ else x += dX;
+ if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY;
+ else y += dY;
+ }
+#endif
+
+ if (x >= vW || y >= vH || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit
+
+ if (reverse ) x = vW - x - 1;
+ if (reverse_y) y = vH - y - 1;
if (transpose) { unsigned t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 4af961764..583496de0 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -782,7 +782,8 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
#endif
i &= 0xFFFF;
- if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit
+ int vL = virtualLength();
+ if (i >= vL || i < 0) return; // if pixel would fall out of segment just exit
#ifndef WLED_DISABLE_2D
if (is2D()) {
@@ -890,7 +891,16 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
#endif
- if (isPixelClipped(i)) return; // handle clipping on 1D
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (!_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) {
+ unsigned prog = 0xFFFF - progress();
+ unsigned dI = prog * vL / 0xFFFF;
+ if (blendingStyle == BLEND_STYLE_PUSH_RIGHT) i -= dI;
+ else i += dI;
+ }
+#endif
+
+ if (i >= vL || i < 0 || isPixelClipped(i)) return; // handle clipping on 1D
unsigned len = length();
uint8_t _bri_t = currentBri();
@@ -978,6 +988,9 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
#endif
i &= 0xFFFF;
+ int vL = virtualLength();
+ if (i >= vL || i < 0) return 0;
+
#ifndef WLED_DISABLE_2D
if (is2D()) {
unsigned vH = virtualHeight(); // segment height in logical pixels
@@ -1029,9 +1042,18 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
}
#endif
- if (isPixelClipped(i)) return 0; // handle clipping on 1D
+#ifndef WLED_DISABLE_MODE_BLEND
+ if (!_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) {
+ unsigned prog = 0xFFFF - progress();
+ unsigned dI = prog * vL / 0xFFFF;
+ if (blendingStyle == BLEND_STYLE_PUSH_RIGHT) i -= dI;
+ else i += dI;
+ }
+#endif
- if (reverse) i = virtualLength() - i - 1;
+ if (i >= vL || i < 0 || isPixelClipped(i)) return 0; // handle clipping on 1D
+
+ if (reverse) i = vL - i - 1;
i *= groupLength();
i += start;
/* offset/phase */
diff --git a/wled00/data/index.htm b/wled00/data/index.htm
index d7ef3707a..df867d3a5 100644
--- a/wled00/data/index.htm
+++ b/wled00/data/index.htm
@@ -271,18 +271,22 @@
From 365c1987ed8f5f8070081c2619d1b784ff354a7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bla=C5=BE=20Kristan?=
Date: Thu, 1 Aug 2024 10:24:40 +0200
Subject: [PATCH 09/14] Missing clipping fix - small speed optimisations
---
wled00/FX.h | 6 +++---
wled00/FX_2Dfcn.cpp | 22 ++++++++++++----------
wled00/FX_fcn.cpp | 35 ++++++++++++++++++++---------------
3 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/wled00/FX.h b/wled00/FX.h
index 3dc69e987..3abc81166 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -351,7 +351,7 @@ typedef enum mapping1D2D {
M12_sPinwheel = 4
} mapping1D2D_t;
-// segment, 80 bytes
+// segment, 68 bytes
typedef struct Segment {
public:
uint16_t start; // start index / start X coordinate 2D (left)
@@ -639,7 +639,7 @@ typedef struct Segment {
uint16_t virtualHeight(void) const; // segment height in virtual pixels (accounts for groupping and spacing)
uint16_t nrOfVStrips(void) const; // returns number of virtual vertical strips in 2D matrix (used to expand 1D effects into 2D)
#ifndef WLED_DISABLE_2D
- uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
+ uint16_t XY(int x, int y); // support function to get relative index within segment
void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
@@ -678,7 +678,7 @@ typedef struct Segment {
inline void blur2d(fract8 blur_amount) { blur(blur_amount); }
inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
#else
- inline uint16_t XY(uint16_t x, uint16_t y) { return x; }
+ inline uint16_t XY(int x, int y) { return x; }
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 846c78675..493cb8963 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -161,8 +161,7 @@ void WS2812FX::setUpMatrix() {
#ifndef WLED_DISABLE_2D
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
-uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y)
-{
+uint16_t IRAM_ATTR Segment::XY(int x, int y) {
unsigned width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
unsigned height = virtualHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%width) + (y%height) * width : 0;
@@ -207,7 +206,7 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
int vH = virtualHeight();
#ifndef WLED_DISABLE_MODE_BLEND
- if (!_modeBlend &&
+ if (isInTransition() && !_modeBlend &&
(blendingStyle == BLEND_STYLE_PUSH_RIGHT ||
blendingStyle == BLEND_STYLE_PUSH_LEFT ||
blendingStyle == BLEND_STYLE_PUSH_UP ||
@@ -239,13 +238,16 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
- if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
+
+ int W = width();
+ int H = height();
+ if (x >= W || y >= H) return; // if pixel would fall out of segment just exit
uint32_t tmpCol = col;
for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally
unsigned xX = (x+g), yY = (y+j);
- if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
+ if (xX >= W || yY >= H) continue; // we have reached one dimension's end
#ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
@@ -255,15 +257,15 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
if (mirror) { //set the corresponding horizontally mirrored pixel
- if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
- else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
+ if (transpose) strip.setPixelColorXY(start + xX, startY + H - yY - 1, tmpCol);
+ else strip.setPixelColorXY(start + W - xX - 1, startY + yY, tmpCol);
}
if (mirror_y) { //set the corresponding vertically mirrored pixel
- if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
- else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
+ if (transpose) strip.setPixelColorXY(start + W - xX - 1, startY + yY, tmpCol);
+ else strip.setPixelColorXY(start + xX, startY + H - yY - 1, tmpCol);
}
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
- strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol);
+ strip.setPixelColorXY(W - xX - 1, H - yY - 1, tmpCol);
}
}
}
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 583496de0..dbbd24b7d 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -754,7 +754,7 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
bool IRAM_ATTR Segment::isPixelClipped(int i) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) {
- bool invert = _clipStart > _clipStop; // ineverted start & stop
+ bool invert = _clipStart > _clipStop; // ineverted start & stop
int start = invert ? _clipStop : _clipStart;
int stop = invert ? _clipStart : _clipStop;
if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
@@ -892,7 +892,8 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
#endif
#ifndef WLED_DISABLE_MODE_BLEND
- if (!_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) {
+ // if we blend using "push" style we need to "shift" new mode to left or right
+ if (isInTransition() && !_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) {
unsigned prog = 0xFFFF - progress();
unsigned dI = prog * vL / 0xFFFF;
if (blendingStyle == BLEND_STYLE_PUSH_RIGHT) i -= dI;
@@ -1043,7 +1044,7 @@ uint32_t IRAM_ATTR Segment::getPixelColor(int i)
#endif
#ifndef WLED_DISABLE_MODE_BLEND
- if (!_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) {
+ if (isInTransition() && !_modeBlend && (blendingStyle == BLEND_STYLE_PUSH_RIGHT || blendingStyle == BLEND_STYLE_PUSH_LEFT)) {
unsigned prog = 0xFFFF - progress();
unsigned dI = prog * vL / 0xFFFF;
if (blendingStyle == BLEND_STYLE_PUSH_RIGHT) i -= dI;
@@ -1425,43 +1426,47 @@ void WS2812FX::service() {
unsigned dw = p * w / 0xFFFFU + 1;
unsigned dh = p * h / 0xFFFFU + 1;
switch (blendingStyle) {
- case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
+ case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
Segment::setClippingRect(0, w, 0, h);
break;
case BLEND_STYLE_SWIPE_RIGHT: // left-to-right
+ case BLEND_STYLE_PUSH_RIGHT: // left-to-right
Segment::setClippingRect(0, dw, 0, h);
break;
- case BLEND_STYLE_SWIPE_LEFT: // right-to-left
+ case BLEND_STYLE_SWIPE_LEFT: // right-to-left
+ case BLEND_STYLE_PUSH_LEFT: // right-to-left
Segment::setClippingRect(w - dw, w, 0, h);
break;
- case BLEND_STYLE_PINCH_OUT: // corners
+ case BLEND_STYLE_PINCH_OUT: // corners
Segment::setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!!
break;
- case BLEND_STYLE_INSIDE_OUT: // outward
+ case BLEND_STYLE_INSIDE_OUT: // outward
Segment::setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2);
break;
- case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D)
+ case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D)
+ case BLEND_STYLE_PUSH_DOWN: // top-to-bottom (2D)
Segment::setClippingRect(0, w, 0, dh);
break;
- case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D)
+ case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D)
+ case BLEND_STYLE_PUSH_UP: // bottom-to-top (2D)
Segment::setClippingRect(0, w, h - dh, h);
break;
- case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D
+ case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D
Segment::setClippingRect((w - dw)/2, (w + dw)/2, 0, h);
break;
- case BLEND_STYLE_OPEN_V: // vertical-outward (2D)
+ case BLEND_STYLE_OPEN_V: // vertical-outward (2D)
Segment::setClippingRect(0, w, (h - dh)/2, (h + dh)/2);
break;
- case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D)
+ case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D)
Segment::setClippingRect(0, dw, 0, dh);
break;
- case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D)
+ case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D)
Segment::setClippingRect(w - dw, w, 0, dh);
break;
- case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D)
+ case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D)
Segment::setClippingRect(w - dw, w, h - dh, h);
break;
- case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D)
+ case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D)
Segment::setClippingRect(0, dw, h - dh, h);
break;
}
From 77723b615f5c482a63053c7a40705b073f072681 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Thu, 8 Aug 2024 21:10:27 +0200
Subject: [PATCH 10/14] Fix compiler warning
---
wled00/FX_2Dfcn.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 36d2038b9..7aec73cad 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -246,7 +246,7 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
uint32_t tmpCol = col;
for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally
- unsigned xX = (x+g), yY = (y+j);
+ int xX = (x+g), yY = (y+j);
if (xX >= W || yY >= H) continue; // we have reached one dimension's end
#ifndef WLED_DISABLE_MODE_BLEND
From ebd8a10cefdfa83f289961de8d3d6ef5fa6e6da6 Mon Sep 17 00:00:00 2001
From: Blaz Kristan
Date: Tue, 3 Sep 2024 17:20:16 +0200
Subject: [PATCH 11/14] Prevent styles on 1px segments
---
wled00/FX_fcn.cpp | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index a5e007e89..f825cecd3 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1428,6 +1428,8 @@ void WS2812FX::service() {
unsigned h = seg.virtualHeight();
unsigned dw = p * w / 0xFFFFU + 1;
unsigned dh = p * h / 0xFFFFU + 1;
+ unsigned orgBS = blendingStyle;
+ if (w*h == 1) blendingStyle = BLEND_STYLE_FADE; // disable belending for single pixel segments (use fade instead)
switch (blendingStyle) {
case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
Segment::setClippingRect(0, w, 0, h);
@@ -1473,9 +1475,8 @@ void WS2812FX::service() {
Segment::setClippingRect(0, dw, h - dh, h);
break;
}
- }
- delay = (*_mode[seg.currentMode()])(); // run new/current mode
- if (seg.isInTransition()) {
+ delay = (*_mode[seg.currentMode()])(); // run new/current mode
+ // now run old/previous mode
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
@@ -1491,10 +1492,10 @@ void WS2812FX::service() {
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
- }
-#else
- delay = (*_mode[seg.mode])(); // run effect mode
+ blendingStyle = orgBS; // restore blending style if it was modified for single pixel segment
+ } else
#endif
+ delay = (*_mode[seg.mode])(); // run effect mode (not in transition)
seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
From a421a90e0ad2432ee2621c41ace63a2702b13452 Mon Sep 17 00:00:00 2001
From: Damian Schneider
Date: Mon, 20 Jan 2025 05:51:04 +0100
Subject: [PATCH 12/14] replacement for fastled sqrt16() (#4426)
* added bitwise operation based sqrt16
- replacement for fastled, it is about 10% slower for numbers smaller 128 but faster for larger numbers. speed difference is irrelevant to WLED but it saves some flash.
* updated to 32bit, improved for typical WLED use
- making it 32bits allows for larger numbers
- added another initial condition check for medium sized numbers
- increased the "small number" optimization to larger numbers: the function is currently only used to calculate sqrt(x^2+y^2) which even for small segments is larger than the initially used 64, so optimizing for 1024 makes more sense, although the value is arbitrarily chosen
---
wled00/FX.cpp | 6 +++---
wled00/FX_fcn.cpp | 4 ++--
wled00/fcn_declare.h | 1 +
wled00/wled_math.cpp | 24 ++++++++++++++++++++++++
4 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 2655d7daa..9fffe4d09 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -5446,15 +5446,15 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have
// and add them together with weightening
unsigned dx = abs(x - x1);
unsigned dy = abs(y - y1);
- unsigned dist = 2 * sqrt16((dx * dx) + (dy * dy));
+ unsigned dist = 2 * sqrt32_bw((dx * dx) + (dy * dy));
dx = abs(x - x2);
dy = abs(y - y2);
- dist += sqrt16((dx * dx) + (dy * dy));
+ dist += sqrt32_bw((dx * dx) + (dy * dy));
dx = abs(x - x3);
dy = abs(y - y3);
- dist += sqrt16((dx * dx) + (dy * dy));
+ dist += sqrt32_bw((dx * dx) + (dy * dy));
// inverse result
int color = dist ? 1000 / dist : 255;
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 5ad2314df..20d99519d 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -679,7 +679,7 @@ uint16_t Segment::virtualLength() const {
vLen = max(vW,vH); // get the longest dimension
break;
case M12_pArc:
- vLen = sqrt16(vH*vH + vW*vW); // use diagonal
+ vLen = sqrt32_bw(vH*vH + vW*vW); // use diagonal
break;
case M12_sPinwheel:
vLen = getPinwheelLength(vW, vH);
@@ -922,7 +922,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
break; }
case M12_pArc:
if (i >= vW && i >= vH) {
- unsigned vI = sqrt16(i*i/2);
+ unsigned vI = sqrt32_bw(i*i/2);
return getPixelColorXY(vI,vI); // use diagonal
}
case M12_pCorner:
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h
index fcfa1bdcc..c8b1f05ab 100644
--- a/wled00/fcn_declare.h
+++ b/wled00/fcn_declare.h
@@ -552,6 +552,7 @@ float asin_t(float x);
template T atan_t(T x);
float floor_t(float x);
float fmod_t(float num, float denom);
+uint32_t sqrt32_bw(uint32_t x);
#define sin_t sin_approx
#define cos_t cos_approx
#define tan_t tan_approx
diff --git a/wled00/wled_math.cpp b/wled00/wled_math.cpp
index a8ec55400..43c593080 100644
--- a/wled00/wled_math.cpp
+++ b/wled00/wled_math.cpp
@@ -220,3 +220,27 @@ float fmod_t(float num, float denom) {
#endif
return res;
}
+
+// bit-wise integer square root calculation (exact)
+uint32_t sqrt32_bw(uint32_t x) {
+ uint32_t res = 0;
+ uint32_t bit;
+ uint32_t num = x; // use 32bit for faster calculation
+
+ if(num < 1 << 10) bit = 1 << 10; // speed optimization for small numbers < 32^2
+ else if (num < 1 << 20) bit = 1 << 20; // speed optimization for medium numbers < 1024^2
+ else bit = 1 << 30; // start with highest power of 4 <= 2^32
+
+ while (bit > num) bit >>= 2; // reduce iterations
+
+ while (bit != 0) {
+ if (num >= res + bit) {
+ num -= res + bit;
+ res = (res >> 1) + bit;
+ } else {
+ res >>= 1;
+ }
+ bit >>= 2;
+ }
+ return res;
+}
From 01a71132d5ee57d53cb083fd08907a4821bf3147 Mon Sep 17 00:00:00 2001
From: Ryan Ross
Date: Sun, 19 Jan 2025 21:12:12 -0800
Subject: [PATCH 13/14] connect the seven segment reloaded usermod to BH1750
usermod (#4503)
---
.../seven_segment_display_reloaded/readme.md | 6 ++---
.../usermod_seven_segment_reloaded.h | 22 +++++++++++++++++++
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/usermods/seven_segment_display_reloaded/readme.md b/usermods/seven_segment_display_reloaded/readme.md
index a3398c3e5..94788df7e 100644
--- a/usermods/seven_segment_display_reloaded/readme.md
+++ b/usermods/seven_segment_display_reloaded/readme.md
@@ -9,7 +9,7 @@ Very loosely based on the existing usermod "seven segment display".
Add the compile-time option `-D USERMOD_SSDR` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SSDR` in `my_config.h`.
-For the auto brightness option, the usermod SN_Photoresistor has to be installed as well. See SN_Photoresistor/readme.md for instructions.
+For the auto brightness option, the usermod SN_Photoresistor or BH1750_V2 has to be installed as well. See SN_Photoresistor/readme.md or BH1750_V2/readme.md for instructions.
## Settings
All settings can be controlled via the usermod settings page.
@@ -28,10 +28,10 @@ Enables the blinking colon(s) if they are defined
Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`)
### enable-auto-brightness
-Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed.
+Enables the auto brightness feature. Can be used only when the usermods SN_Photoresistor or BH1750_V2 are installed.
### auto-brightness-min / auto-brightness-max
-The lux value calculated from usermod SN_Photoresistor will be mapped to the values defined here.
+The lux value calculated from usermod SN_Photoresistor or BH1750_V2 will be mapped to the values defined here.
The mapping, 0 - 1000 lux, will be mapped to auto-brightness-min and auto-brightness-max
WLED current protection will override the calculated value if it is too high.
diff --git a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
index 1436f8fc4..72f4c2dd6 100644
--- a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
+++ b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
@@ -97,6 +97,11 @@ private:
#else
void* ptr = nullptr;
#endif
+#ifdef USERMOD_BH1750
+ Usermod_BH1750* bh1750 = nullptr;
+#else
+ void* bh1750 = nullptr;
+#endif
void _overlaySevenSegmentDraw() {
int displayMaskLen = static_cast(umSSDRDisplayMask.length());
@@ -387,6 +392,9 @@ public:
#ifdef USERMOD_SN_PHOTORESISTOR
ptr = (Usermod_SN_Photoresistor*) UsermodManager::lookup(USERMOD_ID_SN_PHOTORESISTOR);
#endif
+ #ifdef USERMOD_BH1750
+ bh1750 = (Usermod_BH1750*) UsermodManager::lookup(USERMOD_ID_BH1750);
+ #endif
DEBUG_PRINTLN(F("Setup done"));
}
@@ -410,6 +418,20 @@ public:
umSSDRLastRefresh = millis();
}
#endif
+ #ifdef USERMOD_BH1750
+ if(bri != 0 && umSSDREnableLDR && (millis() - umSSDRLastRefresh > umSSDRResfreshTime)) {
+ if (bh1750 != nullptr) {
+ float lux = bh1750->getIlluminance();
+ uint16_t brightness = map(lux, 0, 1000, umSSDRBrightnessMin, umSSDRBrightnessMax);
+ if (bri != brightness) {
+ DEBUG_PRINTF("Adjusting brightness based on lux value: %.2f lx, new brightness: %d\n", lux, brightness);
+ bri = brightness;
+ stateUpdated(1);
+ }
+ }
+ umSSDRLastRefresh = millis();
+ }
+ #endif
}
void handleOverlayDraw() {
From 4951be6999316e74c75770fd160b86514a275ecb Mon Sep 17 00:00:00 2001
From: 5chubrakete <163564943+5chubrakete@users.noreply.github.com>
Date: Mon, 20 Jan 2025 06:24:10 +0100
Subject: [PATCH 14/14] Added some date and time formatting options to
scrolling text effect. (#4195)
Updated to nonbreaking change and auto uppercasing according to review.
---
wled00/FX.cpp | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 9fffe4d09..1459c4a0f 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -6094,13 +6094,23 @@ uint16_t mode_2Dscrollingtext(void) {
if (!strlen(text)) { // fallback if empty segment name: display date and time
sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
} else {
+ if (text[0] == '#') for (auto &c : text) c = std::toupper(c);
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime));
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
- else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), AmPmHour);
- else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), minute(localTime));
+ else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf (text, zero? ("%02d") : ("%d"), AmPmHour);
+ else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf (text, zero? ("%02d") : ("%d"), minute(localTime));
+ else if (!strncmp_P(text,PSTR("#SS"),3)) sprintf (text, ("%02d") , second(localTime));
+ else if (!strncmp_P(text,PSTR("#DD"),3)) sprintf (text, zero? ("%02d") : ("%d"), day(localTime));
+ else if (!strncmp_P(text,PSTR("#DAY"),4)) sprintf (text, ("%s") , dayShortStr(day(localTime)));
+ else if (!strncmp_P(text,PSTR("#DDDD"),5)) sprintf (text, ("%s") , dayStr(day(localTime)));
+ else if (!strncmp_P(text,PSTR("#MO"),3)) sprintf (text, zero? ("%02d") : ("%d"), month(localTime));
+ else if (!strncmp_P(text,PSTR("#MON"),4)) sprintf (text, ("%s") , monthShortStr(month(localTime)));
+ else if (!strncmp_P(text,PSTR("#MMMM"),5)) sprintf (text, ("%s") , monthStr(month(localTime)));
+ else if (!strncmp_P(text,PSTR("#YY"),3)) sprintf (text, ("%02d") , year(localTime)%100);
+ else if (!strncmp_P(text,PSTR("#YYYY"),5)) sprintf_P(text, zero?PSTR("%04d") : ("%d"), year(localTime));
}
const int numberOfLetters = strlen(text);