@@ -282,6 +282,7 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) {
282282 _t->_oldSegment = new (std::nothrow) Segment (*this ); // store/copy current segment settings
283283 _t->_start = millis (); // restart countdown
284284 _t->_dur = dur;
285+ _t->_prevPaletteBlends = 0 ;
285286 if (_t->_oldSegment ) {
286287 _t->_oldSegment ->palette = _t->_palette ; // restore original palette and colors (from start of transition)
287288 for (unsigned i = 0 ; i < NUM_COLORS; i++) _t->_oldSegment ->colors [i] = _t->_colors [i];
@@ -368,6 +369,7 @@ void Segment::beginDraw(uint16_t prog) {
368369 // minimum blend time is 100ms maximum is 65535ms
369370 #ifndef WLED_SAVE_RAM
370371 unsigned noOfBlends = ((255U * prog) / 0xFFFFU ) - _t->_prevPaletteBlends ;
372+ if (noOfBlends > 255 ) noOfBlends = 255 ; // safety check
371373 for (unsigned i = 0 ; i < noOfBlends; i++, _t->_prevPaletteBlends ++) nblendPaletteTowardPalette (_t->_palT , Segment::_currentPalette, 48 );
372374 Segment::_currentPalette = _t->_palT ; // copy transitioning/temporary palette
373375 #else
@@ -1192,8 +1194,9 @@ void WS2812FX::finalizeInit() {
11921194 if (busEnd > _length) _length = busEnd;
11931195 // This must be done after all buses have been created, as some kinds (parallel I2S) interact
11941196 bus->begin ();
1195- bus->setBrightness (bri);
1197+ bus->setBrightness (scaledBri ( bri) );
11961198 }
1199+ BusManager::initializeABL (); // init brightness limiter
11971200 DEBUG_PRINTF_P (PSTR (" Heap after buses: %d\n " ), ESP.getFreeHeap ());
11981201
11991202 Segment::maxWidth = _length;
@@ -1295,7 +1298,7 @@ static uint8_t _add (uint8_t a, uint8_t b) { unsigned t = a + b; return t
12951298static uint8_t _subtract (uint8_t a, uint8_t b) { return b > a ? (b - a) : 0 ; }
12961299static uint8_t _difference (uint8_t a, uint8_t b) { return b > a ? (b - a) : (a - b); }
12971300static uint8_t _average (uint8_t a, uint8_t b) { return (a + b) >> 1 ; }
1298- #ifdef CONFIG_IDF_TARGET_ESP32C3
1301+ #if defined(ESP8266) || defined( CONFIG_IDF_TARGET_ESP32C3)
12991302static uint8_t _multiply (uint8_t a, uint8_t b) { return ((a * b) + 255 ) >> 8 ; } // faster than division on C3 but slightly less accurate
13001303#else
13011304static uint8_t _multiply (uint8_t a, uint8_t b) { return (a * b) / 255 ; } // origianl uses a & b in range [0,1]
@@ -1306,10 +1309,10 @@ static uint8_t _darken (uint8_t a, uint8_t b) { return a < b ? a : b; }
13061309static uint8_t _screen (uint8_t a, uint8_t b) { return 255 - _multiply (~a,~b); } // 255 - (255-a)*(255-b)/255
13071310static uint8_t _overlay (uint8_t a, uint8_t b) { return b < 128 ? 2 * _multiply (a,b) : (255 - 2 * _multiply (~a,~b)); }
13081311static uint8_t _hardlight (uint8_t a, uint8_t b) { return a < 128 ? 2 * _multiply (a,b) : (255 - 2 * _multiply (~a,~b)); }
1309- #ifdef CONFIG_IDF_TARGET_ESP32C3
1310- static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a) + 255 ) >> 8 ) + 2 * a * b + 255 ) >> 8 ; } // Pegtop's formula (1 - 2a)b^2 + 2ab
1312+ #if defined(ESP8266) || defined( CONFIG_IDF_TARGET_ESP32C3)
1313+ static uint8_t _softlight (uint8_t a, uint8_t b) { return (((b * b * (255 - 2 * a)) ) + (( 2 * a * b + 256 ) << 8 )) >> 16 ; } // Pegtop's formula (1 - 2a)b^2
13111314#else
1312- static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) / 255 + 2 * a * b) / 255 ; } // Pegtop's formula (1 - 2a)b^2 + 2ab
1315+ static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) + 255 * 2 * a * b) / ( 255 * 255 ) ; } // Pegtop's formula (1 - 2a)b^2 + 2ab
13131316#endif
13141317static uint8_t _dodge (uint8_t a, uint8_t b) { return _divide (~a,b); }
13151318static uint8_t _burn (uint8_t a, uint8_t b) { return ~_divide (a,~b); }
@@ -1548,66 +1551,6 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
15481551 Segment::setClippingRect (0 , 0 ); // disable clipping for overlays
15491552}
15501553
1551- // To disable brightness limiter we either set output max current to 0 or single LED current to 0
1552- static uint8_t estimateCurrentAndLimitBri (uint8_t brightness, uint32_t *pixels) {
1553- unsigned milliAmpsMax = BusManager::ablMilliampsMax ();
1554- if (milliAmpsMax > 0 ) {
1555- unsigned milliAmpsTotal = 0 ;
1556- unsigned avgMilliAmpsPerLED = 0 ;
1557- unsigned lengthDigital = 0 ;
1558- bool useWackyWS2815PowerModel = false ;
1559-
1560- for (size_t i = 0 ; i < BusManager::getNumBusses (); i++) {
1561- const Bus *bus = BusManager::getBus (i);
1562- if (!(bus && bus->isDigital () && bus->isOk ())) continue ;
1563- unsigned maPL = bus->getLEDCurrent ();
1564- if (maPL == 0 || bus->getMaxCurrent () > 0 ) continue ; // skip buses with 0 mA per LED or max current per bus defined (PP-ABL)
1565- if (maPL == 255 ) {
1566- useWackyWS2815PowerModel = true ;
1567- maPL = 12 ; // WS2815 uses 12mA per channel
1568- }
1569- avgMilliAmpsPerLED += maPL * bus->getLength ();
1570- lengthDigital += bus->getLength ();
1571- // sum up the usage of each LED on digital bus
1572- uint32_t busPowerSum = 0 ;
1573- for (unsigned j = 0 ; j < bus->getLength (); j++) {
1574- uint32_t c = pixels[j + bus->getStart ()];
1575- byte r = R (c), g = G (c), b = B (c), w = W (c);
1576- if (useWackyWS2815PowerModel) { // ignore white component on WS2815 power calculation
1577- busPowerSum += (max (max (r,g),b)) * 3 ;
1578- } else {
1579- busPowerSum += (r + g + b + w);
1580- }
1581- }
1582- // RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
1583- if (bus->hasWhite ()) {
1584- busPowerSum *= 3 ;
1585- busPowerSum >>= 2 ; // same as /= 4
1586- }
1587- // powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
1588- milliAmpsTotal += (busPowerSum * maPL * brightness) / (765 *255 );
1589- }
1590- if (lengthDigital > 0 ) {
1591- avgMilliAmpsPerLED /= lengthDigital;
1592-
1593- if (milliAmpsMax > MA_FOR_ESP && avgMilliAmpsPerLED > 0 ) { // 0 mA per LED and too low numbers turn off calculation
1594- unsigned powerBudget = (milliAmpsMax - MA_FOR_ESP); // 80/120mA for ESP power
1595- if (powerBudget > lengthDigital) { // each LED uses about 1mA in standby, exclude that from power budget
1596- powerBudget -= lengthDigital;
1597- } else {
1598- powerBudget = 0 ;
1599- }
1600- if (milliAmpsTotal > powerBudget) {
1601- // scale brightness down to stay in current limit
1602- unsigned scaleB = powerBudget * 255 / milliAmpsTotal;
1603- brightness = ((brightness * scaleB) >> 8 ) + 1 ;
1604- }
1605- }
1606- }
1607- }
1608- return brightness;
1609- }
1610-
16111554void WS2812FX::show () {
16121555 if (!_pixels) return ; // no pixels allocated, nothing to show
16131556
@@ -1635,10 +1578,6 @@ void WS2812FX::show() {
16351578 show_callback callback = _callback;
16361579 if (callback) callback (); // will call setPixelColor or setRealtimePixelColor
16371580
1638- // determine ABL brightness
1639- uint8_t newBri = estimateCurrentAndLimitBri (_brightness, _pixels);
1640- if (newBri != _brightness) BusManager::setBrightness (newBri);
1641-
16421581 // paint actual pixels
16431582 int oldCCT = Bus::getCCT (); // store original CCT value (since it is global)
16441583 // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1)
@@ -1649,7 +1588,11 @@ void WS2812FX::show() {
16491588 if (_pixelCCT) { // cctFromRgb already exluded at allocation
16501589 if (i == 0 || _pixelCCT[i-1 ] != _pixelCCT[i]) BusManager::setSegmentCCT (_pixelCCT[i], correctWB);
16511590 }
1652- BusManager::setPixelColor (getMappedPixelIndex (i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32 (_pixels[i]));
1591+
1592+ uint32_t c = _pixels[i]; // need a copy, do not modify _pixels directly (no byte access allowed on ESP32)
1593+ if (c > 0 && !(realtimeMode && arlsDisableGammaCorrection))
1594+ c = gamma32 (c); // apply gamma correction if enabled note: applying gamma after brightness has too much color loss
1595+ BusManager::setPixelColor (getMappedPixelIndex (i), c);
16531596 }
16541597 Bus::setCCT (oldCCT); // restore old CCT for ABL adjustments
16551598
@@ -1661,9 +1604,6 @@ void WS2812FX::show() {
16611604 // See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
16621605 BusManager::show ();
16631606
1664- // restore brightness for next frame
1665- if (newBri != _brightness) BusManager::setBrightness (_brightness);
1666-
16671607 if (diff > 0 ) { // skip calculation if no time has passed
16681608 size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math
16691609 _cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2 ) / (FPS_CALC_AVG + 1 ); // "+FPS_CALC_AVG/2" for proper rounding
@@ -1728,7 +1668,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
17281668 if (_brightness == 0 ) { // unfreeze all segments on power off
17291669 for (const Segment &seg : _segments) seg.freeze = false ; // freeze is mutable
17301670 }
1731- BusManager::setBrightness (b );
1671+ BusManager::setBrightness (scaledBri (b) );
17321672 if (!direct) {
17331673 unsigned long t = millis ();
17341674 if (_segments[0 ].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger (); // apply brightness change immediately if no refresh soon
0 commit comments