diff --git a/wled00/FX.cpp b/wled00/FX.cpp index dc39df8164..3c1fc0c9a2 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -110,6 +110,7 @@ static um_data_t* getAudioData() { return um_data; } + // effect functions /* @@ -121,6 +122,48 @@ uint16_t mode_static(void) { } static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid"; +/* + * Copy a segment and perform (optional) color adjustments + */ +uint16_t mode_copy_segment(void) { + uint32_t sourceid = SEGMENT.custom3; + if (sourceid >= strip.getSegmentsNum() || sourceid == strip.getCurrSegmentId()) { // invalid source + SEGMENT.fadeToBlackBy(5); // fade out, clears pixels and allows overlapping segments + return FRAMETIME; + } + Segment sourcesegment = strip.getSegment(sourceid); + if (sourcesegment.isActive()) { + uint32_t sourcecolor; + if(!sourcesegment.is2D()) { // 1D source, source can be expanded into 2D + uint32_t cl; // length to copy + for (unsigned i = 0; i < SEGMENT.virtualLength(); i++) { + sourcecolor = strip.getRenderedPixelXY(sourceid, i); + uint32_t color = adjust_color(sourcecolor, SEGMENT.intensity, SEGMENT.custom1, SEGMENT.custom2); + if(SEGMENT.check3) // overlay + SEGMENT.addPixelColor(i, color); + else + SEGMENT.setPixelColor(i, color); + } + } else { // 2D source, note: 2D to 1D just copies the first row (or first column if 'Switch axis' is checked in FX) + for (unsigned y = 0; y < SEGMENT.virtualHeight(); y++) { + for (unsigned x = 0; x < SEGMENT.virtualWidth(); x++) { + if(SEGMENT.check2) + sourcecolor = strip.getRenderedPixelXY(sourceid, y, x); // flip reading axis (for 2D -> 1D, in 2D Segments this does the same as 'Transpose') + else + sourcecolor = strip.getRenderedPixelXY(sourceid, x, y); + uint32_t color = adjust_color(sourcecolor, SEGMENT.intensity, SEGMENT.custom1, SEGMENT.custom2); + if(SEGMENT.check3) // overlay + SEGMENT.addPixelColorXY(x, y, color); + else + SEGMENT.setPixelColorXY(x, y, color); + } + } + } + } + return FRAMETIME; +} +static const char _data_FX_MODE_COPY[] PROGMEM = "Copy Segment@,Color shift,Lighten,Brighten,ID,,Axis(2D),Overlay;;;12;ix=0,c1=0,c2=0,c3=0"; + /* * Blink/strobe function @@ -7693,6 +7736,7 @@ void WS2812FX::setupEffectData() { _modeData.push_back(_data_RESERVED); } // now replace all pre-allocated effects + addEffect(FX_MODE_COPY, &mode_copy_segment, _data_FX_MODE_COPY); // --- 1D non-audio effects --- addEffect(FX_MODE_BLINK, &mode_blink, _data_FX_MODE_BLINK); addEffect(FX_MODE_BREATH, &mode_breath, _data_FX_MODE_BREATH); diff --git a/wled00/FX.h b/wled00/FX.h index 57df58549b..bd72c000c0 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -322,8 +322,8 @@ extern byte realtimeMode; // used in getMappedPixelIndex() #define FX_MODE_WAVESINS 184 #define FX_MODE_ROCKTAVES 185 #define FX_MODE_2DAKEMI 186 - -#define MODE_COUNT 187 +#define FX_MODE_COPY 187 +#define MODE_COUNT 188 typedef enum mapping1D2D { M12_Pixels = 0, @@ -870,6 +870,7 @@ class WS2812FX { // 96 bytes unsigned long now, timebase; uint32_t getPixelColor(unsigned) const; + uint32_t getRenderedPixelXY(uint8_t segid, unsigned x, unsigned y = 0) const; inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index b9a62bb2ca..9e54476180 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1425,6 +1425,18 @@ uint32_t IRAM_ATTR WS2812FX::getPixelColor(unsigned i) const { return BusManager::getPixelColor(i); } +/* + * Read rendered pixel back (following mirror/reverse/transpose but ignoring grouping) + */ +uint32_t WS2812FX::getRenderedPixelXY(uint8_t segid, unsigned x, unsigned y) const { + // For every group-length pixels, add spacing + x *= _segments[segid].groupLength(); // expand to physical pixels + y *= _segments[segid].groupLength(); // expand to physical pixels + if (x >= _segments[segid].width() || y >= _segments[segid].height()) return 0; // fill out of range pixels with black + uint32_t offset = _segments[segid].is2D() ? 0 : _segments[segid].offset; //offset in 2D segments is undefined, set to zero + return strip.getPixelColorXY(_segments[segid].start + offset + x, _segments[segid].startY + y); +} + void WS2812FX::show() { // avoid race condition, capture _callback value show_callback callback = _callback; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index e64cf67588..865c9acc17 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -83,6 +83,23 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) return scaledcolor; } +/* + * color adjustment in HSV color space (converts RGB to HSV and back), color conversions are not 100% accurate! + shifts hue, increase brightness, decreases saturation (if not black) + note: inputs are 32bit to speed up the function, useful input value ranges are 0-255 + */ +uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten) { + if(rgb == 0 | hueShift + lighten + brighten == 0) return rgb; // black or no change + CHSV32 hsv; + rgb2hsv(rgb, hsv); //convert to HSV + hsv.h += (hueShift << 8); // shift hue (hue is 16 bits) + hsv.s = max((int32_t)0, (int32_t)hsv.s - (int32_t)lighten); // desaturate + hsv.v = min((uint32_t)255, (uint32_t)hsv.v + brighten); // increase brightness + uint32_t rgb_adjusted; + hsv2rgb(hsv, rgb_adjusted); // convert back to RGB TODO: make this into 16 bit conversion + return rgb_adjusted; +} + // 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) { diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 4a57740634..fef3b8904a 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -165,6 +165,7 @@ class NeoGammaWLEDMethod { inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); }; [[gnu::hot]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false); [[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); +[[gnu::hot]] uint32_t adjust_color(uint32_t rgb, uint32_t hueShift, uint32_t lighten, uint32_t brighten); [[gnu::hot]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette();