Skip to content

Commit

Permalink
Merge pull request #4158 from Aircoookie/blending-styles
Browse files Browse the repository at this point in the history
Effect blending styles (recreated PR from #3877)
  • Loading branch information
netmindz authored Jan 20, 2025
2 parents 4951be6 + 3a426e2 commit 2448266
Show file tree
Hide file tree
Showing 19 changed files with 380 additions and 242 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
- v0.15.0-b2
- WS2805 support (RGB + WW + CW, 600kbps)
- Unified PSRAM use
- NeoPixelBus v2.7.9
- NeoPixelBus v2.7.9 (for future WS2805 support)
- Ubiquitous PSRAM mode for all variants of ESP32
- SSD1309_64 I2C Support for FLD Usermod (#3836 by @THATDONFC)
- Palette cycling fix (add support for `{"seg":[{"pal":"X~Y~"}]}` or `{"seg":[{"pal":"X~Yr"}]}`)
Expand Down
111 changes: 0 additions & 111 deletions usermods/stairway_wipe_basic/wled06_usermod.ino

This file was deleted.

40 changes: 35 additions & 5 deletions wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,30 @@ extern byte realtimeMode; // used in getMappedPixelIndex()

#define MODE_COUNT 187


#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_PINCH_OUT 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
// 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


typedef enum mapping1D2D {
M12_Pixels = 0,
M12_pBar = 1,
Expand All @@ -333,7 +357,7 @@ typedef enum mapping1D2D {
M12_sPinwheel = 4
} mapping1D2D_t;

// segment, 80 bytes
// segment, 68 bytes
typedef struct Segment {
public:
uint16_t start; // start index / start X coordinate 2D (left)
Expand Down Expand Up @@ -436,6 +460,9 @@ typedef struct Segment {
static uint16_t _transitionprogress; // current transition progress 0 - 0xFFFF
#ifndef WLED_DISABLE_MODE_BLEND
static bool _modeBlend; // mode/effect blending semaphore
// clipping
static uint16_t _clipStart, _clipStop;
static uint8_t _clipStartY, _clipStopY;
#endif

// transition data, valid only if transitional==true, holds values during transition (72 bytes)
Expand All @@ -446,6 +473,7 @@ typedef struct Segment {
#else
uint32_t _colorT[NUM_COLORS];
#endif
uint8_t _palTid; // previous palette
uint8_t _briT; // temporary brightness
uint8_t _cctT; // temporary CCT
CRGBPalette16 _palT; // temporary palette
Expand Down Expand Up @@ -607,6 +635,10 @@ typedef struct Segment {
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) const { setPixelColor(i, RGBW32(r,g,b,w), aa); }
inline void setPixelColor(float i, CRGB c, bool aa = true) const { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
#endif
#ifndef WLED_DISABLE_MODE_BLEND
static inline void setClippingRect(int startX, int stopX, int startY = 0, int stopY = 1) { _clipStart = startX; _clipStop = stopX; _clipStartY = startY; _clipStopY = stopY; };
#endif
bool isPixelClipped(int i) const;
[[gnu::hot]] uint32_t getPixelColor(int i) const;
// 1D support functions (some implement 2D as well)
void blur(uint8_t, bool smear = false);
Expand Down Expand Up @@ -653,6 +685,7 @@ typedef struct Segment {
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) const { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) const { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
#endif
[[gnu::hot]] bool isPixelXYClipped(int x, int y) const;
[[gnu::hot]] uint32_t getPixelColorXY(int x, int y) const;
// 2D support functions
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); }
Expand Down Expand Up @@ -690,6 +723,7 @@ typedef struct Segment {
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); }
#endif
inline bool isPixelXYClipped(int x, int y) { return isPixelClipped(x); }
inline uint32_t getPixelColorXY(int x, int y) { return getPixelColor(x); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); }
Expand Down Expand Up @@ -734,9 +768,7 @@ class WS2812FX { // 96 bytes
public:

WS2812FX() :
paletteFade(0),
paletteBlend(0),
cctBlending(0),
now(millis()),
timebase(0),
isMatrix(false),
Expand Down Expand Up @@ -824,7 +856,6 @@ class WS2812FX { // 96 bytes
inline void resume() { _suspend = false; } // will resume strip.service() execution

bool
paletteFade,
checkSegmentAlignment() const,
hasRGBWBus() const,
hasCCTBus() const,
Expand All @@ -839,7 +870,6 @@ class WS2812FX { // 96 bytes

uint8_t
paletteBlend,
cctBlending,
getActiveSegmentsNum() const,
getFirstSelectedSegId() const,
getLastActiveSegmentId() const,
Expand Down
66 changes: 62 additions & 4 deletions wled00/FX_2Dfcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint
const int baseY = startY + y;
#ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress());
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress());
#endif
strip.setPixelColorXY(baseX, baseY, col);

Expand All @@ -179,14 +179,57 @@ void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint
}
}

// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false)
// if clipping start > stop the clipping range is inverted
// _modeBlend==true -> old effect during transition
// _modeBlend==false -> new effect during transition
bool IRAM_ATTR_YN Segment::isPixelXYClipped(int x, int y) const {
#ifndef WLED_DISABLE_MODE_BLEND
if (_clipStart != _clipStop && blendingStyle != BLEND_STYLE_FADE) {
const bool invertX = _clipStart > _clipStop;
const bool invertY = _clipStartY > _clipStopY;
const int startX = invertX ? _clipStop : _clipStart;
const int stopX = invertX ? _clipStart : _clipStop;
const int startY = invertY ? _clipStopY : _clipStartY;
const int stopY = invertY ? _clipStartY : _clipStopY;
if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
const unsigned width = stopX - startX; // assumes full segment width (faster than virtualWidth())
const unsigned len = width * (stopY - startY); // assumes full segment height (faster than virtualHeight())
if (len < 2) return false;
const unsigned shuffled = hashInt(x + y * width) % len;
const unsigned pos = (shuffled * 0xFFFFU) / len;
return progress() > pos;
}
bool xInside = (x >= startX && x < stopX); if (invertX) xInside = !xInside;
bool yInside = (y >= startY && y < stopY); if (invertY) yInside = !yInside;
const bool clip = (invertX && invertY) ? !_modeBlend : _modeBlend;
if (xInside && yInside) return clip; // covers window & corners (inverted)
return !clip;
}
#endif
return false;
}

void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) const
{
if (!isActive()) return; // not active

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)
// negative values of x & y cast into unsigend will become very large values and will therefore be greater than vW/vH
if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return; // if pixel would fall out of virtual segment just exit

#ifndef WLED_DISABLE_MODE_BLEND
unsigned prog = 0xFFFF - progress();
if (!prog && !_modeBlend && (blendingStyle & BLEND_STYLE_PUSH_MASK)) {
unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF;
unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF;
if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x += dX;
else x -= dX;
if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY;
else y += dY;
}
#endif

if (x >= vW || y >= vH || x < 0 || y < 0 || isPixelXYClipped(x,y)) return; // if pixel would fall out of virtual segment just exit

// if color is unscaled
if (!_colorScaled) col = color_fade(col, _segBri);
Expand Down Expand Up @@ -259,9 +302,24 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) const
// returns RGBW values of pixel
uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const {
if (!isActive()) return 0; // not active

const int vW = vWidth();
const int vH = vHeight();
if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return 0; // if pixel would fall out of virtual segment just exit

#ifndef WLED_DISABLE_MODE_BLEND
unsigned prog = 0xFFFF - progress();
if (!prog && !_modeBlend && (blendingStyle & BLEND_STYLE_PUSH_MASK)) {
unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF;
unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF;
if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x -= dX;
else x += dX;
if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY;
else y += dY;
}
#endif

if (x >= vW || y >= vH || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit

if (reverse ) x = vW - x - 1;
if (reverse_y) y = vH - y - 1;
if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed
Expand Down
Loading

0 comments on commit 2448266

Please sign in to comment.