From 0b9adf929d0696e7b48497e8cf9e6f4837e06dc4 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 24 Nov 2024 01:39:55 +0100 Subject: [PATCH 1/3] new mapping mode "Waterfall" --- wled00/FX.h | 5 +++- wled00/FX_2Dfcn.cpp | 21 +++++++++++++++++ wled00/FX_fcn.cpp | 57 ++++++++++++++++++++++++++++++++++++--------- wled00/json.cpp | 3 +++ 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index c06934629d..b47bd78718 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -347,7 +347,8 @@ typedef enum mapping1D2D { M12_jMap = 4, //WLEDMM jMap M12_sCircle = 5, //WLEDMM Circle M12_sBlock = 6, //WLEDMM Block - M12_sPinwheel = 7 //WLEDMM Pinwheel + M12_sPinwheel = 7, //WLEDMM Pinwheel + M12_sWaterfall = 8 //WLEDMM Waterfall } mapping1D2D_t; // segment, 72 bytes @@ -424,6 +425,8 @@ typedef struct Segment { bool _isSimpleSegment = false; // simple = no grouping or spacing - mirror, transpose or reverse allowed bool _isSuperSimpleSegment = false; // superSimple = no grouping or spacing, no mirror - only transpose or reverse allowed + bool _is2Deffect = false; // tells us if the strip is running a real 2D effect + #ifdef WLEDMM_FASTPATH // WLEDMM cache some values that won't change while drawing a frame bool _isValid2D = false; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index f6d2bc0b6c..fc14fe5df1 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -262,7 +262,28 @@ void Segment::startFrame(void) { #if 0 && defined(WLED_ENABLE_HUB75MATRIX) _firstFill = true; // dirty HACK #endif +#else + uint16_t _2dHeight = calc_virtualHeight(); + uint16_t _2dWidth = is2D() ? calc_virtualWidth() : calc_virtualLength(); #endif + + // WLEDMM Make sure we don't scroll any real 2D effect + if (!isActive()) _is2Deffect = is2D(); + else { + _is2Deffect = (strstr(strip.getModeData(mode), ";2") != nullptr); // effect data string contains ";2" --> 2d + if (is2D() && (strstr(strip.getModeData(mode),";12") != nullptr)) _is2Deffect = true;// effect data string contains ";12" --> 1d 2d --> maybe 2D + if (is2D() && ((mode == 0) || (mode >= strip.getModeCount()))) _is2Deffect = true; + if (is2D() && nrOfVStrips() > 1) _is2Deffect = true; // 1.5d effect on virtual strips => 2D + if (!strip.isMatrix) _is2Deffect = false; // not running in 2D mode + } + + // WLEDMM Waterfall mapping + if (!_is2Deffect && (map1D2D == M12_pBar) && reverse) { // WLEDMM Waterfall = bar + reverse + for (unsigned myRow = _2dHeight-1; myRow > 0; myRow--) + for (unsigned myCol = 0; myCol < _2dWidth; myCol++) { + setPixelColorXY(myCol, myRow, getPixelColorXY(int(myCol), int(myRow)-1)); + } + } } // WLEDMM end diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index f98f5dcbc9..7ef0be38ce 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -644,7 +644,11 @@ uint16_t Segment::nrOfVStrips() const { uint16_t vLen = 1; #ifndef WLED_DISABLE_2D if (is2D()) { - switch (map1D2D) { + + mapping1D2D_t map1d2d = mapping1D2D_t(map1D2D); + if ((map1d2d == M12_pBar) && reverse) map1d2d = M12_sWaterfall; // WLEDMM hack to support waterfall mapping (= "Bar" + "reverse") + + switch (map1d2d) { case M12_pBar: vLen = calc_virtualWidth(); break; @@ -654,6 +658,9 @@ uint16_t Segment::nrOfVStrips() const { case M12_sBlock: //WLEDMM vLen = (calc_virtualWidth() + calc_virtualHeight()) / 8; // take half of the average width break; + default: + vLen = 1; // WLEDMM other modes do not support virtual strips + break; } } #endif @@ -865,7 +872,14 @@ uint16_t Segment::calc_virtualLength() const { uint16_t vW = calc_virtualWidth(); uint16_t vH = calc_virtualHeight(); uint16_t vLen = vW * vH; // use all pixels from segment - switch (map1D2D) { + + mapping1D2D_t map1d2d = mapping1D2D_t(map1D2D); + if ((map1d2d == M12_pBar) && reverse) map1d2d = M12_sWaterfall; // WLEDMM hack to support waterfall mapping (= "Bar" + "reverse") + + switch (map1d2d) { + case M12_sWaterfall: + vLen = vW; + break; case M12_pBar: vLen = vH; break; @@ -896,6 +910,9 @@ uint16_t Segment::calc_virtualLength() const { case M12_sPinwheel: vLen = getPinwheelLength(vW, vH); break; + default: // M12_Pixels uses all available pixels + vLen = vW * vH; // use all pixels from segment + break; } return vLen; } @@ -946,7 +963,14 @@ void IRAM_ATTR_YN __attribute__((hot)) Segment::setPixelColor(int i, uint32_t co if (is2D()) { uint16_t vH = virtualHeight(); // segment height in logical pixels uint16_t vW = virtualWidth(); - switch (map1D2D) { + + mapping1D2D_t map1d2d = mapping1D2D_t(map1D2D); + if ((map1d2d == M12_pBar) && reverse) map1d2d = M12_sWaterfall; // WLEDMM hack to support waterfall mapping (= "Bar" + "reverse") + + switch (map1d2d) { + case M12_sWaterfall: + setPixelColorXY(i % vW, 0, col); // only set first row + break; case M12_Pixels: // use all available pixels as a long strip setPixelColorXY(i % vW, i / vW, col); @@ -1218,7 +1242,14 @@ uint32_t __attribute__((hot)) Segment::getPixelColor(int i) const if (is2D()) { uint16_t vH = virtualHeight(); // segment height in logical pixels uint16_t vW = virtualWidth(); - switch (map1D2D) { + + mapping1D2D_t map1d2d = mapping1D2D_t(map1D2D); + if ((map1d2d == M12_pBar) && reverse) map1d2d = M12_sWaterfall; // WLEDMM hack to support waterfall mapping (= "Bar" + "reverse") + + switch (map1d2d) { + case M12_sWaterfall: + return getPixelColorXY(i % vW, 0); // only use first row + break; case M12_Pixels: return getPixelColorXY(i % vW, i / vW); break; @@ -1414,7 +1445,8 @@ void __attribute__((hot)) Segment::fill(uint32_t c) { const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D - if (is2D()) { + bool reallyIs2D = _is2Deffect || (is2D() && !(map1D2D == M12_pBar && reverse)); // WLEDMM switch to "1D" mode for Waterfall + if (is2D() && reallyIs2D) { // pre-calculate scaled color uint32_t scaled_col = c; bool simpleSegment = (grouping == 1) && (spacing == 0); @@ -1470,7 +1502,8 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) { */ void __attribute__((hot)) Segment::fade_out(uint8_t rate) { if (!isActive()) return; // not active - const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types + bool reallyIs2D = _is2Deffect || (is2D() && !(map1D2D == M12_pBar && reverse)); // WLEDMM switch to "1D" mode for Bar or waterfall + const uint_fast16_t cols = reallyIs2D ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D uint_fast8_t fadeRate = (255-rate) >> 1; @@ -1483,7 +1516,7 @@ void __attribute__((hot)) Segment::fade_out(uint8_t rate) { int b2 = B(color2); for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) { - uint32_t color = is2D() ? getPixelColorXY(int(x), int(y)) : getPixelColor(int(x)); + uint32_t color = reallyIs2D ? getPixelColorXY(int(x), int(y)) : getPixelColor(int(x)); if (color == color2) continue; // WLEDMM speedup - pixel color = target color, so nothing to do int w1 = W(color); int r1 = R(color); @@ -1503,7 +1536,7 @@ void __attribute__((hot)) Segment::fade_out(uint8_t rate) { uint32_t colorNew = RGBW32(r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); // WLEDMM if (colorNew != color) { // WLEDMM speedup - do not repaint the same color - if (is2D()) setPixelColorXY(int(x), int(y), colorNew); + if (reallyIs2D) setPixelColorXY(int(x), int(y), colorNew); else setPixelColor(int(x), colorNew); } } @@ -1512,12 +1545,13 @@ void __attribute__((hot)) Segment::fade_out(uint8_t rate) { // fades all pixels to black using nscale8() void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) { if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply - const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types + bool reallyIs2D = _is2Deffect || (is2D() && !(map1D2D == M12_pBar && reverse)); // WLEDMM switch to "1D" mode for Bar or waterfall + const uint_fast16_t cols = reallyIs2D ? virtualWidth() : virtualLength(); // WLEDMM use fast int types const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D const uint_fast8_t scaledown = 255-fadeBy; // WLEDMM faster to pre-compute this // WLEDMM minor optimization - if(is2D()) { + if(reallyIs2D) { for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) { uint32_t cc = getPixelColorXY(int(x),int(y)); // WLEDMM avoid RGBW32 -> CRGB -> RGBW32 conversion uint32_t cc2 = color_fade(cc, scaledown); // fade @@ -1539,7 +1573,8 @@ void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) { void __attribute__((hot)) Segment::blur(uint8_t blur_amount, bool smear) { if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur" #ifndef WLED_DISABLE_2D - if (is2D()) { + bool reallyIs2D = _is2Deffect || (is2D() && !(map1D2D == M12_pBar && reverse)); // WLEDMM switch to "1D" mode for Bar or waterfall + if (reallyIs2D) { // compatibility with 2D const uint_fast32_t cols = virtualWidth(); const uint_fast32_t rows = virtualHeight(); diff --git a/wled00/json.cpp b/wled00/json.cpp index 6429bd2edf..1aa7be0413 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -165,6 +165,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) uint16_t of = seg.offset; uint8_t soundSim = elem["si"] | seg.soundSim; uint8_t map1D2D = elem["m12"] | seg.map1D2D; + if (map1D2D > 7) map1D2D = M12_pBar; // WLEDMM fix for "waterfall" mapping which is out of range 0-7 //WLEDMM jMap if (map1D2D == M12_jMap && !seg.jMap) @@ -327,6 +328,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) JsonArray iarr = elem[F("i")]; //set individual LEDs if (!iarr.isNull()) { uint8_t oldMap1D2D = seg.map1D2D; + if (oldMap1D2D > 7) oldMap1D2D = M12_pBar; // WLEDMM fix for "waterfall" mapping which is out of range 0-7 + seg.map1D2D = M12_Pixels; // no mapping // WLEDMM begin - we need to init segment caches before putting any pixels if (strip.isServicing()) { From 540b580781d51b7bc19f853f06449cff49bba698 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Sun, 24 Nov 2024 19:22:39 +0100 Subject: [PATCH 2/3] create segment buffer if not set up already up to 30% faster, and prevents interference with other segments. --- wled00/FX_2Dfcn.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index fc14fe5df1..1285ee78d4 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -279,6 +279,7 @@ void Segment::startFrame(void) { // WLEDMM Waterfall mapping if (!_is2Deffect && (map1D2D == M12_pBar) && reverse) { // WLEDMM Waterfall = bar + reverse + if (!ledsrgb || (ledsrgbSize == 0)) setUpLeds(); // create segment buffer if not set up already ==> 30% faster, and prevents interference with other segments. for (unsigned myRow = _2dHeight-1; myRow > 0; myRow--) for (unsigned myCol = 0; myCol < _2dWidth; myCol++) { setPixelColorXY(myCol, myRow, getPixelColorXY(int(myCol), int(myRow)-1)); From 9f01ec4186be67d75e407bc4f7cf6bce58f131b1 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:56:36 +0100 Subject: [PATCH 3/3] make fade_out work in waterfall mode --- wled00/FX_fcn.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 826a2d5762..32e0e1feef 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1503,8 +1503,8 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) { void __attribute__((hot)) Segment::fade_out(uint8_t rate) { if (!isActive()) return; // not active bool reallyIs2D = _is2Deffect || (is2D() && !(map1D2D == M12_pBar && reverse)); // WLEDMM switch to "1D" mode for Bar or waterfall - const uint_fast16_t cols = reallyIs2D ? virtualWidth() : virtualLength(); // WLEDMM use fast int types - const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D + const uint_fast16_t cols = reallyIs2D ? virtualWidth() : virtualLength(); // WLEDMM use fast int types + const uint_fast16_t rows = reallyIs2D ? virtualHeight() : 1; // must be 1 for 1D uint_fast8_t fadeRate = (255-rate) >> 1; float mappedRate_r = 1.0f / (float(fadeRate) +1.1f); // WLEDMM use reciprocal 1/mappedRate -> faster on non-FPU chips @@ -1547,7 +1547,8 @@ void __attribute__((hot)) Segment::fadeToBlackBy(uint8_t fadeBy) { if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply bool reallyIs2D = _is2Deffect || (is2D() && !(map1D2D == M12_pBar && reverse)); // WLEDMM switch to "1D" mode for Bar or waterfall const uint_fast16_t cols = reallyIs2D ? virtualWidth() : virtualLength(); // WLEDMM use fast int types - const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D + const uint_fast16_t rows = reallyIs2D ? virtualHeight() : 1; // must be 1 for 1D + const uint_fast8_t scaledown = 255-fadeBy; // WLEDMM faster to pre-compute this // WLEDMM minor optimization