robustness improvements from upstream

(from upstream alt-buffer branch)
* prevent drawing into inactive segments
* robustness improvements for transitions
This commit is contained in:
Frank
2023-07-13 18:34:46 +02:00
parent c22c92e3af
commit 0fb5edc4a2
3 changed files with 49 additions and 18 deletions

View File

@@ -429,7 +429,7 @@ typedef struct Segment {
//uint16_t _aux0, _aux1; // previous mode/effect runtime data
//uint32_t _step, _call; // previous mode/effect runtime data
//byte *_data; // previous mode/effect runtime data
uint32_t _start;
unsigned long _start; // must accommodate millis()
uint16_t _dur;
Transition(uint16_t dur=750)
: _briT(255)
@@ -518,8 +518,8 @@ typedef struct Segment {
#endif
if (!Segment::_globalLeds && (ledsrgb != nullptr)) {free(ledsrgb); ledsrgb = nullptr;}
if (name) delete[] name;
if (_t) delete _t;
if (name) { delete[] name; name = nullptr; }
if (_t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
}
@@ -537,7 +537,7 @@ typedef struct Segment {
inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // WLEDMM make sure its always > 0
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength(void) const { return max(1, grouping + spacing); } // WLEDMM length = 0 could lead to div/0 in virtualWidth() and virtualHeight()

View File

@@ -306,6 +306,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa, bool fast
// returns RGBW values of pixel
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
if (!isActive()) return 0; // not active
int i = XY(x,y);
if (ledsrgb) return RGBW32(ledsrgb[i].r, ledsrgb[i].g, ledsrgb[i].b, 0);
if (reverse ) x = virtualWidth() - x - 1;
@@ -324,6 +325,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col);
@@ -343,12 +345,14 @@ void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColorXY(x, y, pix);
}
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
@@ -376,6 +380,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
@@ -455,6 +460,7 @@ void Segment::blur1d(fract8 blur_amount) { //WLEDMM: use fast types
}
void Segment::moveX(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= cols) return;
@@ -472,6 +478,7 @@ void Segment::moveX(int8_t delta, bool wrap) {
}
void Segment::moveY(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= rows) return;
@@ -507,6 +514,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
}
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
// Bresenhams Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
@@ -531,6 +539,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) {
@@ -544,6 +553,7 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
}
void Segment::nscale8(uint8_t scale) { //WLEDMM: use fast types
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
for(uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) {
@@ -553,6 +563,7 @@ void Segment::nscale8(uint8_t scale) { //WLEDMM: use fast types
//line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
@@ -569,6 +580,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
}
void Segment::drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor) {
if (!isActive()) return; // not active
// float step = degrees / (2.85f*MAX(radius,1));
// for (float rad = 0.0f; rad <= degrees+step/2; rad += step) {
// // may want to try float version as well (with or without antialiasing)
@@ -637,6 +649,7 @@ bool Segment::jsonToPixels(char * name, uint8_t fileNr) {
// draws a raster font character on canvas
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) {
if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries
const uint16_t cols = virtualWidth();
@@ -672,6 +685,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
if (!isActive()) return; // not active
// extract the fractional parts and derive their inverses
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel

View File

@@ -96,6 +96,7 @@ uint16_t Segment::maxHeight = 1;
Segment::Segment(const Segment &orig) {
USER_PRINTLN(F("-- Copy segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment)); //WLEDMM copy to this
transitional = false; // copied segment cannot be in transition
name = nullptr;
data = nullptr;
_dataLen = 0;
@@ -103,8 +104,8 @@ Segment::Segment(const Segment &orig) {
if (ledsrgb && !Segment::_globalLeds) {ledsrgb = nullptr; ledsrgbSize = 0;} // WLEDMM
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
else markForReset(); // WLEDMM
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
//else markForReset(); // WLEDMM
// if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM
jMap = nullptr; //WLEDMM jMap
}
@@ -136,6 +137,7 @@ void Segment::allocLeds() {
Segment::Segment(Segment &&orig) noexcept {
USER_PRINTLN(F("-- Move segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
@@ -150,6 +152,7 @@ Segment& Segment::operator= (const Segment &orig) {
USER_PRINTLN(F("-- Copy-assignment segment --"));
if (this != &orig) {
// clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name;
if (_t) delete _t;
CRGB* oldLeds = ledsrgb;
@@ -158,6 +161,7 @@ Segment& Segment::operator= (const Segment &orig) {
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data
name = nullptr;
data = nullptr;
@@ -168,8 +172,8 @@ Segment& Segment::operator= (const Segment &orig) {
// copy source data
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
else markForReset(); // WLEDMM
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
//else markForReset(); // WLEDMM
//if (orig.ledsrgb && !Segment::_globalLeds) { allocLeds(); if (ledsrgb) memcpy(ledsrgb, orig.ledsrgb, sizeof(CRGB)*length()); } // WLEDMM don't copy old buffer
jMap = nullptr; //WLEDMM jMap
}
@@ -180,9 +184,10 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept {
USER_PRINTLN(F("-- Move-assignment segment --"));
if (this != &orig) {
if (name) delete[] name; // free old name
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) delete _t;
if (_t) { delete _t; _t = nullptr; }
if (ledsrgb && !Segment::_globalLeds) free(ledsrgb); //WLEDMM: not needed anymore as we will use leds from copy. no need to nullify ledsrgb as it gets new value in memcpy
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.name = nullptr;
@@ -408,16 +413,16 @@ void Segment::startTransition(uint16_t dur) {
// transition progression between 0-65535
uint16_t Segment::progress() {
if (!transitional || !_t) return 0xFFFFU;
uint32_t timeNow = millis();
unsigned long timeNow = millis();
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU;
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
}
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
if (transitional && _t) {
uint32_t prog = progress() + 1;
if (useCct) return ((briNew * prog) + _t->_cctT * (0x10000 - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0x10000 - prog)) >> 16;
uint32_t prog = progress();
if (transitional && _t && prog < 0xFFFFU) {
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
} else {
return briNew;
}
@@ -437,7 +442,7 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
uint32_t timeMS = millis() - _t->_start;
unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette
@@ -828,6 +833,7 @@ void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH,
void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionaly
{
if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif
@@ -974,6 +980,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT
// anti-aliased normalized version of setPixelColor()
void Segment::setPixelColor(float i, uint32_t col, bool aa)
{
if (!isActive()) return; // not active
int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows)
i -= int(i);
@@ -1005,6 +1012,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i)
{
if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16;
#endif
@@ -1099,6 +1107,11 @@ void Segment::refreshLightCapabilities() {
uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0;
if (!isActive()) {
_capabilities = 0;
return;
}
if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
@@ -1143,6 +1156,7 @@ void Segment::refreshLightCapabilities() {
* Fills segment with color
*/
void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types
const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D
for(uint_fast16_t y = 0; y < rows; y++) for (uint_fast16_t x = 0; x < cols; x++) {
@@ -1158,6 +1172,7 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active
uint32_t col = getPixelColor(n);
uint8_t r = R(col);
uint8_t g = G(col);
@@ -1176,6 +1191,7 @@ void Segment::addPixelColor(int n, uint32_t color, bool fast) {
}
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
setPixelColor(n, pix);
}
@@ -1184,6 +1200,7 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
* fade out function, higher rate = quicker fade
*/
void Segment::fade_out(uint8_t rate) {
if (!isActive()) return; // not active
const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types
const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D
@@ -1222,7 +1239,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply
if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const uint_fast16_t cols = is2D() ? virtualWidth() : virtualLength(); // WLEDMM use fast int types
const uint_fast16_t rows = virtualHeight(); // will be 1 for 1D
const uint_fast8_t scaledown = 255-fadeBy; // WLEDMM faster to pre-compute this
@@ -1244,7 +1261,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/
void Segment::blur(uint8_t blur_amount)
{
if (blur_amount == 0) return; // optimization: 0 means "don't blur"
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D
if (is2D()) {
// compatibility with 2D