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

@@ -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)