diff --git a/wled00/FX.h b/wled00/FX.h index ce7a222237..0564e5b40c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -383,33 +383,33 @@ extern byte realtimeMode; // used in getMappedPixelIndex() #define MODE_COUNT 219 -#define BLEND_STYLE_FADE 0x00 // universal -#define BLEND_STYLE_FAIRY_DUST 0x01 // universal -#define BLEND_STYLE_SWIPE_RIGHT 0x02 // 1D or 2D -#define BLEND_STYLE_SWIPE_LEFT 0x03 // 1D or 2D -#define BLEND_STYLE_OUTSIDE_IN 0x04 // 1D or 2D -#define BLEND_STYLE_INSIDE_OUT 0x05 // 1D or 2D -#define BLEND_STYLE_SWIPE_UP 0x06 // 2D -#define BLEND_STYLE_SWIPE_DOWN 0x07 // 2D -#define BLEND_STYLE_OPEN_H 0x08 // 2D -#define BLEND_STYLE_OPEN_V 0x09 // 2D -#define BLEND_STYLE_SWIPE_TL 0x0A // 2D -#define BLEND_STYLE_SWIPE_TR 0x0B // 2D -#define BLEND_STYLE_SWIPE_BR 0x0C // 2D -#define BLEND_STYLE_SWIPE_BL 0x0D // 2D -#define BLEND_STYLE_CIRCULAR_OUT 0x0E // 2D -#define BLEND_STYLE_CIRCULAR_IN 0x0F // 2D +#define TRANSITION_FADE 0x00 // universal +#define TRANSITION_FAIRY_DUST 0x01 // universal +#define TRANSITION_SWIPE_RIGHT 0x02 // 1D or 2D +#define TRANSITION_SWIPE_LEFT 0x03 // 1D or 2D +#define TRANSITION_OUTSIDE_IN 0x04 // 1D or 2D +#define TRANSITION_INSIDE_OUT 0x05 // 1D or 2D +#define TRANSITION_SWIPE_UP 0x06 // 2D +#define TRANSITION_SWIPE_DOWN 0x07 // 2D +#define TRANSITION_OPEN_H 0x08 // 2D +#define TRANSITION_OPEN_V 0x09 // 2D +#define TRANSITION_SWIPE_TL 0x0A // 2D +#define TRANSITION_SWIPE_TR 0x0B // 2D +#define TRANSITION_SWIPE_BR 0x0C // 2D +#define TRANSITION_SWIPE_BL 0x0D // 2D +#define TRANSITION_CIRCULAR_OUT 0x0E // 2D +#define TRANSITION_CIRCULAR_IN 0x0F // 2D // as there are many push variants to optimise if statements they are groupped together -#define BLEND_STYLE_PUSH_RIGHT 0x10 // 1D or 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_LEFT 0x11 // 1D or 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_UP 0x12 // 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_DOWN 0x13 // 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_TL 0x14 // 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_TR 0x15 // 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_BR 0x16 // 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_BL 0x17 // 2D (& 0b00010000) -#define BLEND_STYLE_PUSH_MASK 0x10 -#define BLEND_STYLE_COUNT 18 +#define TRANSITION_PUSH_RIGHT 0x10 // 1D or 2D (& 0b00010000) +#define TRANSITION_PUSH_LEFT 0x11 // 1D or 2D (& 0b00010000) +#define TRANSITION_PUSH_UP 0x12 // 2D (& 0b00010000) +#define TRANSITION_PUSH_DOWN 0x13 // 2D (& 0b00010000) +#define TRANSITION_PUSH_TL 0x14 // 2D (& 0b00010000) +#define TRANSITION_PUSH_TR 0x15 // 2D (& 0b00010000) +#define TRANSITION_PUSH_BR 0x16 // 2D (& 0b00010000) +#define TRANSITION_PUSH_BL 0x17 // 2D (& 0b00010000) +#define TRANSITION_PUSH_MASK 0x10 +#define TRANSITION_COUNT 18 typedef enum mapping1D2D { @@ -461,9 +461,8 @@ class Segment { bool check1 : 1; // checkmark 1 bool check2 : 1; // checkmark 2 bool check3 : 1; // checkmark 3 - //uint8_t blendMode : 4; // segment blending modes: top, bottom, add, subtract, difference, multiply, divide, lighten, darken, screen, overlay, hardlight, softlight, dodge, burn }; - uint8_t blendMode; // segment blending modes: top, bottom, add, subtract, difference, multiply, divide, lighten, darken, screen, overlay, hardlight, softlight, dodge, burn + uint8_t blendMode; // segment blending modes: top, bottom, add, subtract, difference, average, multiply, divide, lighten, darken, screen, overlay, hardlight, softlight, dodge, burn, stencil char *name; // segment name // runtime data @@ -562,7 +561,7 @@ class Segment { inline static void modeBlend(bool blend) { Segment::_modeBlend = blend; } inline static void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; }; - inline static bool isPreviousMode() { return Segment::_modeBlend; } // needed for determining CCT/opacity during non-BLEND_STYLE_FADE transition + inline static bool isPreviousMode() { return Segment::_modeBlend; } // needed for determining CCT/opacity during non-TRANSITION_FADE transition static void handleRandomPalette(); diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 77e466588a..673e0c6406 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -149,14 +149,14 @@ void WS2812FX::setUpMatrix() { // pixel is clipped if it falls outside clipping range // if clipping start > stop the clipping range is inverted bool Segment::isPixelXYClipped(int x, int y) const { - if (blendingStyle != BLEND_STYLE_FADE && isInTransition() && _clipStart != _clipStop) { + if (blendingStyle != TRANSITION_FADE && isInTransition() && _clipStart != _clipStop) { const bool invertX = _clipStart > _clipStop; const bool invertY = _clipStartY > _clipStopY; const int cStartX = invertX ? _clipStop : _clipStart; const int cStopX = invertX ? _clipStart : _clipStop; const int cStartY = invertY ? _clipStopY : _clipStartY; const int cStopY = invertY ? _clipStartY : _clipStopY; - if (blendingStyle == BLEND_STYLE_FAIRY_DUST) { + if (blendingStyle == TRANSITION_FAIRY_DUST) { const unsigned width = cStopX - cStartX; // assumes full segment width (faster than virtualWidth()) const unsigned len = width * (cStopY - cStartY); // assumes full segment height (faster than virtualHeight()) if (len < 2) return false; @@ -164,10 +164,10 @@ bool Segment::isPixelXYClipped(int x, int y) const { const unsigned pos = (shuffled * 0xFFFFU) / len; return progress() <= pos; } - if (blendingStyle == BLEND_STYLE_CIRCULAR_IN || blendingStyle == BLEND_STYLE_CIRCULAR_OUT) { + if (blendingStyle == TRANSITION_CIRCULAR_IN || blendingStyle == TRANSITION_CIRCULAR_OUT) { const int cx = (cStopX-cStartX+1) / 2; const int cy = (cStopY-cStartY+1) / 2; - const bool out = (blendingStyle == BLEND_STYLE_CIRCULAR_OUT); + const bool out = (blendingStyle == TRANSITION_CIRCULAR_OUT); const unsigned prog = out ? progress() : 0xFFFFU - progress(); int radius2 = max(cx, cy) * prog / 0xFFFF; radius2 = 2 * radius2 * radius2; @@ -179,7 +179,7 @@ bool Segment::isPixelXYClipped(int x, int y) const { } bool xInside = (x >= cStartX && x < cStopX); if (invertX) xInside = !xInside; bool yInside = (y >= cStartY && y < cStopY); if (invertY) yInside = !yInside; - const bool clip = blendingStyle == BLEND_STYLE_OUTSIDE_IN ? xInside || yInside : xInside && yInside; + const bool clip = blendingStyle == TRANSITION_OUTSIDE_IN ? xInside || yInside : xInside && yInside; return !clip; } return false; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index e8f40fdf19..415d4631dd 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -343,7 +343,7 @@ void Segment::updateTransitionProgress() const { uint8_t Segment::currentCCT() const { unsigned prog = progress(); if (prog < 0xFFFFU) { - if (blendingStyle == BLEND_STYLE_FADE) return (cct * prog + (_t->_cct * (0xFFFFU - prog))) / 0xFFFFU; + if (blendingStyle == TRANSITION_FADE) return (cct * prog + (_t->_cct * (0xFFFFU - prog))) / 0xFFFFU; //else return Segment::isPreviousMode() ? _t->_cct : cct; } return cct; @@ -355,7 +355,7 @@ uint8_t Segment::currentBri() const { unsigned curBri = on ? opacity : 0; if (prog < 0xFFFFU) { // this will blend opacity in new mode if style is FADE (single effect call) - if (blendingStyle == BLEND_STYLE_FADE) curBri = (prog * curBri + _t->_bri * (0xFFFFU - prog)) / 0xFFFFU; + if (blendingStyle == TRANSITION_FADE) curBri = (prog * curBri + _t->_bri * (0xFFFFU - prog)) / 0xFFFFU; else curBri = Segment::isPreviousMode() ? _t->_bri : curBri; } return curBri; @@ -371,7 +371,7 @@ void Segment::beginDraw(uint16_t prog) { for (unsigned i = 0; i < NUM_COLORS; i++) _currentColors[i] = colors[i]; // load palette into _currentPalette loadPalette(Segment::_currentPalette, palette); - if (isInTransition() && prog < 0xFFFFU && blendingStyle == BLEND_STYLE_FADE) { + if (isInTransition() && prog < 0xFFFFU && blendingStyle == TRANSITION_FADE) { // blend colors for (unsigned i = 0; i < NUM_COLORS; i++) _currentColors[i] = color_blend16(_t->_colors[i], colors[i], prog); // blend palettes @@ -379,7 +379,7 @@ void Segment::beginDraw(uint16_t prog) { // minimum blend time is 100ms maximum is 65535ms #ifndef WLED_SAVE_RAM unsigned noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends; - if(noOfBlends > 255) noOfBlends = 255; // safety check + if (noOfBlends > 255) noOfBlends = 255; // safety check for (unsigned i = 0; i < noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, Segment::_currentPalette, 48); Segment::_currentPalette = _t->_palT; // copy transitioning/temporary palette #else @@ -506,7 +506,7 @@ Segment &Segment::setColor(uint8_t slot, uint32_t c) { 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(), blendingStyle != BLEND_STYLE_FADE); // start transition prior to change + startTransition(strip.getTransition(), blendingStyle != TRANSITION_FADE); // start transition prior to change colors[slot] = c; stateChanged = true; // send UDP/WS broadcast return *this; @@ -530,7 +530,7 @@ Segment &Segment::setCCT(uint16_t k) { Segment &Segment::setOpacity(uint8_t o) { if (opacity != o) { //DEBUG_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o); - startTransition(strip.getTransition(), blendingStyle != BLEND_STYLE_FADE); // start transition prior to change + startTransition(strip.getTransition(), blendingStyle != TRANSITION_FADE); // start transition prior to change opacity = o; stateChanged = true; // send UDP/WS broadcast } @@ -541,7 +541,7 @@ Segment &Segment::setOption(uint8_t n, bool val) { bool prev = (options >> n) & 0x01; if (val == prev) return *this; //DEBUG_PRINTF_P(PSTR("- Starting option transition: %d\n"), n); - if (n == SEG_OPTION_ON) startTransition(strip.getTransition(), blendingStyle != BLEND_STYLE_FADE); // start transition prior to change + if (n == SEG_OPTION_ON) startTransition(strip.getTransition(), blendingStyle != TRANSITION_FADE); // start transition prior to change if (val) options |= 0x01 << n; else options &= ~(0x01 << n); stateChanged = true; // send UDP/WS broadcast @@ -588,7 +588,7 @@ Segment &Segment::setPalette(uint8_t pal) { if (pal <= 255-customPalettes.size() && pal > FIXED_PALETTE_COUNT) pal = 0; // not built in palette or custom palette if (pal != palette) { //DEBUG_PRINTF_P(PSTR("- Starting palette transition: %d\n"), pal); - startTransition(strip.getTransition(), blendingStyle != BLEND_STYLE_FADE); // start transition prior to change (no need to copy segment) + startTransition(strip.getTransition(), blendingStyle != TRANSITION_FADE); // start transition prior to change (no need to copy segment) palette = pal; stateChanged = true; // send UDP/WS broadcast } @@ -692,11 +692,11 @@ uint16_t Segment::maxMappingLength() const { // pixel is clipped if it falls outside clipping range // if clipping start > stop the clipping range is inverted bool Segment::isPixelClipped(int i) const { - if (blendingStyle != BLEND_STYLE_FADE && isInTransition() && _clipStart != _clipStop) { + if (blendingStyle != TRANSITION_FADE && isInTransition() && _clipStart != _clipStop) { bool invert = _clipStart > _clipStop; // ineverted start & stop int start = invert ? _clipStop : _clipStart; int stop = invert ? _clipStart : _clipStop; - if (blendingStyle == BLEND_STYLE_FAIRY_DUST) { + if (blendingStyle == TRANSITION_FAIRY_DUST) { unsigned len = stop - start; if (len < 2) return false; unsigned shuffled = hashInt(i) % len; @@ -1304,7 +1304,7 @@ void WS2812FX::service() { // if segment is in transition and no old segment exists we don't need to run the old mode // (blendSegments() takes care of On/Off transitions and clipping) Segment *segO = seg.getOldSegment(); - if (segO && segO->isActive() && (seg.mode != segO->mode || blendingStyle != BLEND_STYLE_FADE || + if (segO && segO->isActive() && (seg.mode != segO->mode || blendingStyle != TRANSITION_FADE || (segO->name != seg.name && segO->name && seg.name && strncmp(segO->name, seg.name, WLED_MAX_SEGNAME_LEN) != 0))) { Segment::modeBlend(true); // set semaphore for beginDraw() to blend colors and palette segO->beginDraw(prog); // set up palette & colors (also sets draw dimensions), parent segment has transition progress @@ -1337,9 +1337,9 @@ void WS2812FX::service() { } // https://en.wikipedia.org/wiki/Blend_modes but using a for top layer & b for bottom layer -static uint8_t _top (uint8_t a, uint8_t b) { return a; } -static uint8_t _bottom (uint8_t a, uint8_t b) { return b; } -static uint8_t _add (uint8_t a, uint8_t b) { unsigned t = a + b; return t > 255 ? 255 : t; } +static uint8_t _top (uint8_t a, uint8_t b) { return a; } // function unused +static uint8_t _bottom (uint8_t a, uint8_t b) { return b; } // function unused +static uint8_t _add (uint8_t a, uint8_t b) { unsigned t = a + b; return t > 255 ? 255 : t; } // function unused static uint8_t _subtract (uint8_t a, uint8_t b) { return b > a ? (b - a) : 0; } static uint8_t _difference(uint8_t a, uint8_t b) { return b > a ? (b - a) : (a - b); } static uint8_t _average (uint8_t a, uint8_t b) { return (a + b) >> 1; } @@ -1361,24 +1361,41 @@ static uint8_t _softlight (uint8_t a, uint8_t b) { return (b * b * (255 - 2 * a) #endif static uint8_t _dodge (uint8_t a, uint8_t b) { return _divide(~a,b); } static uint8_t _burn (uint8_t a, uint8_t b) { return ~_divide(a,~b); } +static uint8_t _stencil (uint8_t a, uint8_t b) { return a ? a : b; } // function unused +static uint8_t _dummy (uint8_t a, uint8_t b) { return a; } // dummy (same as _top) to fill the function list and make it safe from OOB access -void WS2812FX::blendSegment(const Segment &topSegment) const { +#define BLENDMODES 17 // number of blend modes must match "bm" in index.js, all cases must be handled in segblend() @ blendSegment() +void WS2812FX::blendSegment(const Segment &topSegment) const { typedef uint8_t(*FuncType)(uint8_t, uint8_t); + // function pointer array: fill with _dummy if using special case: avoid OOB access and always provide a valid path FuncType funcs[] = { - _top, _bottom, - _add, _subtract, _difference, _average, - _multiply, _divide, _lighten, _darken, _screen, _overlay, - _hardlight, _softlight, _dodge, _burn + _dummy, _dummy, _dummy, _subtract, + _difference, _average, _dummy, _divide, + _lighten, _darken, _screen, _overlay, + _hardlight, _softlight, _dodge, _burn, + _dummy }; - const size_t blendMode = topSegment.blendMode < (sizeof(funcs) / sizeof(FuncType)) ? topSegment.blendMode : 0; - const auto func = funcs[blendMode]; // blendMode % (sizeof(funcs) / sizeof(FuncType)) - const auto blend = [&](uint32_t top, uint32_t bottom){ return RGBW32(func(R(top),R(bottom)), func(G(top),G(bottom)), func(B(top),B(bottom)), func(W(top),W(bottom))); }; + const size_t blendMode = topSegment.blendMode < BLENDMODES ? topSegment.blendMode : 0; // default to top if unsupported mode + const auto segblend = [&](uint32_t t, uint32_t b){ + // use direct calculations/returns for simple/frequent modes (faster) + switch (blendMode) { + case 0 : return t; // top + case 1 : return b; // bottom + case 2 : return color_add(t,b,true); // add with preserve color ratio to avoid color clipping + case 6 : return RGBW32(_multiply(R(t),R(b)), _multiply(G(t),G(b)), _multiply(B(t),B(b)), _multiply(W(t),W(b))); // multiply (7% faster than lambda at 100bytes flash cost) + case 16: return t ? t : b; // stencil (use top layer if not black, else bottom) + } + // default: use function pointer from array + const auto func = funcs[blendMode]; + return RGBW32(func(R(t),R(b)), func(G(t),G(b)), func(B(t),B(b)), func(W(t),W(b))); + }; const int length = topSegment.length(); // physical segment length (counts all pixels in 2D segment) const int width = topSegment.width(); const int height = topSegment.height(); + //const uint32_t bgColor = topSegment.colors[1]; // background color (unused, could add it to stencil mode if requested) const auto XY = [](int x, int y){ return x + y*Segment::maxWidth; }; const size_t matrixSize = Segment::maxWidth * Segment::maxHeight; const size_t startIndx = XY(topSegment.start, topSegment.startY); @@ -1391,58 +1408,58 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { Segment::setClippingRect(0, 0); // disable clipping by default - const unsigned dw = (blendingStyle==BLEND_STYLE_OUTSIDE_IN ? progInv : progress) * width / 0xFFFFU + 1; - const unsigned dh = (blendingStyle==BLEND_STYLE_OUTSIDE_IN ? progInv : progress) * height / 0xFFFFU + 1; + const unsigned dw = (blendingStyle==TRANSITION_OUTSIDE_IN ? progInv : progress) * width / 0xFFFFU + 1; + const unsigned dh = (blendingStyle==TRANSITION_OUTSIDE_IN ? progInv : progress) * height / 0xFFFFU + 1; const unsigned orgBS = blendingStyle; - if (width*height == 1) blendingStyle = BLEND_STYLE_FADE; // disable style for single pixel segments (use fade instead) + if (width*height == 1) blendingStyle = TRANSITION_FADE; // disable style for single pixel segments (use fade instead) switch (blendingStyle) { - case BLEND_STYLE_CIRCULAR_IN: // (must set entire segment, see isPixelXYClipped()) - case BLEND_STYLE_CIRCULAR_OUT:// (must set entire segment, see isPixelXYClipped()) - case BLEND_STYLE_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped()) + case TRANSITION_CIRCULAR_IN: // (must set entire segment, see isPixelXYClipped()) + case TRANSITION_CIRCULAR_OUT:// (must set entire segment, see isPixelXYClipped()) + case TRANSITION_FAIRY_DUST: // fairy dust (must set entire segment, see isPixelXYClipped()) Segment::setClippingRect(0, width, 0, height); break; - case BLEND_STYLE_SWIPE_RIGHT: // left-to-right - case BLEND_STYLE_PUSH_RIGHT: // left-to-right + case TRANSITION_SWIPE_RIGHT: // left-to-right + case TRANSITION_PUSH_RIGHT: // left-to-right Segment::setClippingRect(0, dw, 0, height); break; - case BLEND_STYLE_SWIPE_LEFT: // right-to-left - case BLEND_STYLE_PUSH_LEFT: // right-to-left + case TRANSITION_SWIPE_LEFT: // right-to-left + case TRANSITION_PUSH_LEFT: // right-to-left Segment::setClippingRect(width - dw, width, 0, height); break; - case BLEND_STYLE_OUTSIDE_IN: // corners + case TRANSITION_OUTSIDE_IN: // corners Segment::setClippingRect((width + dw)/2, (width - dw)/2, (height + dh)/2, (height - dh)/2); // inverted!! break; - case BLEND_STYLE_INSIDE_OUT: // outward + case TRANSITION_INSIDE_OUT: // outward Segment::setClippingRect((width - dw)/2, (width + dw)/2, (height - dh)/2, (height + dh)/2); break; - case BLEND_STYLE_SWIPE_DOWN: // top-to-bottom (2D) - case BLEND_STYLE_PUSH_DOWN: // top-to-bottom (2D) + case TRANSITION_SWIPE_DOWN: // top-to-bottom (2D) + case TRANSITION_PUSH_DOWN: // top-to-bottom (2D) Segment::setClippingRect(0, width, 0, dh); break; - case BLEND_STYLE_SWIPE_UP: // bottom-to-top (2D) - case BLEND_STYLE_PUSH_UP: // bottom-to-top (2D) + case TRANSITION_SWIPE_UP: // bottom-to-top (2D) + case TRANSITION_PUSH_UP: // bottom-to-top (2D) Segment::setClippingRect(0, width, height - dh, height); break; - case BLEND_STYLE_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D + case TRANSITION_OPEN_H: // horizontal-outward (2D) same look as INSIDE_OUT on 1D Segment::setClippingRect((width - dw)/2, (width + dw)/2, 0, height); break; - case BLEND_STYLE_OPEN_V: // vertical-outward (2D) + case TRANSITION_OPEN_V: // vertical-outward (2D) Segment::setClippingRect(0, width, (height - dh)/2, (height + dh)/2); break; - case BLEND_STYLE_SWIPE_TL: // TL-to-BR (2D) - case BLEND_STYLE_PUSH_TL: // TL-to-BR (2D) + case TRANSITION_SWIPE_TL: // TL-to-BR (2D) + case TRANSITION_PUSH_TL: // TL-to-BR (2D) Segment::setClippingRect(0, dw, 0, dh); break; - case BLEND_STYLE_SWIPE_TR: // TR-to-BL (2D) - case BLEND_STYLE_PUSH_TR: // TR-to-BL (2D) + case TRANSITION_SWIPE_TR: // TR-to-BL (2D) + case TRANSITION_PUSH_TR: // TR-to-BL (2D) Segment::setClippingRect(width - dw, width, 0, dh); break; - case BLEND_STYLE_SWIPE_BR: // BR-to-TL (2D) - case BLEND_STYLE_PUSH_BR: // BR-to-TL (2D) + case TRANSITION_SWIPE_BR: // BR-to-TL (2D) + case TRANSITION_PUSH_BR: // BR-to-TL (2D) Segment::setClippingRect(width - dw, width, height - dh, height); break; - case BLEND_STYLE_SWIPE_BL: // BL-to-TR (2D) - case BLEND_STYLE_PUSH_BL: // BL-to-TR (2D) + case TRANSITION_SWIPE_BL: // BL-to-TR (2D) + case TRANSITION_PUSH_BL: // BL-to-TR (2D) Segment::setClippingRect(0, dw, height - dh, height); break; } @@ -1459,18 +1476,18 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { const int baseX = topSegment.start + x; const int baseY = topSegment.startY + y; size_t indx = XY(baseX, baseY); // absolute address on strip - _pixels[indx] = color_blend(_pixels[indx], blend(c, _pixels[indx]), o); + _pixels[indx] = color_blend(_pixels[indx], segblend(c, _pixels[indx]), o); if (_pixelCCT) _pixelCCT[indx] = cct; - // Apply mirroring + // Apply mirroring if enabled if (topSegment.mirror || topSegment.mirror_y) { const int mirrorX = topSegment.start + width - x - 1; const int mirrorY = topSegment.startY + height - y - 1; const size_t idxMX = XY(topSegment.transpose ? baseX : mirrorX, topSegment.transpose ? mirrorY : baseY); const size_t idxMY = XY(topSegment.transpose ? mirrorX : baseX, topSegment.transpose ? baseY : mirrorY); const size_t idxMM = XY(mirrorX, mirrorY); - if (topSegment.mirror) _pixels[idxMX] = color_blend(_pixels[idxMX], blend(c, _pixels[idxMX]), o); - if (topSegment.mirror_y) _pixels[idxMY] = color_blend(_pixels[idxMY], blend(c, _pixels[idxMY]), o); - if (topSegment.mirror && topSegment.mirror_y) _pixels[idxMM] = color_blend(_pixels[idxMM], blend(c, _pixels[idxMM]), o); + if (topSegment.mirror) _pixels[idxMX] = color_blend(_pixels[idxMX], segblend(c, _pixels[idxMX]), o); + if (topSegment.mirror_y) _pixels[idxMY] = color_blend(_pixels[idxMY], segblend(c, _pixels[idxMY]), o); + if (topSegment.mirror && topSegment.mirror_y) _pixels[idxMM] = color_blend(_pixels[idxMM], segblend(c, _pixels[idxMM]), o); if (_pixelCCT) { if (topSegment.mirror) _pixelCCT[idxMX] = cct; if (topSegment.mirror_y) _pixelCCT[idxMY] = cct; @@ -1480,9 +1497,22 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { }; // if we blend using "push" style we need to "shift" canvas to left/right/up/down - unsigned offsetX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : progInv * nCols / 0xFFFFU; - unsigned offsetY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : progInv * nRows / 0xFFFFU; - + unsigned offsetX = (blendingStyle == TRANSITION_PUSH_UP || blendingStyle == TRANSITION_PUSH_DOWN) ? 0 : progInv * nCols / 0xFFFFU; + unsigned offsetY = (blendingStyle == TRANSITION_PUSH_LEFT || blendingStyle == TRANSITION_PUSH_RIGHT) ? 0 : progInv * nRows / 0xFFFFU; + const unsigned groupLen = topSegment.groupLength(); + bool applyReverse = topSegment.reverse || topSegment.reverse_y || topSegment.transpose; + int pushOffsetX = 0, pushOffsetY = 0; + // if we blend using "push" style we need to "shift" canvas to left/right/up/down + switch (blendingStyle) { + case TRANSITION_PUSH_RIGHT: pushOffsetX = offsetX; break; + case TRANSITION_PUSH_LEFT: pushOffsetX = -offsetX + nCols; break; + case TRANSITION_PUSH_DOWN: pushOffsetY = offsetY; break; + case TRANSITION_PUSH_UP: pushOffsetY = -offsetY + nRows; break; + case TRANSITION_PUSH_TL: pushOffsetX = offsetX; pushOffsetY = offsetY; break; // unused + case TRANSITION_PUSH_TR: pushOffsetX = -offsetX + nCols; pushOffsetY = offsetY; break; // unused + case TRANSITION_PUSH_BR: pushOffsetX = -offsetX + nCols; pushOffsetY = -offsetY + nRows; break; // unused + case TRANSITION_PUSH_BL: pushOffsetX = offsetX; pushOffsetY = -offsetY + nRows; break; // unused + } // we only traverse new segment, not old one for (int r = 0; r < nRows; r++) for (int c = 0; c < nCols; c++) { const bool clipped = topSegment.isPixelXYClipped(c, r); @@ -1492,21 +1522,16 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { int vRows = seg == segO ? oRows : nRows; // old segment may have different dimensions int x = c; int y = r; - // if we blend using "push" style we need to "shift" canvas to left/right/up/down - switch (blendingStyle) { - case BLEND_STYLE_PUSH_RIGHT: x = (x + offsetX) % nCols; break; - case BLEND_STYLE_PUSH_LEFT: x = (x - offsetX + nCols) % nCols; break; - case BLEND_STYLE_PUSH_DOWN: y = (y + offsetY) % nRows; break; - case BLEND_STYLE_PUSH_UP: y = (y - offsetY + nRows) % nRows; break; - } + if (pushOffsetX != 0) x = (x + pushOffsetX) % nCols; + if (pushOffsetY != 0) y = (y + pushOffsetY) % nRows; uint32_t c_a = BLACK; if (x < vCols && y < vRows) c_a = seg->getPixelColorRaw(x + y*vCols); // will get clipped pixel from old segment or unclipped pixel from new segment - if (segO && blendingStyle == BLEND_STYLE_FADE + if (segO && blendingStyle == TRANSITION_FADE && (topSegment.mode != segO->mode || (segO->name != topSegment.name && segO->name && topSegment.name && strncmp(segO->name, topSegment.name, WLED_MAX_SEGNAME_LEN) != 0)) && x < oCols && y < oRows) { // we need to blend old segment using fade as pixels are not clipped c_a = color_blend16(c_a, segO->getPixelColorRaw(x + y*oCols), progInv); - } else if (blendingStyle != BLEND_STYLE_FADE) { + } else if (blendingStyle != TRANSITION_FADE) { // if we have global brightness change (not On/Off change) we will ignore transition style and just fade brightness (see led.cpp) // workaround for On/Off transition // (bri != briT) && !bri => from On to Off @@ -1516,11 +1541,12 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { // map it into frame buffer x = c; // restore coordiates if we were PUSHing y = r; - if (topSegment.reverse ) x = nCols - x - 1; - if (topSegment.reverse_y) y = nRows - y - 1; - if (topSegment.transpose) std::swap(x,y); // swap X & Y if segment transposed + if (applyReverse) { + if (topSegment.reverse ) x = nCols - x - 1; + if (topSegment.reverse_y) y = nRows - y - 1; + if (topSegment.transpose) std::swap(x,y); // swap X & Y if segment transposed + } // expand pixel - const unsigned groupLen = topSegment.groupLength(); if (groupLen == 1) { setMirroredPixel(x, y, c_a, opacity); } else { @@ -1549,12 +1575,12 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { unsigned indxM = topSegment.stop - i - 1; indxM += topSegment.offset; // offset/phase if (indxM >= topSegment.stop) indxM -= length; // wrap - _pixels[indxM] = color_blend(_pixels[indxM], blend(c, _pixels[indxM]), o); + _pixels[indxM] = color_blend(_pixels[indxM], segblend(c, _pixels[indxM]), o); if (_pixelCCT) _pixelCCT[indxM] = cct; } indx += topSegment.offset; // offset/phase if (indx >= topSegment.stop) indx -= length; // wrap - _pixels[indx] = color_blend(_pixels[indx], blend(c, _pixels[indx]), o); + _pixels[indx] = color_blend(_pixels[indx], segblend(c, _pixels[indx]), o); if (_pixelCCT) _pixelCCT[indx] = cct; }; @@ -1569,15 +1595,15 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { int i = k; // if we blend using "push" style we need to "shift" canvas to left or right switch (blendingStyle) { - case BLEND_STYLE_PUSH_RIGHT: i = (i + offsetI) % nLen; break; - case BLEND_STYLE_PUSH_LEFT: i = (i - offsetI + nLen) % nLen; break; + case TRANSITION_PUSH_RIGHT: i = (i + offsetI) % nLen; break; + case TRANSITION_PUSH_LEFT: i = (i - offsetI + nLen) % nLen; break; } uint32_t c_a = BLACK; if (i < vLen) c_a = seg->getPixelColorRaw(i); // will get clipped pixel from old segment or unclipped pixel from new segment - if (segO && blendingStyle == BLEND_STYLE_FADE && topSegment.mode != segO->mode && i < oLen) { + if (segO && blendingStyle == TRANSITION_FADE && topSegment.mode != segO->mode && i < oLen) { // we need to blend old segment using fade as pixels are not clipped c_a = color_blend16(c_a, segO->getPixelColorRaw(i), progInv); - } else if (blendingStyle != BLEND_STYLE_FADE) { + } else if (blendingStyle != TRANSITION_FADE) { // if we have global brightness change (not On/Off change) we will ignore transition style and just fade brightness (see led.cpp) // workaround for On/Off transition // (bri != briT) && !bri => from On to Off @@ -1642,7 +1668,7 @@ void WS2812FX::show() { } uint32_t c = _pixels[i]; // need a copy, do not modify _pixels directly (no byte access allowed on ESP32) - if(c > 0 && !(realtimeMode && arlsDisableGammaCorrection)) + if (c > 0 && !(realtimeMode && arlsDisableGammaCorrection)) c = gamma32(c); // apply gamma correction if enabled note: applying gamma after brightness has too much color loss BusManager::setPixelColor(getMappedPixelIndex(i), c); } diff --git a/wled00/data/index.js b/wled00/data/index.js index c6e620628c..cccc9103b8 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -798,6 +798,7 @@ function populateSegments(s) ``+ ``+ ``+ + ``+ ``+ ``; let sndSim = `