Skip to content

Commit 933aa7e

Browse files
committed
Perlinscape FX by @BobLoeffler68 and more debugging
- some tuning in starting transition
1 parent 3dff190 commit 933aa7e

3 files changed

Lines changed: 144 additions & 41 deletions

File tree

wled00/FX.cpp

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ static uint8_t qadd8(uint8_t i, uint8_t j) {
113113
/*
114114
* No blinking. Just plain old static light.
115115
*/
116-
uint16_t mode_static(void) {
116+
uint16_t mode_static() {
117117
SEGMENT.fill(SEGCOLOR(0));
118118
return strip.isOffRefreshRequired() ? FRAMETIME : 350;
119119
}
@@ -6444,6 +6444,106 @@ uint16_t mode_2Dminesweeper() {
64446444
static const char _data_FX_MODE_2DMINESWEEPER[] PROGMEM = "Minesweeper@;;;2";
64456445
*/
64466446

6447+
/*
6448+
/ Perlinscape effect - a Perlin noise Landscape
6449+
* Created by stepko (@St3p40 https://github.com/St3p40) as part of Stepko Land on soulmatelights.com
6450+
* Adapted to WLED by Bob Loeffler (@bobloeffler68) with additional features (and help from Claude)
6451+
* First slider is for speed
6452+
* Second slider is for zooming in/out (Perlin scaling)
6453+
* Third slider is the X multiplier
6454+
* Fourth slider is the Y multiplier
6455+
* Second checkbox will rotate the image
6456+
* Third checkbox will randomize the horizonal and vertical directions
6457+
*/
6458+
uint16_t mode_2D_perlinscape() {
6459+
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
6460+
6461+
const uint16_t width = SEG_W;
6462+
const uint16_t height = SEG_H;
6463+
6464+
if (!SEGENV.allocateData(4 * sizeof(float) + sizeof(uint32_t))) return mode_static();
6465+
6466+
uint32_t speedDiv = map(SEGMENT.speed, 0, 255, 50, 1);
6467+
uint32_t t = strip.now / speedDiv;
6468+
uint8_t Xmult = SEGMENT.custom1 >> 2;
6469+
uint8_t Ymult = SEGMENT.custom2 >> 2;
6470+
6471+
uint32_t prevT = reinterpret_cast<uint32_t&>(*SEGENV.data);
6472+
float offX = reinterpret_cast<float&>(*(SEGENV.data + sizeof(uint32_t)));
6473+
float offY = reinterpret_cast<float&>(*(SEGENV.data + sizeof(uint32_t) + sizeof(float)));
6474+
float stepX = reinterpret_cast<float&>(*(SEGENV.data + sizeof(uint32_t) + 2*sizeof(float)));
6475+
float stepY = reinterpret_cast<float&>(*(SEGENV.data + sizeof(uint32_t) + 3*sizeof(float)));
6476+
6477+
if (SEGENV.call == 0) {
6478+
SEGENV.aux0 = hw_random16(5000, 10000);
6479+
SEGENV.aux1 = 0;
6480+
offX = 0.0f;
6481+
offY = 0.0f;
6482+
stepX = 1.0f;
6483+
stepY = 1.0f;
6484+
prevT = t;
6485+
}
6486+
6487+
if (SEGMENT.check3 && (strip.now - SEGENV.step > SEGENV.aux0)) {
6488+
// if randomizing, change/randomize direction (aux1) every 5-10s (aux0)
6489+
SEGENV.aux0 = hw_random16(5000, 10000);
6490+
SEGENV.aux1 = hw_random8(4);
6491+
SEGENV.step = strip.now;
6492+
}
6493+
6494+
bool flipX = SEGMENT.check3 ? (SEGENV.aux1 & 0x01) : false;
6495+
bool flipY = SEGMENT.check3 ? (SEGENV.aux1 & 0x02) : false;
6496+
6497+
float targetX = flipX ? -1.0f : 1.0f;
6498+
float targetY = flipY ? -1.0f : 1.0f;
6499+
stepX += (targetX - stepX) * 0.05f;
6500+
stepY += (targetY - stepY) * 0.05f;
6501+
6502+
uint32_t dt = t - prevT;
6503+
offX += stepX * dt;
6504+
offY += stepY * dt;
6505+
prevT = t;
6506+
6507+
int32_t tX = (int32_t)offX;
6508+
int32_t tY = (int32_t)offY;
6509+
6510+
// Rotation
6511+
float cosA = 1.0f, sinA = 0.0f;
6512+
float cx = width * 0.5f;
6513+
float cy = height * 0.5f;
6514+
6515+
if (SEGMENT.check2) {
6516+
float angle = strip.now / 5000.0f;
6517+
cosA = cos_approx(angle);
6518+
sinA = sin_approx(angle);
6519+
}
6520+
6521+
float scale = map(SEGMENT.intensity, 0, 255, 10, 200) / 100.0f; // range 0.1 to 2.0
6522+
6523+
for (byte x = 0; x < width; x++) {
6524+
for (byte y = 0; y < height; y++) {
6525+
float rx = cosA * (x - cx) - sinA * (y - cy) + cx;
6526+
float ry = sinA * (x - cx) + cosA * (y - cy) + cy;
6527+
6528+
float scaled_x = rx * Xmult * scale;
6529+
float scaled_y = ry * Ymult * scale;
6530+
6531+
if (SEGMENT.palette) {
6532+
// Palette mode (i.e. not a Default palette)
6533+
uint8_t paletteIndex = perlin8(scaled_x, scaled_y, t);
6534+
uint8_t brightness = perlin8(scaled_x + tX, scaled_y + tY);
6535+
SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(paletteIndex, false, PALETTE_FIXED, brightness));
6536+
} else {
6537+
// Raw RGB mode
6538+
SEGMENT.setPixelColorXY(x, y, perlin8(scaled_x, scaled_y, t), perlin8(scaled_x, scaled_y + tY), perlin8(scaled_x + tX, scaled_y));
6539+
}
6540+
}
6541+
}
6542+
return FRAMETIME;
6543+
}
6544+
static const char _data_FX_MODE_2DPERLINSCAPE[] PROGMEM = "Perlinscape@!,Zoom,X multiplier,Y multiplier,,,Rotate,Random;;!;2;";
6545+
6546+
64476547
////////////////////////////////////////////
64486548
// PARTICLE SYSTEM EFFECTS
64496549
////////////////////////////////////////////
@@ -8961,6 +9061,7 @@ void WS2812FX::setupEffectData() {
89619061
addEffect(FX_MODE_2DSOAP, &mode_2Dsoap, _data_FX_MODE_2DSOAP);
89629062
addEffect(FX_MODE_2DOCTOPUS, &mode_2Doctopus, _data_FX_MODE_2DOCTOPUS);
89639063
addEffect(FX_MODE_2DWAVINGCELL, &mode_2Dwavingcell, _data_FX_MODE_2DWAVINGCELL);
9064+
addEffect(FX_MODE_2DPERLINSCAPE, &mode_2D_perlinscape, _data_FX_MODE_2DPERLINSCAPE);
89649065
#ifndef WLED_DISABLE_PARTICLESYSTEM2D
89659066
addEffect(FX_MODE_PARTICLEVOLCANO, &mode_particlevolcano, _data_FX_MODE_PARTICLEVOLCANO);
89669067
addEffect(FX_MODE_PARTICLEFIREWORKS, &mode_particlefireworks, _data_FX_MODE_PARTICLEFIREWORKS);

wled00/FX.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
253253
#define FX_MODE_2DPLASMAROTOZOOM 114 // was Candy Cane prior to 0.14 (use Chase 2)
254254
#define FX_MODE_BLENDS 115
255255
#define FX_MODE_TV_SIMULATOR 116
256-
//#define FX_MODE_DYNAMIC_SMOOTH 117 // candidate for removal (check3 in dynamic)
256+
#define FX_MODE_2DPERLINSCAPE 117 // was Dynamic Smooth (use Dynamic with check 3)
257257
#define FX_MODE_SHIMMER 161 // gap fill, non SR 1D effect
258258

259259
// new 0.14 2D effects
@@ -512,7 +512,7 @@ class Segment {
512512
, _cct(0)
513513
{}
514514
~Transition() {
515-
//DEBUGFX_PRINTF_P(PSTR("-- Destroying transition: %p\n"), this);
515+
DEBUGFX_PRINTF_P(PSTR("-- Destroying transition: %p\n"), this);
516516
if (_oldSegment) delete _oldSegment;
517517
}
518518
} *_t;
@@ -732,10 +732,6 @@ class Segment {
732732
[[gnu::hot]] void setPixelColorXY(int x, int y, CRGBA c) const; // set relative pixel within segment with color
733733
inline void setPixelColorXY(unsigned x, unsigned y, CRGBA c) const { setPixelColorXY(int(x), int(y), c); }
734734
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) const { setPixelColorXY(x, y, CRGBA(r,g,b)); }
735-
#ifdef WLED_USE_AA_PIXELS
736-
void setPixelColorXY(float x, float y, CRGBA c, bool aa = true) const;
737-
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) const { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
738-
#endif
739735
[[gnu::hot]] bool isPixelXYClipped(int x, int y) const;
740736
[[gnu::hot]] CRGBA getPixelColorXY(int x, int y) const;
741737
// 2D support functions
@@ -763,10 +759,6 @@ class Segment {
763759
inline void setPixelColorXY(int x, int y, CRGBA c) const { setPixelColor(x, c); }
764760
inline void setPixelColorXY(unsigned x, unsigned y, CRGBA c) const { setPixelColor(int(x), c); }
765761
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) const { setPixelColor(x, CRGBA(r,g,b)); }
766-
#ifdef WLED_USE_AA_PIXELS
767-
inline void setPixelColorXY(float x, float y, CRGBA c, bool aa = true) const { setPixelColor(x, c, aa); }
768-
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, CRGBA(r,g,b), aa); }
769-
#endif
770762
inline bool isPixelXYClipped(int x, int y) const { return isPixelClipped(x); }
771763
inline CRGBA getPixelColorXY(int x, int y) const { return getPixelColor(x); }
772764
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGBA c, uint8_t blend) const { blendPixelColor(x, c, blend); }

wled00/FX_fcn.cpp

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ uint8_t Segment::_clipStopY = 1;
4747

4848
// copy constructor
4949
Segment::Segment(const Segment &orig) {
50-
//DEBUGFX_PRINTF_P(PSTR("-- Copy segment constructor: %p -> %p\n"), &orig, this);
50+
DEBUGFX_PRINTF_P(PSTR("-- Copy segment constructor: %p -> %p\n"), &orig, this);
5151
memcpy((void*)this, (void*)&orig, sizeof(Segment));
5252
_t = nullptr; // copied segment cannot be in transition
5353
name = nullptr;
@@ -72,7 +72,7 @@ Segment::Segment(const Segment &orig) {
7272

7373
// move constructor
7474
Segment::Segment(Segment &&orig) noexcept {
75-
//DEBUGFX_PRINTF_P(PSTR("-- Move segment constructor: %p -> %p\n"), &orig, this);
75+
DEBUGFX_PRINTF_P(PSTR("-- Move segment constructor: %p -> %p\n"), &orig, this);
7676
memcpy((void*)this, (void*)&orig, sizeof(Segment));
7777
orig._t = nullptr; // old segment cannot be in transition any more
7878
orig.name = nullptr;
@@ -83,7 +83,7 @@ Segment::Segment(Segment &&orig) noexcept {
8383

8484
// copy assignment
8585
Segment& Segment::operator= (const Segment &orig) {
86-
//DEBUGFX_PRINTF_P(PSTR("-- Copying segment: %p -> %p\n"), &orig, this);
86+
DEBUGFX_PRINTF_P(PSTR("-- Copying segment: %p -> %p\n"), &orig, this);
8787
if (this != &orig) {
8888
// clean destination
8989
if (name) { d_free(name); name = nullptr; }
@@ -117,7 +117,7 @@ Segment& Segment::operator= (const Segment &orig) {
117117

118118
// move assignment
119119
Segment& Segment::operator= (Segment &&orig) noexcept {
120-
//DEBUGFX_PRINTF_P(PSTR("-- Moving segment: %p -> %p\n"), &orig, this);
120+
DEBUGFX_PRINTF_P(PSTR("-- Moving segment: %p -> %p\n"), &orig, this);
121121
if (this != &orig) {
122122
if (name) { d_free(name); name = nullptr; } // free old name
123123
if (isInTransition()) stopTransition(); // also erases _t
@@ -188,7 +188,7 @@ void Segment::deallocateData() {
188188
*/
189189
void Segment::resetIfRequired() {
190190
if (!reset || !isActive()) return;
191-
//DEBUGFX_PRINTF_P(PSTR("-- Segment reset: %p\n"), this);
191+
DEBUGFX_PRINTF_P(PSTR("-- Segment reset: %p\n"), this);
192192
if (data && _dataLen > 0) memset(data, 0, _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
193193
if (_dataLen > FAIR_DATA_PER_SEG) deallocateData(); // do not keep large allocations
194194
if (pixels) for (size_t i = 0; i < length(); i++) pixels[i] = hasWhite() ? BLACK : CRGBA(BLACK); // clear pixel buffer
@@ -254,6 +254,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
254254

255255
// starting a transition has to occur before change so we get current values 1st
256256
void Segment::startTransition(uint16_t dur, bool segmentCopy) {
257+
DEBUGFX_PRINTF_P(PSTR("-- Starting transition: %ums w/%s segment copy\n"), (unsigned)dur, (segmentCopy ? "" : "o"));
257258
if (dur == 0 || !isActive()) {
258259
if (isInTransition()) _t->_dur = 0;
259260
return;
@@ -273,6 +274,7 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) {
273274
// as the JSON (or HTTP) API is processed. First call will either make a copy of segment or store original values
274275
// for colors, palettes & brightness. Subsequent calls should only modify these values if transition
275276
// has already progressed beyond 0 (or 1 frame to be exact)
277+
// _t->_progress is updated in WS2812FX::service() using handleTransition() call and changing _t->_start has no effect
276278
if (isInTransition()) {
277279
if (segmentCopy && !_t->_oldSegment) {
278280
// already in transition but segment copy requested and not yet created
@@ -281,38 +283,40 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) {
281283
_t->_dur = dur;
282284
_t->_prevPaletteBlends = 0; // reset previous palette blends
283285
if (_t->_oldSegment) {
286+
if (!_t->_oldSegment->isActive()) { // even though segment's copy may be created it may lack pixel buffer
287+
stopTransition(); // in such case it will be marked inactive so stop transition entirely
288+
return;
289+
}
290+
_t->_oldSegment->opacity = _t->_bri; // restore original opacity
291+
_t->_oldSegment->cct = _t->_cct; // restore original CCT
284292
_t->_oldSegment->palette = _t->_palette; // restore original palette and colors (from start of transition)
285293
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i];
286-
// if already partway through a FADE transition, set old segment's colors to current blend to avoid jumping back to original colors
287-
if (_t->_progress > 0)
288-
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i].nblend(colors[i], _t->_progress);
289294
DEBUGFX_PRINTF_P(PSTR("-- Updated transition with segment copy: S=%p T(%p) O[%p] OP[%p]\n"), this, _t, _t->_oldSegment, _t->_oldSegment->pixels);
290-
if (!_t->_oldSegment->isActive()) stopTransition();
291295
} else {
292296
DEBUGFX_PRINTLN(F("-- Error allocating memory for segment copy."));
293297
errorFlag = ERR_NORAM;
294298
}
295-
} else if (_t->_progress > 0) {
299+
}
300+
if (_t->_progress > 0) {
296301
// If we are already in transition we need to copy current intermediate color into old color
297-
// so that changing it will not produce abrup change. We will also do similar for palette.
298-
// However, palette change progress is recorded in _palT (see beginDraw()).
302+
// so that changing it will not produce abrup change. We will also do similar for brightness and CCT.
303+
// However, palette change progress is recorded in _palT (see beginDraw()) so we just reset blend count.
304+
// We also need to restart transition (for all transitions!)
305+
_t->_start = millis(); // restart countdown
306+
_t->_dur = dur; // update duration
307+
_t->_prevPaletteBlends = 0; // reset previous palette blends
299308
if (_t->_oldSegment) {
300309
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i].nblend(colors[i], _t->_progress);
301310
_t->_oldSegment->palette = _t->_palette; // update palette and colors (from middle of transition)
311+
_t->_oldSegment->opacity = currentBri(); // update opacity (from middle of transition)
312+
_t->_oldSegment->cct = currentCCT(); // update CCT (from middle of transition)
302313
} else {
303314
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_colors[i].nblend(colors[i], _t->_progress);
304315
_t->_palette = palette; // update palette and colors (from middle of transition)
316+
_t->_bri = currentBri(); // update opacity (from middle of transition)
317+
_t->_cct = currentCCT(); // update CCT (from middle of transition)
305318
}
306-
// we do the same for opacity and CCT (using methods which work correctly as we are already in transition)
307-
_t->_bri = currentBri();
308-
_t->_cct = currentCCT();
309319
DEBUGFX_PRINTF_P(PSTR("-- Updated transition: S=%p T(%p) O[%p]\n"), this, _t, _t->_oldSegment);
310-
// we should not restart timers for non-FADE transitions
311-
if (transitionStyle == TRANSITION_FADE) {
312-
_t->_start = millis(); // restart countdown
313-
_t->_dur = dur; // update duration
314-
_t->_prevPaletteBlends = 0; // reset previous palette blends
315-
}
316320
}
317321
return;
318322
}
@@ -341,7 +345,7 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) {
341345

342346
void Segment::stopTransition() {
343347
DEBUGFX_PRINTF_P(PSTR("-- Stopping transition: S=%p T(%p) O[%p]\n"), this, _t, _t->_oldSegment);
344-
delete _t;
348+
delete _t; // will also call destructor for _oldSegment and free its memory
345349
_t = nullptr;
346350
}
347351

@@ -537,7 +541,7 @@ Segment &Segment::setColor(uint8_t slot, CRGBA c) {
537541
if (slot == 0 && c == BLACK) return *this; // on/off segment cannot have primary color black
538542
if (slot == 1 && c != BLACK) return *this; // on/off segment cannot have secondary color non black
539543
}
540-
//DEBUGFX_PRINTF_P(PSTR("- Starting color transition: %d [0x%X]\n"), slot, c);
544+
DEBUGFX_PRINTF_P(PSTR("- Starting color transition: %d [0x%X]\n"), slot, c);
541545
startTransition(strip.getTransition(), transitionStyle != TRANSITION_FADE); // start transition prior to change
542546
colors[slot] = c;
543547
stateChanged = true; // send UDP/WS broadcast
@@ -551,7 +555,7 @@ Segment &Segment::setCCT(uint16_t k) {
551555
k = (k - 1900) >> 5;
552556
}
553557
if (cct != k) {
554-
//DEBUGFX_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k);
558+
DEBUGFX_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k);
555559
startTransition(strip.getTransition(), false); // start transition prior to change (no need to copy segment)
556560
cct = k;
557561
stateChanged = true; // send UDP/WS broadcast
@@ -561,7 +565,7 @@ Segment &Segment::setCCT(uint16_t k) {
561565

562566
Segment &Segment::setOpacity(uint8_t o) {
563567
if (opacity != o) {
564-
//DEBUGFX_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o);
568+
DEBUGFX_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o);
565569
startTransition(strip.getTransition(), transitionStyle != TRANSITION_FADE); // start transition prior to change
566570
opacity = o;
567571
stateChanged = true; // send UDP/WS broadcast
@@ -572,8 +576,10 @@ Segment &Segment::setOpacity(uint8_t o) {
572576
Segment &Segment::setOption(uint8_t n, bool val) {
573577
bool prev = (options >> n) & 0x01;
574578
if (val == prev) return *this;
575-
//DEBUGFX_PRINTF_P(PSTR("- Starting option transition: %d\n"), n);
576-
if (n == SEG_OPTION_ON) startTransition(strip.getTransition(), transitionStyle != TRANSITION_FADE); // start transition prior to change
579+
if (n == SEG_OPTION_ON) {
580+
DEBUGFX_PRINTF_P(PSTR("- Starting on/off transition: %d\n"), n);
581+
startTransition(strip.getTransition(), transitionStyle != TRANSITION_FADE); // start transition prior to change
582+
}
577583
if (val) options |= 0x01 << n;
578584
else options &= ~(0x01 << n);
579585
stateChanged = true; // send UDP/WS broadcast
@@ -622,7 +628,7 @@ Segment &Segment::setMode(uint8_t fx, bool loadDefaults) {
622628
Segment &Segment::setPalette(uint8_t pal) {
623629
if (pal <= 255-customPalettes.size() && pal > FIXED_PALETTE_COUNT) pal = 0; // not built in palette or custom palette
624630
if (pal != palette) {
625-
//DEBUGFX_PRINTF_P(PSTR("- Starting palette transition: %d\n"), pal);
631+
DEBUGFX_PRINTF_P(PSTR("- Starting palette transition: %d\n"), pal);
626632
startTransition(strip.getTransition(), transitionStyle != TRANSITION_FADE); // start transition prior to change (no need to copy segment)
627633
palette = pal;
628634
stateChanged = true; // send UDP/WS broadcast
@@ -1582,6 +1588,7 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
15821588
const int oCols = segO ? segO->virtualWidth() : nCols;
15831589
const int oRows = segO ? segO->virtualHeight() : nRows;
15841590

1591+
// TODO: CCT would require same treatement as opacity (but is rare and hence not yet implemented)
15851592
const auto setMirroredPixel = [&](int x, int y, uint32_t c, uint8_t o) {
15861593
const int baseX = topSegment.start + x;
15871594
const int baseY = topSegment.startY + y;
@@ -1746,7 +1753,8 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
17461753
// if segment is in transition and pixel is clipped take old segment's pixel and opacity
17471754
// for clipped pixels use old segment's opacity (allow segment on/off/brightness transitions)
17481755
if (clipped && segO) {
1749-
o = segO->currentBri();
1756+
o = segO->on ? segO->opacity : 0; // old segment is never in transition (_t == nullptr) so no need for currentBri()
1757+
//cct = segO->cct; // TODO: CCT would require same treatement as opacity
17501758
_pixelsR = _pixelsO;
17511759
vCols = oCols;
17521760
vRows = oRows;
@@ -1800,6 +1808,7 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
18001808
const int nLen = topSegment.virtualLength();
18011809
const int oLen = segO ? segO->virtualLength() : nLen;
18021810

1811+
// TODO: CCT would require same treatement as opacity (but is rare and hence not yet implemented)
18031812
const auto setMirroredPixel = [&](int i, uint32_t c, uint8_t o) {
18041813
int indx = topSegment.start + i;
18051814
// Apply mirroring
@@ -1853,7 +1862,8 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
18531862
const Segment *seg;
18541863
int vLen;
18551864
if (clipped && segO) {
1856-
o = segO->currentBri();
1865+
o = segO->on ? segO->opacity : 0; // old segment is never in transition (_t == nullptr) so no need for currentBri()
1866+
//cct = segO->cct; // TODO: CCT would require same treatement as opacity
18571867
vLen = oLen;
18581868
seg = segO;
18591869
} else {

0 commit comments

Comments
 (0)