From 7f6486c77dd4a8fc340e61ffa74023873c32e25b Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 12 Oct 2023 23:06:39 +0200 Subject: [PATCH] Custom palette gamma Mode blending option Optimisations --- wled00/FX.cpp | 10 +- wled00/FX.h | 53 +++---- wled00/FX_2Dfcn.cpp | 75 +++------ wled00/FX_fcn.cpp | 285 ++++++++++++++-------------------- wled00/cfg.cpp | 12 +- wled00/colors.cpp | 60 +++++-- wled00/const.h | 10 +- wled00/data/settings_leds.htm | 1 + wled00/fcn_declare.h | 4 +- wled00/set.cpp | 1 + wled00/util.cpp | 24 ++- wled00/wled.h | 1 + wled00/xml.cpp | 3 +- 13 files changed, 255 insertions(+), 284 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 188022b0..429ce8f5 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -177,11 +177,11 @@ uint16_t color_wipe(bool rev, bool useRandomColors) { SEGENV.step = 3; } if (SEGENV.step == 1) { //if flag set, change to new random color - SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0); + SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0); SEGENV.step = 2; } if (SEGENV.step == 3) { - SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1); + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1); SEGENV.step = 0; } } @@ -271,7 +271,7 @@ uint16_t mode_random_color(void) { if (it != SEGENV.step) //new color { SEGENV.aux1 = SEGENV.aux0; - SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index SEGENV.step = it; } @@ -816,7 +816,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett if (a < SEGENV.step) //we hit the start again, choose new color for Chase random { SEGENV.aux1 = SEGENV.aux0; //store previous random color - SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); } color1 = SEGMENT.color_wheel(SEGENV.aux0); } @@ -1056,7 +1056,7 @@ uint16_t mode_chase_flash_random(void) { SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN; if (SEGENV.aux1 == 0) { - SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); + SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); } } return delay; diff --git a/wled00/FX.h b/wled00/FX.h index c9306e80..d3be5a6c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -109,20 +109,15 @@ #define PINK (uint32_t)0xFF1493 #define ULTRAWHITE (uint32_t)0xFFFFFFFF #define DARKSLATEGRAY (uint32_t)0x2F4F4F -#define DARKSLATEGREY (uint32_t)0x2F4F4F +#define DARKSLATEGREY DARKSLATEGRAY -// options -// bit 7: segment is in transition mode -// bits 4-6: TBD -// bit 3: mirror effect within segment -// bit 2: segment is on -// bit 1: reverse segment -// bit 0: segment is selected +// segment options #define NO_OPTIONS (uint16_t)0x0000 -#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed -#define REVERSE_Y_2D (uint16_t)0x0200 -#define MIRROR_Y_2D (uint16_t)0x0100 -#define TRANSITIONAL (uint16_t)0x0080 +#define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed +#define MIRROR_Y_2D (uint16_t)0x0080 +#define REVERSE_Y_2D (uint16_t)0x0040 +#define RESET_REQ (uint16_t)0x0020 +#define FROZEN (uint16_t)0x0010 #define MIRROR (uint16_t)0x0008 #define SEGMENT_ON (uint16_t)0x0004 #define REVERSE (uint16_t)0x0002 @@ -348,12 +343,11 @@ typedef struct Segment { bool mirror : 1; // 3 : mirrored bool freeze : 1; // 4 : paused/frozen bool reset : 1; // 5 : indicates that Segment runtime requires reset - bool transitional: 1; // 6 : transitional (there is transition occuring) - bool reverse_y : 1; // 7 : reversed Y (2D) - bool mirror_y : 1; // 8 : mirrored Y (2D) - bool transpose : 1; // 9 : transposed (2D, swapped X & Y) - uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...) - uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off") + bool reverse_y : 1; // 6 : reversed Y (2D) + bool mirror_y : 1; // 7 : mirrored Y (2D) + bool transpose : 1; // 8 : transposed (2D, swapped X & Y) + uint8_t map1D2D : 3; // 9-11 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...) + uint8_t soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off") uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups }; }; @@ -484,7 +478,6 @@ typedef struct Segment { _dataLen(0), _t(nullptr) { - //refreshLightCapabilities(); #ifdef WLED_DEBUG //Serial.printf("-- Creating segment: %p\n", this); #endif @@ -519,6 +512,7 @@ typedef struct Segment { inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } inline bool isSelected(void) const { return selected; } + inline bool isInTransition(void) const { return _t != nullptr; } inline bool isActive(void) const { return stop > start; } inline bool is2D(void) const { return (width()>1 && height()>1); } inline bool hasRGB(void) const { return _isRGB; } @@ -569,15 +563,16 @@ typedef struct Segment { void restoreSegenv(tmpsegd_t &tmpSegD); #endif uint16_t progress(void); //transition progression between 0-65535 - uint8_t currentBri(uint8_t briNew, bool useCct = false); - uint8_t currentMode(uint8_t modeNew); - uint32_t currentColor(uint8_t slot, uint32_t colorNew); + uint8_t currentBri(bool useCct = false); + uint8_t currentMode(void); + uint32_t currentColor(uint8_t slot); CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID); // 1D strip uint16_t virtualLength(void) const; void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color + void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline void setPixelColor(float i, uint32_t c, bool aa = true); @@ -595,7 +590,6 @@ typedef struct Segment { void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline void fadePixelColor(uint16_t n, uint8_t fade); - uint8_t get_random_wheel_index(uint8_t pos); uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); uint32_t color_wheel(uint8_t pos); @@ -606,6 +600,7 @@ typedef struct Segment { #ifndef WLED_DISABLE_2D uint16_t XY(uint16_t x, uint16_t 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 + void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true); @@ -881,16 +876,14 @@ class WS2812FX { // 96 bytes std::vector panel; #endif - void - setUpMatrix(), - setPixelColorXY(int x, int y, uint32_t c); + void setUpMatrix(); // outsmart the compiler :) by correctly overloading - inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline - inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(y * Segment::maxWidth + x, 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)); } + inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } - uint32_t - getPixelColorXY(uint16_t, uint16_t); + inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);} // end 2D support diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 60a7e68f..5dc9e9ff 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() { #ifdef WLED_DEBUG DEBUG_PRINT(F("Matrix ledmap:")); - for (uint16_t i=0; i= _length) return; - busses.setPixelColor(index, col); -} - -// returns RGBW values of pixel -uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { -#ifndef WLED_DISABLE_2D - uint16_t index = (y * Segment::maxWidth + x); -#else - uint16_t index = x; -#endif - if (index < customMappingSize) index = customMappingTable[index]; - if (index >= _length) return 0; - return busses.getPixelColor(index); -} /////////////////////////////////////////////////////////// // Segment:: routines @@ -188,18 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { #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(uint16_t x, uint16_t y) +{ uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive) uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1) return isActive() ? (x%width) + (y%height) * width : 0; } -void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col) +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 - uint8_t _bri_t = currentBri(on ? opacity : 0); + uint8_t _bri_t = currentBri(); if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); @@ -310,32 +286,17 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { 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 - uint32_t col = getPixelColorXY(x,y); - uint8_t r = R(col); - uint8_t g = G(col); - uint8_t b = B(col); - uint8_t w = W(col); - if (fast) { - r = qadd8(r, R(color)); - g = qadd8(g, G(color)); - b = qadd8(b, B(color)); - w = qadd8(w, W(color)); - col = RGBW32(r,g,b,w); - } else { - col = color_add(col, color); - } - setPixelColorXY(x, y, col); + setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast)); } void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { if (!isActive()) return; // not active - CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); - setPixelColorXY(x, y, pix); + setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } // blurRow: perform a blur on a row of a rectangular matrix void Segment::blurRow(uint16_t row, fract8 blur_amount) { - if (!isActive()) return; // not active + if (!isActive() || blur_amount == 0) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -344,7 +305,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) { uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint_fast16_t x = 0; x < cols; x++) { + for (unsigned x = 0; x < cols; x++) { CRGB cur = getPixelColorXY(x, row); CRGB before = cur; // remember color before blur CRGB part = cur; @@ -363,7 +324,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) { // blurCol: perform a blur on a column of a rectangular matrix void Segment::blurCol(uint16_t col, fract8 blur_amount) { - if (!isActive()) return; // not active + if (!isActive() || blur_amount == 0) return; // not active const uint_fast16_t cols = virtualWidth(); const uint_fast16_t rows = virtualHeight(); @@ -372,7 +333,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) { uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; CRGB carryover = CRGB::Black; - for (uint_fast16_t y = 0; y < rows; y++) { + for (unsigned y = 0; y < rows; y++) { CRGB cur = getPixelColorXY(col, y); CRGB part = cur; CRGB before = cur; // remember color before blur @@ -391,7 +352,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) { // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { - if (!isActive()) return; // not active + if (!isActive() || blur_amount == 0) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); const uint16_t dim1 = vertical ? rows : cols; @@ -401,7 +362,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { const float keep = 3.f - 2.f*seep; // 1D box blur CRGB tmp[dim1]; - for (uint16_t j = 0; j < dim1; j++) { + for (int j = 0; j < dim1; j++) { uint16_t x = vertical ? i : j; uint16_t y = vertical ? j : i; int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow @@ -417,7 +378,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { b = (curr.b*keep + (prev.b + next.b)*seep) / 3; tmp[j] = CRGB(r,g,b); } - for (uint16_t j = 0; j < dim1; j++) { + for (int j = 0; j < dim1; j++) { uint16_t x = vertical ? i : j; uint16_t y = vertical ? j : i; setPixelColorXY(x, y, tmp[j]); @@ -440,7 +401,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { void Segment::blur1d(fract8 blur_amount) { const uint16_t rows = virtualHeight(); - for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount); + for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount); } void Segment::moveX(int8_t delta, bool wrap) { @@ -498,7 +459,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { } void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { - if (!isActive()) return; // not active + if (!isActive() || radius == 0) return; // not active // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; @@ -523,7 +484,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { - if (!isActive()) return; // not active + if (!isActive() || radius == 0) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); for (int16_t y = -radius; y <= radius; y++) { @@ -540,7 +501,7 @@ void Segment::nscale8(uint8_t scale) { if (!isActive()) return; // not active const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); - for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale)); } } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index dc4f3211..930f1f34 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -89,25 +89,19 @@ bool Segment::_modeBlend = false; Segment::Segment(const Segment &orig) { //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this); memcpy((void*)this, (void*)&orig, sizeof(Segment)); - transitional = false; // copied segment cannot be in transition - name = nullptr; - data = nullptr; - _dataLen = 0; - _t = nullptr; - if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } - if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } - //if (orig._t) { _t = new Transition(orig._t->_dur); } + _t = nullptr; // copied segment cannot be in transition + if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } else { name = nullptr; } + if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } else { data = nullptr; _dataLen = 0; } } // move constructor Segment::Segment(Segment &&orig) noexcept { //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this); memcpy((void*)this, (void*)&orig, sizeof(Segment)); - orig.transitional = false; // old segment cannot be in transition any more orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; - orig._t = nullptr; + orig._t = nullptr; // old segment cannot be in transition any more } // copy assignment @@ -115,27 +109,23 @@ Segment& Segment::operator= (const Segment &orig) { //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this); if (this != &orig) { // clean destination - transitional = false; // copied segment cannot be in transition - if (name) delete[] name; + if (name) { delete[] name; name = nullptr; } + if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (_t) { #ifndef WLED_DISABLE_MODE_BLEND if (_t->_segT._dataT) free(_t->_segT._dataT); #endif delete _t; + _t = nullptr; // copied segment cannot be in transition } deallocateData(); // copy source memcpy((void*)this, (void*)&orig, sizeof(Segment)); - transitional = false; // erase pointers to allocated data - name = nullptr; data = nullptr; _dataLen = 0; - _t = nullptr; // copy source data - if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } - //if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); } } return *this; } @@ -144,9 +134,7 @@ Segment& Segment::operator= (const Segment &orig) { Segment& Segment::operator= (Segment &&orig) noexcept { //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this); if (this != &orig) { - transitional = false; // just temporary if (name) { delete[] name; name = nullptr; } // free old name - deallocateData(); // free old runtime data if (_t) { #ifndef WLED_DISABLE_MODE_BLEND if (_t->_segT._dataT) free(_t->_segT._dataT); @@ -154,12 +142,12 @@ Segment& Segment::operator= (Segment &&orig) noexcept { delete _t; _t = nullptr; } + deallocateData(); // free old runtime data memcpy((void*)this, (void*)&orig, sizeof(Segment)); - orig.transitional = false; // old segment cannot be in transition orig.name = nullptr; orig.data = nullptr; orig._dataLen = 0; - orig._t = nullptr; + orig._t = nullptr; // old segment cannot be in transition } return *this; } @@ -237,7 +225,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { switch (pal) { case 0: //default palette. Exceptions for specific effects above targetPalette = PartyColors_p; break; - case 1: {//periodically replace palette with a random one. Transition palette change in 500ms + case 1: {//periodically replace palette with a random one unsigned long timeSinceLastChange = millis() - _lastPaletteChange; if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { _randomPalette = _newRandomPalette; @@ -301,47 +289,45 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { } void Segment::startTransition(uint16_t dur) { - if (!dur) { - if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton() - else transitional = false; + if (dur == 0) { + if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton() return; } - if (transitional && _t) return; // already in transition no need to store anything + if (isInTransition()) return; // already in transition no need to store anything // starting a transition has to occur before change so we get current values 1st _t = new Transition(dur); // no previous transition running if (!_t) return; // failed to allocate data //DEBUG_PRINTF("-- Started transition: %p\n", this); - CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); - _t->_palT = _palT; + loadPalette(_t->_palT, palette); _t->_briT = on ? opacity : 0; _t->_cctT = cct; #ifndef WLED_DISABLE_MODE_BLEND - swapSegenv(_t->_segT); - _t->_modeT = mode; - _t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional - _t->_segT._dataLenT = 0; - _t->_segT._dataT = nullptr; - if (_dataLen > 0 && data) { - _t->_segT._dataT = (byte *)malloc(_dataLen); - if (_t->_segT._dataT) { - //DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT); - memcpy(_t->_segT._dataT, data, _dataLen); - _t->_segT._dataLenT = _dataLen; + 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("-- Allocated duplicate data (%d): %p\n", _dataLen, _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]; #endif - transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true); } void Segment::stopTransition() { - if (!transitional) return; - transitional = false; // finish transitioning segment //DEBUG_PRINTF("-- Stopping transition: %p\n", this); - if (_t) { + if (isInTransition()) { #ifndef WLED_DISABLE_MODE_BLEND if (_t->_segT._dataT && _t->_segT._dataLenT > 0) { //DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT); @@ -356,14 +342,13 @@ void Segment::stopTransition() { } void Segment::handleTransition() { - if (!transitional) return; uint16_t _progress = progress(); if (_progress == 0xFFFFU) stopTransition(); } // transition progression between 0-65535 uint16_t Segment::progress() { - if (transitional && _t) { + if (isInTransition()) { unsigned long timeNow = millis(); if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; } @@ -420,8 +405,8 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { _t->_segT._stepT = step; _t->_segT._callT = call; //if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data); - _t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) - _t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) + _t->_segT._dataT = data; + _t->_segT._dataLenT = _dataLen; } options = tmpSeg._optionsT; for (size_t i=0; i_cctT * (0xFFFFU - prog)) >> 16; - else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; + uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog; + curBri += (useCct ? _t->_cctT : (on ? _t->_briT : 0)) * (0xFFFFU - prog); + return curBri / 0xFFFFU; } - return briNew; + return (useCct ? cct : (on ? opacity : 0)); } -uint8_t Segment::currentMode(uint8_t newMode) { +uint8_t Segment::currentMode() { #ifndef WLED_DISABLE_MODE_BLEND - uint16_t prog = progress(); // implicit check for transitional & _t in progress() - if (prog < 0xFFFFU) return _t->_modeT; + uint16_t prog = progress(); + if (modeBlending && prog < 0xFFFFU) return _t->_modeT; #endif - return newMode; + return mode; } -uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) { +uint32_t Segment::currentColor(uint8_t slot) { #ifndef WLED_DISABLE_MODE_BLEND - return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew; + return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot]; #else - return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew; + return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot]; #endif } CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { loadPalette(targetPalette, pal); - if (progress() < 0xFFFFU) { + uint16_t prog = progress(); + if (strip.paletteFade && 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 - unsigned long timeMS = millis() - _t->_start; - uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends; + uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends; for (int i=0; i_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); targetPalette = _t->_palT; // copy transitioning/temporary palette } @@ -500,6 +486,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t && (!grp || (grouping == grp && spacing == spc)) && (ofs == UINT16_MAX || ofs == offset)) return; + stateChanged = true; // send UDP/WS broadcast + if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing) if (grp) { // prevent assignment of 0 grouping = grp; @@ -510,6 +498,10 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t } if (ofs < UINT16_MAX) offset = ofs; + DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1); + DEBUG_PRINT(','); DEBUG_PRINT(i2); + DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y); + DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y); markForReset(); if (boundsUnchanged) return; @@ -564,7 +556,6 @@ void Segment::setCCT(uint16_t k) { void Segment::setOpacity(uint8_t o) { if (opacity == o) return; if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change - DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o); opacity = o; stateChanged = true; // send UDP/WS broadcast } @@ -574,14 +565,16 @@ void Segment::setOption(uint8_t n, bool val) { if (fadeTransition && 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 || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast + if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast } void Segment::setMode(uint8_t fx, bool loadDefaults) { // if we have a valid mode & is not reserved if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx != mode) { - if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions +#ifndef WLED_DISABLE_MODE_BLEND + if (modeBlending) startTransition(strip.getTransition()); // set effect transitions +#endif mode = fx; // load default values from effect string if (loadDefaults) { @@ -743,7 +736,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) #endif uint16_t len = length(); - uint8_t _bri_t = currentBri(on ? opacity : 0); + uint8_t _bri_t = currentBri(); if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); @@ -877,10 +870,11 @@ uint8_t Segment::differs(Segment& b) const { if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS; if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS; - //bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected] - if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT; + //bit pattern: (msb first) + // set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected] + if ((options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT; if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL; - for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; + for (unsigned i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL; return d; } @@ -912,7 +906,7 @@ void Segment::refreshLightCapabilities() { segStopIdx = stop; } - for (uint8_t b = 0; b < busses.getNumBusses(); b++) { + for (unsigned b = 0; b < busses.getNumBusses(); b++) { Bus *bus = busses.getBus(b); if (bus == nullptr || bus->getLength()==0) break; if (!bus->isOk()) continue; @@ -942,7 +936,7 @@ void Segment::fill(uint32_t c) { if (!isActive()) return; // not active const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D - for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { if (is2D()) setPixelColorXY(x, y, c); else setPixelColor(x, c); } @@ -956,27 +950,12 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) { // Adds the specified color with the existing pixel color perserving color balance. void Segment::addPixelColor(int n, uint32_t color, bool fast) { if (!isActive()) return; // not active - uint32_t col = getPixelColor(n); - uint8_t r = R(col); - uint8_t g = G(col); - uint8_t b = B(col); - uint8_t w = W(col); - if (fast) { - r = qadd8(r, R(color)); - g = qadd8(g, G(color)); - b = qadd8(b, B(color)); - w = qadd8(w, W(color)); - col = RGBW32(r,g,b,w); - } else { - col = color_add(col, color); - } - setPixelColor(n, col); + setPixelColor(n, color_add(getPixelColor(n), color, fast)); } void Segment::fadePixelColor(uint16_t n, uint8_t fade) { if (!isActive()) return; // not active - CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); - setPixelColor(n, pix); + setPixelColor(n, color_fade(getPixelColor(n), fade, true)); } /* @@ -996,7 +975,7 @@ void Segment::fade_out(uint8_t rate) { int g2 = G(color); int b2 = B(color); - for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x); int w1 = W(color); int r1 = R(color); @@ -1025,9 +1004,9 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) { const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t rows = virtualHeight(); // will be 1 for 1D - for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { - if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy)); - else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy)); + for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { + if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy)); + else setPixelColor(x, color_fade(getPixelColor(x), 255-fadeBy)); } } @@ -1040,34 +1019,26 @@ void Segment::blur(uint8_t blur_amount) #ifndef WLED_DISABLE_2D if (is2D()) { // compatibility with 2D - const uint_fast16_t cols = virtualWidth(); - const uint_fast16_t rows = virtualHeight(); - for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows - for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns + const unsigned cols = virtualWidth(); + const unsigned rows = virtualHeight(); + for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows + for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns return; } #endif uint8_t keep = 255 - blur_amount; uint8_t seep = blur_amount >> 1; - CRGB carryover = CRGB::Black; - uint_fast16_t vlength = virtualLength(); - for(uint_fast16_t i = 0; i < vlength; i++) - { - CRGB cur = CRGB(getPixelColor(i)); - CRGB part = cur; - CRGB before = cur; // remember color before blur - part.nscale8(seep); - cur.nscale8(keep); - cur += carryover; - if(i > 0) { + uint32_t carryover = BLACK; + unsigned vlength = virtualLength(); + for (unsigned i = 0; i < vlength; i++) { + uint32_t cur = getPixelColor(i); + uint32_t part = color_fade(cur, seep); + cur = color_add(color_fade(cur, keep), carryover, true); + if (i > 0) { uint32_t c = getPixelColor(i-1); - uint8_t r = R(c); - uint8_t g = G(c); - uint8_t b = B(c); - setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue)); + setPixelColor(i-1, color_add(c, part, true)); } - if (before != cur) // optimization: only set pixel if color has changed - setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue); + setPixelColor(i, cur); carryover = part; } } @@ -1080,7 +1051,7 @@ void Segment::blur(uint8_t blur_amount) uint32_t Segment::color_wheel(uint8_t pos) { if (palette) return color_from_palette(pos, false, true, 0); pos = 255 - pos; - if(pos < 85) { + if (pos < 85) { return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); } else if(pos < 170) { pos -= 85; @@ -1091,21 +1062,6 @@ uint32_t Segment::color_wheel(uint8_t pos) { } } -/* - * Returns a new, random wheel index with a minimum distance of 42 from pos. - */ -uint8_t Segment::get_random_wheel_index(uint8_t pos) { - uint8_t r = 0, x = 0, y = 0, d = 0; - - while(d < 42) { - r = random8(); - x = abs(pos - r); - y = 255 - x; - d = MIN(x, y); - } - return r; -} - /* * Gets a single color from the currently selected palette. * @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically. @@ -1119,20 +1075,20 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ { // default palette or no RGB support on segment if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { - uint32_t color = currentColor(mcol, colors[mcol]); + uint32_t color = currentColor(mcol); color = gamma32(color); if (pbri == 255) return color; - return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri)); + return color_fade(color, pbri, true); } uint8_t paletteIndex = i; if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); - if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGB fastled_col; + if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" CRGBPalette16 curPal; - if (transitional && _t) curPal = _t->_palT; - else loadPalette(curPal, palette); - fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + curPal = currentPalette(curPal, palette); + //if (isInTransition()) curPal = _t->_palT; + //else loadPalette(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, 0); } @@ -1166,7 +1122,7 @@ void WS2812FX::finalizeInit(void) const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0])); const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); uint16_t prevLen = 0; - for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { + for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { uint8_t defPin[] = {defDataPins[i]}; uint16_t start = prevLen; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; @@ -1177,7 +1133,7 @@ void WS2812FX::finalizeInit(void) } _length = 0; - for (uint8_t i=0; igetStart() + bus->getLength() > MAX_LEDS) break; @@ -1234,24 +1190,24 @@ void WS2812FX::service() { if (!seg.freeze) { //only run effect function if not frozen _virtualSegmentLength = seg.virtualLength(); - _colors_t[0] = seg.currentColor(0, seg.colors[0]); - _colors_t[1] = seg.currentColor(1, seg.colors[1]); - _colors_t[2] = seg.currentColor(2, seg.colors[2]); - seg.currentPalette(_currentPalette, seg.palette); + _colors_t[0] = seg.currentColor(0); + _colors_t[1] = seg.currentColor(1); + _colors_t[2] = seg.currentColor(2); + seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference - if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); - for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); + if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB); + for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); // Effect blending // When two effects are being blended, each may have different segment data, this - // data needs to be saved first and then restored before running previous/transitional mode. + // data needs to be saved first and then restored before running previous mode. // The blending will largely depend on the effect behaviour since actual output (LEDs) may be // 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(seg.mode); // this will return old mode while in transition + [[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 - if (seg.mode != tmpMode) { + if (modeBlending && seg.mode != tmpMode) { Segment::tmpsegd_t _tmpSegData; Segment::modeBlend(true); // set semaphore seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) @@ -1262,7 +1218,7 @@ void WS2812FX::service() { } #endif if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; - if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition + if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition } seg.next_time = nowUp + delay; @@ -1399,12 +1355,12 @@ void WS2812FX::show(void) { // or async show has a separate buffer (ESP32 RMT and I2S are ok) if (newBri < _brightness) busses.setBrightness(_brightness); - unsigned long now = millis(); - size_t diff = now - _lastShow; + unsigned long showNow = millis(); + size_t diff = showNow - _lastShow; size_t fpsCurr = 200; if (diff > 0) fpsCurr = 1000 / diff; _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) - _lastShow = now; + _lastShow = showNow; } /** @@ -1597,10 +1553,12 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group _qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY; _qGrouping = grouping; _qSpacing = spacing; _qOffset = offset; _queuedChangesSegId = segId; + DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId); return; // queued changes are applied immediately after effect function returns } _segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); + if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector } void WS2812FX::setUpSegmentFromQueuedChanges() { @@ -1729,7 +1687,7 @@ void WS2812FX::fixInvalidSegments() { bool WS2812FX::checkSegmentAlignment() { bool aligned = false; for (segment &seg : _segments) { - for (uint8_t b = 0; bgetStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; } @@ -1752,13 +1710,8 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) { } void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) { - if (i2 >= i) - { - for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col); - } else - { - for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col); - } + if (i2 < i) std::swap(i,i2); + for (unsigned x = i; x <= i2; x++) setPixelColor(x, col); } void WS2812FX::setTransitionMode(bool t) { @@ -1793,7 +1746,7 @@ void WS2812FX::loadCustomPalettes() { if (readObjectFromFile(fileName, nullptr, &pDoc)) { JsonArray pal = pDoc[F("palette")]; - if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries) + if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries) if (pal[0].is() && pal[1].is()) { // we have an array of index & hex strings size_t palSize = MIN(pal.size(), 36); @@ -1802,7 +1755,7 @@ void WS2812FX::loadCustomPalettes() { uint8_t rgbw[] = {0,0,0,0}; tcp[ j ] = (uint8_t) pal[ i ].as(); // index colorFromHexString(rgbw, pal[i+1].as()); // will catch non-string entires - for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component + for (size_t c=0; c<3; c++) tcp[j+1+c] = gamma8(rgbw[c]); // only use RGB component DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3])); } } else { @@ -1810,13 +1763,15 @@ void WS2812FX::loadCustomPalettes() { palSize -= palSize % 4; // make sure size is multiple of 4 for (size_t i=0; i()<256; i+=4) { tcp[ i ] = (uint8_t) pal[ i ].as(); // index - tcp[i+1] = (uint8_t) pal[i+1].as(); // R - tcp[i+2] = (uint8_t) pal[i+2].as(); // G - tcp[i+3] = (uint8_t) pal[i+3].as(); // B + tcp[i+1] = gamma8((uint8_t) pal[i+1].as()); // R + tcp[i+2] = gamma8((uint8_t) pal[i+2].as()); // G + tcp[i+3] = gamma8((uint8_t) pal[i+3].as()); // B DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3])); } } customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp)); + } else { + DEBUG_PRINTLN(F("Wrong palette format.")); } } } else { @@ -1832,7 +1787,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { char fileName[32]; strcpy_P(fileName, PSTR("/ledmap")); if (n) sprintf(fileName +7, "%d", n); - strcat(fileName, ".json"); + strcat_P(fileName, PSTR(".json")); bool isFile = WLED_FS.exists(fileName); if (!isFile) { @@ -1866,7 +1821,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { if (!map.isNull() && map.size()) { // not an empty map customMappingSize = map.size(); customMappingTable = new uint16_t[customMappingSize]; - for (uint16_t i=0; i= 0) transitionDelay = transitionDelayDefault = tdd * 100; CJSON(strip.paletteFade, light_tr["pal"]); @@ -827,6 +830,7 @@ void serializeConfig() { 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; @@ -888,6 +892,7 @@ void serializeConfig() { if_live[F("no-gc")] = arlsDisableGammaCorrection; if_live[F("offset")] = arlsOffset; +#ifndef WLED_DISABLE_ALEXA JsonObject if_va = interfaces.createNestedObject("va"); if_va[F("alexa")] = alexaEnabled; @@ -896,6 +901,7 @@ void serializeConfig() { if_va_macros.add(macroAlexaOff); if_va["p"] = alexaNumPresets; +#endif #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); @@ -1033,7 +1039,7 @@ bool deserializeConfigSec() { JsonObject ap = doc["ap"]; getStringFromJson(apPass, ap["psk"] , 65); - JsonObject interfaces = doc["if"]; + [[maybe_unused]] JsonObject interfaces = doc["if"]; #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces["mqtt"]; @@ -1072,7 +1078,7 @@ void serializeConfigSec() { JsonObject ap = doc.createNestedObject("ap"); ap["psk"] = apPass; - JsonObject interfaces = doc.createNestedObject("if"); + [[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if"); #ifdef WLED_ENABLE_MQTT JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); if_mqtt["psk"] = mqttPass; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 8c4baabb..21c27d65 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -35,23 +35,59 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) * color add function that preserves ratio * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule */ -uint32_t color_add(uint32_t c1, uint32_t c2) +uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) { - uint32_t r = R(c1) + R(c2); - uint32_t g = G(c1) + G(c2); - uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2); - uint16_t max = r; - if (g > max) max = g; - if (b > max) max = b; - if (w > max) max = w; - if (max < 256) return RGBW32(r, g, b, w); - else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + if (fast) { + uint8_t r = R(c1); + uint8_t g = G(c1); + uint8_t b = B(c1); + uint8_t w = W(c1); + r = qadd8(r, R(c2)); + g = qadd8(g, G(c2)); + b = qadd8(b, B(c2)); + w = qadd8(w, W(c2)); + return RGBW32(r,g,b,w); + } else { + uint32_t r = R(c1) + R(c2); + uint32_t g = G(c1) + G(c2); + uint32_t b = B(c1) + B(c2); + uint32_t w = W(c1) + W(c2); + uint16_t max = r; + if (g > max) max = g; + if (b > max) max = b; + if (w > max) max = w; + if (max < 256) return RGBW32(r, g, b, w); + else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + } +} + +/* + * fades color toward black + * if using "video" method the resulting color will never become black unless it is already black + */ +uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) +{ + uint8_t r = R(c1); + uint8_t g = G(c1); + uint8_t b = B(c1); + uint8_t w = W(c1); + if (video) { + r = scale8_video(r, amount); + g = scale8_video(g, amount); + b = scale8_video(b, amount); + w = scale8_video(w, amount); + } else { + r = scale8(r, amount); + g = scale8(g, amount); + b = scale8(b, amount); + w = scale8(w, amount); + } + return RGBW32(r, g, b, w); } void setRandomColor(byte* rgb) { - lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex); + lastRandomIndex = get_random_wheel_index(lastRandomIndex); colorHStoRGB(lastRandomIndex*256,255,rgb); } diff --git a/wled00/const.h b/wled00/const.h index 6ee83451..657cb88b 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -166,7 +166,7 @@ #define CALL_MODE_NO_NOTIFY 5 #define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_HUE 7 -#define CALL_MODE_PRESET_CYCLE 8 +#define CALL_MODE_PRESET_CYCLE 8 //no longer used #define CALL_MODE_BLYNK 9 //no longer used #define CALL_MODE_ALEXA 10 #define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only @@ -313,10 +313,9 @@ #define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment #define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed #define SEG_OPTION_RESET 5 //Segment runtime requires reset -#define SEG_OPTION_TRANSITIONAL 6 -#define SEG_OPTION_REVERSED_Y 7 -#define SEG_OPTION_MIRROR_Y 8 -#define SEG_OPTION_TRANSPOSED 9 +#define SEG_OPTION_REVERSED_Y 6 +#define SEG_OPTION_MIRROR_Y 7 +#define SEG_OPTION_TRANSPOSED 8 //Segment differs return byte #define SEG_DIFFERS_BRI 0x01 // opacity @@ -345,6 +344,7 @@ #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached #define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist #define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist +#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist #define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured #define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index fde0d0c5..a283fcfe 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -773,6 +773,7 @@ Length: %

Transitions

Crossfade:
+ Effect blending:
Transition Time: ms
Enable Palette transitions:
Random Cycle Palette Time: s
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index c67fdbf3..e046c40d 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -63,7 +63,8 @@ class NeoGammaWLEDMethod { #define gamma32(c) NeoGammaWLEDMethod::Correct32(c) #define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); -uint32_t color_add(uint32_t,uint32_t); +uint32_t color_add(uint32_t,uint32_t, bool fast=false); +uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorKtoRGB(uint16_t kelvin, byte* rgb); @@ -353,6 +354,7 @@ void checkSettingsPIN(const char *pin); 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); #ifdef WLED_ADD_EEPROM_SUPPORT //wled_eeprom.cpp diff --git a/wled00/set.cpp b/wled00/set.cpp index 744c0eee..01ebf5db 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -247,6 +247,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) } fadeTransition = request->hasArg(F("TF")); + modeBlending = request->hasArg(F("EB")); t = request->arg(F("TD")).toInt(); if (t >= 0) transitionDelayDefault = t; strip.paletteFade = request->hasArg(F("PF")); diff --git a/wled00/util.cpp b/wled00/util.cpp index 1902f5cb..9023dfdc 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -416,9 +416,9 @@ uint16_t crc16(const unsigned char* data_p, size_t length) { // (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type) typedef enum UM_SoundSimulations { UMS_BeatSin = 0, - UMS_WeWillRockYou - //UMS_10_13, - //UMS_14_3 + UMS_WeWillRockYou, + UMS_10_13, + UMS_14_3 } um_soundSimulations_t; um_data_t* simulateSound(uint8_t simulationId) @@ -503,7 +503,7 @@ um_data_t* simulateSound(uint8_t simulationId) fftResult[i] = 0; } break; - /*case UMS_10_3: + case UMS_10_13: for (int i = 0; i<16; i++) fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); volumeSmth = fftResult[8]; @@ -512,7 +512,7 @@ um_data_t* simulateSound(uint8_t simulationId) for (int i = 0; i<16; i++) fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); volumeSmth = fftResult[8]; - break;*/ + break; } samplePeak = random8() > 250; @@ -573,3 +573,17 @@ void enumerateLedmaps() { } } + +/* + * Returns a new, random color wheel index with a minimum distance of 42 from pos. + */ +uint8_t get_random_wheel_index(uint8_t pos) { + uint8_t r = 0, x = 0, y = 0, d = 0; + while (d < 42) { + r = random8(); + x = abs(pos - r); + y = 255 - x; + d = MIN(x, y); + } + return r; +} diff --git a/wled00/wled.h b/wled00/wled.h index 79885128..668afd95 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -351,6 +351,7 @@ WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlig WLED_GLOBAL byte nightlightDelayMins _INIT(60); WLED_GLOBAL byte nightlightMode _INIT(NL_MODE_FADE); // See const.h for available modes. Was nightlightFade WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading color transition +WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duration in ms WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127) diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 23de8179..f3344ea9 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -441,6 +441,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',SET_F("GC"),gammaCorrectCol); dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS); sappend('c',SET_F("TF"),fadeTransition); + sappend('c',SET_F("EB"),modeBlending); sappend('v',SET_F("TD"),transitionDelayDefault); sappend('c',SET_F("PF"),strip.paletteFade); sappend('v',SET_F("TP"),randomPaletteChangeTime); @@ -478,7 +479,7 @@ void getSettingsJS(byte subPage, char* dest) if (subPage == SUBPAGE_SYNC) { - char nS[32]; + [[maybe_unused]] char nS[32]; sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("U2"),udpPort2); sappend('v',SET_F("GS"),syncGroups);