Mode blending styles

- alternative to #3669
This commit is contained in:
Blaz Kristan
2024-04-03 18:38:06 +02:00
parent 24c5935661
commit 0c8d9d5614
9 changed files with 212 additions and 15 deletions

View File

@@ -84,6 +84,10 @@ uint16_t Segment::_lastPaletteBlend = 0; //in millis (lowest 16 bits only)
#ifndef WLED_DISABLE_MODE_BLEND
bool Segment::_modeBlend = false;
uint16_t Segment::_clipStart = 0;
uint16_t Segment::_clipStop = 0;
uint8_t Segment::_clipStartY = 0;
uint8_t Segment::_clipStopY = 1;
#endif
// copy constructor
@@ -413,12 +417,17 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
uint32_t prog = progress();
uint32_t curBri = useCct ? cct : (on ? opacity : 0);
if (prog < 0xFFFFU) {
uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
curBri += (useCct ? _t->_cctT : _t->_briT) * (0xFFFFU - prog);
uint8_t tmpBri = useCct ? _t->_cctT : (_t->_segT._optionsT & 0x0004 ? _t->_briT : 0);
#ifndef WLED_DISABLE_MODE_BLEND
if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? tmpBri : curBri; // not fade/blend transition, each effect uses its brightness
#endif
curBri *= prog;
curBri += tmpBri * (0xFFFFU - prog);
return curBri / 0xFFFFU;
}
return (useCct ? cct : (on ? opacity : 0));
return curBri;
}
uint8_t IRAM_ATTR Segment::currentMode() {
@@ -431,16 +440,23 @@ uint8_t IRAM_ATTR Segment::currentMode() {
uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
if (slot >= NUM_COLORS) slot = 0;
uint32_t prog = progress();
if (prog == 0xFFFFU) return colors[slot];
#ifndef WLED_DISABLE_MODE_BLEND
return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? _t->_segT._colorT[slot] : colors[slot]; // not fade/blend transition, each effect uses its color
return color_blend(_t->_segT._colorT[slot], colors[slot], prog, true);
#else
return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
return color_blend(_t->_colorT[slot], colors[slot], prog, true);
#endif
}
CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
loadPalette(targetPalette, pal);
uint16_t prog = progress();
#ifndef WLED_DISABLE_MODE_BLEND
if (prog < 0xFFFFU && blendingStyle > BLEND_STYLE_FADE && _modeBlend) targetPalette = _t->_palT; // not fade/blend transition, each effect uses its palette
else
#endif
if (strip.paletteFade && prog < 0xFFFFU) {
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
@@ -456,9 +472,9 @@ CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, u
void Segment::handleRandomPalette() {
// is it time to generate a new palette?
if ((millis()/1000U) - _lastPaletteChange > randomPaletteChangeTime) {
_newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
_lastPaletteChange = millis()/1000U;
_lastPaletteBlend = (uint16_t)(millis() & 0xFFFF)-512; // starts blending immediately
_newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
_lastPaletteChange = millis()/1000U;
_lastPaletteBlend = (uint16_t)(millis() & 0xFFFF)-512; // starts blending immediately
}
// if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls)
@@ -662,6 +678,32 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
return vLength;
}
// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false)
// if clipping start > stop the clipping range is inverted
// _modeBlend==true -> old effect during transition
// _modeBlend==false -> new effect during transition
bool IRAM_ATTR Segment::isPixelClipped(int i) {
#ifndef WLED_DISABLE_MODE_BLEND
if (_clipStart != _clipStop && blendingStyle > BLEND_STYLE_FADE) {
bool invert = _clipStart > _clipStop;
unsigned start = invert ? _clipStop : _clipStart;
unsigned stop = invert ? _clipStart : _clipStop;
if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
unsigned len = stop - start;
if (len < 2) return false;
unsigned shuffled = hashInt(i) % len;
unsigned pos = (shuffled * 0xFFFFU) / len;
return progress() <= pos;
}
const bool iInside = (i >= start && i < stop);
if (!invert && iInside) return _modeBlend;
if ( invert && !iInside) return _modeBlend;
return !_modeBlend;
}
#endif
return false;
}
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{
if (!isActive()) return; // not active
@@ -732,6 +774,8 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
#endif
if (isPixelClipped(i)) return; // handle clipping on 1D
uint16_t len = length();
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
@@ -763,14 +807,16 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
// _modeBlend==true -> old effect
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexMir, tmpCol);
}
indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
// _modeBlend==true -> old effect
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
#endif
strip.setPixelColor(indexSet, tmpCol);
}
@@ -1062,7 +1108,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
// paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined)
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGBPalette16 curPal;
curPal = currentPalette(curPal, palette);
currentPalette(curPal, palette);
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color));
@@ -1185,8 +1231,59 @@ void WS2812FX::service() {
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
delay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
seg.setClippingRect(0, 0); // disable clipping
if (modeBlending && seg.mode != tmpMode) {
// set clipping rectangle
// new mode is run inside clipping area and old mode outside clipping area
unsigned p = seg.progress();
unsigned w = seg.is2D() ? seg.virtualWidth() : _virtualSegmentLength;
unsigned h = seg.virtualHeight();
unsigned dw = p * w / 0xFFFFU + 1;
unsigned dh = p * h / 0xFFFFU + 1;
switch (blendingStyle) {
case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped())
seg.setClippingRect(0, w, 0, h);
break;
case BLEND_STYLE_SWIPE_RIGHT: // left-to-right
seg.setClippingRect(0, dw, 0, h);
break;
case BLEND_STYLE_SWIPE_LEFT: // right-to-left
seg.setClippingRect(w - dw, w, 0, h);
break;
case BLEND_STYLE_PINCH_OUT: // corners
seg.setClippingRect((w + dw)/2, (w - dw)/2, (h + dh)/2, (h - dh)/2); // inverted!!
break;
case BLEND_STYLE_INSIDE_OUT: // outward
seg.setClippingRect((w - dw)/2, (w + dw)/2, (h - dh)/2, (h + dh)/2);
break;
case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D)
seg.setClippingRect(0, w, 0, dh);
break;
case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D)
seg.setClippingRect(0, w, h - dh, h);
break;
case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D
seg.setClippingRect((w - dw)/2, (w + dw)/2, 0, h);
break;
case BLEND_STYLE_OPEN_V: // vertical-outward (2D)
seg.setClippingRect(0, w, (h - dh)/2, (h + dh)/2);
break;
case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D)
seg.setClippingRect(0, dw, 0, dh);
break;
case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D)
seg.setClippingRect(w - dw, w, 0, dh);
break;
case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D)
seg.setClippingRect(w - dw, w, h - dh, h);
break;
case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D)
seg.setClippingRect(0, dw, h - dh, h);
break;
}
}
delay = (*_mode[seg.mode])(); // run new/current mode
if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore
@@ -1197,6 +1294,8 @@ void WS2812FX::service() {
delay = MIN(delay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
}
#else
delay = (*_mode[seg.mode])(); // run effect mode
#endif
seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition