Custom palette gamma

Mode blending option
Optimisations
This commit is contained in:
Blaz Kristan 2023-10-12 23:06:39 +02:00
parent 5dadf92a62
commit 7f6486c77d
13 changed files with 255 additions and 284 deletions

View File

@ -177,11 +177,11 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
SEGENV.step = 3; SEGENV.step = 3;
} }
if (SEGENV.step == 1) { //if flag set, change to new random color 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; SEGENV.step = 2;
} }
if (SEGENV.step == 3) { if (SEGENV.step == 3) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1); SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
SEGENV.step = 0; SEGENV.step = 0;
} }
} }
@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
if (it != SEGENV.step) //new color if (it != SEGENV.step) //new color
{ {
SEGENV.aux1 = SEGENV.aux0; 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; 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 if (a < SEGENV.step) //we hit the start again, choose new color for Chase random
{ {
SEGENV.aux1 = SEGENV.aux0; //store previous random color 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); color1 = SEGMENT.color_wheel(SEGENV.aux0);
} }
@ -1056,7 +1056,7 @@ uint16_t mode_chase_flash_random(void) {
SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN; SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
if (SEGENV.aux1 == 0) { if (SEGENV.aux1 == 0) {
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
} }
} }
return delay; return delay;

View File

@ -109,20 +109,15 @@
#define PINK (uint32_t)0xFF1493 #define PINK (uint32_t)0xFF1493
#define ULTRAWHITE (uint32_t)0xFFFFFFFF #define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F #define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY (uint32_t)0x2F4F4F #define DARKSLATEGREY DARKSLATEGRAY
// options // segment 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
#define NO_OPTIONS (uint16_t)0x0000 #define NO_OPTIONS (uint16_t)0x0000
#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed #define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed
#define REVERSE_Y_2D (uint16_t)0x0200 #define MIRROR_Y_2D (uint16_t)0x0080
#define MIRROR_Y_2D (uint16_t)0x0100 #define REVERSE_Y_2D (uint16_t)0x0040
#define TRANSITIONAL (uint16_t)0x0080 #define RESET_REQ (uint16_t)0x0020
#define FROZEN (uint16_t)0x0010
#define MIRROR (uint16_t)0x0008 #define MIRROR (uint16_t)0x0008
#define SEGMENT_ON (uint16_t)0x0004 #define SEGMENT_ON (uint16_t)0x0004
#define REVERSE (uint16_t)0x0002 #define REVERSE (uint16_t)0x0002
@ -348,12 +343,11 @@ typedef struct Segment {
bool mirror : 1; // 3 : mirrored bool mirror : 1; // 3 : mirrored
bool freeze : 1; // 4 : paused/frozen bool freeze : 1; // 4 : paused/frozen
bool reset : 1; // 5 : indicates that Segment runtime requires reset bool reset : 1; // 5 : indicates that Segment runtime requires reset
bool transitional: 1; // 6 : transitional (there is transition occuring) bool reverse_y : 1; // 6 : reversed Y (2D)
bool reverse_y : 1; // 7 : reversed Y (2D) bool mirror_y : 1; // 7 : mirrored Y (2D)
bool mirror_y : 1; // 8 : mirrored Y (2D) bool transpose : 1; // 8 : transposed (2D, swapped X & Y)
bool transpose : 1; // 9 : 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 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 : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
}; };
}; };
@ -484,7 +478,6 @@ typedef struct Segment {
_dataLen(0), _dataLen(0),
_t(nullptr) _t(nullptr)
{ {
//refreshLightCapabilities();
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
//Serial.printf("-- Creating segment: %p\n", this); //Serial.printf("-- Creating segment: %p\n", this);
#endif #endif
@ -519,6 +512,7 @@ typedef struct Segment {
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); } inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
inline bool isSelected(void) const { return selected; } 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 isActive(void) const { return stop > start; }
inline bool is2D(void) const { return (width()>1 && height()>1); } inline bool is2D(void) const { return (width()>1 && height()>1); }
inline bool hasRGB(void) const { return _isRGB; } inline bool hasRGB(void) const { return _isRGB; }
@ -569,15 +563,16 @@ typedef struct Segment {
void restoreSegenv(tmpsegd_t &tmpSegD); void restoreSegenv(tmpsegd_t &tmpSegD);
#endif #endif
uint16_t progress(void); //transition progression between 0-65535 uint16_t progress(void); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false); uint8_t currentBri(bool useCct = false);
uint8_t currentMode(uint8_t modeNew); uint8_t currentMode(void);
uint32_t currentColor(uint8_t slot, uint32_t colorNew); uint32_t currentColor(uint8_t slot);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
CRGBPalette16 &currentPalette(CRGBPalette16 &tgt, uint8_t paletteID); CRGBPalette16 &currentPalette(CRGBPalette16 &tgt, uint8_t paletteID);
// 1D strip // 1D strip
uint16_t virtualLength(void) const; uint16_t virtualLength(void) const;
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color 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, 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(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); 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, 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 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); 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_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
uint32_t color_wheel(uint8_t pos); uint32_t color_wheel(uint8_t pos);
@ -606,6 +600,7 @@ typedef struct Segment {
#ifndef WLED_DISABLE_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(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(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, 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(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); void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
@ -881,16 +876,14 @@ class WS2812FX { // 96 bytes
std::vector<Panel> panel; std::vector<Panel> panel;
#endif #endif
void void setUpMatrix();
setUpMatrix(),
setPixelColorXY(int x, int y, uint32_t c);
// outsmart the compiler :) by correctly overloading // 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, uint32_t c) { setPixelColor(y * Segment::maxWidth + x, c); }
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, 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 inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);}
getPixelColorXY(uint16_t, uint16_t);
// end 2D support // end 2D support

View File

@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:")); DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) { for (unsigned i=0; i<customMappingSize; i++) {
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN(); if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF("%4d,", customMappingTable[i]); DEBUG_PRINTF("%4d,", customMappingTable[i]);
} }
@ -155,31 +155,6 @@ void WS2812FX::setUpMatrix() {
#endif #endif
} }
// absolute matrix version of setPixelColor()
void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
{
#ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * Segment::maxWidth + x;
#else
uint16_t index = x;
#endif
if (index < customMappingSize) index = customMappingTable[index];
if (index >= _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 // Segment:: routines
@ -188,18 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // 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 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) uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%width) + (y%height) * width : 0; 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 (!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) 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) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(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) { void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active 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) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y); setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast));
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);
} }
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade); setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true));
setPixelColorXY(x, y, pix);
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) { 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 cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); 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 keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; 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 cur = getPixelColorXY(x, row);
CRGB before = cur; // remember color before blur CRGB before = cur; // remember color before blur
CRGB part = cur; 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 // blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) { 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 cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); 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 keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; 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 cur = getPixelColorXY(col, y);
CRGB part = cur; CRGB part = cur;
CRGB before = cur; // remember color before blur 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]) // 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) { 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 cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols; 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; const float keep = 3.f - 2.f*seep;
// 1D box blur // 1D box blur
CRGB tmp[dim1]; 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 x = vertical ? i : j;
uint16_t y = vertical ? j : i; uint16_t y = vertical ? j : i;
int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow 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; b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
tmp[j] = CRGB(r,g,b); 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 x = vertical ? i : j;
uint16_t y = vertical ? j : i; uint16_t y = vertical ? j : i;
setPixelColorXY(x, y, tmp[j]); 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) { void Segment::blur1d(fract8 blur_amount) {
const uint16_t rows = virtualHeight(); 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) { 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) { 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
// Bresenhams Algorithm // Bresenhams Algorithm
int d = 3 - (2*radius); int d = 3 - (2*radius);
int y = radius, x = 0; 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 // 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) { 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 cols = virtualWidth();
const uint16_t rows = virtualHeight(); const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) { for (int16_t y = -radius; y <= radius; y++) {
@ -540,7 +501,7 @@ void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight(); 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)); setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
} }
} }

View File

@ -89,25 +89,19 @@ bool Segment::_modeBlend = false;
Segment::Segment(const Segment &orig) { Segment::Segment(const Segment &orig) {
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition _t = nullptr; // copied segment cannot be in transition
name = nullptr; if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); } else { name = nullptr; }
data = nullptr; if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); } else { data = nullptr; _dataLen = 0; }
_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); }
} }
// move constructor // move constructor
Segment::Segment(Segment &&orig) noexcept { Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr; // old segment cannot be in transition any more
} }
// copy assignment // copy assignment
@ -115,27 +109,23 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
// clean destination // clean destination
transitional = false; // copied segment cannot be in transition if (name) { delete[] name; name = nullptr; }
if (name) delete[] name; if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (_t) { if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT); if (_t->_segT._dataT) free(_t->_segT._dataT);
#endif #endif
delete _t; delete _t;
_t = nullptr; // copied segment cannot be in transition
} }
deallocateData(); deallocateData();
// copy source // copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data // erase pointers to allocated data
name = nullptr;
data = nullptr; data = nullptr;
_dataLen = 0; _dataLen = 0;
_t = nullptr;
// copy source data // 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.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; return *this;
} }
@ -144,9 +134,7 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept { Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this); //DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
if (this != &orig) { if (this != &orig) {
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) { if (_t) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT) free(_t->_segT._dataT); if (_t->_segT._dataT) free(_t->_segT._dataT);
@ -154,12 +142,12 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
delete _t; delete _t;
_t = nullptr; _t = nullptr;
} }
deallocateData(); // free old runtime data
memcpy((void*)this, (void*)&orig, sizeof(Segment)); memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr; orig.name = nullptr;
orig.data = nullptr; orig.data = nullptr;
orig._dataLen = 0; orig._dataLen = 0;
orig._t = nullptr; orig._t = nullptr; // old segment cannot be in transition
} }
return *this; return *this;
} }
@ -237,7 +225,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
switch (pal) { switch (pal) {
case 0: //default palette. Exceptions for specific effects above case 0: //default palette. Exceptions for specific effects above
targetPalette = PartyColors_p; break; 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; unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) { if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
_randomPalette = _newRandomPalette; _randomPalette = _newRandomPalette;
@ -301,47 +289,45 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
} }
void Segment::startTransition(uint16_t dur) { void Segment::startTransition(uint16_t dur) {
if (!dur) { if (dur == 0) {
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton() if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton()
else transitional = false;
return; 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 // starting a transition has to occur before change so we get current values 1st
_t = new Transition(dur); // no previous transition running _t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this); //DEBUG_PRINTF("-- Started transition: %p\n", this);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette); loadPalette(_t->_palT, palette);
_t->_palT = _palT;
_t->_briT = on ? opacity : 0; _t->_briT = on ? opacity : 0;
_t->_cctT = cct; _t->_cctT = cct;
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
swapSegenv(_t->_segT); if (modeBlending) {
_t->_modeT = mode; swapSegenv(_t->_segT);
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional _t->_modeT = mode;
_t->_segT._dataLenT = 0; _t->_segT._dataLenT = 0;
_t->_segT._dataT = nullptr; _t->_segT._dataT = nullptr;
if (_dataLen > 0 && data) { if (_dataLen > 0 && data) {
_t->_segT._dataT = (byte *)malloc(_dataLen); _t->_segT._dataT = (byte *)malloc(_dataLen);
if (_t->_segT._dataT) { if (_t->_segT._dataT) {
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT); //DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
memcpy(_t->_segT._dataT, data, _dataLen); memcpy(_t->_segT._dataT, data, _dataLen);
_t->_segT._dataLenT = _dataLen; _t->_segT._dataLenT = _dataLen;
}
} }
} else {
for (size_t i=0; i<NUM_COLORS; i++) _t->_segT._colorT[i] = colors[i];
} }
#else #else
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i]; for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
#endif #endif
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
} }
void Segment::stopTransition() { void Segment::stopTransition() {
if (!transitional) return;
transitional = false; // finish transitioning segment
//DEBUG_PRINTF("-- Stopping transition: %p\n", this); //DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (_t) { if (isInTransition()) {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) { if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT); //DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
@ -356,14 +342,13 @@ void Segment::stopTransition() {
} }
void Segment::handleTransition() { void Segment::handleTransition() {
if (!transitional) return;
uint16_t _progress = progress(); uint16_t _progress = progress();
if (_progress == 0xFFFFU) stopTransition(); if (_progress == 0xFFFFU) stopTransition();
} }
// transition progression between 0-65535 // transition progression between 0-65535
uint16_t Segment::progress() { uint16_t Segment::progress() {
if (transitional && _t) { if (isInTransition()) {
unsigned long timeNow = millis(); unsigned long timeNow = millis();
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur; 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._stepT = step;
_t->_segT._callT = call; _t->_segT._callT = call;
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data); //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._dataT = data;
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!) _t->_segT._dataLenT = _dataLen;
} }
options = tmpSeg._optionsT; options = tmpSeg._optionsT;
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i]; for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
@ -443,39 +428,40 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
} }
#endif #endif
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) { uint8_t Segment::currentBri(bool useCct) {
uint32_t prog = progress(); uint32_t prog = progress();
if (prog < 0xFFFFU) { if (prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16; uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16; 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 #ifndef WLED_DISABLE_MODE_BLEND
uint16_t prog = progress(); // implicit check for transitional & _t in progress() uint16_t prog = progress();
if (prog < 0xFFFFU) return _t->_modeT; if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
#endif #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 #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 #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 #endif
} }
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) { CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal); loadPalette(targetPalette, pal);
if (progress() < 0xFFFFU) { uint16_t prog = progress();
if (strip.paletteFade && prog < 0xFFFFU) {
// blend palettes // blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time) // 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 // minimum blend time is 100ms maximum is 65535ms
unsigned long timeMS = millis() - _t->_start; uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48); for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette 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)) && (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return; && (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 (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0 if (grp) { // prevent assignment of 0
grouping = grp; 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; 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(); markForReset();
if (boundsUnchanged) return; if (boundsUnchanged) return;
@ -564,7 +556,6 @@ void Segment::setCCT(uint16_t k) {
void Segment::setOpacity(uint8_t o) { void Segment::setOpacity(uint8_t o) {
if (opacity == o) return; if (opacity == o) return;
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
opacity = o; opacity = o;
stateChanged = true; // send UDP/WS broadcast 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 (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
if (val) options |= 0x01 << n; if (val) options |= 0x01 << n;
else 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) { void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved // if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) { if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
if (fx != mode) { 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; mode = fx;
// load default values from effect string // load default values from effect string
if (loadDefaults) { if (loadDefaults) {
@ -743,7 +736,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
#endif #endif
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0); uint8_t _bri_t = currentBri();
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(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 (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) 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] //bit pattern: (msb first)
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT; // 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; 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; return d;
} }
@ -912,7 +906,7 @@ void Segment::refreshLightCapabilities() {
segStopIdx = stop; segStopIdx = stop;
} }
for (uint8_t b = 0; b < busses.getNumBusses(); b++) { for (unsigned b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break; if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue; if (!bus->isOk()) continue;
@ -942,7 +936,7 @@ void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength(); const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D 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); if (is2D()) setPixelColorXY(x, y, c);
else setPixelColor(x, 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. // Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) { void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
uint32_t col = getPixelColor(n); setPixelColor(n, color_add(getPixelColor(n), color, fast));
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);
} }
void Segment::fadePixelColor(uint16_t n, uint8_t fade) { void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade); setPixelColor(n, color_fade(getPixelColor(n), fade, true));
setPixelColor(n, pix);
} }
/* /*
@ -996,7 +975,7 @@ void Segment::fade_out(uint8_t rate) {
int g2 = G(color); int g2 = G(color);
int b2 = B(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); color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
int w1 = W(color); int w1 = W(color);
int r1 = R(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 cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D 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, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy)); if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(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 #ifndef WLED_DISABLE_2D
if (is2D()) { if (is2D()) {
// compatibility with 2D // compatibility with 2D
const uint_fast16_t cols = virtualWidth(); const unsigned cols = virtualWidth();
const uint_fast16_t rows = virtualHeight(); const unsigned rows = virtualHeight();
for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows for (unsigned 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 for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
return; return;
} }
#endif #endif
uint8_t keep = 255 - blur_amount; uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; uint32_t carryover = BLACK;
uint_fast16_t vlength = virtualLength(); unsigned vlength = virtualLength();
for(uint_fast16_t i = 0; i < vlength; i++) for (unsigned i = 0; i < vlength; i++) {
{ uint32_t cur = getPixelColor(i);
CRGB cur = CRGB(getPixelColor(i)); uint32_t part = color_fade(cur, seep);
CRGB part = cur; cur = color_add(color_fade(cur, keep), carryover, true);
CRGB before = cur; // remember color before blur if (i > 0) {
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if(i > 0) {
uint32_t c = getPixelColor(i-1); uint32_t c = getPixelColor(i-1);
uint8_t r = R(c); setPixelColor(i-1, color_add(c, part, true));
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));
} }
if (before != cur) // optimization: only set pixel if color has changed setPixelColor(i, cur);
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
carryover = part; carryover = part;
} }
} }
@ -1080,7 +1051,7 @@ void Segment::blur(uint8_t blur_amount)
uint32_t Segment::color_wheel(uint8_t pos) { uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0); if (palette) return color_from_palette(pos, false, true, 0);
pos = 255 - pos; pos = 255 - pos;
if(pos < 85) { if (pos < 85) {
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3);
} else if(pos < 170) { } else if(pos < 170) {
pos -= 85; 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. * 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. * @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 // default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
uint32_t color = currentColor(mcol, colors[mcol]); uint32_t color = currentColor(mcol);
color = gamma32(color); color = gamma32(color);
if (pbri == 255) return 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; uint8_t paletteIndex = i;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
CRGBPalette16 curPal; CRGBPalette16 curPal;
if (transitional && _t) curPal = _t->_palT; curPal = currentPalette(curPal, palette);
else loadPalette(curPal, palette); //if (isInTransition()) curPal = _t->_palT;
fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global //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); 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 defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
uint16_t prevLen = 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]}; uint8_t defPin[] = {defDataPins[i]};
uint16_t start = prevLen; uint16_t start = prevLen;
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
@ -1177,7 +1133,7 @@ void WS2812FX::finalizeInit(void)
} }
_length = 0; _length = 0;
for (uint8_t i=0; i<busses.getNumBusses(); i++) { for (int i=0; i<busses.getNumBusses(); i++) {
Bus *bus = busses.getBus(i); Bus *bus = busses.getBus(i);
if (bus == nullptr) continue; if (bus == nullptr) continue;
if (bus->getStart() + bus->getLength() > MAX_LEDS) break; if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
@ -1234,24 +1190,24 @@ void WS2812FX::service() {
if (!seg.freeze) { //only run effect function if not frozen if (!seg.freeze) { //only run effect function if not frozen
_virtualSegmentLength = seg.virtualLength(); _virtualSegmentLength = seg.virtualLength();
_colors_t[0] = seg.currentColor(0, seg.colors[0]); _colors_t[0] = seg.currentColor(0);
_colors_t[1] = seg.currentColor(1, seg.colors[1]); _colors_t[1] = seg.currentColor(1);
_colors_t[2] = seg.currentColor(2, seg.colors[2]); _colors_t[2] = seg.currentColor(2);
seg.currentPalette(_currentPalette, seg.palette); seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB); if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]); for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
// Effect blending // Effect blending
// When two effects are being blended, each may have different segment data, this // 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 // 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 // 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. // 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 delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (seg.mode != tmpMode) { if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData; Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
@ -1262,7 +1218,7 @@ void WS2812FX::service() {
} }
#endif #endif
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++; 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; 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) // or async show has a separate buffer (ESP32 RMT and I2S are ok)
if (newBri < _brightness) busses.setBrightness(_brightness); if (newBri < _brightness) busses.setBrightness(_brightness);
unsigned long now = millis(); unsigned long showNow = millis();
size_t diff = now - _lastShow; size_t diff = showNow - _lastShow;
size_t fpsCurr = 200; size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff; if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5) _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; _qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset; _qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
_queuedChangesSegId = segId; _queuedChangesSegId = segId;
DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
return; // queued changes are applied immediately after effect function returns return; // queued changes are applied immediately after effect function returns
} }
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY); _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() { void WS2812FX::setUpSegmentFromQueuedChanges() {
@ -1729,7 +1687,7 @@ void WS2812FX::fixInvalidSegments() {
bool WS2812FX::checkSegmentAlignment() { bool WS2812FX::checkSegmentAlignment() {
bool aligned = false; bool aligned = false;
for (segment &seg : _segments) { for (segment &seg : _segments) {
for (uint8_t b = 0; b<busses.getNumBusses(); b++) { for (unsigned b = 0; b<busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b); Bus *bus = busses.getBus(b);
if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; if (seg.start == bus->getStart() && 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) { void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
if (i2 >= i) if (i2 < i) std::swap(i,i2);
{ for (unsigned x = i; x <= i2; x++) setPixelColor(x, col);
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
} else
{
for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
}
} }
void WS2812FX::setTransitionMode(bool t) { void WS2812FX::setTransitionMode(bool t) {
@ -1793,7 +1746,7 @@ void WS2812FX::loadCustomPalettes() {
if (readObjectFromFile(fileName, nullptr, &pDoc)) { if (readObjectFromFile(fileName, nullptr, &pDoc)) {
JsonArray pal = pDoc[F("palette")]; 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<int>() && pal[1].is<const char *>()) { if (pal[0].is<int>() && pal[1].is<const char *>()) {
// we have an array of index & hex strings // we have an array of index & hex strings
size_t palSize = MIN(pal.size(), 36); size_t palSize = MIN(pal.size(), 36);
@ -1802,7 +1755,7 @@ void WS2812FX::loadCustomPalettes() {
uint8_t rgbw[] = {0,0,0,0}; uint8_t rgbw[] = {0,0,0,0};
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires colorFromHexString(rgbw, pal[i+1].as<const char *>()); // 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])); 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 { } else {
@ -1810,13 +1763,15 @@ void WS2812FX::loadCustomPalettes() {
palSize -= palSize % 4; // make sure size is multiple of 4 palSize -= palSize % 4; // make sure size is multiple of 4
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) { for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
tcp[i+1] = (uint8_t) pal[i+1].as<int>(); // R tcp[i+1] = gamma8((uint8_t) pal[i+1].as<int>()); // R
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G tcp[i+2] = gamma8((uint8_t) pal[i+2].as<int>()); // G
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B tcp[i+3] = gamma8((uint8_t) pal[i+3].as<int>()); // 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])); 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)); customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
} else {
DEBUG_PRINTLN(F("Wrong palette format."));
} }
} }
} else { } else {
@ -1832,7 +1787,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
char fileName[32]; char fileName[32];
strcpy_P(fileName, PSTR("/ledmap")); strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n); if (n) sprintf(fileName +7, "%d", n);
strcat(fileName, ".json"); strcat_P(fileName, PSTR(".json"));
bool isFile = WLED_FS.exists(fileName); bool isFile = WLED_FS.exists(fileName);
if (!isFile) { if (!isFile) {
@ -1866,7 +1821,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
if (!map.isNull() && map.size()) { // not an empty map if (!map.isNull() && map.size()) { // not an empty map
customMappingSize = map.size(); customMappingSize = map.size();
customMappingTable = new uint16_t[customMappingSize]; customMappingTable = new uint16_t[customMappingSize];
for (uint16_t i=0; i<customMappingSize; i++) { for (unsigned i=0; i<customMappingSize; i++) {
customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]); customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
} }
} }

View File

@ -216,7 +216,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0)) if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0))
{ {
// not an ADC analog pin // not an ADC analog pin
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s); DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]);
DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s);
DEBUG_PRINTLN(F(" is not an analog pin!"));
btnPin[s] = -1; btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button); pinManager.deallocatePin(pin,PinOwner::Button);
} }
@ -357,6 +359,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject light_tr = light["tr"]; JsonObject light_tr = light["tr"];
CJSON(fadeTransition, light_tr["mode"]); CJSON(fadeTransition, light_tr["mode"]);
CJSON(modeBlending, light_tr["fx"]);
int tdd = light_tr["dur"] | -1; int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
CJSON(strip.paletteFade, light_tr["pal"]); CJSON(strip.paletteFade, light_tr["pal"]);
@ -827,6 +830,7 @@ void serializeConfig() {
JsonObject light_tr = light.createNestedObject("tr"); JsonObject light_tr = light.createNestedObject("tr");
light_tr["mode"] = fadeTransition; light_tr["mode"] = fadeTransition;
light_tr["fx"] = modeBlending;
light_tr["dur"] = transitionDelayDefault / 100; light_tr["dur"] = transitionDelayDefault / 100;
light_tr["pal"] = strip.paletteFade; light_tr["pal"] = strip.paletteFade;
light_tr[F("rpc")] = randomPaletteChangeTime; light_tr[F("rpc")] = randomPaletteChangeTime;
@ -888,6 +892,7 @@ void serializeConfig() {
if_live[F("no-gc")] = arlsDisableGammaCorrection; if_live[F("no-gc")] = arlsDisableGammaCorrection;
if_live[F("offset")] = arlsOffset; if_live[F("offset")] = arlsOffset;
#ifndef WLED_DISABLE_ALEXA
JsonObject if_va = interfaces.createNestedObject("va"); JsonObject if_va = interfaces.createNestedObject("va");
if_va[F("alexa")] = alexaEnabled; if_va[F("alexa")] = alexaEnabled;
@ -896,6 +901,7 @@ void serializeConfig() {
if_va_macros.add(macroAlexaOff); if_va_macros.add(macroAlexaOff);
if_va["p"] = alexaNumPresets; if_va["p"] = alexaNumPresets;
#endif
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
@ -1033,7 +1039,7 @@ bool deserializeConfigSec() {
JsonObject ap = doc["ap"]; JsonObject ap = doc["ap"];
getStringFromJson(apPass, ap["psk"] , 65); getStringFromJson(apPass, ap["psk"] , 65);
JsonObject interfaces = doc["if"]; [[maybe_unused]] JsonObject interfaces = doc["if"];
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces["mqtt"]; JsonObject if_mqtt = interfaces["mqtt"];
@ -1072,7 +1078,7 @@ void serializeConfigSec() {
JsonObject ap = doc.createNestedObject("ap"); JsonObject ap = doc.createNestedObject("ap");
ap["psk"] = apPass; ap["psk"] = apPass;
JsonObject interfaces = doc.createNestedObject("if"); [[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if");
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
JsonObject if_mqtt = interfaces.createNestedObject("mqtt"); JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
if_mqtt["psk"] = mqttPass; if_mqtt["psk"] = mqttPass;

View File

@ -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 * color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule * 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); if (fast) {
uint32_t g = G(c1) + G(c2); uint8_t r = R(c1);
uint32_t b = B(c1) + B(c2); uint8_t g = G(c1);
uint32_t w = W(c1) + W(c2); uint8_t b = B(c1);
uint16_t max = r; uint8_t w = W(c1);
if (g > max) max = g; r = qadd8(r, R(c2));
if (b > max) max = b; g = qadd8(g, G(c2));
if (w > max) max = w; b = qadd8(b, B(c2));
if (max < 256) return RGBW32(r, g, b, w); w = qadd8(w, W(c2));
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); 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) void setRandomColor(byte* rgb)
{ {
lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex); lastRandomIndex = get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb); colorHStoRGB(lastRandomIndex*256,255,rgb);
} }

View File

@ -166,7 +166,7 @@
#define CALL_MODE_NO_NOTIFY 5 #define CALL_MODE_NO_NOTIFY 5
#define CALL_MODE_FX_CHANGED 6 //no longer used #define CALL_MODE_FX_CHANGED 6 //no longer used
#define CALL_MODE_HUE 7 #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_BLYNK 9 //no longer used
#define CALL_MODE_ALEXA 10 #define CALL_MODE_ALEXA 10
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only #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_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_FREEZE 4 //Segment contents will not be refreshed
#define SEG_OPTION_RESET 5 //Segment runtime requires reset #define SEG_OPTION_RESET 5 //Segment runtime requires reset
#define SEG_OPTION_TRANSITIONAL 6 #define SEG_OPTION_REVERSED_Y 6
#define SEG_OPTION_REVERSED_Y 7 #define SEG_OPTION_MIRROR_Y 7
#define SEG_OPTION_MIRROR_Y 8 #define SEG_OPTION_TRANSPOSED 8
#define SEG_OPTION_TRANSPOSED 9
//Segment differs return byte //Segment differs return byte
#define SEG_DIFFERS_BRI 0x01 // opacity #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_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_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_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_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_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) #define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)

View File

@ -773,6 +773,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> % Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
<h3>Transitions</h3> <h3>Transitions</h3>
Crossfade: <input type="checkbox" name="TF"><br> Crossfade: <input type="checkbox" name="TF"><br>
Effect blending: <input type="checkbox" name="EB"><br>
Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br> Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br>
Enable Palette transitions: <input type="checkbox" name="PF"><br> Enable Palette transitions: <input type="checkbox" name="PF"><br>
<i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br> <i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br>

View File

@ -63,7 +63,8 @@ class NeoGammaWLEDMethod {
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c) #define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) #define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); 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]))); } 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 colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* 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); uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId); um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps(); void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
#ifdef WLED_ADD_EEPROM_SUPPORT #ifdef WLED_ADD_EEPROM_SUPPORT
//wled_eeprom.cpp //wled_eeprom.cpp

View File

@ -247,6 +247,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
fadeTransition = request->hasArg(F("TF")); fadeTransition = request->hasArg(F("TF"));
modeBlending = request->hasArg(F("EB"));
t = request->arg(F("TD")).toInt(); t = request->arg(F("TD")).toInt();
if (t >= 0) transitionDelayDefault = t; if (t >= 0) transitionDelayDefault = t;
strip.paletteFade = request->hasArg(F("PF")); strip.paletteFade = request->hasArg(F("PF"));

View File

@ -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) // (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
typedef enum UM_SoundSimulations { typedef enum UM_SoundSimulations {
UMS_BeatSin = 0, UMS_BeatSin = 0,
UMS_WeWillRockYou UMS_WeWillRockYou,
//UMS_10_13, UMS_10_13,
//UMS_14_3 UMS_14_3
} um_soundSimulations_t; } um_soundSimulations_t;
um_data_t* simulateSound(uint8_t simulationId) um_data_t* simulateSound(uint8_t simulationId)
@ -503,7 +503,7 @@ um_data_t* simulateSound(uint8_t simulationId)
fftResult[i] = 0; fftResult[i] = 0;
} }
break; break;
/*case UMS_10_3: case UMS_10_13:
for (int i = 0; i<16; i++) for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3); fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8]; volumeSmth = fftResult[8];
@ -512,7 +512,7 @@ um_data_t* simulateSound(uint8_t simulationId)
for (int i = 0; i<16; i++) for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3); fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8]; volumeSmth = fftResult[8];
break;*/ break;
} }
samplePeak = random8() > 250; 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;
}

View File

@ -351,6 +351,7 @@ WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlig
WLED_GLOBAL byte nightlightDelayMins _INIT(60); WLED_GLOBAL byte nightlightDelayMins _INIT(60);
WLED_GLOBAL byte nightlightMode _INIT(NL_MODE_FADE); // See const.h for available modes. Was nightlightFade 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 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 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) 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)

View File

@ -441,6 +441,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',SET_F("GC"),gammaCorrectCol); sappend('c',SET_F("GC"),gammaCorrectCol);
dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS); dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS);
sappend('c',SET_F("TF"),fadeTransition); sappend('c',SET_F("TF"),fadeTransition);
sappend('c',SET_F("EB"),modeBlending);
sappend('v',SET_F("TD"),transitionDelayDefault); sappend('v',SET_F("TD"),transitionDelayDefault);
sappend('c',SET_F("PF"),strip.paletteFade); sappend('c',SET_F("PF"),strip.paletteFade);
sappend('v',SET_F("TP"),randomPaletteChangeTime); sappend('v',SET_F("TP"),randomPaletteChangeTime);
@ -478,7 +479,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == SUBPAGE_SYNC) if (subPage == SUBPAGE_SYNC)
{ {
char nS[32]; [[maybe_unused]] char nS[32];
sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2); sappend('v',SET_F("U2"),udpPort2);
sappend('v',SET_F("GS"),syncGroups); sappend('v',SET_F("GS"),syncGroups);