Merge branch 'main' into blending-styles
This commit is contained in:
@@ -2,24 +2,10 @@
|
||||
WS2812FX_fcn.cpp contains all utility functions
|
||||
Harm Aldick - 2016
|
||||
www.aldick.org
|
||||
LICENSE
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Harm Aldick
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Licensed under the EUPL v. 1.2 or later
|
||||
Adapted from code originally licensed under the MIT license
|
||||
|
||||
Modified heavily for WLED
|
||||
*/
|
||||
@@ -80,15 +66,21 @@ static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTy
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Segment class implementation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
uint16_t Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[]
|
||||
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
|
||||
uint16_t Segment::maxHeight = 1;
|
||||
|
||||
unsigned Segment::_usedSegmentData = 0U; // amount of RAM all segments use for their data[]
|
||||
uint16_t Segment::maxWidth = DEFAULT_LED_COUNT;
|
||||
uint16_t Segment::maxHeight = 1;
|
||||
unsigned Segment::_vLength = 0;
|
||||
unsigned Segment::_vWidth = 0;
|
||||
unsigned Segment::_vHeight = 0;
|
||||
uint8_t Segment::_segBri = 0;
|
||||
uint32_t Segment::_currentColors[NUM_COLORS] = {0,0,0};
|
||||
bool Segment::_colorScaled = false;
|
||||
CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black);
|
||||
CRGBPalette16 Segment::_randomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR);
|
||||
CRGBPalette16 Segment::_newRandomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR);
|
||||
uint16_t Segment::_lastPaletteChange = 0; // perhaps it should be per segment
|
||||
uint16_t Segment::_lastPaletteBlend = 0; //in millis (lowest 16 bits only)
|
||||
uint16_t Segment::_transitionprogress = 0xFFFF;
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
bool Segment::_modeBlend = false;
|
||||
@@ -213,24 +205,12 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
|
||||
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip
|
||||
//default palette. Differs depending on effect
|
||||
if (pal == 0) switch (mode) {
|
||||
case FX_MODE_FIRE_2012 : pal = 35; break; // heat palette
|
||||
case FX_MODE_COLORWAVES : pal = 26; break; // landscape 33
|
||||
case FX_MODE_FILLNOISE8 : pal = 9; break; // ocean colors
|
||||
case FX_MODE_NOISE16_1 : pal = 20; break; // Drywet
|
||||
case FX_MODE_NOISE16_2 : pal = 43; break; // Blue cyan yellow
|
||||
case FX_MODE_NOISE16_3 : pal = 35; break; // heat palette
|
||||
case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33
|
||||
case FX_MODE_GLITTER : pal = 11; break; // rainbow colors
|
||||
case FX_MODE_SUNRISE : pal = 35; break; // heat palette
|
||||
case FX_MODE_RAILWAY : pal = 3; break; // prim + sec
|
||||
case FX_MODE_2DSOAP : pal = 11; break; // rainbow colors
|
||||
}
|
||||
if (pal == 0) pal = _default_palette; //load default palette set in FX _data, party colors as default
|
||||
switch (pal) {
|
||||
case 0: //default palette. Exceptions for specific effects above
|
||||
targetPalette = PartyColors_p; break;
|
||||
case 1: //randomly generated palette
|
||||
targetPalette = _randomPalette; //random palette is generated at intervals in handleRandomPalette()
|
||||
targetPalette = _randomPalette; //random palette is generated at intervals in handleRandomPalette()
|
||||
break;
|
||||
case 2: {//primary color only
|
||||
CRGB prim = gamma32(colors[0]);
|
||||
@@ -254,23 +234,11 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
targetPalette = CRGBPalette16(prim,prim,prim,prim,prim,prim,prim,prim,sec,sec,sec,sec,sec,sec,sec,sec);
|
||||
}
|
||||
break;}
|
||||
case 6: //Party colors
|
||||
targetPalette = PartyColors_p; break;
|
||||
case 7: //Cloud colors
|
||||
targetPalette = CloudColors_p; break;
|
||||
case 8: //Lava colors
|
||||
targetPalette = LavaColors_p; break;
|
||||
case 9: //Ocean colors
|
||||
targetPalette = OceanColors_p; break;
|
||||
case 10: //Forest colors
|
||||
targetPalette = ForestColors_p; break;
|
||||
case 11: //Rainbow colors
|
||||
targetPalette = RainbowColors_p; break;
|
||||
case 12: //Rainbow stripe colors
|
||||
targetPalette = RainbowStripeColors_p; break;
|
||||
default: //progmem palettes
|
||||
if (pal>245) {
|
||||
targetPalette = strip.customPalettes[255-pal]; // we checked bounds above
|
||||
} else if (pal < 13) { // palette 6 - 12, fastled palettes
|
||||
targetPalette = *fastledPalettes[pal-6];
|
||||
} else {
|
||||
byte tcp[72];
|
||||
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[pal-13])), 72);
|
||||
@@ -332,12 +300,12 @@ void Segment::stopTransition() {
|
||||
}
|
||||
|
||||
// transition progression between 0-65535
|
||||
uint16_t IRAM_ATTR Segment::progress() const {
|
||||
inline void Segment::updateTransitionProgress() {
|
||||
_transitionprogress = 0xFFFFU;
|
||||
if (isInTransition()) {
|
||||
unsigned diff = millis() - _t->_start;
|
||||
if (_t->_dur > 0 && diff < _t->_dur) return diff * 0xFFFFU / _t->_dur;
|
||||
if (_t->_dur > 0 && diff < _t->_dur) _transitionprogress = diff * 0xFFFFU / _t->_dur;
|
||||
}
|
||||
return 0xFFFFU;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
@@ -411,13 +379,14 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t IRAM_ATTR Segment::currentBri(bool useCct) const {
|
||||
uint32_t prog = progress();
|
||||
uint8_t Segment::currentBri(bool useCct) const {
|
||||
unsigned prog = progress();
|
||||
uint32_t curBri = useCct ? cct : (on ? opacity : 0);
|
||||
if (prog < 0xFFFFU) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
uint8_t tmpBri = useCct ? _t->_cctT : (_t->_segT._optionsT & 0x0004 ? _t->_briT : 0);
|
||||
if (blendingStyle > BLEND_STYLE_FADE) return _modeBlend ? tmpBri : curBri; // not fade/blend transition, each effect uses its brightness
|
||||
// _modeBlend==true -> old effect
|
||||
if (blendingStyle != BLEND_STYLE_FADE) return _modeBlend ? tmpBri : curBri; // not fade/blend transition, each effect uses its brightness
|
||||
#else
|
||||
uint8_t tmpBri = useCct ? _t->_cctT : _t->_briT;
|
||||
#endif
|
||||
@@ -432,48 +401,51 @@ uint8_t Segment::currentMode() const {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
unsigned prog = progress();
|
||||
if (prog == 0xFFFFU) return mode;
|
||||
if (blendingStyle > BLEND_STYLE_FADE) {
|
||||
if (blendingStyle != BLEND_STYLE_FADE) {
|
||||
// workaround for on/off transition to respect blending style
|
||||
uint8_t modeT = (bri != briT) && bri ? FX_MODE_STATIC : _t->_modeT; // On/Off transition active (bri!=briT) and final bri>0 : old mode is STATIC
|
||||
uint8_t modeS = (bri != briT) && !bri ? FX_MODE_STATIC : mode; // On/Off transition active (bri!=briT) and final bri==0 : new mode is STATIC
|
||||
return _modeBlend ? modeT : modeS;
|
||||
return _modeBlend ? modeT : modeS; // _modeBlend==true -> old effect
|
||||
}
|
||||
return _modeBlend ? _t->_modeT : mode;
|
||||
return _modeBlend ? _t->_modeT : mode; // _modeBlend==true -> old effect
|
||||
#else
|
||||
return mode;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const {
|
||||
uint32_t Segment::currentColor(uint8_t slot) const {
|
||||
if (slot >= NUM_COLORS) slot = 0;
|
||||
uint32_t prog = progress();
|
||||
unsigned prog = progress();
|
||||
if (prog == 0xFFFFU) return colors[slot];
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (blendingStyle > BLEND_STYLE_FADE) {
|
||||
if (blendingStyle != BLEND_STYLE_FADE) {
|
||||
// workaround for on/off transition to respect blending style
|
||||
uint32_t colT = (bri != briT) && bri ? BLACK : _t->_segT._colorT[slot]; // On/Off transition active (bri!=briT) and final bri>0 : old color is BLACK
|
||||
uint32_t colS = (bri != briT) && !bri ? BLACK : colors[slot]; // On/Off transition active (bri!=briT) and final bri==0 : new color is BLACK
|
||||
return _modeBlend ? colT : colS;
|
||||
return _modeBlend ? colT : colS; // _modeBlend==true -> old effect
|
||||
}
|
||||
return color_blend(_t->_segT._colorT[slot], colors[slot], prog, true);
|
||||
return color_blend16(_t->_segT._colorT[slot], colors[slot], prog);
|
||||
#else
|
||||
return color_blend(_t->_colorT[slot], colors[slot], prog, true);
|
||||
return color_blend16(_t->_colorT[slot], colors[slot], prog);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR Segment::currentPalette() const {
|
||||
unsigned prog = progress();
|
||||
if (prog < 0xFFFFU) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (blendingStyle > BLEND_STYLE_FADE && _modeBlend) return _t->_palTid;
|
||||
#else
|
||||
return _t->_palTid;
|
||||
#endif
|
||||
// pre-calculate drawing parameters for faster access (based on the idea from @softhack007 from MM fork)
|
||||
void Segment::beginDraw() {
|
||||
_vWidth = virtualWidth();
|
||||
_vHeight = virtualHeight();
|
||||
_vLength = virtualLength();
|
||||
_segBri = currentBri();
|
||||
// adjust gamma for effects
|
||||
for (unsigned i = 0; i < NUM_COLORS; i++) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
uint32_t col = isInTransition() ? color_blend16(_t->_segT._colorT[i], colors[i], progress()) : colors[i];
|
||||
#else
|
||||
uint32_t col = isInTransition() ? color_blend16(_t->_colorT[i], colors[i], progress()) : colors[i];
|
||||
#endif
|
||||
_currentColors[i] = gamma32(col);
|
||||
}
|
||||
return palette;
|
||||
}
|
||||
|
||||
void Segment::setCurrentPalette() {
|
||||
// load palette into _currentPalette
|
||||
loadPalette(_currentPalette, palette);
|
||||
unsigned prog = progress();
|
||||
if (prog < 0xFFFFU) {
|
||||
@@ -510,8 +482,10 @@ void Segment::handleRandomPalette() {
|
||||
nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
|
||||
}
|
||||
|
||||
// segId is given when called from network callback, changes are queued if that segment is currently in its effect function
|
||||
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
|
||||
// sets Segment geometry (length or width/height and grouping, spacing and offset as well as 2D mapping)
|
||||
// strip must be suspended (strip.suspend()) before calling this function
|
||||
// this function may call fill() to clear pixels if spacing or mapping changed (which requires setting _vWidth, _vHeight, _vLength or beginDraw())
|
||||
void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t m12) {
|
||||
// return if neither bounds nor grouping have changed
|
||||
bool boundsUnchanged = (start == i1 && stop == i2);
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@@ -519,11 +493,19 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
|
||||
#endif
|
||||
if (boundsUnchanged
|
||||
&& (!grp || (grouping == grp && spacing == spc))
|
||||
&& (ofs == UINT16_MAX || ofs == offset)) return;
|
||||
&& (ofs == UINT16_MAX || ofs == offset)
|
||||
&& (m12 == map1D2D)
|
||||
) return;
|
||||
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
|
||||
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
|
||||
if (stop || spc != spacing || m12 != map1D2D) {
|
||||
_vWidth = virtualWidth();
|
||||
_vHeight = virtualHeight();
|
||||
_vLength = virtualLength();
|
||||
_segBri = currentBri();
|
||||
fill(BLACK); // turn old segment range off or clears pixels if changing spacing (requires _vWidth/_vHeight/_vLength/_segBri)
|
||||
}
|
||||
if (grp) { // prevent assignment of 0
|
||||
grouping = grp;
|
||||
spacing = spc;
|
||||
@@ -532,6 +514,7 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
|
||||
spacing = 0;
|
||||
}
|
||||
if (ofs < UINT16_MAX) offset = ofs;
|
||||
map1D2D = constrain(m12, 0, 7);
|
||||
|
||||
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
|
||||
DEBUG_PRINT(','); DEBUG_PRINT(i2);
|
||||
@@ -564,49 +547,54 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
|
||||
}
|
||||
|
||||
|
||||
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
|
||||
if (slot >= NUM_COLORS || c == colors[slot]) return false;
|
||||
Segment &Segment::setColor(uint8_t slot, uint32_t c) {
|
||||
if (slot >= NUM_COLORS || c == colors[slot]) return *this;
|
||||
if (!_isRGB && !_hasW) {
|
||||
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
|
||||
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
|
||||
if (slot == 0 && c == BLACK) return *this; // on/off segment cannot have primary color black
|
||||
if (slot == 1 && c != BLACK) return *this; // on/off segment cannot have secondary color non black
|
||||
}
|
||||
//DEBUG_PRINTF_P(PSTR("- Starting color transition: %d [0x%X]\n"), slot, c);
|
||||
startTransition(strip.getTransition()); // start transition prior to change
|
||||
colors[slot] = c;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
return true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Segment::setCCT(uint16_t k) {
|
||||
Segment &Segment::setCCT(uint16_t k) {
|
||||
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;
|
||||
//DEBUG_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k);
|
||||
startTransition(strip.getTransition()); // start transition prior to change
|
||||
cct = k;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
if (cct != k) {
|
||||
//DEBUG_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k);
|
||||
startTransition(strip.getTransition()); // start transition prior to change
|
||||
cct = k;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Segment::setOpacity(uint8_t o) {
|
||||
if (opacity == o) return;
|
||||
//DEBUG_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o);
|
||||
startTransition(strip.getTransition()); // start transition prior to change
|
||||
opacity = o;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
Segment &Segment::setOpacity(uint8_t o) {
|
||||
if (opacity != o) {
|
||||
//DEBUG_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o);
|
||||
startTransition(strip.getTransition()); // start transition prior to change
|
||||
opacity = o;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Segment::setOption(uint8_t n, bool val) {
|
||||
Segment &Segment::setOption(uint8_t n, bool val) {
|
||||
bool prevOn = on;
|
||||
if (n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
|
||||
if (val) options |= 0x01 << n;
|
||||
else options &= ~(0x01 << n);
|
||||
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
Segment &Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
// skip reserved
|
||||
while (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4) == 0) fx++;
|
||||
if (fx >= strip.getModeCount()) fx = 0; // set solid mode
|
||||
@@ -617,9 +605,9 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
startTransition(strip.getTransition()); // set effect transitions
|
||||
#endif
|
||||
mode = fx;
|
||||
int sOpt;
|
||||
// load default values from effect string
|
||||
if (loadDefaults) {
|
||||
int sOpt;
|
||||
sOpt = extractModeDefaults(fx, "sx"); speed = (sOpt >= 0) ? sOpt : DEFAULT_SPEED;
|
||||
sOpt = extractModeDefaults(fx, "ix"); intensity = (sOpt >= 0) ? sOpt : DEFAULT_INTENSITY;
|
||||
sOpt = extractModeDefaults(fx, "c1"); custom1 = (sOpt >= 0) ? sOpt : DEFAULT_C1;
|
||||
@@ -636,12 +624,16 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
|
||||
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0) setPalette(sOpt); //else setPalette(0);
|
||||
}
|
||||
sOpt = extractModeDefaults(fx, "pal"); // always extract 'pal' to set _default_palette
|
||||
if(sOpt <= 0) sOpt = 6; // partycolors if zero or not set
|
||||
_default_palette = sOpt; // _deault_palette is loaded into pal0 in loadPalette() (if selected)
|
||||
markForReset();
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Segment::setPalette(uint8_t pal) {
|
||||
Segment &Segment::setPalette(uint8_t pal) {
|
||||
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes
|
||||
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // custom palettes
|
||||
if (pal != palette) {
|
||||
@@ -650,37 +642,24 @@ void Segment::setPalette(uint8_t pal) {
|
||||
palette = pal;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// 2D matrix
|
||||
uint16_t IRAM_ATTR Segment::virtualWidth() const {
|
||||
unsigned Segment::virtualWidth() const {
|
||||
unsigned groupLen = groupLength();
|
||||
unsigned vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
|
||||
if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vWidth;
|
||||
}
|
||||
|
||||
uint16_t IRAM_ATTR Segment::virtualHeight() const {
|
||||
unsigned Segment::virtualHeight() const {
|
||||
unsigned groupLen = groupLength();
|
||||
unsigned vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
|
||||
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vHeight;
|
||||
}
|
||||
|
||||
uint16_t IRAM_ATTR_YN Segment::nrOfVStrips() const {
|
||||
unsigned vLen = 1;
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
switch (map1D2D) {
|
||||
case M12_pBar:
|
||||
vLen = virtualWidth();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return vLen;
|
||||
}
|
||||
|
||||
// Constants for mapping mode "Pinwheel"
|
||||
#ifndef WLED_DISABLE_2D
|
||||
constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16
|
||||
@@ -718,7 +697,7 @@ static int getPinwheelLength(int vW, int vH) {
|
||||
#endif
|
||||
|
||||
// 1D strip
|
||||
uint16_t IRAM_ATTR Segment::virtualLength() const {
|
||||
uint16_t Segment::virtualLength() const {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
unsigned vW = virtualWidth();
|
||||
@@ -779,19 +758,31 @@ bool IRAM_ATTR_YN Segment::isPixelClipped(int i) const {
|
||||
|
||||
void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
{
|
||||
if (!isActive()) return; // not active
|
||||
if (!isActive() || i < 0) return; // not active or invalid index
|
||||
#ifndef WLED_DISABLE_2D
|
||||
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
|
||||
int vStrip = 0;
|
||||
#endif
|
||||
i &= 0xFFFF;
|
||||
|
||||
int vL = virtualLength();
|
||||
if (i >= vL || i < 0) return; // if pixel would fall out of segment just exit
|
||||
int vL = vLength();
|
||||
// if the 1D effect is using virtual strips "i" will have virtual strip id stored in upper 16 bits
|
||||
// in such case "i" will be > virtualLength()
|
||||
if (i >= vL) {
|
||||
// check if this is a virtual strip
|
||||
#ifndef WLED_DISABLE_2D
|
||||
vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
|
||||
i &= 0xFFFF; //truncate vstrip index
|
||||
if (i >= vL) return; // if pixel would still fall out of segment just exit
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
int vH = virtualHeight(); // segment height in logical pixels
|
||||
int vW = virtualWidth();
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
// pre-scale color for all pixels
|
||||
col = color_fade(col, _segBri);
|
||||
_colorScaled = true;
|
||||
switch (map1D2D) {
|
||||
case M12_Pixels:
|
||||
// use all available pixels as a long strip
|
||||
@@ -799,12 +790,12 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
break;
|
||||
case M12_pBar:
|
||||
// expand 1D effect vertically or have it play on virtual strips
|
||||
if (vStrip>0) setPixelColorXY(vStrip - 1, vH - i - 1, col);
|
||||
else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col);
|
||||
if (vStrip > 0) setPixelColorXY(vStrip - 1, vH - i - 1, col);
|
||||
else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col);
|
||||
break;
|
||||
case M12_pArc:
|
||||
// expand in circular fashion from center
|
||||
if (i==0)
|
||||
if (i == 0)
|
||||
setPixelColorXY(0, 0, col);
|
||||
else {
|
||||
float r = i;
|
||||
@@ -861,7 +852,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
// Odd rays start further from center if prevRay started at center.
|
||||
static int prevRay = INT_MIN; // previous ray number
|
||||
if ((i % 2 == 1) && (i - 1 == prevRay || i + 1 == prevRay)) {
|
||||
int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
|
||||
int jump = min(vW/3, vH/3); // can add 2 if using medium pinwheel
|
||||
posx += inc_x * jump;
|
||||
posy += inc_y * jump;
|
||||
}
|
||||
@@ -883,13 +874,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
break;
|
||||
}
|
||||
}
|
||||
_colorScaled = false;
|
||||
return;
|
||||
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
|
||||
} else if (Segment::maxHeight != 1 && (width() == 1 || height() == 1)) {
|
||||
if (start < Segment::maxWidth*Segment::maxHeight) {
|
||||
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
|
||||
int x = 0, y = 0;
|
||||
if (virtualHeight()>1) y = i;
|
||||
if (virtualWidth() >1) x = i;
|
||||
if (vHeight() > 1) y = i;
|
||||
if (vWidth() > 1) x = i;
|
||||
setPixelColorXY(x, y, col);
|
||||
return;
|
||||
}
|
||||
@@ -909,10 +901,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
if (i >= vL || i < 0 || isPixelClipped(i)) return; // handle clipping on 1D
|
||||
|
||||
unsigned len = length();
|
||||
uint8_t _bri_t = currentBri();
|
||||
if (_bri_t < 255) {
|
||||
col = color_fade(col, _bri_t);
|
||||
}
|
||||
// if color is unscaled
|
||||
if (!_colorScaled) col = color_fade(col, _segBri);
|
||||
|
||||
// expand pixel (taking into account start, grouping, spacing [and offset])
|
||||
i = i * groupLength();
|
||||
@@ -936,7 +926,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
if (indexMir >= stop) indexMir -= len; // wrap
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
// _modeBlend==true -> old effect
|
||||
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
|
||||
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend16(strip.getPixelColor(indexMir), col, 0xFFFFU - progress());
|
||||
#endif
|
||||
strip.setPixelColor(indexMir, tmpCol);
|
||||
}
|
||||
@@ -944,7 +934,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
|
||||
if (indexSet >= stop) indexSet -= len; // wrap
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
// _modeBlend==true -> old effect
|
||||
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
|
||||
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) tmpCol = color_blend16(strip.getPixelColor(indexSet), col, 0xFFFFU - progress());
|
||||
#endif
|
||||
strip.setPixelColor(indexSet, tmpCol);
|
||||
}
|
||||
@@ -989,26 +979,23 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
|
||||
uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
|
||||
{
|
||||
if (!isActive()) return 0; // not active
|
||||
#ifndef WLED_DISABLE_2D
|
||||
int vStrip = i>>16;
|
||||
#endif
|
||||
i &= 0xFFFF;
|
||||
|
||||
int vL = virtualLength();
|
||||
int vL = vLength();
|
||||
if (i >= vL || i < 0) return 0;
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
int vH = virtualHeight(); // segment height in logical pixels
|
||||
int vW = virtualWidth();
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
switch (map1D2D) {
|
||||
case M12_Pixels:
|
||||
return getPixelColorXY(i % vW, i / vW);
|
||||
break;
|
||||
case M12_pBar:
|
||||
if (vStrip>0) return getPixelColorXY(vStrip - 1, vH - i -1);
|
||||
else return getPixelColorXY(0, vH - i -1);
|
||||
break;
|
||||
case M12_pBar: {
|
||||
int vStrip = i>>16; // virtual strips are only relevant in Bar expansion mode
|
||||
if (vStrip > 0) return getPixelColorXY(vStrip - 1, vH - (i & 0xFFFF) -1);
|
||||
else return getPixelColorXY(0, vH - i -1);
|
||||
break; }
|
||||
case M12_pArc:
|
||||
if (i >= vW && i >= vH) {
|
||||
unsigned vI = sqrt16(i*i/2);
|
||||
@@ -1072,7 +1059,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
|
||||
return strip.getPixelColor(i);
|
||||
}
|
||||
|
||||
uint8_t Segment::differs(Segment& b) const {
|
||||
uint8_t Segment::differs(const Segment& b) const {
|
||||
uint8_t d = 0;
|
||||
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
|
||||
@@ -1152,12 +1139,16 @@ void Segment::refreshLightCapabilities() {
|
||||
*/
|
||||
void Segment::fill(uint32_t c) {
|
||||
if (!isActive()) return; // not active
|
||||
const int cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const int rows = virtualHeight(); // will be 1 for 1D
|
||||
const int cols = is2D() ? vWidth() : vLength();
|
||||
const int rows = vHeight(); // will be 1 for 1D
|
||||
// pre-scale color for all pixels
|
||||
c = color_fade(c, _segBri);
|
||||
_colorScaled = true;
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, c);
|
||||
else setPixelColor(x, c);
|
||||
}
|
||||
_colorScaled = false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1165,8 +1156,8 @@ void Segment::fill(uint32_t c) {
|
||||
*/
|
||||
void Segment::fade_out(uint8_t rate) {
|
||||
if (!isActive()) return; // not active
|
||||
const int cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const int rows = virtualHeight(); // will be 1 for 1D
|
||||
const int cols = is2D() ? vWidth() : vLength();
|
||||
const int rows = vHeight(); // will be 1 for 1D
|
||||
|
||||
rate = (255-rate) >> 1;
|
||||
float mappedRate = 1.0f / (float(rate) + 1.1f);
|
||||
@@ -1204,8 +1195,8 @@ void Segment::fade_out(uint8_t rate) {
|
||||
// fades all pixels to black using nscale8()
|
||||
void Segment::fadeToBlackBy(uint8_t fadeBy) {
|
||||
if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
|
||||
const int cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const int rows = virtualHeight(); // will be 1 for 1D
|
||||
const int cols = is2D() ? vWidth() : vLength();
|
||||
const int rows = vHeight(); // will be 1 for 1D
|
||||
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
|
||||
@@ -1215,20 +1206,21 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
|
||||
|
||||
/*
|
||||
* blurs segment content, source: FastLED colorutils.cpp
|
||||
* Note: for blur_amount > 215 this function does not work properly (creates alternating pattern)
|
||||
*/
|
||||
void Segment::blur(uint8_t blur_amount, bool smear) {
|
||||
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
// compatibility with 2D
|
||||
blur2D(blur_amount, smear);
|
||||
blur2D(blur_amount, blur_amount, smear); // symmetrical 2D blur
|
||||
//box_blur(map(blur_amount,1,255,1,3), smear);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint8_t keep = smear ? 255 : 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> (1 + smear);
|
||||
unsigned vlength = virtualLength();
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
unsigned vlength = vLength();
|
||||
uint32_t carryover = BLACK;
|
||||
uint32_t lastnew;
|
||||
uint32_t last;
|
||||
@@ -1238,12 +1230,11 @@ void Segment::blur(uint8_t blur_amount, bool smear) {
|
||||
uint32_t part = color_fade(cur, seep);
|
||||
curnew = color_fade(cur, keep);
|
||||
if (i > 0) {
|
||||
if (carryover) curnew = color_add(curnew, carryover, true);
|
||||
uint32_t prev = color_add(lastnew, part, true);
|
||||
if (carryover) curnew = color_add(curnew, carryover);
|
||||
uint32_t prev = color_add(lastnew, part);
|
||||
// optimization: only set pixel if color has changed
|
||||
if (last != prev) setPixelColor(i - 1, prev);
|
||||
} else // first pixel
|
||||
setPixelColor(i, curnew);
|
||||
} else setPixelColor(i, curnew); // first pixel
|
||||
lastnew = curnew;
|
||||
last = cur; // save original value for comparison on next iteration
|
||||
carryover = part;
|
||||
@@ -1258,11 +1249,11 @@ void Segment::blur(uint8_t blur_amount, bool smear) {
|
||||
*/
|
||||
uint32_t Segment::color_wheel(uint8_t pos) const {
|
||||
if (palette) return color_from_palette(pos, false, true, 0); // perhaps "strip.paletteBlend < 2" should be better instead of "true"
|
||||
uint8_t w = W(currentColor(0));
|
||||
uint8_t w = W(getCurrentColor(0));
|
||||
pos = 255 - pos;
|
||||
if (pos < 85) {
|
||||
return RGBW32((255 - pos * 3), 0, (pos * 3), w);
|
||||
} else if(pos < 170) {
|
||||
} else if (pos < 170) {
|
||||
pos -= 85;
|
||||
return RGBW32(0, (pos * 3), (255 - pos * 3), w);
|
||||
} else {
|
||||
@@ -1281,18 +1272,21 @@ uint32_t Segment::color_wheel(uint8_t pos) const {
|
||||
* @returns Single color from palette
|
||||
*/
|
||||
uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) const {
|
||||
uint32_t color = gamma32(currentColor(mcol));
|
||||
|
||||
uint32_t color = getCurrentColor(mcol < NUM_COLORS ? mcol : 0);
|
||||
// default palette or no RGB support on segment
|
||||
if ((currentPalette() == 0 && mcol < NUM_COLORS) || !_isRGB) return (pbri == 255) ? color : color_fade(color, pbri, true);
|
||||
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
|
||||
return color_fade(color, pbri, true);
|
||||
}
|
||||
|
||||
const int vL = vLength();
|
||||
unsigned paletteIndex = i;
|
||||
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
|
||||
if (mapping && vL > 1) paletteIndex = (i*255)/(vL -1);
|
||||
// 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"
|
||||
CRGB fastled_col = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
CRGBW palcol = ColorFromPaletteWLED(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
palcol.w = W(color);
|
||||
|
||||
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color));
|
||||
return palcol.color32;
|
||||
}
|
||||
|
||||
|
||||
@@ -1303,10 +1297,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
|
||||
//do not call this method from system context (network callback)
|
||||
void WS2812FX::finalizeInit() {
|
||||
//reset segment runtimes
|
||||
for (segment &seg : _segments) {
|
||||
seg.markForReset();
|
||||
seg.resetIfRequired();
|
||||
}
|
||||
restartRuntime();
|
||||
|
||||
// for the lack of better place enumerate ledmaps here
|
||||
// if we do it in json.cpp (serializeInfo()) we are getting flashes on LEDs
|
||||
@@ -1328,7 +1319,7 @@ void WS2812FX::finalizeInit() {
|
||||
|
||||
static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins),
|
||||
"The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES");
|
||||
|
||||
|
||||
unsigned prevLen = 0;
|
||||
unsigned pinsIndex = 0;
|
||||
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
@@ -1339,7 +1330,7 @@ void WS2812FX::finalizeInit() {
|
||||
|
||||
// if we need more pins than available all outputs have been configured
|
||||
if (pinsIndex + busPins > defNumPins) break;
|
||||
|
||||
|
||||
// Assign all pins first so we can check for conflicts on this bus
|
||||
for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j];
|
||||
|
||||
@@ -1392,7 +1383,7 @@ void WS2812FX::finalizeInit() {
|
||||
// if we have less counts than pins and they do not align, use last known count to set current count
|
||||
unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
// analog always has length 1
|
||||
if (Bus::isPWM(dataType)) count = 1;
|
||||
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
|
||||
prevLen += count;
|
||||
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
|
||||
if (BusManager::add(defCfg) == -1) break;
|
||||
@@ -1410,14 +1401,9 @@ void WS2812FX::finalizeInit() {
|
||||
_isOffRefreshRequired |= bus->isOffRefreshRequired() && !bus->isPWM(); // use refresh bit for phase shift with analog
|
||||
unsigned busEnd = bus->getStart() + bus->getLength();
|
||||
if (busEnd > _length) _length = busEnd;
|
||||
#ifdef ESP8266
|
||||
// why do we need to reinitialise GPIO3???
|
||||
//if (!bus->isDigital() || bus->is2Pin()) continue;
|
||||
//uint8_t pins[5];
|
||||
//if (!bus->getPins(pins)) continue;
|
||||
//BusDigital* bd = static_cast<BusDigital*>(bus);
|
||||
//if (pins[0] == 3) bd->reinit();
|
||||
#endif
|
||||
|
||||
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
|
||||
bus->begin();
|
||||
}
|
||||
|
||||
Segment::maxWidth = _length;
|
||||
@@ -1433,9 +1419,15 @@ void WS2812FX::finalizeInit() {
|
||||
void WS2812FX::service() {
|
||||
unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
|
||||
now = nowUp + timebase;
|
||||
if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return;
|
||||
if (_suspend) return;
|
||||
unsigned long elapsed = nowUp - _lastServiceShow;
|
||||
|
||||
if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited
|
||||
if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime
|
||||
if (elapsed < _frametime) return; // too early for service
|
||||
}
|
||||
|
||||
bool doShow = false;
|
||||
int pal = -1; // optimise palette loading
|
||||
|
||||
_isServicing = true;
|
||||
_segment_index = 0;
|
||||
@@ -1451,19 +1443,13 @@ void WS2812FX::service() {
|
||||
if (!seg.isActive()) continue;
|
||||
|
||||
// last condition ensures all solid segments are updated at the same time
|
||||
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
|
||||
if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
|
||||
{
|
||||
doShow = true;
|
||||
unsigned delay = FRAMETIME;
|
||||
unsigned frameDelay = FRAMETIME;
|
||||
|
||||
if (!seg.freeze) { //only run effect function if not frozen
|
||||
int oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
|
||||
_virtualSegmentLength = seg.virtualLength(); //SEGLEN
|
||||
_colors_t[0] = gamma32(seg.currentColor(0));
|
||||
_colors_t[1] = gamma32(seg.currentColor(1));
|
||||
_colors_t[2] = gamma32(seg.currentColor(2));
|
||||
if (seg.currentPalette() != pal) seg.setCurrentPalette(); // load actual palette
|
||||
pal = seg.currentPalette();
|
||||
// when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio
|
||||
// when cctFromRgb is true we implicitly calculate WW and CW from RGB values
|
||||
if (cctFromRgb) BusManager::setSegmentCCT(-1);
|
||||
@@ -1474,14 +1460,15 @@ void WS2812FX::service() {
|
||||
// The blending will largely depend on the effect behaviour since actual output (LEDs) may be
|
||||
// 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.
|
||||
seg.beginDraw(); // set up parameters for get/setPixelColor()
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
Segment::setClippingRect(0, 0); // disable clipping (just in case)
|
||||
if (seg.isInTransition()) {
|
||||
// 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 w = seg.is2D() ? Segment::vWidth() : Segment::vLength();
|
||||
unsigned h = Segment::vHeight();
|
||||
unsigned dw = p * w / 0xFFFFU + 1;
|
||||
unsigned dh = p * h / 0xFFFFU + 1;
|
||||
unsigned orgBS = blendingStyle;
|
||||
@@ -1531,51 +1518,44 @@ void WS2812FX::service() {
|
||||
Segment::setClippingRect(0, dw, h - dh, h);
|
||||
break;
|
||||
}
|
||||
delay = (*_mode[seg.currentMode()])(); // run new/current mode
|
||||
frameDelay = (*_mode[seg.currentMode()])(); // run new/current mode
|
||||
// now run old/previous mode
|
||||
Segment::tmpsegd_t _tmpSegData;
|
||||
Segment::modeBlend(true); // set semaphore
|
||||
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
|
||||
_virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed)
|
||||
_colors_t[0] = gamma32(seg.currentColor(0));
|
||||
_colors_t[1] = gamma32(seg.currentColor(1));
|
||||
_colors_t[2] = gamma32(seg.currentColor(2));
|
||||
if (seg.currentPalette() != pal) {
|
||||
seg.setCurrentPalette(); // load actual palette
|
||||
pal = seg.currentPalette();
|
||||
}
|
||||
unsigned d2 = (*_mode[seg.currentMode()])(); // run old mode
|
||||
seg.beginDraw(); // set up parameters for get/setPixelColor()
|
||||
frameDelay = min(frameDelay, (unsigned)(*_mode[seg.currentMode()])()); // run old mode
|
||||
seg.call++; // increment old mode run counter
|
||||
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
|
||||
delay = MIN(delay,d2); // use shortest delay
|
||||
Segment::modeBlend(false); // unset semaphore
|
||||
blendingStyle = orgBS; // restore blending style if it was modified for single pixel segment
|
||||
} else
|
||||
#endif
|
||||
delay = (*_mode[seg.mode])(); // run effect mode (not in transition)
|
||||
frameDelay = (*_mode[seg.mode])(); // run effect mode (not in transition)
|
||||
seg.call++;
|
||||
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
|
||||
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
|
||||
if (seg.isInTransition() && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition
|
||||
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
|
||||
}
|
||||
|
||||
seg.next_time = nowUp + delay;
|
||||
seg.next_time = nowUp + frameDelay;
|
||||
}
|
||||
_segment_index++;
|
||||
}
|
||||
Segment::setClippingRect(0, 0); // disable clipping for overlays
|
||||
_virtualSegmentLength = 0;
|
||||
_isServicing = false;
|
||||
_triggered = false;
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
|
||||
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
|
||||
#endif
|
||||
if (doShow) {
|
||||
yield();
|
||||
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
|
||||
show();
|
||||
_lastServiceShow = nowUp; // update timestamp, for precise FPS control
|
||||
}
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
|
||||
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1585,7 +1565,7 @@ void IRAM_ATTR WS2812FX::setPixelColor(unsigned i, uint32_t col) {
|
||||
BusManager::setPixelColor(i, col);
|
||||
}
|
||||
|
||||
uint32_t IRAM_ATTR WS2812FX::getPixelColor(uint16_t i) const {
|
||||
uint32_t IRAM_ATTR WS2812FX::getPixelColor(unsigned i) const {
|
||||
i = getMappedPixelIndex(i);
|
||||
if (i >= _length) return 0;
|
||||
return BusManager::getPixelColor(i);
|
||||
@@ -1595,61 +1575,26 @@ void WS2812FX::show() {
|
||||
// avoid race condition, capture _callback value
|
||||
show_callback callback = _callback;
|
||||
if (callback) callback();
|
||||
unsigned long showNow = millis();
|
||||
|
||||
// some buses send asynchronously and this method will return before
|
||||
// all of the data has been sent.
|
||||
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
|
||||
BusManager::show();
|
||||
|
||||
unsigned long showNow = millis();
|
||||
size_t diff = showNow - _lastShow;
|
||||
size_t fpsCurr = 200;
|
||||
if (diff > 0) fpsCurr = 1000 / diff;
|
||||
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
|
||||
_lastShow = showNow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a true value if any of the strips are still being updated.
|
||||
* On some hardware (ESP32), strip updates are done asynchronously.
|
||||
*/
|
||||
bool WS2812FX::isUpdating() const {
|
||||
return !BusManager::canAllShow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough.
|
||||
* Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies
|
||||
*/
|
||||
uint16_t WS2812FX::getFps() const {
|
||||
if (millis() - _lastShow > 2000) return 0;
|
||||
return _cumulativeFps +1;
|
||||
}
|
||||
|
||||
void WS2812FX::setTargetFps(uint8_t fps) {
|
||||
if (fps > 0 && fps <= 120) _targetFps = fps;
|
||||
_frametime = 1000 / _targetFps;
|
||||
}
|
||||
|
||||
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
if (segid >= _segments.size()) return;
|
||||
|
||||
if (m >= getModeCount()) m = getModeCount() - 1;
|
||||
|
||||
if (_segments[segid].mode != m) {
|
||||
_segments[segid].setMode(m); // do not load defaults
|
||||
if (diff > 0) { // skip calculation if no time has passed
|
||||
size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math
|
||||
_cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2) / (FPS_CALC_AVG + 1); // "+FPS_CALC_AVG/2" for proper rounding
|
||||
_lastShow = showNow;
|
||||
}
|
||||
}
|
||||
|
||||
//applies to all active and selected segments
|
||||
void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
if (slot >= NUM_COLORS) return;
|
||||
|
||||
for (segment &seg : _segments) {
|
||||
if (seg.isActive() && seg.isSelected()) {
|
||||
seg.setColor(slot, c);
|
||||
}
|
||||
}
|
||||
void WS2812FX::setTargetFps(unsigned fps) {
|
||||
if (fps <= 250) _targetFps = fps;
|
||||
if (_targetFps > 0) _frametime = 1000 / _targetFps;
|
||||
else _frametime = MIN_FRAME_DELAY; // unlimited mode
|
||||
}
|
||||
|
||||
void WS2812FX::setCCT(uint16_t k) {
|
||||
@@ -1676,7 +1621,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
|
||||
BusManager::setBrightness(b);
|
||||
if (!direct) {
|
||||
unsigned long t = millis();
|
||||
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
|
||||
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1698,7 +1643,7 @@ uint8_t WS2812FX::getFirstSelectedSegId() const {
|
||||
return getMainSegmentId();
|
||||
}
|
||||
|
||||
void WS2812FX::setMainSegmentId(uint8_t n) {
|
||||
void WS2812FX::setMainSegmentId(unsigned n) {
|
||||
_mainSegment = 0;
|
||||
if (n < _segments.size()) {
|
||||
_mainSegment = n;
|
||||
@@ -1774,23 +1719,10 @@ void WS2812FX::purgeSegments() {
|
||||
}
|
||||
}
|
||||
|
||||
Segment& WS2812FX::getSegment(uint8_t id) {
|
||||
Segment& WS2812FX::getSegment(unsigned id) {
|
||||
return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors
|
||||
}
|
||||
|
||||
// sets new segment bounds, queues if that segment is currently running
|
||||
void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
|
||||
if (segId >= getSegmentsNum()) {
|
||||
if (i2 <= i1) return; // do not append empty/inactive segments
|
||||
appendSegment(Segment(0, strip.getLengthTotal()));
|
||||
segId = getSegmentsNum()-1; // segments are added at the end of list
|
||||
}
|
||||
suspend();
|
||||
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
|
||||
resume();
|
||||
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
_segments.clear(); // destructs all Segment as part of clearing
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@@ -1989,7 +1921,7 @@ void WS2812FX::loadCustomPalettes() {
|
||||
}
|
||||
|
||||
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
|
||||
bool WS2812FX::deserializeMap(uint8_t n) {
|
||||
bool WS2812FX::deserializeMap(unsigned n) {
|
||||
// 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
|
||||
|
||||
char fileName[32];
|
||||
@@ -2041,14 +1973,6 @@ bool WS2812FX::deserializeMap(uint8_t n) {
|
||||
return (customMappingSize > 0);
|
||||
}
|
||||
|
||||
uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) const {
|
||||
// convert logical address to physical
|
||||
if (index < customMappingSize
|
||||
&& (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index];
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
||||
|
||||
@@ -2061,5 +1985,5 @@ const char JSON_palette_names[] PROGMEM = R"=====([
|
||||
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
|
||||
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf",
|
||||
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
|
||||
"Candy2"
|
||||
"Candy2","Traffic Light"
|
||||
])=====";
|
||||
|
||||
Reference in New Issue
Block a user