Refactoring WS2812FX class.

- effect functions no longer part of class
- methods to access private members
- separate Segment, Segment_runtime, ColorTransition from WS2812FX
This commit is contained in:
Blaz Kristan 2022-07-06 13:13:54 +02:00
parent febd7cbca8
commit 64fd207533
22 changed files with 1696 additions and 1988 deletions

View File

@ -103,7 +103,7 @@ class Animated_Staircase : public Usermod {
void updateSegments() {
mainSegmentId = strip.getMainSegmentId();
WS2812FX::Segment* segments = strip.getSegments();
Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;
@ -290,7 +290,7 @@ class Animated_Staircase : public Usermod {
}
} else {
// Restore segment options
WS2812FX::Segment* segments = strip.getSegments();
Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
maxSegmentId = i - 1;

View File

@ -355,7 +355,7 @@ public:
// Color in grayscale bitmaps if Segment 1 exists
// TODO If secondary and tertiary are black, color all in primary,
// else color first three from Seg 1 color slots and last three from Seg 2 color slots
WS2812FX::Segment& seg1 = strip.getSegment(tubeSegment);
Segment& seg1 = strip.getSegment(tubeSegment);
if (seg1.isActive()) {
digitColor = strip.getPixelColor(seg1.start + digit);
dimming = seg1.opacity;

View File

@ -63,7 +63,7 @@ class ElekstubeIPSUsermod : public Usermod {
if (!toki.isTick()) return;
updateLocalTime();
WS2812FX::Segment& seg1 = strip.getSegment(tfts.tubeSegment);
Segment& seg1 = strip.getSegment(tfts.tubeSegment);
if (seg1.isActive()) {
bool update = false;
if (seg1.opacity != lastBri) update = true;

View File

@ -96,7 +96,7 @@ class StairwayWipeUsermod : public Usermod {
resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
WS2812FX::Segment& seg = strip.getSegment(0);
Segment& seg = strip.getSegment(0);
bool doReverse = (userVar0 == 2);
seg.setOption(1, doReverse);

View File

@ -89,7 +89,7 @@ void startWipe()
resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
WS2812FX::Segment& seg = strip.getSegment(0);
Segment& seg = strip.getSegment(0);
bool doReverse = (userVar0 == 2);
seg.setOption(1, doReverse);

View File

@ -528,12 +528,12 @@ public:
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
strip.setMode(i, effectCurrent);
}
} else {
//WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
//Segment& seg = strip.getSegment(strip.getMainSegmentId());
strip.setMode(strip.getMainSegmentId(), effectCurrent);
}
lampUdated();
@ -556,12 +556,12 @@ public:
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.speed = effectSpeed;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.speed = effectSpeed;
}
lampUdated();
@ -584,12 +584,12 @@ public:
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.intensity = effectIntensity;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.intensity = effectIntensity;
}
lampUdated();
@ -618,7 +618,7 @@ public:
default: val = strip.getSegment(id).custom1 = max(min((increase ? strip.getSegment(id).custom1+fadeAmount : strip.getSegment(id).custom1-fadeAmount), 255), 0); break;
}
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || i == id) continue;
switch (par) {
case 3: strip.getSegment(i).custom3 = strip.getSegment(id).custom3; break;
@ -627,7 +627,7 @@ public:
}
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
switch (par) {
case 3: val = seg.custom3 = max(min((increase ? seg.custom3+fadeAmount : seg.custom3-fadeAmount), 255), 0); break;
case 2: val = seg.custom2 = max(min((increase ? seg.custom2+fadeAmount : seg.custom2-fadeAmount), 255), 0); break;
@ -657,12 +657,12 @@ public:
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.palette = effectPalette;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.palette = effectPalette;
}
lampUdated();
@ -686,12 +686,12 @@ public:
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
@ -715,12 +715,12 @@ public:
colorHStoRGB(currentHue1*256, currentSat1, col);
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
@ -775,12 +775,12 @@ public:
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
// if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.setCCT(currentCCT, i);
}
// } else {
// WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
// Segment& seg = strip.getSegment(strip.getMainSegmentId());
// seg.setCCT(currentCCT, strip.getMainSegmentId());
// }
lampUdated();

View File

@ -27,14 +27,14 @@ saveMacro(1, "&FX=0&R=255&G=255&B=255", false);
//strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//select first two segments (background color + FX settable)
WS2812FX::Segment &seg = strip.getSegment(0);
Segment &seg = strip.getSegment(0);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF)));
strip.getSegment(0).setOption(0, false);
strip.getSegment(0).setOption(2, false);
//other segments are text
for (int i = 1; i < 10; i++)
{
WS2812FX::Segment &seg = strip.getSegment(i);
Segment &seg = strip.getSegment(i);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF)));
strip.getSegment(i).setOption(0, true);
strip.setBrightness(128);
@ -50,7 +50,7 @@ void selectWordSegments(bool state)
{
for (int i = 1; i < 10; i++)
{
//WS2812FX::Segment &seg = strip.getSegment(i);
//Segment &seg = strip.getSegment(i);
strip.getSegment(i).setOption(0, state);
// strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//seg.mode = 12;

File diff suppressed because it is too large Load Diff

View File

@ -54,10 +54,15 @@
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
#endif
//colors.cpp (.h does not like including other .h)
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16);
uint32_t color_add(uint32_t,uint32_t);
/* Not used in all effects yet */
#define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME _frametime
//#define FRAMETIME _frametime
#define FRAMETIME strip.getFrameTime()
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
@ -82,11 +87,14 @@
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT _segments[_segment_index]
#define SEGCOLOR(x) _colors_t[x]
#define SEGENV _segment_runtimes[_segment_index]
#define SEGLEN _virtualSegmentLength
#define SEGACT SEGMENT.stop
//#define SEGMENT _segments[_segment_index]
//#define SEGCOLOR(x) _colors_t[x]
//#define SEGENV _segment_runtimes[_segment_index]
//#define SEGLEN _virtualSegmentLength
#define SEGMENT strip.getSegment(strip.getCurrSegmentId())
#define SEGENV strip.getSegmentRuntime(strip.getCurrSegmentId())
#define SEGCOLOR(x) strip.segColor(x)
#define SEGLEN strip.segLen()
#define SPEED_FORMULA_L 5U + (50U*(255U - SEGMENT.speed))/SEGLEN
// some common colors
@ -110,15 +118,15 @@
// bit 2: segment is on
// bit 1: reverse segment
// bit 0: segment is selected
#define NO_OPTIONS (uint8_t)0x00
#define TRANSPOSED (uint8_t)0x400 // rotated 90deg & reversed
#define REVERSE_Y_2D (uint8_t)0x200
#define MIRROR_Y_2D (uint8_t)0x100
#define TRANSITIONAL (uint8_t)0x080
#define MIRROR (uint8_t)0x008
#define SEGMENT_ON (uint8_t)0x004
#define REVERSE (uint8_t)0x002
#define SELECTED (uint8_t)0x001
#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 MIRROR (uint16_t)0x0008
#define SEGMENT_ON (uint16_t)0x0004
#define REVERSE (uint16_t)0x0002
#define SELECTED (uint16_t)0x0001
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
@ -359,249 +367,98 @@
#endif
class WS2812FX {
typedef uint16_t (WS2812FX::*mode_ptr)(void);
// segment parameters
typedef struct Segment { // 35 (36 in memory) bytes
uint16_t start; // start index / start X coordinate 2D (left)
uint16_t stop; // stop index / stop X coordinate 2D (right); segment is invalid if stop == 0
uint16_t offset;
uint8_t speed;
uint8_t intensity;
uint8_t palette;
uint8_t mode;
uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
uint8_t cct; //0==1900K, 255==10091K
uint8_t _capabilities;
uint8_t custom1, custom2, custom3; // custom FX parameters
uint16_t startY; // start Y coodrinate 2D (top)
uint16_t stopY; // stop Y coordinate 2D (bottom)
char *name;
inline bool getOption(uint8_t n) { return ((options >> n) & 0x01); }
inline bool isSelected() { return getOption(0); }
inline bool isActive() { return stop > start; }
inline uint16_t width() { return stop - start; }
inline uint16_t height() { return stopY - startY; }
inline uint16_t length() { return width(); }
inline uint16_t groupLength() { return grouping + spacing; }
inline uint8_t getLightCapabilities() { return _capabilities; }
bool setColor(uint8_t slot, uint32_t c, uint8_t segn); //returns true if changed
void setCCT(uint16_t k, uint8_t segn);
void setOpacity(uint8_t o, uint8_t segn);
void setOption(uint8_t n, bool val, uint8_t segn = 255);
// 2D matrix
uint16_t virtualWidth();
uint16_t virtualHeight();
// 1D strip
uint16_t virtualLength();
uint8_t differs(Segment& b);
void refreshLightCapabilities();
} segment;
// pre show callback
typedef void (*show_callback) (void);
// segment runtime parameters
typedef struct Segment_runtime { // 28 bytes
unsigned long next_time; // millis() of next update
uint32_t step; // custom "step" var
uint32_t call; // call counter
uint16_t aux0; // custom var
uint16_t aux1; // custom var
byte* data = nullptr;
bool allocateData(uint16_t len);
void deallocateData();
void resetIfRequired();
/**
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests.
*/
inline void markForReset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
} segment_runtime;
// color transitions
typedef struct ColorTransition { // 12 bytes
uint32_t colorOld = 0;
uint32_t transitionStart;
uint16_t transitionDur;
uint8_t segment = 0xFF; //lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel
uint8_t briOld = 0;
static void startTransition(uint8_t oldBri, uint32_t oldCol, uint8_t segn, uint8_t slot);
uint8_t currentBri(bool turningOff = false, uint8_t slot = 0);
uint16_t progress(bool allowEnd = false); //transition progression between 0-65535
uint32_t currentColor(uint32_t colorNew) {
return color_blend(colorOld, colorNew, progress(true), true);
}
} color_transition;
// main "strip" class
class WS2812FX {
typedef uint16_t (*mode_ptr)(void); // pointer to mode function
typedef void (*show_callback)(void); // pre show callback
static WS2812FX* instance;
public:
// segment parameters
typedef struct Segment { // 35 (36 in memory) bytes
uint16_t start; // start index / start X coordinate 2D (left)
uint16_t stop; // stop index / stop X coordinate 2D (right); segment is invalid if stop == 0
uint16_t offset;
uint8_t speed;
uint8_t intensity;
uint8_t palette;
uint8_t mode;
uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
uint8_t cct; //0==1900K, 255==10091K
uint8_t _capabilities;
uint8_t custom1, custom2, custom3; // custom FX parameters
uint16_t startY; // start Y coodrinate 2D (top)
uint16_t stopY; // stop Y coordinate 2D (bottom)
char *name;
inline bool getOption(uint8_t n) { return ((options >> n) & 0x01); }
inline bool isSelected() { return getOption(0); }
inline bool isActive() { return stop > start; }
inline uint16_t width() { return stop - start; }
inline uint16_t height() { return stopY - startY; }
inline uint16_t length() { return width(); }
inline uint16_t groupLength() { return grouping + spacing; }
inline uint8_t getLightCapabilities() { return _capabilities; }
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false;
uint8_t b = (slot == 1) ? cct : opacity;
ColorTransition::startTransition(b, colors[slot], instance->_transitionDur, segn, slot);
colors[slot] = c; return true;
}
void setCCT(uint16_t k, uint8_t segn) {
if (segn >= MAX_NUM_SEGMENTS) return;
if (k > 255) { //kelvin value, convert to 0-255
if (k < 1900) k = 1900;
if (k > 10091) k = 10091;
k = (k - 1900) >> 5;
}
if (cct == k) return;
ColorTransition::startTransition(cct, colors[1], instance->_transitionDur, segn, 1);
cct = k;
}
void setOpacity(uint8_t o, uint8_t segn) {
if (segn >= MAX_NUM_SEGMENTS) return;
if (opacity == o) return;
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
opacity = o;
}
void setOption(uint8_t n, bool val, uint8_t segn = 255) {
bool prevOn = false;
if (n == SEG_OPTION_ON) {
prevOn = getOption(SEG_OPTION_ON);
if (!val && prevOn) { //fade off
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
}
}
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
if (n == SEG_OPTION_ON && val && !prevOn) { //fade on
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
}
}
// 2D matrix
uint16_t virtualWidth() {
uint16_t groupLen = groupLength();
uint16_t vWidth = ((getOption(SEG_OPTION_TRANSPOSED) ? height() : width()) + groupLen - 1) / groupLen;
if (getOption(SEG_OPTION_MIRROR)) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vWidth;
}
uint16_t virtualHeight() {
uint16_t groupLen = groupLength();
uint16_t vHeight = ((getOption(SEG_OPTION_TRANSPOSED) ? width() : height()) + groupLen - 1) / groupLen;
if (getOption(SEG_OPTION_MIRROR_Y)) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vHeight;
}
// 1D strip
uint16_t virtualLength() {
uint16_t groupLen = groupLength();
uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (getOption(SEG_OPTION_MIRROR)) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength;
}
uint8_t differs(Segment& b);
void refreshLightCapabilities();
} segment;
// segment runtime parameters
typedef struct Segment_runtime { // 28 bytes
unsigned long next_time; // millis() of next update
uint32_t step; // custom "step" var
uint32_t call; // call counter
uint16_t aux0; // custom var
uint16_t aux1; // custom var
byte* data = nullptr;
bool allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated
deallocateData();
if (WS2812FX::instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
// if possible use SPI RAM on ESP32
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
data = (byte*) ps_malloc(len);
else
#endif
data = (byte*) malloc(len);
if (!data) return false; //allocation failed
WS2812FX::instance->_usedSegmentData += len;
_dataLen = len;
memset(data, 0, len);
return true;
}
void deallocateData(){
free(data);
data = nullptr;
WS2812FX::instance->_usedSegmentData -= _dataLen;
_dataLen = 0;
}
/**
* If reset of this segment was request, clears runtime
* settings of this segment.
* Must not be called while an effect mode function is running
* because it could access the data buffer and this method
* may free that data buffer.
*/
void resetIfRequired() {
if (_requiresReset) {
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
deallocateData();
_requiresReset = false;
}
}
/**
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests.
*/
inline void markForReset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
} segment_runtime;
typedef struct ColorTransition { // 12 bytes
uint32_t colorOld = 0;
uint32_t transitionStart;
uint16_t transitionDur;
uint8_t segment = 0xFF; //lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel
uint8_t briOld = 0;
static void startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) {
if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0) return;
if (instance->_brightness == 0) return; //do not need transitions if master bri is off
if (!instance->_segments[segn].getOption(SEG_OPTION_ON)) return; //not if segment is off either
uint8_t tIndex = 0xFF; //none found
uint16_t tProgression = 0;
uint8_t s = segn + (slot << 6); //merge slot and segment into one byte
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
uint8_t tSeg = instance->transitions[i].segment;
//see if this segment + color already has a running transition
if (tSeg == s) {
tIndex = i; break;
}
if (tSeg == 0xFF) { //free transition
tIndex = i; tProgression = 0xFFFF;
}
}
if (tIndex == 0xFF) { //no slot found yet
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
//find most progressed transition to overwrite
uint16_t prog = instance->transitions[i].progress();
if (prog > tProgression) {
tIndex = i; tProgression = prog;
}
}
}
ColorTransition& t = instance->transitions[tIndex];
if (t.segment == s) //this is an active transition on the same segment+color
{
bool wasTurningOff = (oldBri == 0);
t.briOld = t.currentBri(wasTurningOff, slot);
t.colorOld = t.currentColor(oldCol);
} else {
t.briOld = oldBri;
t.colorOld = oldCol;
uint8_t prevSeg = t.segment & 0x3F;
if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments[prevSeg].setOption(SEG_OPTION_TRANSITIONAL, false);
}
t.transitionDur = dur;
t.transitionStart = millis();
t.segment = s;
instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, true);
//refresh immediately, required for Solid mode
if (instance->_segment_runtimes[segn].next_time > t.transitionStart + 22) instance->_segment_runtimes[segn].next_time = t.transitionStart;
}
uint16_t progress(bool allowEnd = false) { //transition progression between 0-65535
uint32_t timeNow = millis();
if (timeNow - transitionStart > transitionDur) {
if (allowEnd) {
uint8_t segn = segment & 0x3F;
if (segn < MAX_NUM_SEGMENTS) instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, false);
segment = 0xFF;
}
return 0xFFFF;
}
uint32_t elapsed = timeNow - transitionStart;
uint32_t prog = elapsed * 0xFFFF / transitionDur;
return (prog > 0xFFFF) ? 0xFFFF : prog;
}
uint32_t currentColor(uint32_t colorNew) {
return instance->color_blend(colorOld, colorNew, progress(true), true);
}
uint8_t currentBri(bool turningOff = false, uint8_t slot = 0) {
uint8_t segn = segment & 0x3F;
if (segn >= MAX_NUM_SEGMENTS) return 0;
uint8_t briNew = instance->_segments[segn].opacity;
if (slot == 0) {
if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0;
} else { //transition slot 1 brightness for CCT transition
briNew = instance->_segments[segn].cct;
}
uint32_t prog = progress() + 1;
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
}
} color_transition;
WS2812FX() {
WS2812FX::instance = this;
setupEffectData();
@ -614,6 +471,8 @@ class WS2812FX {
resetSegments();
}
static WS2812FX* getInstance(void) { return instance; }
void
finalizeInit(),
service(void),
@ -627,11 +486,8 @@ class WS2812FX {
setCCT(uint16_t k),
setBrightness(uint8_t b, bool direct = false),
setRange(uint16_t i, uint16_t i2, uint32_t col),
setShowCallback(show_callback cb),
setTransition(uint16_t t),
setTransitionMode(bool t),
calcGammaTable(float),
trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=0),
setMainSegmentId(uint8_t n),
restartRuntime(),
@ -640,6 +496,7 @@ class WS2812FX {
fixInvalidSegments(),
setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = false),
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend),
show(void),
setTargetFps(uint8_t fps),
deserializeMap(uint8_t n=0);
@ -652,6 +509,9 @@ class WS2812FX {
inline void setPixelColor(int n, CRGB c) {setPixelColor(n, c.red, c.green, c.blue);}
inline void setPixelColor(float i, uint32_t c, bool aa=true) {setPixelColor(i, byte(c>>16), byte(c>>8), byte(c), byte(c>>24), aa);}
inline void setPixelColor(float i, CRGB c, bool aa=true) {setPixelColor(i, c.red, c.green, c.blue, 0, aa);}
inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments.
inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; }
bool
gammaCorrectBri = false,
@ -662,24 +522,28 @@ class WS2812FX {
// return true if the strip is being sent pixel updates
isUpdating(void);
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;}
uint8_t
paletteFade = 0,
paletteBlend = 0,
milliampsPerLed = 55,
cctBlending = 0,
getBrightness(void),
getPaletteCount(void),
getMaxSegments(void),
getActiveSegmentsNum(void),
getFirstSelectedSegId(void),
getMainSegmentId(void),
getLastActiveSegmentId(void),
getTargetFps(void),
setPixelSegment(uint8_t n),
gamma8(uint8_t),
gamma8_cal(uint8_t, float),
get_random_wheel_index(uint8_t);
inline uint8_t getBrightness(void) { return _brightness; }
inline uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; }
inline uint8_t getCurrSegmentId(void) { return _segment_index; }
inline uint8_t getMainSegmentId(void) { return _mainSegment; }
inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; }
inline uint8_t getTargetFps() { return _targetFps; }
inline uint8_t getModeCount() { return _modeCount; }
inline uint8_t sin_gap(uint16_t in) {
if (in & 0x100) return 0;
@ -693,165 +557,39 @@ class WS2812FX {
ablMilliampsMax,
currentMilliamps,
triwave16(uint16_t),
getLengthTotal(void),
getLengthPhysical(void),
getFps();
inline uint16_t getMinShowDelay() { return MIN_SHOW_DELAY; }
inline uint16_t getFrameTime(void) { return _frametime; }
inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; }
inline uint16_t getLengthTotal(void) { return _length; }
inline uint16_t segLen(void) { return _virtualSegmentLength; }
uint32_t
now,
timebase,
color_wheel(uint8_t),
color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255),
color_blend(uint32_t,uint32_t,uint16_t,bool b16=false),
color_add(uint32_t,uint32_t),
currentColor(uint32_t colorNew, uint8_t tNr),
gamma32(uint32_t),
getLastShow(void),
getPixelColor(uint16_t);
inline uint32_t getLastShow(void) { return _lastShow; }
inline uint32_t segColor(uint8_t i) { return _colors_t[i]; }
const char *
getModeData(uint8_t id = 0) { return id<_modeCount ? _modeData[id] : nullptr; }
const char **
getModeDataSrc(void) { return _modeData; }
WS2812FX::Segment
&getSegment(uint8_t n),
&getFirstSelectedSeg(void),
&getMainSegment(void);
inline Segment& getSegment(uint8_t id) { return _segments[id >= MAX_NUM_SEGMENTS ? getMainSegmentId() : id]; }
inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; }
inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; }
inline Segment* getSegments(void) { return _segments; }
inline Segment_runtime& getSegmentRuntime(uint8_t id) { return _segment_runtimes[id >= MAX_NUM_SEGMENTS ? getMainSegmentId() : id]; }
WS2812FX::Segment*
getSegments(void);
// builtin modes
uint16_t
mode_static(void),
mode_blink(void),
mode_blink_rainbow(void),
mode_strobe(void),
mode_strobe_rainbow(void),
mode_color_wipe(void),
mode_color_sweep(void),
mode_color_wipe_random(void),
mode_color_sweep_random(void),
mode_random_color(void),
mode_dynamic(void),
mode_breath(void),
mode_fade(void),
mode_scan(void),
mode_dual_scan(void),
mode_theater_chase(void),
mode_theater_chase_rainbow(void),
mode_rainbow(void),
mode_rainbow_cycle(void),
mode_running_lights(void),
mode_saw(void),
mode_twinkle(void),
mode_dissolve(void),
mode_dissolve_random(void),
mode_sparkle(void),
mode_flash_sparkle(void),
mode_hyper_sparkle(void),
mode_multi_strobe(void),
mode_android(void),
mode_chase_color(void),
mode_chase_random(void),
mode_chase_rainbow(void),
mode_chase_flash(void),
mode_chase_flash_random(void),
mode_chase_rainbow_white(void),
mode_colorful(void),
mode_traffic_light(void),
mode_running_color(void),
mode_aurora(void),
mode_running_random(void),
mode_larson_scanner(void),
mode_comet(void),
mode_fireworks(void),
mode_rain(void),
mode_tetrix(void),
mode_halloween(void),
mode_fire_flicker(void),
mode_gradient(void),
mode_loading(void),
mode_police(void),
mode_fairy(void),
mode_two_dots(void),
mode_fairytwinkle(void),
mode_running_dual(void),
mode_bicolor_chase(void),
mode_tricolor_chase(void),
mode_tricolor_wipe(void),
mode_tricolor_fade(void),
mode_lightning(void),
mode_icu(void),
mode_multi_comet(void),
mode_dual_larson_scanner(void),
mode_random_chase(void),
mode_oscillate(void),
mode_fire_2012(void),
mode_pride_2015(void),
mode_bpm(void),
mode_juggle(void),
mode_palette(void),
mode_colorwaves(void),
mode_fillnoise8(void),
mode_noise16_1(void),
mode_noise16_2(void),
mode_noise16_3(void),
mode_noise16_4(void),
mode_colortwinkle(void),
mode_lake(void),
mode_meteor(void),
mode_meteor_smooth(void),
mode_railway(void),
mode_ripple(void),
mode_twinklefox(void),
mode_twinklecat(void),
mode_halloween_eyes(void),
mode_static_pattern(void),
mode_tri_static_pattern(void),
mode_spots(void),
mode_spots_fade(void),
mode_glitter(void),
mode_candle(void),
mode_starburst(void),
mode_exploding_fireworks(void),
mode_bouncing_balls(void),
mode_sinelon(void),
mode_sinelon_dual(void),
mode_sinelon_rainbow(void),
mode_popcorn(void),
mode_drip(void),
mode_plasma(void),
mode_percent(void),
mode_ripple_rainbow(void),
mode_heartbeat(void),
mode_pacifica(void),
mode_candle_multi(void),
mode_solid_glitter(void),
mode_sunrise(void),
mode_phased(void),
mode_twinkleup(void),
mode_noisepal(void),
mode_sinewave(void),
mode_phased_noise(void),
mode_flow(void),
mode_chunchun(void),
mode_dancing_shadows(void),
mode_washing_machine(void),
mode_candy_cane(void),
mode_blends(void),
mode_tv_simulator(void),
mode_dynamic_smooth(void),
// non-audio transfered from WLED-SR
mode_perlinmove(void),
mode_wavesins(void),
mode_FlowStripe(void);
// 2D support (panels)
// 2D support (panels)
bool
isMatrix = false;
@ -915,119 +653,12 @@ class WS2812FX {
uint32_t
getPixelColorXY(uint16_t, uint16_t);
// end 2D support
// end 2D support
// 2D modes
uint16_t
mode_2Dspaceships(void),
mode_2Dcrazybees(void),
mode_2Dghostrider(void),
mode_2Dfloatingblobs(void),
mode_2Dscrollingtext(void),
mode_2Ddriftrose(void);
// WLED-SR modes
#ifndef USERMOD_AUDIOREACTIVE
uint16_t
mode_2Dnoise(void),
mode_2Dfirenoise(void),
mode_2Dsquaredswirl(void),
mode_2Ddna(void),
mode_2Dmatrix(void),
mode_2Dmetaballs(void),
mode_2DPulser(void),
mode_2Dgameoflife(void),
mode_2Dtartan(void),
mode_2DPolarLights(void),
mode_2DSwirl(void),
mode_2DLissajous(void),
mode_2DFrizzles(void),
mode_2DPlasmaball(void),
mode_2DHiphotic(void),
mode_2DSindots(void),
mode_2DDNASpiral(void),
mode_2DBlackHole(void),
mode_2DSunradiation(void),
mode_2DWaverly(void),
mode_2DDrift(void),
mode_2DColoredBursts(void),
mode_2DJulia(void),
mode_gravimeter(void),
mode_gravcenter(void),
mode_gravcentric(void),
mode_juggles(void),
mode_matripix(void),
mode_midnoise(void),
mode_noisemeter(void),
mode_noisefire(void),
mode_pixelwave(void),
mode_plasmoid(void),
mode_puddles(void),
mode_puddlepeak(void),
mode_ripplepeak(void),
mode_2DAkemi(void);
#else
uint16_t
mode_pixels(void),
mode_pixelwave(void),
mode_juggles(void),
mode_matripix(void),
mode_gravimeter(void),
mode_plasmoid(void),
mode_puddles(void),
mode_midnoise(void),
mode_noisemeter(void),
mode_freqwave(void),
mode_freqmatrix(void),
mode_2DGEQ(void),
mode_waterfall(void),
mode_freqpixels(void),
mode_binmap(void),
mode_noisefire(void),
mode_puddlepeak(void),
mode_noisemove(void),
mode_2Dnoise(void),
mode_ripplepeak(void),
mode_2Dfirenoise(void),
mode_2Dsquaredswirl(void),
mode_2Ddna(void),
mode_2Dmatrix(void),
mode_2Dmetaballs(void),
mode_freqmap(void),
mode_gravcenter(void),
mode_gravcentric(void),
mode_gravfreq(void),
mode_DJLight(void),
mode_2DFunkyPlank(void),
mode_2DPulser(void),
mode_blurz(void),
mode_2Dgameoflife(void),
mode_2Dtartan(void),
mode_2DPolarLights(void),
mode_2DSwirl(void),
mode_2DLissajous(void),
mode_2DFrizzles(void),
mode_2DPlasmaball(void),
mode_2DHiphotic(void),
mode_2DSindots(void),
mode_2DDNASpiral(void),
mode_2DBlackHole(void),
mode_rocktaves(void),
mode_2DAkemi(void),
mode_2DSunradiation(void),
mode_2DWaverly(void),
mode_2DDrift(void),
mode_2DColoredBursts(void),
mode_2DJulia(void),
mode_customEffect(void); //WLEDSR Custom Effects
#endif
private:
uint32_t crgb_to_col(CRGB fastled);
CRGB col_to_crgb(uint32_t);
CRGBPalette16 currentPalette;
CRGBPalette16 targetPalette;
private:
uint16_t _length, _virtualSegmentLength;
uint16_t _rand16seed;
uint8_t _brightness;
@ -1050,38 +681,6 @@ class WS2812FX {
show_callback _callback = nullptr;
// mode helper functions
uint16_t
blink(uint32_t, uint32_t, bool strobe, bool),
candle(bool),
color_wipe(bool, bool),
dynamic(bool),
scan(bool),
fireworks_base(CRGB*),
running_base(bool,bool),
larson_scanner(bool),
sinelon_base(bool,bool),
dissolve(uint32_t),
chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool),
ripple_base(bool),
police_base(uint32_t, uint32_t),
running(uint32_t, uint32_t, bool theatre=false),
tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool),
spots_base(uint16_t),
phased_base(uint8_t);
CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat);
CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff);
void
blendPixelColor(uint16_t n, uint32_t color, uint8_t blend),
startTransition(uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot),
estimateCurrentAndLimitBri(void),
load_gradient_palette(uint8_t),
handle_palette(void);
uint16_t* customMappingTable = nullptr;
uint16_t customMappingSize = 0;
@ -1100,18 +699,21 @@ class WS2812FX {
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities, custom 1, custom 2, custom 3
{0, 7, 0, DEFAULT_SPEED, DEFAULT_INTENSITY, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0, DEFAULT_C1, DEFAULT_C2, DEFAULT_C3, 0, 1}
};
friend class Segment;
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
friend class Segment_runtime;
ColorTransition transitions[MAX_NUM_TRANSITIONS]; //12 bytes per element
friend class ColorTransition;
void
estimateCurrentAndLimitBri(void),
load_gradient_palette(uint8_t),
handle_palette(void);
uint16_t
transitionProgress(uint8_t tNr);
public:
inline bool hasWhiteChannel(void) {return _hasWhiteChannel;}
inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;}
};
extern const char JSON_mode_names[];

View File

@ -110,15 +110,6 @@ void WS2812FX::setUpMatrix() {
uint16_t IRAM_ATTR WS2812FX::XY(uint16_t x, uint16_t y) {
uint16_t width = SEGMENT.virtualWidth(); // segment width in logical pixels
uint16_t height = SEGMENT.virtualHeight(); // segment height in logical pixels
/*
if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) {
uint16_t t;
// swap X & Y if segment transposed
t = x; x = y; y = t;
// swap width & height if segment transposed
t = width; width = height; height = t;
}
*/
return (x%width) + (y%height) * width;
}
@ -294,13 +285,13 @@ void WS2812FX::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) {
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < cols; x++) {
CRGB cur = leds ? leds[XY(x,row)] : col_to_crgb(getPixelColorXY(x, row));
CRGB cur = leds ? leds[XY(x,row)] : CRGB(getPixelColorXY(x, row));
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (x) {
CRGB prev = (leds ? leds[XY(x-1,row)] : col_to_crgb(getPixelColorXY(x-1, row))) + part;
CRGB prev = (leds ? leds[XY(x-1,row)] : CRGB(getPixelColorXY(x-1, row))) + part;
if (leds) leds[XY(x-1,row)] = prev;
else setPixelColorXY(x-1, row, prev);
}
@ -321,13 +312,13 @@ void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < rows; i++) {
CRGB cur = leds ? leds[XY(col,i)] : col_to_crgb(getPixelColorXY(col, i));
CRGB cur = leds ? leds[XY(col,i)] : CRGB(getPixelColorXY(col, i));
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (i) {
CRGB prev = (leds ? leds[XY(col,i-1)] : col_to_crgb(getPixelColorXY(col, i-1))) + part;
CRGB prev = (leds ? leds[XY(col,i-1)] : CRGB(getPixelColorXY(col, i-1))) + part;
if (leds) leds[XY(col,i-1)] = prev;
else setPixelColorXY(col, i-1, prev);
}
@ -374,9 +365,9 @@ void WS2812FX::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds)
uint16_t yp = vertical ? y-1 : y;
uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y;
CRGB curr = leds ? leds[XY(x,y)] : col_to_crgb(getPixelColorXY(x,y));
CRGB prev = (xp<0 || yp<0) ? CRGB::Black : (leds ? leds[XY(xp,yp)] : col_to_crgb(getPixelColorXY(xp,yp)));
CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : (leds ? leds[XY(xn,yn)] : col_to_crgb(getPixelColorXY(xn,yn)));
CRGB curr = leds ? leds[XY(x,y)] : CRGB(getPixelColorXY(x,y));
CRGB prev = (xp<0 || yp<0) ? CRGB::Black : (leds ? leds[XY(xp,yp)] : CRGB(getPixelColorXY(xp,yp)));
CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : (leds ? leds[XY(xn,yn)] : CRGB(getPixelColorXY(xn,yn)));
uint16_t r, g, b;
r = (curr.r*keep + (prev.r + next.r)*seep) / 3;
g = (curr.g*keep + (prev.g + next.g)*seep) / 3;
@ -491,7 +482,7 @@ void WS2812FX::nscale8(CRGB* leds, uint8_t scale) {
const uint16_t rows = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (leds) leds[XY(x,y)].nscale8(scale);
else setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x, y)).nscale8(scale));
else setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
}
}

View File

@ -69,6 +69,253 @@
#error "Max segments must be at least max number of busses!"
#endif
bool Segment::setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false;
uint8_t b = (slot == 1) ? cct : opacity;
ColorTransition::startTransition(b, colors[slot], segn, slot);
colors[slot] = c; return true;
}
void Segment::setCCT(uint16_t k, uint8_t segn) {
if (segn >= MAX_NUM_SEGMENTS) return;
if (k > 255) { //kelvin value, convert to 0-255
if (k < 1900) k = 1900;
if (k > 10091) k = 10091;
k = (k - 1900) >> 5;
}
if (cct == k) return;
ColorTransition::startTransition(cct, colors[1], segn, 1);
cct = k;
}
void Segment::setOpacity(uint8_t o, uint8_t segn) {
if (segn >= MAX_NUM_SEGMENTS) return;
if (opacity == o) return;
ColorTransition::startTransition(opacity, colors[0], segn, 0);
opacity = o;
}
void Segment::setOption(uint8_t n, bool val, uint8_t segn) {
bool prevOn = false;
if (n == SEG_OPTION_ON) {
prevOn = getOption(SEG_OPTION_ON);
if (!val && prevOn) { //fade off
ColorTransition::startTransition(opacity, colors[0], segn, 0);
}
}
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
if (n == SEG_OPTION_ON && val && !prevOn) { //fade on
ColorTransition::startTransition(0, colors[0], segn, 0);
}
}
// 2D matrix
uint16_t Segment::virtualWidth() {
uint16_t groupLen = groupLength();
uint16_t vWidth = ((getOption(SEG_OPTION_TRANSPOSED) ? height() : width()) + groupLen - 1) / groupLen;
if (getOption(SEG_OPTION_MIRROR)) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vWidth;
}
uint16_t Segment::virtualHeight() {
uint16_t groupLen = groupLength();
uint16_t vHeight = ((getOption(SEG_OPTION_TRANSPOSED) ? width() : height()) + groupLen - 1) / groupLen;
if (getOption(SEG_OPTION_MIRROR_Y)) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vHeight;
}
// 1D strip
uint16_t Segment::virtualLength() {
uint16_t groupLen = groupLength();
uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (getOption(SEG_OPTION_MIRROR)) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength;
}
uint8_t Segment::differs(Segment& b) {
uint8_t d = 0;
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
if (offset != b.offset) d |= SEG_DIFFERS_GSO;
if (grouping != b.grouping) d |= SEG_DIFFERS_GSO;
if (spacing != b.spacing) d |= SEG_DIFFERS_GSO;
if (opacity != b.opacity) d |= SEG_DIFFERS_BRI;
if (mode != b.mode) d |= SEG_DIFFERS_FX;
if (speed != b.speed) d |= SEG_DIFFERS_FX;
if (intensity != b.intensity) d |= SEG_DIFFERS_FX;
if (palette != b.palette) d |= SEG_DIFFERS_FX;
if (custom1 != b.custom1) d |= SEG_DIFFERS_FX;
if (custom2 != b.custom2) d |= SEG_DIFFERS_FX;
if (custom3 != b.custom3) d |= SEG_DIFFERS_FX;
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
//bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
if ((options & 0b11100101110) != (b.options & 0b11100101110)) d |= SEG_DIFFERS_OPT;
if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
return d;
}
void Segment::refreshLightCapabilities() {
if (!isActive()) {
_capabilities = 0; return;
}
uint8_t capabilities = 0;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue;
if (bus->getStart() >= stop) continue;
if (bus->getStart() + bus->getLength() <= start) continue;
uint8_t type = bus->getType();
if (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH)) capabilities |= 0x01; // segment supports RGB (full color)
if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel
if (!cctFromRgb) {
switch (type) {
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
capabilities |= 0x04; //segment supports white CCT
}
}
if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider)
uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode();
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB)
}
_capabilities = capabilities;
}
bool Segment_runtime::allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated
WS2812FX *instance = WS2812FX::getInstance();
deallocateData();
if (instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory
// if possible use SPI RAM on ESP32
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound())
data = (byte*) ps_malloc(len);
else
#endif
data = (byte*) malloc(len);
if (!data) return false; //allocation failed
instance->_usedSegmentData += len;
_dataLen = len;
memset(data, 0, len);
return true;
}
void Segment_runtime::deallocateData(){
free(data);
data = nullptr;
WS2812FX::getInstance()->_usedSegmentData -= _dataLen;
_dataLen = 0;
}
/**
* If reset of this segment was request, clears runtime
* settings of this segment.
* Must not be called while an effect mode function is running
* because it could access the data buffer and this method
* may free that data buffer.
*/
void Segment_runtime::resetIfRequired() {
if (_requiresReset) {
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
deallocateData();
_requiresReset = false;
}
}
void ColorTransition::startTransition(uint8_t oldBri, uint32_t oldCol, uint8_t segn, uint8_t slot) {
WS2812FX *instance = WS2812FX::getInstance();
if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || instance->_transitionDur == 0) return;
if (instance->_brightness == 0) return; //do not need transitions if master bri is off
if (!instance->_segments[segn].getOption(SEG_OPTION_ON)) return; //not if segment is off either
uint8_t tIndex = 0xFF; //none found
uint16_t tProgression = 0;
uint8_t s = segn + (slot << 6); //merge slot and segment into one byte
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
uint8_t tSeg = instance->transitions[i].segment;
//see if this segment + color already has a running transition
if (tSeg == s) {
tIndex = i; break;
}
if (tSeg == 0xFF) { //free transition
tIndex = i; tProgression = 0xFFFF;
}
}
if (tIndex == 0xFF) { //no slot found yet
for (uint8_t i = 0; i < MAX_NUM_TRANSITIONS; i++) {
//find most progressed transition to overwrite
uint16_t prog = instance->transitions[i].progress();
if (prog > tProgression) {
tIndex = i; tProgression = prog;
}
}
}
ColorTransition& t = instance->transitions[tIndex];
if (t.segment == s) //this is an active transition on the same segment+color
{
bool wasTurningOff = (oldBri == 0);
t.briOld = t.currentBri(wasTurningOff, slot);
t.colorOld = t.currentColor(oldCol);
} else {
t.briOld = oldBri;
t.colorOld = oldCol;
uint8_t prevSeg = t.segment & 0x3F;
if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments[prevSeg].setOption(SEG_OPTION_TRANSITIONAL, false);
}
t.transitionDur = instance->_transitionDur;
t.transitionStart = millis();
t.segment = s;
instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, true);
//refresh immediately, required for Solid mode
if (instance->_segment_runtimes[segn].next_time > t.transitionStart + 22) instance->_segment_runtimes[segn].next_time = t.transitionStart;
}
uint16_t ColorTransition::progress(bool allowEnd) { //transition progression between 0-65535
WS2812FX *instance = WS2812FX::getInstance();
uint32_t timeNow = millis();
if (timeNow - transitionStart > transitionDur) {
if (allowEnd) {
uint8_t segn = segment & 0x3F;
if (segn < MAX_NUM_SEGMENTS) instance->_segments[segn].setOption(SEG_OPTION_TRANSITIONAL, false);
segment = 0xFF;
}
return 0xFFFF;
}
uint32_t elapsed = timeNow - transitionStart;
uint32_t prog = elapsed * 0xFFFF / transitionDur;
return (prog > 0xFFFF) ? 0xFFFF : prog;
}
uint8_t ColorTransition::currentBri(bool turningOff, uint8_t slot) {
WS2812FX *instance = WS2812FX::getInstance();
uint8_t segn = segment & 0x3F;
if (segn >= MAX_NUM_SEGMENTS) return 0;
uint8_t briNew = instance->_segments[segn].opacity;
if (slot == 0) {
if (!instance->_segments[segn].getOption(SEG_OPTION_ON) || turningOff) briNew = 0;
} else { //transition slot 1 brightness for CCT transition
briNew = instance->_segments[segn].cct;
}
uint32_t prog = progress() + 1;
return ((briNew * prog) + (briOld * (0x10000 - prog))) >> 16;
}
//do not call this method from system context (network callback)
void WS2812FX::finalizeInit(void)
{
@ -149,8 +396,11 @@ void WS2812FX::service() {
uint16_t delay = FRAMETIME;
if (!SEGMENT.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
SEGLEN = SEGMENT.virtualLength();
_bri_t = SEGMENT.opacity; _colors_t[0] = SEGMENT.colors[0]; _colors_t[1] = SEGMENT.colors[1]; _colors_t[2] = SEGMENT.colors[2];
_virtualSegmentLength = SEGMENT.virtualLength();
_bri_t = SEGMENT.opacity;
_colors_t[0] = SEGMENT.colors[0];
_colors_t[1] = SEGMENT.colors[1];
_colors_t[2] = SEGMENT.colors[2];
uint8_t _cct_t = SEGMENT.cct;
if (!SEGMENT.getOption(SEG_OPTION_ON)) _bri_t = 0;
for (uint8_t t = 0; t < MAX_NUM_TRANSITIONS; t++) {
@ -166,14 +416,14 @@ void WS2812FX::service() {
}
handle_palette();
delay = (this->*_mode[SEGMENT.mode])(); // effect function (NOTE: may add SEGMENT and SEGENV to parameters)
delay = (*_mode[SEGMENT.mode])();
if (SEGMENT.mode != FX_MODE_HALLOWEEN_EYES) SEGENV.call++;
}
SEGENV.next_time = nowUp + delay;
}
}
SEGLEN = 0;
_virtualSegmentLength = 0;
busses.setSegmentCCT(-1);
if(doShow) {
yield();
@ -187,7 +437,7 @@ void /*IRAM_ATTR*/ WS2812FX::setPixelColor(float i, byte r, byte g, byte b, byte
{
if (i<0.0f || i>1.0f) return; // not normalized
float fC = i * (SEGLEN-1);
float fC = i * (_virtualSegmentLength-1);
if (aa) {
uint16_t iL = roundf(fC-0.49f);
uint16_t iR = roundf(fC+0.49f);
@ -213,8 +463,8 @@ void /*IRAM_ATTR*/ WS2812FX::setPixelColor(float i, byte r, byte g, byte b, byte
void IRAM_ATTR WS2812FX::setPixelColor(int i, byte r, byte g, byte b, byte w)
{
uint8_t segIdx = SEGLEN ? _segment_index : _mainSegment;
if (isMatrix && SEGLEN) {
uint8_t segIdx = _virtualSegmentLength ? _segment_index : _mainSegment;
if (isMatrix && _virtualSegmentLength) {
// map linear pixel into 2D segment area (even for 1D segments, expanding vertically)
uint16_t h = _segments[segIdx].virtualHeight(); // segment height in logical pixels
for (uint16_t y = 0; y < h; y++) { // expand 1D effect vertically
@ -223,9 +473,9 @@ void IRAM_ATTR WS2812FX::setPixelColor(int i, byte r, byte g, byte b, byte w)
return;
}
if (SEGLEN || (realtimeMode && useMainSegmentOnly)) {
if (_virtualSegmentLength || (realtimeMode && useMainSegmentOnly)) {
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (SEGLEN && _bri_t < 255) { // SEGLEN!=0 -> from segment/FX
if (_virtualSegmentLength && _bri_t < 255) { // _virtualSegmentLength!=0 -> from segment/FX
r = scale8(r, _bri_t);
g = scale8(g, _bri_t);
b = scale8(b, _bri_t);
@ -392,22 +642,11 @@ uint16_t WS2812FX::getFps() {
return _cumulativeFps +1;
}
uint8_t WS2812FX::getTargetFps() {
return _targetFps;
}
void WS2812FX::setTargetFps(uint8_t fps) {
if (fps > 0 && fps <= 120) _targetFps = fps;
_frametime = 1000 / _targetFps;
}
/**
* Forces the next frame to be computed on all active segments.
*/
void WS2812FX::trigger() {
_triggered = true;
}
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (segid >= MAX_NUM_SEGMENTS) return;
@ -420,11 +659,6 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
}
}
uint8_t WS2812FX::getPaletteCount()
{
return 13 + GRADIENT_PALETTE_COUNT;
}
//applies to all active and selected segments
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
if (slot >= NUM_COLORS) return;
@ -465,14 +699,6 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
}
}
uint8_t WS2812FX::getBrightness(void) {
return _brightness;
}
uint8_t WS2812FX::getMaxSegments(void) {
return MAX_NUM_SEGMENTS;
}
uint8_t WS2812FX::getFirstSelectedSegId(void)
{
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
@ -499,10 +725,6 @@ void WS2812FX::setMainSegmentId(uint8_t n) {
return;
}
uint8_t WS2812FX::getMainSegmentId(void) {
return _mainSegment;
}
uint8_t WS2812FX::getLastActiveSegmentId(void) {
for (uint8_t i = MAX_NUM_SEGMENTS -1; i > 0; i--) {
if (_segments[i].isActive()) return i;
@ -532,7 +754,7 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
//}
i += SEGMENT.start;
if (SEGLEN) {
if (_virtualSegmentLength) {
/* offset/phase */
i += SEGMENT.offset;
if (i >= SEGMENT.stop) i -= SEGMENT.length();
@ -544,31 +766,6 @@ uint32_t WS2812FX::getPixelColor(uint16_t i)
return busses.getPixelColor(i);
}
WS2812FX::Segment& WS2812FX::getSegment(uint8_t id) {
if (id >= MAX_NUM_SEGMENTS) return _segments[getMainSegmentId()];
return _segments[id];
}
WS2812FX::Segment& WS2812FX::getFirstSelectedSeg(void) {
return _segments[getFirstSelectedSegId()];
}
WS2812FX::Segment& WS2812FX::getMainSegment(void) {
return _segments[getMainSegmentId()];
}
WS2812FX::Segment* WS2812FX::getSegments(void) {
return _segments;
}
uint32_t WS2812FX::getLastShow(void) {
return _lastShow;
}
uint16_t WS2812FX::getLengthTotal(void) {
return _length;
}
uint16_t WS2812FX::getLengthPhysical(void) {
uint16_t len = 0;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
@ -579,64 +776,6 @@ uint16_t WS2812FX::getLengthPhysical(void) {
return len;
}
uint8_t WS2812FX::Segment::differs(Segment& b) {
uint8_t d = 0;
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
if (offset != b.offset) d |= SEG_DIFFERS_GSO;
if (grouping != b.grouping) d |= SEG_DIFFERS_GSO;
if (spacing != b.spacing) d |= SEG_DIFFERS_GSO;
if (opacity != b.opacity) d |= SEG_DIFFERS_BRI;
if (mode != b.mode) d |= SEG_DIFFERS_FX;
if (speed != b.speed) d |= SEG_DIFFERS_FX;
if (intensity != b.intensity) d |= SEG_DIFFERS_FX;
if (palette != b.palette) d |= SEG_DIFFERS_FX;
if (custom1 != b.custom1) d |= SEG_DIFFERS_FX;
if (custom2 != b.custom2) d |= SEG_DIFFERS_FX;
if (custom3 != b.custom3) d |= SEG_DIFFERS_FX;
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
//bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
if ((options & 0b11100101110) != (b.options & 0b11100101110)) d |= SEG_DIFFERS_OPT;
if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
return d;
}
void WS2812FX::Segment::refreshLightCapabilities() {
if (!isActive()) {
_capabilities = 0; return;
}
uint8_t capabilities = 0;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
if (!bus->isOk()) continue;
if (bus->getStart() >= stop) continue;
if (bus->getStart() + bus->getLength() <= start) continue;
uint8_t type = bus->getType();
if (type != TYPE_ANALOG_1CH && (cctFromRgb || type != TYPE_ANALOG_2CH)) capabilities |= 0x01; // segment supports RGB (full color)
if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel
if (!cctFromRgb) {
switch (type) {
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
capabilities |= 0x04; //segment supports white CCT
}
}
if (correctWB && type != TYPE_ANALOG_1CH) capabilities |= 0x04; //white balance correction (uses CCT slider)
uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode();
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB)
}
_capabilities = capabilities;
}
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
//returns if there is an RGBW bus (supports RGB and White, not only white)
//not influenced by auto-white mode, also true if white slider does not affect output white channel
@ -847,7 +986,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n)
uint8_t prevSegId = _segment_index;
if (n < MAX_NUM_SEGMENTS) {
_segment_index = n;
SEGLEN = SEGMENT.virtualLength();
_virtualSegmentLength = SEGMENT.virtualLength();
}
return prevSegId;
}
@ -863,16 +1002,6 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
}
}
void WS2812FX::setShowCallback(show_callback cb)
{
_callback = cb;
}
void WS2812FX::setTransition(uint16_t t)
{
_transitionDur = t;
}
void WS2812FX::setTransitionMode(bool t)
{
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
@ -885,51 +1014,6 @@ void WS2812FX::setTransitionMode(bool t)
}
}
/*
* color blend function
*/
uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
uint8_t shift = b16 ? 16 : 8;
uint32_t w1 = W(color1);
uint32_t r1 = R(color1);
uint32_t g1 = G(color1);
uint32_t b1 = B(color1);
uint32_t w2 = W(color2);
uint32_t r2 = R(color2);
uint32_t g2 = G(color2);
uint32_t b2 = B(color2);
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return RGBW32(r3, g3, b3, w3);
}
/*
* color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
*/
uint32_t WS2812FX::color_add(uint32_t c1, uint32_t c2)
{
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);
}
/*
* Fills segment with color
*/
@ -998,8 +1082,8 @@ void WS2812FX::fadeToBlackBy(uint8_t fadeBy) {
const uint16_t rows = SEGMENT.virtualHeight(); // will be 1 for 1D
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (isMatrix) setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x,y)).nscale8(255-fadeBy));
else setPixelColor(x, col_to_crgb(getPixelColor(x)).nscale8(255-fadeBy));
if (isMatrix) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy));
else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy));
}
}
@ -1019,9 +1103,9 @@ void WS2812FX::blur(uint8_t blur_amount)
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for(uint16_t i = 0; i < SEGLEN; i++)
for(uint16_t i = 0; i < _virtualSegmentLength; i++)
{
CRGB cur = col_to_crgb(getPixelColor(i));
CRGB cur = CRGB(getPixelColor(i));
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
@ -1038,7 +1122,7 @@ void WS2812FX::blur(uint8_t blur_amount)
}
}
uint16_t IRAM_ATTR WS2812FX::triwave16(uint16_t in)
uint16_t WS2812FX::triwave16(uint16_t in)
{
if (in < 0x8000) return in *2;
return 0xFFFF - (in - 0x8000)*2;
@ -1105,22 +1189,6 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) {
}
uint32_t IRAM_ATTR WS2812FX::crgb_to_col(CRGB fastled)
{
return RGBW32(fastled.red, fastled.green, fastled.blue, 0);
}
CRGB IRAM_ATTR WS2812FX::col_to_crgb(uint32_t color)
{
CRGB fastled_col;
fastled_col.red = R(color);
fastled_col.green = G(color);
fastled_col.blue = B(color);
return fastled_col;
}
void WS2812FX::load_gradient_palette(uint8_t index)
{
byte i = constrain(index, 0, GRADIENT_PALETTE_COUNT -1);
@ -1176,22 +1244,22 @@ void WS2812FX::handle_palette(void)
_lastPaletteChange = millis();
} break;}
case 2: {//primary color only
CRGB prim = col_to_crgb(SEGCOLOR(0));
CRGB prim = CRGB(SEGCOLOR(0));
targetPalette = CRGBPalette16(prim); break;}
case 3: {//primary + secondary
CRGB prim = col_to_crgb(SEGCOLOR(0));
CRGB sec = col_to_crgb(SEGCOLOR(1));
CRGB prim = CRGB(SEGCOLOR(0));
CRGB sec = CRGB(SEGCOLOR(1));
targetPalette = CRGBPalette16(prim,prim,sec,sec); break;}
case 4: {//primary + secondary + tertiary
CRGB prim = col_to_crgb(SEGCOLOR(0));
CRGB sec = col_to_crgb(SEGCOLOR(1));
CRGB ter = col_to_crgb(SEGCOLOR(2));
CRGB prim = CRGB(SEGCOLOR(0));
CRGB sec = CRGB(SEGCOLOR(1));
CRGB ter = CRGB(SEGCOLOR(2));
targetPalette = CRGBPalette16(ter,sec,prim); break;}
case 5: {//primary + secondary (+tert if not off), more distinct
CRGB prim = col_to_crgb(SEGCOLOR(0));
CRGB sec = col_to_crgb(SEGCOLOR(1));
CRGB prim = CRGB(SEGCOLOR(0));
CRGB sec = CRGB(SEGCOLOR(1));
if (SEGCOLOR(2)) {
CRGB ter = col_to_crgb(SEGCOLOR(2));
CRGB ter = CRGB(SEGCOLOR(2));
targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,ter,ter,ter,ter,ter,prim);
} else {
targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec);
@ -1227,7 +1295,7 @@ void WS2812FX::handle_palette(void)
/*
* Gets a single color from the currently selected palette.
* @param i Palette Index (if mapping is true, the full palette will be SEGLEN 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.
* @param mapping if true, LED position in segment is considered for color
* @param wrap FastLED palettes will usally wrap back to the start smoothly. Set false to get a hard edge
* @param mcol If the default palette 0 is selected, return the standard color 0, 1 or 2 instead. If >2, Party palette is used instead
@ -1243,12 +1311,12 @@ uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool w
}
uint8_t paletteIndex = i;
if (mapping && SEGLEN > 1) paletteIndex = (i*255)/(SEGLEN -1);
if (mapping && _virtualSegmentLength > 1) paletteIndex = (i*255)/(_virtualSegmentLength -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
fastled_col = ColorFromPalette(currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
return crgb_to_col(fastled_col);
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0);
}
@ -1302,7 +1370,7 @@ void WS2812FX::deserializeMap(uint8_t n) {
}
//gamma 2.8 lookup table used for color correction
byte gammaT[] = {
static byte gammaT[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
@ -1321,7 +1389,7 @@ byte gammaT[] = {
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
uint8_t WS2812FX::gamma8_cal(uint8_t b, float gamma) {
return (int)(pow((float)b / 255.0, gamma) * 255 + 0.5);
return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f);
}
void WS2812FX::calcGammaTable(float gamma)

View File

@ -195,7 +195,7 @@ void handleAnalog(uint8_t b)
colorHStoRGB(aRead*256,255,col);
} else {
// otherwise use "double press" for segment selection
WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]);
Segment& seg = strip.getSegment(macroDoublePress[b]);
if (aRead == 0) {
seg.setOption(SEG_OPTION_ON, 0); // off
} else {

View File

@ -1,9 +1,54 @@
#include "wled.h"
/*
* Color conversion methods
* Color conversion & utility methods
*/
/*
* color blend function
*/
uint32_t IRAM_ATTR color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
uint8_t shift = b16 ? 16 : 8;
uint32_t w1 = W(color1);
uint32_t r1 = R(color1);
uint32_t g1 = G(color1);
uint32_t b1 = B(color1);
uint32_t w2 = W(color2);
uint32_t r2 = R(color2);
uint32_t g2 = G(color2);
uint32_t b2 = B(color2);
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return RGBW32(r3, g3, b3, w3);
}
/*
* 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 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);
}
void setRandomColor(byte* rgb)
{
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex);

View File

@ -5,6 +5,8 @@
* Readability defines and their associated numerical values + compile-time constants
*/
#define GRADIENT_PALETTE_COUNT 58
//Defaults
#define DEFAULT_CLIENT_SSID "Your_Network"
#define DEFAULT_AP_PASS "wled1234"

View File

@ -58,6 +58,9 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t);
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);
@ -129,7 +132,7 @@ void handleIR();
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root);
void serializeModeNames(JsonArray arr, const char *qstring);

View File

@ -90,7 +90,7 @@ void changeEffect(uint8_t fx)
{
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
strip.setMode(i, fx);
}
@ -106,7 +106,7 @@ void changePalette(uint8_t pal)
{
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.palette = pal;
}
@ -125,7 +125,7 @@ void changeEffectSpeed(int8_t amount)
effectSpeed = (byte)constrain(new_val,0,255);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.speed = effectSpeed;
}
@ -135,7 +135,7 @@ void changeEffectSpeed(int8_t amount)
setValuesFromMainSeg();
}
} else { // if Effect == "solid Color", change the hue of the primary color
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col;
fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]);
@ -148,7 +148,7 @@ void changeEffectSpeed(int8_t amount)
hsv2rgb_rainbow(prim_hsv, fastled_col);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
}
@ -172,7 +172,7 @@ void changeEffectIntensity(int8_t amount)
effectIntensity = (byte)constrain(new_val,0,255);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.intensity = effectIntensity;
}
@ -182,7 +182,7 @@ void changeEffectIntensity(int8_t amount)
setValuesFromMainSeg();
}
} else { // if Effect == "solid Color", change the saturation of the primary color
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col;
fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]);
@ -193,7 +193,7 @@ void changeEffectIntensity(int8_t amount)
hsv2rgb_rainbow(prim_hsv, fastled_col);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
}
@ -215,7 +215,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
if (irApplyToAllSelected) {
// main segment may not be selected!
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
byte capabilities = seg.getLightCapabilities();
uint32_t mask = 0;
@ -233,7 +233,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
setValuesFromFirstSelectedSeg();
} else {
byte i = strip.getMainSegmentId();
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
byte capabilities = seg.getLightCapabilities();
uint32_t mask = 0;
bool isRGB = GET_BIT(capabilities, 0); // is segment RGB capable
@ -253,7 +253,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
void changeWhite(int8_t amount, int16_t cct=-1)
{
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
byte r = R(seg.colors[0]);
byte g = G(seg.colors[0]);
byte b = B(seg.colors[0]);
@ -424,7 +424,7 @@ void decodeIR24CT(uint32_t code)
void decodeIR40(uint32_t code)
{
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
byte r = R(seg.colors[0]);
byte g = G(seg.colors[0]);
byte b = B(seg.colors[0]);

View File

@ -11,8 +11,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte id = elem["id"] | it;
if (id >= strip.getMaxSegments()) return;
WS2812FX::Segment& seg = strip.getSegment(id);
WS2812FX::Segment prev = seg; //make a backup so we can tell if something changed
Segment& seg = strip.getSegment(id);
Segment prev = seg; //make a backup so we can tell if something changed
uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1;
@ -327,7 +327,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
//apply all selected segments
//bool didSet = false;
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
Segment &sg = strip.getSegment(s);
if (sg.isActive()) {
if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
@ -396,7 +396,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
return stateResponse;
}
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, bool segmentBounds)
{
root["id"] = id;
if (segmentBounds) {
@ -494,7 +494,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
bool selectedSegmentsOnly = root[F("sc")] | false;
JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
Segment &sg = strip.getSegment(s);
if (selectedSegmentsOnly && !sg.isSelected()) continue;
if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject();

View File

@ -8,7 +8,7 @@ void setValuesFromMainSeg() { setValuesFromSegment(strip.getMainSegment
void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelectedSegId()); }
void setValuesFromSegment(uint8_t s)
{
WS2812FX::Segment& seg = strip.getSegment(s);
Segment& seg = strip.getSegment(s);
col[0] = R(seg.colors[0]);
col[1] = G(seg.colors[0]);
col[2] = B(seg.colors[0]);
@ -30,9 +30,9 @@ void applyValuesToSelectedSegs()
{
// copy of first selected segment to tell if value was updated
uint8_t firstSel = strip.getFirstSelectedSegId();
WS2812FX::Segment selsegPrev = strip.getSegment(firstSel);
Segment selsegPrev = strip.getSegment(firstSel);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue;
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}

View File

@ -13,8 +13,6 @@
#ifndef PalettesWLED_h
#define PalettesWLED_h
#define GRADIENT_PALETTE_COUNT 58
const byte ib_jul01_gp[] PROGMEM = {
0, 194, 1, 1,
94, 1, 29, 18,

View File

@ -612,7 +612,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
}
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) {
byte t = getNumVal(&req, pos);
@ -823,7 +823,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
// apply to main and all selected segments to prevent #1618.
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all
if (fxModeChanged) strip.setMode(i, effectIn);
if (speedChanged) seg.speed = speedIn;

View File

@ -28,7 +28,7 @@ void notify(byte callMode, bool followUp)
default: return;
}
byte udpOut[WLEDPACKETSIZE];
WS2812FX::Segment& mainseg = strip.getMainSegment();
Segment& mainseg = strip.getMainSegment();
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
udpOut[1] = callMode;
udpOut[2] = bri;
@ -92,7 +92,7 @@ void notify(byte callMode, bool followUp)
udpOut[39] = strip.getMaxSegments();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i);
Segment &selseg = strip.getSegment(i);
uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[0 +ofs] = i;
udpOut[1 +ofs] = selseg.start >> 8;
@ -143,7 +143,7 @@ void realtimeLock(uint32_t timeoutMs, byte md)
if (!realtimeMode && !realtimeOverride) {
uint16_t stop, start;
if (useMainSegmentOnly) {
WS2812FX::Segment& mainseg = strip.getMainSegment();
Segment& mainseg = strip.getMainSegment();
start = mainseg.start;
stop = mainseg.stop;
mainseg.setOption(SEG_OPTION_FREEZE, true, strip.getMainSegmentId());
@ -343,7 +343,7 @@ void handleNotifications()
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
uint8_t id = udpIn[0 +ofs];
if (id > strip.getMaxSegments()) break;
WS2812FX::Segment& selseg = strip.getSegment(id);
Segment& selseg = strip.getSegment(id);
uint16_t start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
uint16_t stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
@ -378,7 +378,7 @@ void handleNotifications()
// simple effect sync, applies to all selected segments
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
if (udpIn[8] < strip.getModeCount()) strip.setMode(i, udpIn[8]);
seg.speed = udpIn[9];

View File

@ -418,7 +418,7 @@ void deEEP() {
segObj[F("ix")] = EEPROM.read(i+16);
segObj["pal"] = EEPROM.read(i+17);
} else {
WS2812FX::Segment* seg = strip.getSegments();
Segment* seg = strip.getSegments();
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
if (ver == 2) { //versions before 2004230 did not have opacity
for (byte j = 0; j < strip.getMaxSegments(); j++)