From c3f472fbcb0d766f8c6cce1bf8841770b4b15bac Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 11 Sep 2024 21:41:42 +0200 Subject: [PATCH 01/37] some improvements to consider no real difference in FPS but code is faster. also 160bytes smaller, meaning it is actually faster --- wled00/FX_2Dfcn.cpp | 25 +++++++++--------- wled00/colors.cpp | 62 +++++++++++++++++++++++++++++++++++++------- wled00/fcn_declare.h | 2 ++ 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 26ec1d608a..ae76379ede 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -173,11 +173,6 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - uint8_t _bri_t = currentBri(); - if (_bri_t < 255) { - col = color_fade(col, _bri_t); - } - if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed @@ -189,7 +184,11 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) int H = height(); if (x >= W || y >= H) return; // if pixel would fall out of segment just exit - uint32_t tmpCol = col; + uint8_t _bri_t = currentBri(); + if (_bri_t < 255) { + col = color_fade(col, _bri_t); + } + for (int j = 0; j < grouping; j++) { // groupping vertically for (int g = 0; g < grouping; g++) { // groupping horizontally int xX = (x+g), yY = (y+j); @@ -197,21 +196,21 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) #ifndef WLED_DISABLE_MODE_BLEND // if blending modes, blend with underlying pixel - if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); + if (_modeBlend) col = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); #endif - strip.setPixelColorXY(start + xX, startY + yY, tmpCol); + strip.setPixelColorXY(start + xX, startY + yY, col); if (mirror) { //set the corresponding horizontally mirrored pixel - if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol); - else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol); + if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); + else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); } if (mirror_y) { //set the corresponding vertically mirrored pixel - if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol); - else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol); + if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); + else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); } if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel - strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, tmpCol); + strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, col); } } } diff --git a/wled00/colors.cpp b/wled00/colors.cpp index ac1dee00a1..791aad9828 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -72,25 +72,69 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { if (c1 == BLACK || amount + video == 0) return BLACK; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB - uint32_t r = R(c1); - uint32_t g = G(c1); - uint32_t b = B(c1); - uint32_t w = W(c1); uint32_t scale = amount; // 32bit for faster calculation if (video) { + uint32_t r = R(c1); + uint32_t g = G(c1); + uint32_t b = B(c1); + uint32_t w = W(c1); scaledcolor = (((r * scale) >> 8) + ((r && scale) ? 1 : 0)) << 16; scaledcolor |= (((g * scale) >> 8) + ((g && scale) ? 1 : 0)) << 8; scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0); scaledcolor |= (((w * scale) >> 8) + ((w && scale) ? 1 : 0)) << 24; - } else { - scaledcolor = ((r * scale) >> 8) << 16; - scaledcolor |= ((g * scale) >> 8) << 8; - scaledcolor |= (b * scale) >> 8; - scaledcolor |= ((w * scale) >> 8) << 24; + } else { // according to compile explorer, this is 15% faster but cannot be used for video (its not faster if the assignments are seperated) + uint32_t r = (((c1&0x00FF0000) * scale) >> 8) & 0x00FF0000; + uint32_t g = (((c1&0x0000FF00) * scale) >> 8) & 0x0000FF00; + uint32_t b = ((c1&0x000000FF) * scale) >> 8; + uint32_t w = (((c1 & 0xFF000000) >> 8) * scale) & 0xFF000000; // Scale w and keep it in position + scaledcolor = r | g | b | w; } return scaledcolor; } +// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) +CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) +{ + if ( blendType == LINEARBLEND_NOWRAP) { + //index = map8(index, 0, 239); + index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + unsigned hi4 = byte(index) >> 4; + unsigned lo4 = index & 0x0F; + unsigned hi4XsizeofCRGB = hi4 * sizeof(CRGB); + // We then add that to a base array pointer. + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB); + unsigned red1 = entry->red; + unsigned green1 = entry->green; + unsigned blue1 = entry->blue; + if(blendType != NOBLEND) { + if(hi4 == 15) entry = &(pal[0]); + else ++entry; + unsigned red2 = entry->red; + unsigned green2 = entry->green; + unsigned blue2 = entry->blue; + unsigned f2 = (lo4 << 4)+1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 + unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max + red1 *= f1; + green1 *= f1; + blue1 *= f1; + red2 *= f2; + green2 *= f2; + blue2 *= f2; + red1 = (red1 + red2) >> 8; + green1 = (green1 + green2) >> 8; + blue1 = (blue1 + blue2) >> 8; + } + if( brightness != 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + uint32_t scale = brightness; + scale++; // adjust for rounding (bitshift) + red1 = (red1 * scale) >> 8; + green1 = (green1 * scale) >> 8; + blue1 = (blue1 * scale) >> 8; + } + return CRGB((uint8_t)red1, (uint8_t)green1, (uint8_t)blue1); +} + void setRandomColor(byte* rgb) { lastRandomIndex = get_random_wheel_index(lastRandomIndex); diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index f8399b1ad6..2f9bc44d01 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -66,6 +66,7 @@ typedef struct WiFiConfig { } wifi_config; //colors.cpp +#define ColorFromPalette ColorFromPaletteWLED // override fastled version // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) class NeoGammaWLEDMethod { public: @@ -81,6 +82,7 @@ class NeoGammaWLEDMethod { [[gnu::hot]] uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); [[gnu::hot]] uint32_t color_add(uint32_t,uint32_t, bool fast=false); [[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); +CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette(); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } From 934176818f32ffa0ffe359192dbf7d769c3e740f Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 12 Sep 2024 06:43:20 +0200 Subject: [PATCH 02/37] more improvements to color_scale() now even faster. tested and working, also tested video --- wled00/colors.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 791aad9828..c3ac0cb61a 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -70,25 +70,22 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { - if (c1 == BLACK || amount + video == 0) return BLACK; + if (c1 == BLACK || amount == 0) return BLACK; + else if (amount == 255) return c1; + video = true; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB uint32_t scale = amount; // 32bit for faster calculation - if (video) { - uint32_t r = R(c1); - uint32_t g = G(c1); - uint32_t b = B(c1); - uint32_t w = W(c1); - scaledcolor = (((r * scale) >> 8) + ((r && scale) ? 1 : 0)) << 16; - scaledcolor |= (((g * scale) >> 8) + ((g && scale) ? 1 : 0)) << 8; - scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0); - scaledcolor |= (((w * scale) >> 8) + ((w && scale) ? 1 : 0)) << 24; - } else { // according to compile explorer, this is 15% faster but cannot be used for video (its not faster if the assignments are seperated) - uint32_t r = (((c1&0x00FF0000) * scale) >> 8) & 0x00FF0000; - uint32_t g = (((c1&0x0000FF00) * scale) >> 8) & 0x0000FF00; - uint32_t b = ((c1&0x000000FF) * scale) >> 8; - uint32_t w = (((c1 & 0xFF000000) >> 8) * scale) & 0xFF000000; // Scale w and keep it in position - scaledcolor = r | g | b | w; + uint32_t addRemains = 0; + if (!video) amount++; // add one for correct scaling using bitshifts + else { // video scaling: make sure colors do not dim to zero if they started non-zero + addRemains = R(c1) ? 0x00010000 : 0; + addRemains |= G(c1) ? 0x00000100 : 0; + addRemains |= B(c1) ? 0x00000001 : 0; + addRemains |= W(c1) ? 0x01000000 : 0; } + uint32_t rb = (((c1 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue + uint32_t wg = (((c1 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green + scaledcolor = (rb | wg) + addRemains; return scaledcolor; } From feac45fd0aed860235aef4e03fe91c5b49f95a89 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 12 Sep 2024 07:45:49 +0200 Subject: [PATCH 03/37] improvement in color_add its not faster but cleaner (and uses less flash) --- wled00/colors.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index c3ac0cb61a..960cef3137 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -39,21 +39,17 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; - if (fast) { - uint8_t r = R(c1); - uint8_t g = G(c1); - uint8_t b = B(c1); - uint8_t w = W(c1); - r = qadd8(r, R(c2)); - g = qadd8(g, G(c2)); - b = qadd8(b, B(c2)); - w = qadd8(w, W(c2)); + uint32_t r = R(c1) + R(c2); + uint32_t g = G(c1) + G(c2); + uint32_t b = B(c1) + B(c2); + uint32_t w = W(c1) + W(c2); + if (fast) { + r = r > 255 ? 255 : r; + g = g > 255 ? 255 : g; + b = b > 255 ? 255 : b; + w = w > 255 ? 255 : w; return RGBW32(r,g,b,w); } else { - uint32_t r = R(c1) + R(c2); - uint32_t g = G(c1) + G(c2); - uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2); unsigned max = r; if (g > max) max = g; if (b > max) max = b; @@ -72,7 +68,6 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { if (c1 == BLACK || amount == 0) return BLACK; else if (amount == 255) return c1; - video = true; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB uint32_t scale = amount; // 32bit for faster calculation uint32_t addRemains = 0; From 992d11be105ac00380874a11f6ba33e9acfc24e1 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 12 Sep 2024 08:28:30 +0200 Subject: [PATCH 04/37] Improvements in get/set PixelColor() -calculations for virtual strips are done on each call, which is unnecessary. moved them into the if statement. --- wled00/FX_fcn.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index d3521c90ce..4364b9f438 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -705,11 +705,16 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) { if (!isActive()) return; // not active #ifndef WLED_DISABLE_2D - int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) + int vStrip; #endif - i &= 0xFFFF; - - if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit + if (i >= virtualLength() || i<0) // pixel would fall out of segment, check if this is a virtual strip NOTE: this is almost always false if not virtual strip, saves the calculation on 'standard' call + { + #ifndef WLED_DISABLE_2D + vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) + #endif + i &= 0xFFFF; //truncate vstrip index + if (i >= virtualLength() || i<0) return; // if pixel would still fall out of segment just exit + } #ifndef WLED_DISABLE_2D if (is2D()) { @@ -900,8 +905,7 @@ 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; +#endif #ifndef WLED_DISABLE_2D if (is2D()) { @@ -912,7 +916,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const return getPixelColorXY(i % vW, i / vW); break; case M12_pBar: - if (vStrip>0) return getPixelColorXY(vStrip - 1, vH - i -1); + if (vStrip>0) { i &= 0xFFFF; return getPixelColorXY(vStrip - 1, vH - i -1); } else return getPixelColorXY(0, vH - i -1); break; case M12_pArc: From b07658b46060c73c4bd94a9a4e4289d44639c173 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 12 Sep 2024 14:09:09 +0200 Subject: [PATCH 05/37] improved Segment::setPixelColorXY a tiny bit uses less flash so it should be faster (did not notice any FPS difference though) also cleaned code in ColorFromPaletteWLED (it is not faster, same amount of code) --- wled00/FX_2Dfcn.cpp | 26 ++++++++++++-------------- wled00/colors.cpp | 33 +++++++++++---------------------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index ae76379ede..01410b8113 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -173,34 +173,30 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit + uint8_t _bri_t = currentBri(); + if (_bri_t < 255) { + col = color_fade(col, _bri_t); + } + if (reverse ) x = virtualWidth() - x - 1; if (reverse_y) y = virtualHeight() - y - 1; if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed - x *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels - int W = width(); int H = height(); - if (x >= W || y >= H) return; // if pixel would fall out of segment just exit - - uint8_t _bri_t = currentBri(); - if (_bri_t < 255) { - col = color_fade(col, _bri_t); - } - + + int yY = y; for (int j = 0; j < grouping; j++) { // groupping vertically + if(yY >= H) continue; + int xX = x; for (int g = 0; g < grouping; g++) { // groupping horizontally - int xX = (x+g), yY = (y+j); - if (xX >= W || yY >= H) continue; // we have reached one dimension's end - + if (xX >= W) continue; // we have reached one dimension's end #ifndef WLED_DISABLE_MODE_BLEND // if blending modes, blend with underlying pixel if (_modeBlend) col = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); #endif - strip.setPixelColorXY(start + xX, startY + yY, col); - if (mirror) { //set the corresponding horizontally mirrored pixel if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); @@ -212,7 +208,9 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, col); } + xX++; } + yY++; } } diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 960cef3137..b6cb3ac512 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -92,34 +92,23 @@ CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brig index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping } unsigned hi4 = byte(index) >> 4; - unsigned lo4 = index & 0x0F; - unsigned hi4XsizeofCRGB = hi4 * sizeof(CRGB); // We then add that to a base array pointer. - const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + hi4XsizeofCRGB); - unsigned red1 = entry->red; - unsigned green1 = entry->green; - unsigned blue1 = entry->blue; + const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); + unsigned red1 = entry->r; + unsigned green1 = entry->g; + unsigned blue1 = entry->b; if(blendType != NOBLEND) { if(hi4 == 15) entry = &(pal[0]); else ++entry; - unsigned red2 = entry->red; - unsigned green2 = entry->green; - unsigned blue2 = entry->blue; - unsigned f2 = (lo4 << 4)+1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 + // unsigned red2 = entry->red; + unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max - red1 *= f1; - green1 *= f1; - blue1 *= f1; - red2 *= f2; - green2 *= f2; - blue2 *= f2; - red1 = (red1 + red2) >> 8; - green1 = (green1 + green2) >> 8; - blue1 = (blue1 + blue2) >> 8; + red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; + green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; + blue1 = (green1 * f1 + (unsigned)entry->b * f2) >> 8; } - if( brightness != 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted - uint32_t scale = brightness; - scale++; // adjust for rounding (bitshift) + if( brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + uint32_t scale = brightness + 1; // adjust for rounding (bitshift) red1 = (red1 * scale) >> 8; green1 = (green1 * scale) >> 8; blue1 = (blue1 * scale) >> 8; From 09428dcade03aa4544f85f81f2d8a160d144db9c Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 12 Sep 2024 16:34:55 +0200 Subject: [PATCH 06/37] inlined getMappedPixelIndex, improved color_add, bugfix in colorFromPalette inlining getMappedPixelIndex gets rid of function entry instructions (hopefully) so it should be faster. also added the 'multi color math' trick to color_add function (it will not make much difference but code shrinks by a few bytes) --- wled00/FX_fcn.cpp | 2 +- wled00/colors.cpp | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 4364b9f438..cddf1ece70 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1820,7 +1820,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { return (customMappingSize > 0); } -uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) const { +__attribute__ ((always_inline)) inline 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]; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index b6cb3ac512..1f10696555 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -39,10 +39,17 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; - uint32_t r = R(c1) + R(c2); + /*uint32_t r = R(c1) + R(c2); uint32_t g = G(c1) + G(c2); uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2); + uint32_t w = W(c1) + W(c2);*/ + uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); + uint32_t r = rb >> 16; + uint32_t b = rb & 0xFFFF; + uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF); + uint32_t w = wg >> 16; + uint32_t g = wg & 0xFFFF; + if (fast) { r = r > 255 ? 255 : r; g = g > 255 ? 255 : g; @@ -105,7 +112,7 @@ CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brig unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; - blue1 = (green1 * f1 + (unsigned)entry->b * f2) >> 8; + blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; } if( brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted uint32_t scale = brightness + 1; // adjust for rounding (bitshift) From ec938f254cf3fdecf0757a2dee4d9364973d4941 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 12 Sep 2024 21:25:08 +0200 Subject: [PATCH 07/37] removed old code --- wled00/colors.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 1f10696555..ce1d2f2f94 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -39,10 +39,6 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; - /*uint32_t r = R(c1) + R(c2); - uint32_t g = G(c1) + G(c2); - uint32_t b = B(c1) + B(c2); - uint32_t w = W(c1) + W(c2);*/ uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); uint32_t r = rb >> 16; uint32_t b = rb & 0xFFFF; From d45b4ad1340bccfeb9093b1ff460f1919721a277 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 13 Sep 2024 19:01:54 +0200 Subject: [PATCH 08/37] fixes and consistency --- wled00/colors.cpp | 8 ++++---- wled00/fcn_declare.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index ce1d2f2f94..7747216f77 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -54,9 +54,9 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) return RGBW32(r,g,b,w); } else { unsigned max = r; - if (g > max) max = g; - if (b > max) max = b; - if (w > max) max = w; + max = g > max ? g : max; + max = b > max ? b : max; + max = w > max ? w : max; if (max < 256) return RGBW32(r, g, b, w); else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); } @@ -70,7 +70,7 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { if (c1 == BLACK || amount == 0) return BLACK; - else if (amount == 255) return c1; + if (amount == 255) return c1; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB uint32_t scale = amount; // 32bit for faster calculation uint32_t addRemains = 0; diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 2f9bc44d01..0ebcd64da1 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -82,7 +82,7 @@ class NeoGammaWLEDMethod { [[gnu::hot]] uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); [[gnu::hot]] uint32_t color_add(uint32_t,uint32_t, bool fast=false); [[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); -CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); +[[gnu::hot]] CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette(); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } From 2afff0501401c7161eae841107868884635779ae Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 14 Sep 2024 11:45:27 +0200 Subject: [PATCH 09/37] minor tweak (break instead of continue in setPixelColorXY) --- wled00/FX_2Dfcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 01410b8113..57ee2e5e35 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -188,7 +188,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) int yY = y; for (int j = 0; j < grouping; j++) { // groupping vertically - if(yY >= H) continue; + if(yY >= H) break; int xX = x; for (int g = 0; g < grouping; g++) { // groupping horizontally if (xX >= W) continue; // we have reached one dimension's end From 6a37f25c5d994b914222999fdce441c3427e1fcd Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 14 Sep 2024 14:10:46 +0200 Subject: [PATCH 10/37] memory improvement: dropped static gamma table - there already is a method to calculate the table on the fly, there is no need to store it in flash, it can just be calculated at bootup (or cfg change) --- wled00/cfg.cpp | 11 +++++------ wled00/colors.cpp | 19 ++----------------- wled00/set.cpp | 5 ++--- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index a6c3ab74de..978ed6eba9 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -440,13 +440,12 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { else gammaCorrectBri = false; if (light_gc_col > 1.0f) gammaCorrectCol = true; else gammaCorrectCol = false; - if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) { - if (gammaCorrectVal != 2.8f) NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); - } else { - gammaCorrectVal = 1.0f; // no gamma correction - gammaCorrectBri = false; - gammaCorrectCol = false; + if (gammaCorrectVal <= 1.0f || gammaCorrectVal > 3) { + gammaCorrectVal = 1.0f; // no gamma correction + gammaCorrectBri = false; + gammaCorrectCol = false; } + NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table JsonObject light_tr = light["tr"]; CJSON(fadeTransition, light_tr["mode"]); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 7747216f77..4afe4c0d7c 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -481,23 +481,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { } //gamma 2.8 lookup table used for color correction -uint8_t NeoGammaWLEDMethod::gammaT[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, - 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, - 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, - 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, - 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, - 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, - 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, - 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, - 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, - 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, - 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, - 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; +uint8_t NeoGammaWLEDMethod::gammaT[256]; // re-calculates & fills gamma table void NeoGammaWLEDMethod::calcGammaTable(float gamma) @@ -505,6 +489,7 @@ void NeoGammaWLEDMethod::calcGammaTable(float gamma) for (size_t i = 0; i < 256; i++) { gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f); } + Serial.println("****GAMMA***"); //!!! } uint8_t IRAM_ATTR NeoGammaWLEDMethod::Correct(uint8_t value) diff --git a/wled00/set.cpp b/wled00/set.cpp index 7814e55d96..2fe01a54c7 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -319,13 +319,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) gammaCorrectBri = request->hasArg(F("GB")); gammaCorrectCol = request->hasArg(F("GC")); gammaCorrectVal = request->arg(F("GV")).toFloat(); - if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) - NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); - else { + if (gammaCorrectVal <= 1.0f || gammaCorrectVal > 3) { gammaCorrectVal = 1.0f; // no gamma correction gammaCorrectBri = false; gammaCorrectCol = false; } + NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table fadeTransition = request->hasArg(F("TF")); modeBlending = request->hasArg(F("EB")); From 0e5bd4ed7428ab7540393935ced7e8ef4e937fc2 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 14 Sep 2024 14:11:29 +0200 Subject: [PATCH 11/37] remove test printout --- wled00/colors.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 4afe4c0d7c..74723471cc 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -489,7 +489,6 @@ void NeoGammaWLEDMethod::calcGammaTable(float gamma) for (size_t i = 0; i < 256; i++) { gammaT[i] = (int)(powf((float)i / 255.0f, gamma) * 255.0f + 0.5f); } - Serial.println("****GAMMA***"); //!!! } uint8_t IRAM_ATTR NeoGammaWLEDMethod::Correct(uint8_t value) From f3137eb0a9b8d12759a207b910bc854dc1b36ec3 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 14 Sep 2024 14:49:36 +0200 Subject: [PATCH 12/37] updated Segment::color_from_palette - gamma correction only where needed - paletteIndex should be uint8_t (it is only used as that) note: integrating the new `ColorFromPaletteWLED()` into this would require a whole lot of code rewrite and would result in more color conversions from 32bit to CRGB. It would be really useful only if CRGB is replaced with native 32bit colors. --- wled00/FX_fcn.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index cddf1ece70..7e8bd6471c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1181,18 +1181,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 = currentColor(mcol); // default palette or no RGB support on segment - if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) return (pbri == 255) ? color : color_fade(color, pbri, true); + if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { + color = gamma32(color); + return (pbri == 255) ? color : color_fade(color, pbri, true); + } - unsigned paletteIndex = i; + uint8_t paletteIndex = i; if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -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 - return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color)); + return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, gamma8(W(color))); } From 696290527abfd01fcff3d4bd5fbfdc9c53050787 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 18 Sep 2024 22:10:27 +0200 Subject: [PATCH 13/37] cleanup and improved color_add() - optimized color_add() again: now it is as fast with preserved ratio scaling than the "fast" variant was before (if no scaling is needed, it is even faster). plus it saves 250 bytes of flash - bugfix in `color_fade()` - removed a lot of whitespaces --- wled00/FX.h | 24 +++++++-------- wled00/FX_2Dfcn.cpp | 26 ++++++++-------- wled00/FX_fcn.cpp | 16 +++++----- wled00/colors.cpp | 72 +++++++++++++++++++++----------------------- wled00/fcn_declare.h | 2 +- 5 files changed, 68 insertions(+), 72 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 3c28274d60..bea4dbcb8a 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -595,9 +595,9 @@ typedef struct Segment { void fadeToBlackBy(uint8_t fadeBy); inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); } inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColor(int n, uint32_t color, bool fast = false) { setPixelColor(n, color_add(getPixelColor(n), color, fast)); } - inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } - inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } + inline void addPixelColor(int n, uint32_t color) { setPixelColor(n, color_add(getPixelColor(n), color)); } + inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0) { addPixelColor(n, RGBW32(r,g,b,w)); } + inline void addPixelColor(int n, CRGB c) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); } [[gnu::hot]] uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const; [[gnu::hot]] uint32_t color_wheel(uint8_t pos) const; @@ -605,11 +605,11 @@ typedef struct Segment { // 2D Blur: shortcuts for bluring columns or rows only (50% faster than full 2D blur) inline void blurCols(fract8 blur_amount, bool smear = false) { // blur all columns const unsigned cols = virtualWidth(); - for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear); + for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear); } inline void blurRows(fract8 blur_amount, bool smear = false) { // blur all rows const unsigned rows = virtualHeight(); - for ( unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear); + for ( unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear); } // 2D matrix @@ -632,10 +632,10 @@ typedef struct Segment { // 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)); } inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast)); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } - inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } - inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } + inline void addPixelColorXY(int x, int y, uint32_t color) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color)); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColorXY(x, y, RGBW32(r,g,b,w)); } + inline void addPixelColorXY(int x, int y, CRGB c) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur void blur2D(uint8_t blur_amount, bool smear = false); void blurRow(uint32_t row, fract8 blur_amount, bool smear = false); @@ -670,9 +670,9 @@ typedef struct Segment { 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); } - inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); } - inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } + inline void addPixelColorXY(int x, int y, uint32_t color) { addPixelColor(x, color); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColor(x, RGBW32(r,g,b,w)); } + inline void addPixelColorXY(int x, int y, CRGB c) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} inline void blur2D(uint8_t blur_amount, bool smear = false) {} diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 57ee2e5e35..10b85a82e9 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -173,7 +173,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) if (!isActive()) return; // not active if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - uint8_t _bri_t = currentBri(); + uint8_t _bri_t = currentBri(); if (_bri_t < 255) { col = color_fade(col, _bri_t); } @@ -185,11 +185,11 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) y *= groupLength(); // expand to physical pixels int W = width(); int H = height(); - + int yY = y; for (int j = 0; j < grouping; j++) { // groupping vertically if(yY >= H) break; - int xX = x; + int xX = x; for (int g = 0; g < grouping; g++) { // groupping horizontally if (xX >= W) continue; // we have reached one dimension's end #ifndef WLED_DISABLE_MODE_BLEND @@ -293,8 +293,8 @@ void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ curnew = color_fade(cur, keep); if (x > 0) { if (carryover) - curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); + curnew = color_add(curnew, carryover); + uint32_t prev = color_add(lastnew, part); if (last != prev) // optimization: only set pixel if color has changed setPixelColorXY(x - 1, row, prev); } else // first pixel @@ -326,15 +326,15 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { curnew = color_fade(cur, keep); if (y > 0) { if (carryover) - curnew = color_add(curnew, carryover, true); - uint32_t prev = color_add(lastnew, part, true); + curnew = color_add(curnew, carryover); + uint32_t prev = color_add(lastnew, part); if (last != prev) // optimization: only set pixel if color has changed setPixelColorXY(col, y - 1, prev); } else // first pixel setPixelColorXY(col, y, curnew); lastnew = curnew; last = cur; //save original value for comparison on next iteration - carryover = part; + carryover = part; } setPixelColorXY(col, rows - 1, curnew); } @@ -356,8 +356,8 @@ void Segment::blur2D(uint8_t blur_amount, bool smear) { uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); if (x > 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) setPixelColorXY(x - 1, row, prev); } else setPixelColorXY(x, row, curnew); // first pixel @@ -375,14 +375,14 @@ void Segment::blur2D(uint8_t blur_amount, bool smear) { uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); if (y > 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) setPixelColorXY(col, y - 1, prev); } else setPixelColorXY(col, y, curnew); // first pixel lastnew = curnew; last = cur; //save original value for comparison on next iteration - carryover = part; + carryover = part; } setPixelColorXY(col, rows - 1, curnew); } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 236f7ad4a5..66aeaab63c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -712,12 +712,12 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) { if (!isActive()) return; // not active #ifndef WLED_DISABLE_2D - int vStrip; + int vStrip; #endif if (i >= virtualLength() || i<0) // pixel would fall out of segment, check if this is a virtual strip NOTE: this is almost always false if not virtual strip, saves the calculation on 'standard' call { #ifndef WLED_DISABLE_2D - vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) + vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows) #endif i &= 0xFFFF; //truncate vstrip index if (i >= virtualLength() || i<0) return; // if pixel would still fall out of segment just exit @@ -735,7 +735,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) 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); + else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col); break; case M12_pArc: // expand in circular fashion from center @@ -796,7 +796,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; } @@ -1145,8 +1145,8 @@ 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 @@ -1188,7 +1188,7 @@ 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 = currentColor(mcol); // default palette or no RGB support on segment if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { @@ -1196,7 +1196,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ return (pbri == 255) ? color : color_fade(color, pbri, true); } - uint8_t paletteIndex = i; + unsigned paletteIndex = i; if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -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" diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 104d25e60f..54469ebe04 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -33,33 +33,32 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) /* * color add function that preserves ratio - * idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + * original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule + * heavily optimized for speed by @dedehai */ -uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) +uint32_t color_add(uint32_t c1, uint32_t c2) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; - uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); - uint32_t r = rb >> 16; - uint32_t b = rb & 0xFFFF; - uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF); + uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once + uint32_t wg = ((c1>>8) & 0x00FF00FF) + ((c2>>8) & 0x00FF00FF); + uint32_t r = rb >> 16; // extract single color values + uint32_t b = rb & 0xFFFF; uint32_t w = wg >> 16; - uint32_t g = wg & 0xFFFF; - - if (fast) { - r = r > 255 ? 255 : r; - g = g > 255 ? 255 : g; - b = b > 255 ? 255 : b; - w = w > 255 ? 255 : w; - return RGBW32(r,g,b,w); - } else { - unsigned max = r; - max = g > max ? g : max; - max = b > max ? b : max; - max = w > max ? w : max; - if (max < 256) return RGBW32(r, g, b, w); - else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max); + uint32_t g = wg & 0xFFFF; + + unsigned max = r; // check for overflow note: not checking and just topping out at 255 (formerly 'fast') is not any faster (but even slower if not overflowing) + max = g > max ? g : max; + max = b > max ? b : max; + max = w > max ? w : max; + + if (max > 255) { + uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead + rb = ((rb * scale) >> 8) & 0x00FF00FF; // + wg = (wg * scale) & 0xFF00FF00; } + else wg = wg << 8; //shift white and green back to correct position + return rb | wg; } /* @@ -70,52 +69,49 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { if (c1 == BLACK || amount == 0) return BLACK; - if (amount == 255) return c1; + if (amount == 255) return c1; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB uint32_t scale = amount; // 32bit for faster calculation uint32_t addRemains = 0; - if (!video) amount++; // add one for correct scaling using bitshifts + if (!video) scale++; // add one for correct scaling using bitshifts else { // video scaling: make sure colors do not dim to zero if they started non-zero - addRemains = R(c1) ? 0x00010000 : 0; + addRemains = R(c1) ? 0x00010000 : 0; addRemains |= G(c1) ? 0x00000100 : 0; addRemains |= B(c1) ? 0x00000001 : 0; addRemains |= W(c1) ? 0x01000000 : 0; } uint32_t rb = (((c1 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue uint32_t wg = (((c1 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green - scaledcolor = (rb | wg) + addRemains; + scaledcolor = (rb | wg) + addRemains; return scaledcolor; } // 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) { - if ( blendType == LINEARBLEND_NOWRAP) { - //index = map8(index, 0, 239); + if (blendType == LINEARBLEND_NOWRAP) { index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping } unsigned hi4 = byte(index) >> 4; - // We then add that to a base array pointer. const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); unsigned red1 = entry->r; unsigned green1 = entry->g; - unsigned blue1 = entry->b; + unsigned blue1 = entry->b; if(blendType != NOBLEND) { if(hi4 == 15) entry = &(pal[0]); else ++entry; - // unsigned red2 = entry->red; unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max - red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; - green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; - blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; + red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; + green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; + blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; } if( brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted - uint32_t scale = brightness + 1; // adjust for rounding (bitshift) + uint32_t scale = brightness + 1; // adjust for rounding (bitshift) red1 = (red1 * scale) >> 8; green1 = (green1 * scale) >> 8; blue1 = (blue1 * scale) >> 8; - } + } return CRGB((uint8_t)red1, (uint8_t)green1, (uint8_t)blue1); } @@ -176,7 +172,7 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette) harmonics[1] = basehue + 205 + random8(10); harmonics[2] = basehue - 5 + random8(10); break; - + case 3: // square harmonics[0] = basehue + 85 + random8(10); harmonics[1] = basehue + 175 + random8(10); @@ -213,9 +209,9 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette) //apply saturation & gamma correction CRGB RGBpalettecolors[4]; for (int i = 0; i < 4; i++) { - if (makepastelpalette && palettecolors[i].saturation > 180) { + if (makepastelpalette && palettecolors[i].saturation > 180) { palettecolors[i].saturation -= 160; //desaturate all four colors - } + } RGBpalettecolors[i] = (CRGB)palettecolors[i]; //convert to RGB RGBpalettecolors[i] = gamma32(((uint32_t)RGBpalettecolors[i]) & 0x00FFFFFFU); //strip alpha from CRGB } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index ac941dc97a..be7ed44627 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -80,7 +80,7 @@ class NeoGammaWLEDMethod { #define gamma32(c) NeoGammaWLEDMethod::Correct32(c) #define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) [[gnu::hot]] uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); -[[gnu::hot]] uint32_t color_add(uint32_t,uint32_t, bool fast=false); +[[gnu::hot]] uint32_t color_add(uint32_t,uint32_t); [[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); [[gnu::hot]] CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); From a88436c620bed17a66bf7f1563888036c3a4d10f Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 19 Sep 2024 08:49:18 +0200 Subject: [PATCH 14/37] revert removal of adding with saturation, renamed 'fast' to 'saturate' - blurring now uses desaturated adding: it is faster most of the times and blurring adds scaled colors so should rarely (ever?) saturate, I saw no visual difference in tests. - formatting --- wled00/FX.h | 18 +++++++++--------- wled00/FX_2Dfcn.cpp | 2 +- wled00/FX_fcn.cpp | 2 +- wled00/colors.cpp | 31 ++++++++++++++++++++----------- wled00/fcn_declare.h | 4 ++-- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index bea4dbcb8a..50bcd66243 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -595,9 +595,9 @@ typedef struct Segment { void fadeToBlackBy(uint8_t fadeBy); inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); } inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColor(int n, uint32_t color) { setPixelColor(n, color_add(getPixelColor(n), color)); } - inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0) { addPixelColor(n, RGBW32(r,g,b,w)); } - inline void addPixelColor(int n, CRGB c) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } + inline void addPixelColor(int n, uint32_t color, bool saturate = false) { setPixelColor(n, color_add(getPixelColor(n), color, saturate)); } + inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColor(n, RGBW32(r,g,b,w), saturate); } + inline void addPixelColor(int n, CRGB c, bool saturate = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), saturate); } inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); } [[gnu::hot]] uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const; [[gnu::hot]] uint32_t color_wheel(uint8_t pos) const; @@ -632,9 +632,9 @@ typedef struct Segment { // 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)); } inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColorXY(int x, int y, uint32_t color) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color)); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColorXY(x, y, RGBW32(r,g,b,w)); } - inline void addPixelColorXY(int x, int y, CRGB c) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + inline void addPixelColorXY(int x, int y, uint32_t color, bool saturate = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, saturate)); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), saturate); } + inline void addPixelColorXY(int x, int y, CRGB c, bool saturate = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), saturate); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur void blur2D(uint8_t blur_amount, bool smear = false); @@ -670,9 +670,9 @@ typedef struct Segment { 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); } - inline void addPixelColorXY(int x, int y, uint32_t color) { addPixelColor(x, color); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColor(x, RGBW32(r,g,b,w)); } - inline void addPixelColorXY(int x, int y, CRGB c) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } + inline void addPixelColorXY(int x, int y, uint32_t color, bool saturate = false) { addPixelColor(x, color, saturate); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColor(x, RGBW32(r,g,b,w), saturate); } + inline void addPixelColorXY(int x, int y, CRGB c, bool saturate = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), saturate); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} inline void blur2D(uint8_t blur_amount, bool smear = false) {} diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 10b85a82e9..63595d9301 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -188,7 +188,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) int yY = y; for (int j = 0; j < grouping; j++) { // groupping vertically - if(yY >= H) break; + if (yY >= H) break; int xX = x; for (int g = 0; g < grouping; g++) { // groupping horizontally if (xX >= W) continue; // we have reached one dimension's end diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 66aeaab63c..7159d21c73 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1169,7 +1169,7 @@ uint32_t Segment::color_wheel(uint8_t pos) const { 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 { diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 54469ebe04..13405be6e6 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -36,7 +36,7 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) * original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule * heavily optimized for speed by @dedehai */ -uint32_t color_add(uint32_t c1, uint32_t c2) +uint32_t color_add(uint32_t c1, uint32_t c2, bool desat) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; @@ -47,18 +47,27 @@ uint32_t color_add(uint32_t c1, uint32_t c2) uint32_t w = wg >> 16; uint32_t g = wg & 0xFFFF; - unsigned max = r; // check for overflow note: not checking and just topping out at 255 (formerly 'fast') is not any faster (but even slower if not overflowing) - max = g > max ? g : max; - max = b > max ? b : max; - max = w > max ? w : max; + if(desat) { // desaturate + unsigned max = r; // check for overflow note + max = g > max ? g : max; + max = b > max ? b : max; + max = w > max ? w : max; - if (max > 255) { - uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead - rb = ((rb * scale) >> 8) & 0x00FF00FF; // - wg = (wg * scale) & 0xFF00FF00; + if (max > 255) { + uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead + rb = ((rb * scale) >> 8) & 0x00FF00FF; // + wg = (wg * scale) & 0xFF00FF00; + } + else wg = wg << 8; //shift white and green back to correct position + return rb | wg; + } + else { + r = r > 255 ? 255 : r; + g = g > 255 ? 255 : g; + b = b > 255 ? 255 : b; + w = w > 255 ? 255 : w; + return RGBW32(r,g,b,w); } - else wg = wg << 8; //shift white and green back to correct position - return rb | wg; } /* diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index be7ed44627..d32356d7ba 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -79,8 +79,8 @@ class NeoGammaWLEDMethod { }; #define gamma32(c) NeoGammaWLEDMethod::Correct32(c) #define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) -[[gnu::hot]] uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false); -[[gnu::hot]] uint32_t color_add(uint32_t,uint32_t); +[[gnu::hot]] uint32_t color_blend(uint32_t, uint32_t, uint16_t, bool b16=false); +[[gnu::hot]] uint32_t color_add(uint32_t, uint32_t, bool desat = false); [[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); [[gnu::hot]] CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); From 17d59d333710264a6e0772b3e9d29c8a59c1e189 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 22 Sep 2024 09:02:42 +0200 Subject: [PATCH 15/37] adding initialization to vStrip, added comment on padding bytes --- wled00/FX.h | 1 + wled00/FX_fcn.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/wled00/FX.h b/wled00/FX.h index 50bcd66243..49277ba117 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -368,6 +368,7 @@ typedef struct Segment { }; uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows + //note: here are 3 free bytes of padding char *name; // runtime data diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7159d21c73..e78608a14a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -712,7 +712,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) { if (!isActive()) return; // not active #ifndef WLED_DISABLE_2D - int vStrip; + int vStrip = 0; #endif if (i >= virtualLength() || i<0) // pixel would fall out of segment, check if this is a virtual strip NOTE: this is almost always false if not virtual strip, saves the calculation on 'standard' call { From 0a5400263be34b6a9b45e7d11acefce584f17614 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 22 Sep 2024 13:52:56 +0200 Subject: [PATCH 16/37] removed IRAM_ATTR from inlined function when the function is inlined into a IRAM_ATTR function, it will also reside in IRAM. Forced inlining is recommended by Espressif if I understand this correctly: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/hardware-abstraction.html --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index e78608a14a..9fd78e3143 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1884,7 +1884,7 @@ bool WS2812FX::deserializeMap(uint8_t n) { return (customMappingSize > 0); } -__attribute__ ((always_inline)) inline uint16_t IRAM_ATTR WS2812FX::getMappedPixelIndex(uint16_t index) const { +__attribute__ ((always_inline)) inline uint16_t WS2812FX::getMappedPixelIndex(uint16_t index) const { // convert logical address to physical if (index < customMappingSize && (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index]; From 33cf82a9822a427d941107eba76e562a446a72ab Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 23 Sep 2024 18:03:17 +0200 Subject: [PATCH 17/37] Indentations and a few optimisations Restore addPixelColor() behaviour. --- wled00/FX.h | 22 ++++++++++++------- wled00/FX_2Dfcn.cpp | 16 ++++++-------- wled00/FX_fcn.cpp | 48 +++++++++++++++------------------------- wled00/cfg.cpp | 6 ++--- wled00/colors.cpp | 52 ++++++++++++++++++++++---------------------- wled00/fcn_declare.h | 2 +- 6 files changed, 69 insertions(+), 77 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 49277ba117..989dfbe331 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -56,6 +56,9 @@ #define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) #endif +extern bool realtimeRespectLedMaps; // used in getMappedPixelIndex() +extern byte realtimeMode; // used in getMappedPixelIndex() + /* Not used in all effects yet */ #define WLED_FPS 42 #define FRAMETIME_FIXED (1000/WLED_FPS) @@ -596,9 +599,9 @@ typedef struct Segment { void fadeToBlackBy(uint8_t fadeBy); inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); } inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColor(int n, uint32_t color, bool saturate = false) { setPixelColor(n, color_add(getPixelColor(n), color, saturate)); } - inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColor(n, RGBW32(r,g,b,w), saturate); } - inline void addPixelColor(int n, CRGB c, bool saturate = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), saturate); } + inline void addPixelColor(int n, uint32_t color, bool preserveCR = true) { setPixelColor(n, color_add(getPixelColor(n), color, preserveCR)); } + inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColor(n, RGBW32(r,g,b,w), preserveCR); } + inline void addPixelColor(int n, CRGB c, bool preserveCR = true) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), preserveCR); } inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); } [[gnu::hot]] uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const; [[gnu::hot]] uint32_t color_wheel(uint8_t pos) const; @@ -633,9 +636,9 @@ typedef struct Segment { // 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)); } inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } - inline void addPixelColorXY(int x, int y, uint32_t color, bool saturate = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, saturate)); } - inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), saturate); } - inline void addPixelColorXY(int x, int y, CRGB c, bool saturate = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), saturate); } + inline void addPixelColorXY(int x, int y, uint32_t color, bool preserveCR = true) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, preserveCR)); } + inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(r,g,b,w), preserveCR); } + inline void addPixelColorXY(int x, int y, CRGB c, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), preserveCR); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur void blur2D(uint8_t blur_amount, bool smear = false); @@ -837,13 +840,16 @@ class WS2812FX { // 96 bytes uint16_t getLengthPhysical() const, getLengthTotal() const, // will include virtual/nonexistent pixels in matrix - getFps() const, - getMappedPixelIndex(uint16_t index) const; + getFps() const; inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms) inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H) inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms) + inline uint16_t getMappedPixelIndex(uint16_t index) const { // convert logical address to physical + if (index < customMappingSize && (realtimeMode == REALTIME_MODE_INACTIVE || realtimeRespectLedMaps)) index = customMappingTable[index]; + return index; + }; uint32_t now, diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 63595d9301..ca28a95869 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -191,7 +191,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) if (yY >= H) break; int xX = x; for (int g = 0; g < grouping; g++) { // groupping horizontally - if (xX >= W) continue; // we have reached one dimension's end + if (xX >= W) break; // we have reached X dimension's end #ifndef WLED_DISABLE_MODE_BLEND // if blending modes, blend with underlying pixel if (_modeBlend) col = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); @@ -292,11 +292,10 @@ void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); if (x > 0) { - if (carryover) - curnew = color_add(curnew, carryover); + if (carryover) curnew = color_add(curnew, carryover); uint32_t prev = color_add(lastnew, part); - if (last != prev) // optimization: only set pixel if color has changed - setPixelColorXY(x - 1, row, prev); + // optimization: only set pixel if color has changed + if (last != prev) setPixelColorXY(x - 1, row, prev); } else // first pixel setPixelColorXY(x, row, curnew); lastnew = curnew; @@ -325,11 +324,10 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); if (y > 0) { - if (carryover) - curnew = color_add(curnew, carryover); + if (carryover) curnew = color_add(curnew, carryover); uint32_t prev = color_add(lastnew, part); - if (last != prev) // optimization: only set pixel if color has changed - setPixelColorXY(col, y - 1, prev); + // optimization: only set pixel if color has changed + if (last != prev) setPixelColorXY(col, y - 1, prev); } else // first pixel setPixelColorXY(col, y, curnew); lastnew = curnew; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 9fd78e3143..545c92f258 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -628,13 +628,7 @@ uint16_t IRAM_ATTR Segment::virtualHeight() const { 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; - } - } + if (is2D() && map1D2D == M12_pBar) vLen = virtualWidth(); #endif return vLen; } @@ -710,17 +704,21 @@ uint16_t IRAM_ATTR Segment::virtualLength() 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 = 0; #endif - if (i >= virtualLength() || i<0) // pixel would fall out of segment, check if this is a virtual strip NOTE: this is almost always false if not virtual strip, saves the calculation on 'standard' call - { + // 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 >= virtualLength()) { + // 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 >= virtualLength()) return; // if pixel would still fall out of segment just exit + #else + return; #endif - i &= 0xFFFF; //truncate vstrip index - if (i >= virtualLength() || i<0) return; // if pixel would still fall out of segment just exit } #ifndef WLED_DISABLE_2D @@ -734,12 +732,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; @@ -910,9 +908,6 @@ 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 #ifndef WLED_DISABLE_2D if (is2D()) { @@ -922,10 +917,11 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const case M12_Pixels: return getPixelColorXY(i % vW, i / vW); break; - case M12_pBar: - if (vStrip>0) { i &= 0xFFFF; 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); @@ -1884,14 +1880,6 @@ bool WS2812FX::deserializeMap(uint8_t n) { return (customMappingSize > 0); } -__attribute__ ((always_inline)) inline uint16_t 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; diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 8983da65bb..00e219138e 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -437,9 +437,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (light_gc_col > 1.0f) gammaCorrectCol = true; else gammaCorrectCol = false; if (gammaCorrectVal <= 1.0f || gammaCorrectVal > 3) { - gammaCorrectVal = 1.0f; // no gamma correction - gammaCorrectBri = false; - gammaCorrectCol = false; + gammaCorrectVal = 1.0f; // no gamma correction + gammaCorrectBri = false; + gammaCorrectCol = false; } NeoGammaWLEDMethod::calcGammaTable(gammaCorrectVal); // fill look-up table diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 13405be6e6..233d3d1168 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -34,9 +34,9 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) /* * color add function that preserves ratio * original idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule - * heavily optimized for speed by @dedehai + * speed optimisations by @dedehai */ -uint32_t color_add(uint32_t c1, uint32_t c2, bool desat) +uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; @@ -47,21 +47,21 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool desat) uint32_t w = wg >> 16; uint32_t g = wg & 0xFFFF; - if(desat) { // desaturate - unsigned max = r; // check for overflow note - max = g > max ? g : max; - max = b > max ? b : max; - max = w > max ? w : max; - + if (preserveCR) { // preserve color ratios + unsigned max = std::max(r,g); // check for overflow note + max = std::max(max,b); + max = std::max(max,w); + //unsigned max = r; // check for overflow note + //max = g > max ? g : max; + //max = b > max ? b : max; + //max = w > max ? w : max; if (max > 255) { uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead rb = ((rb * scale) >> 8) & 0x00FF00FF; // wg = (wg * scale) & 0xFF00FF00; - } - else wg = wg << 8; //shift white and green back to correct position + } else wg = wg << 8; //shift white and green back to correct position return rb | wg; - } - else { + } else { r = r > 255 ? 255 : r; g = g > 255 ? 255 : g; b = b > 255 ? 255 : b; @@ -106,20 +106,20 @@ CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brig unsigned red1 = entry->r; unsigned green1 = entry->g; unsigned blue1 = entry->b; - if(blendType != NOBLEND) { - if(hi4 == 15) entry = &(pal[0]); - else ++entry; - unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 - unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max - red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; - green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; - blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; + if (blendType != NOBLEND) { + if (hi4 == 15) entry = &(pal[0]); + else ++entry; + unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 + unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max + red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; + green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; + blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; } - if( brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted - uint32_t scale = brightness + 1; // adjust for rounding (bitshift) - red1 = (red1 * scale) >> 8; - green1 = (green1 * scale) >> 8; - blue1 = (blue1 * scale) >> 8; + if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + uint32_t scale = brightness + 1; // adjust for rounding (bitshift) + red1 = (red1 * scale) >> 8; + green1 = (green1 * scale) >> 8; + blue1 = (blue1 * scale) >> 8; } return CRGB((uint8_t)red1, (uint8_t)green1, (uint8_t)blue1); } @@ -485,7 +485,7 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) { } } -//gamma 2.8 lookup table used for color correction +// gamma lookup table used for color correction (filled on 1st use (cfg.cpp & set.cpp)) uint8_t NeoGammaWLEDMethod::gammaT[256]; // re-calculates & fills gamma table diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index d32356d7ba..741e1eefdf 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -80,7 +80,7 @@ class NeoGammaWLEDMethod { #define gamma32(c) NeoGammaWLEDMethod::Correct32(c) #define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) [[gnu::hot]] uint32_t color_blend(uint32_t, uint32_t, uint16_t, bool b16=false); -[[gnu::hot]] uint32_t color_add(uint32_t, uint32_t, bool desat = false); +[[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]] CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); From 906f8fc2e72cc901be7136fe533a40a27a97fd2d Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 25 Sep 2024 18:49:10 +0200 Subject: [PATCH 18/37] Fix C3 compiler issue. --- wled00/colors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 233d3d1168..00f1846012 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -48,7 +48,7 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) uint32_t g = wg & 0xFFFF; if (preserveCR) { // preserve color ratios - unsigned max = std::max(r,g); // check for overflow note + uint32_t max = std::max(r,g); // check for overflow note max = std::max(max,b); max = std::max(max,w); //unsigned max = r; // check for overflow note From bef1ac2668eb17312549e637f81c3954ac3052cd Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 25 Sep 2024 19:36:20 +0200 Subject: [PATCH 19/37] Added HSV2RGB and RGB2HSV functions for higher accuracy conversions - also added a struct to handle HSV with 16bit hue better (including some conversions, can be extended easily) - the functions are optimized for speed and flash use. They are faster and more accurate than what fastled offers (and use much less flash). - replaced colorHStoRGB() with a call to the new hsv2rgb() function, saving even more flash (new function is untested!) - the 16bit hue calculations result in an almost perfect conversion from RGB to HSV and back, the maximum error was 1/255 in the cases I tested. --- wled00/colors.cpp | 74 +++++++++++++++++++++++++++++++++----------- wled00/fcn_declare.h | 29 ++++++++++++++++- wled00/ir.cpp | 4 +-- 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 00f1846012..163429f7f5 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -239,26 +239,64 @@ CRGBPalette16 generateRandomPalette() //generate fully random palette CHSV(random8(), random8(160, 255), random8(128, 255))); } -void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb +void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0) { - float h = ((float)hue)/10922.5f; // hue*6/65535 - float s = ((float)sat)/255.0f; - int i = int(h); - float f = h - i; - int p = int(255.0f * (1.0f-s)); - int q = int(255.0f * (1.0f-s*f)); - int t = int(255.0f * (1.0f-s*(1.0f-f))); - p = constrain(p, 0, 255); - q = constrain(q, 0, 255); - t = constrain(t, 0, 255); - switch (i%6) { - case 0: rgb[0]=255,rgb[1]=t, rgb[2]=p; break; - case 1: rgb[0]=q, rgb[1]=255,rgb[2]=p; break; - case 2: rgb[0]=p, rgb[1]=255,rgb[2]=t; break; - case 3: rgb[0]=p, rgb[1]=q, rgb[2]=255;break; - case 4: rgb[0]=t, rgb[1]=p, rgb[2]=255;break; - case 5: rgb[0]=255,rgb[1]=p, rgb[2]=q; break; + unsigned int remainder, region, p, q, t; + unsigned int h = hsv.h; + unsigned int s = hsv.s; + unsigned int v = hsv.v; + if (s == 0) { + rgb = v << 16 | v << 8 | v; + return; } + region = h / 10923; // 65536 / 6 = 10923 + remainder = (h - (region * 10923)) * 6; + p = (v * (256 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 16))) >> 8; + t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8; + switch (region) { + case 0: + rgb = v << 16 | t << 8 | p; break; + case 1: + rgb = q << 16 | v << 8 | p; break; + case 2: + rgb = p << 16 | v << 8 | t; break; + case 3: + rgb = p << 16 | q << 8 | v; break; + case 4: + rgb = t << 16 | p << 8 | v; break; + default: + rgb = v << 16 | p << 8 | q; break; + } +} + +void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version +{ + hsv.raw = 0; + int32_t r = (rgb>>16)&0xFF; + int32_t g = (rgb>>8)&0xFF; + int32_t b = rgb&0xFF; + int32_t minval, maxval, delta; + minval = min(r, g); + minval = min(minval, b); + maxval = max(r, g); + maxval = max(maxval, b); + if (maxval == 0) return; // black + hsv.v = maxval; + delta = maxval - minval; + hsv.s = (255 * delta) / maxval; + if (hsv.s == 0) return; // gray value + if (maxval == r) hsv.h = (10923 * (g - b)) / delta; + else if (maxval == g) hsv.h = 21845 + (10923 * (b - r)) / delta; + else hsv.h = 43690 + (10923 * (r - g)) / delta; +} + +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb + uint32_t crgb; + hsv2rgb(CHSV32(hue, sat, 255), crgb); + rgb[0] = byte((crgb) >> 16); + rgb[1] = byte((crgb) >> 8); + rgb[2] = byte(crgb); } //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 741e1eefdf..b5bd1c28e2 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -67,6 +67,30 @@ typedef struct WiFiConfig { //colors.cpp #define ColorFromPalette ColorFromPaletteWLED // override fastled version + +struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions + union { + struct { + uint16_t h; // hue + uint8_t s; // saturation + uint8_t v; // value + }; + uint32_t raw; // 32bit access + }; + inline CHSV32() __attribute__((always_inline)) = default; // default constructor + + /// Allow construction from hue, saturation, and value + /// @param ih input hue + /// @param is input saturation + /// @param iv input value + inline CHSV32(uint16_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 16bit h, s, v + : h(ih), s(is), v(iv) {} + inline CHSV32(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) // constructor from 8bit h, s, v + : h((uint16_t)ih << 8), s(is), v(iv) {} + inline CHSV32(const CHSV& chsv) __attribute__((always_inline)) // constructor from CHSV + : h((uint16_t)chsv.h << 8), s(chsv.s), v(chsv.v) {} + inline operator CHSV() const { return CHSV((uint8_t)(h >> 8), s, v); } // typecast to CHSV +}; // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) class NeoGammaWLEDMethod { public: @@ -86,7 +110,10 @@ class NeoGammaWLEDMethod { CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette(); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } -void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); +void rgb2hsv(const uint32_t rgb, CHSV32& hsv); +inline CHSV rgb2hsv(const CRGB c) { CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); } // CRGB to hsv void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO diff --git a/wled00/ir.cpp b/wled00/ir.cpp index e4541cd909..f094d3b874 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -129,7 +129,7 @@ static void changeEffectSpeed(int8_t amount) } else { // if Effect == "solid Color", change the hue of the primary color Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); CRGB fastled_col = CRGB(sseg.colors[0]); - CHSV prim_hsv = rgb2hsv_approximate(fastled_col); + CHSV prim_hsv = rgb2hsv(fastled_col); int16_t new_val = (int16_t)prim_hsv.h + amount; if (new_val > 255) new_val -= 255; // roll-over if bigger than 255 if (new_val < 0) new_val += 255; // roll-over if smaller than 0 @@ -173,7 +173,7 @@ static void changeEffectIntensity(int8_t amount) } else { // if Effect == "solid Color", change the saturation of the primary color Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); CRGB fastled_col = CRGB(sseg.colors[0]); - CHSV prim_hsv = rgb2hsv_approximate(fastled_col); + CHSV prim_hsv = rgb2hsv(fastled_col); int16_t new_val = (int16_t) prim_hsv.s + amount; prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255 hsv2rgb_rainbow(prim_hsv, fastled_col); From b40445836931f769dce87168ff80c0ec489afc2c Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 26 Sep 2024 18:29:31 +0200 Subject: [PATCH 20/37] fixed one forgotten replacement of rgb2hsv_approximate --- wled00/colors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 163429f7f5..d705b9a7fe 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -138,7 +138,7 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette) { CHSV palettecolors[4]; //array of colors for the new palette uint8_t keepcolorposition = random8(4); //color position of current random palette to keep - palettecolors[keepcolorposition] = rgb2hsv_approximate(basepalette.entries[keepcolorposition*5]); //read one of the base colors of the current palette + palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); //read one of the base colors of the current palette palettecolors[keepcolorposition].hue += random8(10)-5; // +/- 5 randomness of base color //generate 4 saturation and brightness value numbers //only one saturation is allowed to be below 200 creating mostly vibrant colors From a76a895f1d86f9405b94fca5dc91a6649bdf48dd Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 27 Sep 2024 06:17:26 +0200 Subject: [PATCH 21/37] bugfix --- wled00/colors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index d705b9a7fe..1b6a6f8d91 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -251,7 +251,7 @@ void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB } region = h / 10923; // 65536 / 6 = 10923 remainder = (h - (region * 10923)) * 6; - p = (v * (256 - s)) >> 8; + p = (v * (255 - s)) >> 8; q = (v * (255 - ((s * remainder) >> 16))) >> 8; t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8; switch (region) { From 7c0fe1285aa21e20b16c1185e93fc5e65cd32b3c Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 28 Sep 2024 15:26:14 +0200 Subject: [PATCH 22/37] updated setPixelColor() and getPixelColor() functions uint16_t to unsigned to make it consisten throughout the hand-down. colorFromPaletteWLED now returns uint32_t which saves the conversion to CRGB and back to uint32_t (in most uses at least). also added (preliminary) CRGBW struct. I tried to use it in place of uint32_t colors but it adds a lot of overhead when passing the struct so reverted to uint32_t in most places. updated a few FX to use the CRGBW struct and also cleaned some code to improve flash useage. --- wled00/FX.cpp | 92 ++++++++++++++++++------------------------ wled00/FX.h | 6 +-- wled00/FX_2Dfcn.cpp | 2 +- wled00/FX_fcn.cpp | 7 ++-- wled00/bus_manager.cpp | 20 ++++----- wled00/bus_manager.h | 24 +++++------ wled00/colors.cpp | 4 +- wled00/fcn_declare.h | 60 ++++++++++++++++++++++++++- 8 files changed, 129 insertions(+), 86 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index ad843f0f95..3df91547de 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1937,7 +1937,7 @@ uint16_t mode_juggle(void) { for (int i = 0; i < 8; i++) { int index = 0 + beatsin88((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1); fastled_col = CRGB(SEGMENT.getPixelColor(index)); - fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255); + fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):CRGB(ColorFromPalette(SEGPALETTE, dothue, 255)); SEGMENT.setPixelColor(index, fastled_col); dothue += 32; } @@ -2282,33 +2282,33 @@ uint16_t mode_colortwinkle() { unsigned dataSize = (SEGLEN+7) >> 3; //1 bit per LED if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed - CRGB fastled_col, prev; + CRGBW col, prev; fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness(); fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness(); for (int i = 0; i < SEGLEN; i++) { - fastled_col = SEGMENT.getPixelColor(i); - prev = fastled_col; + CRGBW cur = SEGMENT.getPixelColor(i); + prev = cur; unsigned index = i >> 3; unsigned bitNum = i & 0x07; bool fadeUp = bitRead(SEGENV.data[index], bitNum); if (fadeUp) { - CRGB incrementalColor = fastled_col; - incrementalColor.nscale8_video(fadeUpAmount); - fastled_col += incrementalColor; + CRGBW incrementalColor = color_fade(col, fadeUpAmount, true); + col = color_add(cur, incrementalColor); - if (fastled_col.red == 255 || fastled_col.green == 255 || fastled_col.blue == 255) { + if (col.r == 255 || col.g == 255 || col.b == 255) { bitWrite(SEGENV.data[index], bitNum, false); } - SEGMENT.setPixelColor(i, fastled_col.red, fastled_col.green, fastled_col.blue); - if (SEGMENT.getPixelColor(i) == RGBW32(prev.r, prev.g, prev.b, 0)) { //fix "stuck" pixels - fastled_col += fastled_col; - SEGMENT.setPixelColor(i, fastled_col); + if (cur == prev) { //fix "stuck" pixels + color_add(col, col); + SEGMENT.setPixelColor(i, col); } - } else { - fastled_col.nscale8(255 - fadeDownAmount); - SEGMENT.setPixelColor(i, fastled_col); + else SEGMENT.setPixelColor(i, col); + } + else { + col = color_fade(cur, 255 - fadeDownAmount); + SEGMENT.setPixelColor(i, col); } } @@ -2317,11 +2317,10 @@ uint16_t mode_colortwinkle() { for (unsigned times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times int i = random16(SEGLEN); if (SEGMENT.getPixelColor(i) == 0) { - fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND); unsigned index = i >> 3; unsigned bitNum = i & 0x07; bitWrite(SEGENV.data[index], bitNum, true); - SEGMENT.setPixelColor(i, fastled_col); + SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND)); break; //only spawn 1 new pixel per frame per 50 LEDs } } @@ -2378,8 +2377,7 @@ uint16_t mode_meteor() { index = map(i,0,SEGLEN,0,max); bri = trail[i]; } - uint32_t col = SEGMENT.color_from_palette(index, false, false, idx, bri); // full brightness for Fire - SEGMENT.setPixelColor(i, col); + SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, idx, bri)); // full brightness for Fire } } @@ -2392,8 +2390,7 @@ uint16_t mode_meteor() { i = map(index,0,SEGLEN,0,max); idx = 0; } - uint32_t col = SEGMENT.color_from_palette(i, false, false, idx, 255); // full brightness - SEGMENT.setPixelColor(index, col); + SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(i, false, false, idx, 255)); // full brightness } return FRAMETIME; @@ -2419,8 +2416,7 @@ uint16_t mode_meteor_smooth() { if (/*trail[i] != 0 &&*/ random8() <= 255 - SEGMENT.intensity) { int change = trail[i] + 4 - random8(24); //change each time between -20 and +4 trail[i] = constrain(change, 0, max); - uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255); - SEGMENT.setPixelColor(i, col); + SEGMENT.setPixelColor(i, SEGMENT.check1 ? SEGMENT.color_from_palette(i, true, false, 0, trail[i]) : SEGMENT.color_from_palette(trail[i], false, true, 255)); } } @@ -2431,8 +2427,7 @@ uint16_t mode_meteor_smooth() { index -= SEGLEN; } trail[index] = max; - uint32_t col = SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255); - SEGMENT.setPixelColor(index, col); + SEGMENT.setPixelColor(index, SEGMENT.check1 ? SEGMENT.color_from_palette(index, true, false, 0, trail[index]) : SEGMENT.color_from_palette(trail[index], false, true, 255)); } SEGENV.step += SEGMENT.speed +1; @@ -2680,7 +2675,7 @@ static uint16_t twinklefox_base(bool cat) if (deltabright >= 32 || (!bg)) { // If the new pixel is significantly brighter than the background color, // use the new color. - SEGMENT.setPixelColor(i, c.red, c.green, c.blue); + SEGMENT.setPixelColor(i, c); } else if (deltabright > 0) { // If the new pixel is just slightly brighter than the background color, // mix a blend of the new color and the background color @@ -2688,7 +2683,7 @@ static uint16_t twinklefox_base(bool cat) } else { // if the new pixel is not at all brighter than the background color, // just use the background color. - SEGMENT.setPixelColor(i, bg.r, bg.g, bg.b); + SEGMENT.setPixelColor(i, bg); } } return FRAMETIME; @@ -3532,7 +3527,7 @@ uint16_t mode_starburst(void) { if (start == end) end++; if (end > SEGLEN) end = SEGLEN; for (int p = start; p < end; p++) { - SEGMENT.setPixelColor(p, c.r, c.g, c.b); + SEGMENT.setPixelColor(p, c); } } } @@ -3650,17 +3645,17 @@ uint16_t mode_exploding_fireworks(void) if (SEGMENT.is2D() && !(sparks[i].posX >= 0 && sparks[i].posX < cols)) continue; unsigned prog = sparks[i].col; uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0); - CRGB c = CRGB::Black; //HeatColor(sparks[i].col); + CRGBW c = BLACK; //HeatColor(sparks[i].col); if (prog > 300) { //fade from white to spark color - c = CRGB(color_blend(spColor, WHITE, (prog - 300)*5)); + c = color_blend(spColor, WHITE, (prog - 300)*5); } else if (prog > 45) { //fade from spark color to black - c = CRGB(color_blend(BLACK, spColor, prog - 45)); + c = color_blend(BLACK, spColor, prog - 45); unsigned cooling = (300 - prog) >> 5; c.g = qsub8(c.g, cooling); c.b = qsub8(c.b, cooling * 2); } - if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c.red, c.green, c.blue); - else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c.red, c.green, c.blue); + if (SEGMENT.is2D()) SEGMENT.setPixelColorXY(int(sparks[i].posX), rows - int(sparks[i].pos) - 1, c); + else SEGMENT.setPixelColor(int(sparks[i].posX) ? rows - int(sparks[i].pos) - 1 : int(sparks[i].pos), c); } } if (SEGMENT.check3) SEGMENT.blur(16); @@ -4006,7 +4001,7 @@ static CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, u ci += (cs * i); unsigned sindex16 = sin16(ci) + 32768; unsigned sindex8 = scale16(sindex16, 240); - return ColorFromPalette(p, sindex8, bri, LINEARBLEND); + return CRGB(ColorFromPalette(p, sindex8, bri, LINEARBLEND)); } uint16_t mode_pacifica() @@ -4077,7 +4072,7 @@ uint16_t mode_pacifica() c.green = scale8(c.green, 200); c |= CRGB( 2, 5, 7); - SEGMENT.setPixelColor(i, c.red, c.green, c.blue); + SEGMENT.setPixelColor(i, c); } strip.now = nowOld; @@ -4119,13 +4114,10 @@ uint16_t mode_sunrise() { for (int i = 0; i <= SEGLEN/2; i++) { - //default palette is Fire - uint32_t c = SEGMENT.color_from_palette(0, false, true, 255); //background - + //default palette is Fire unsigned wave = triwave16((i * stage) / SEGLEN); - wave = (wave >> 8) + ((wave * SEGMENT.intensity) >> 15); - + uint32_t c; if (wave > 240) { //clipped, full white sun c = SEGMENT.color_from_palette( 240, false, true, 255); } else { //transition @@ -4217,8 +4209,6 @@ uint16_t mode_noisepal(void) { // Slow noise palettes[1] = CRGBPalette16(CHSV(baseI+random8(64), 255, random8(128,255)), CHSV(baseI+128, 255, random8(128,255)), CHSV(baseI+random8(92), 192, random8(128,255)), CHSV(baseI+random8(92), 255, random8(128,255))); } - CRGB color; - //EVERY_N_MILLIS(10) { //(don't have to time this, effect function is only called every 24ms) nblendPaletteTowardPalette(palettes[0], palettes[1], 48); // Blend towards the target palette over 48 iterations. @@ -4226,8 +4216,7 @@ uint16_t mode_noisepal(void) { // Slow noise for (int i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. - color = ColorFromPalette(palettes[0], index, 255, LINEARBLEND); // Use the my own palette. - SEGMENT.setPixelColor(i, color.red, color.green, color.blue); + SEGMENT.setPixelColor(i, ColorFromPalette(palettes[0], index, 255, LINEARBLEND)); // Use my own palette. } SEGENV.aux0 += beatsin8(10,1,4); // Moving along the distance. Vary it a bit with a sine wave. @@ -4314,9 +4303,8 @@ uint16_t mode_chunchun(void) counter -= span; unsigned megumin = sin16(counter) + 0x8000; unsigned bird = uint32_t(megumin * SEGLEN) >> 16; - uint32_t c = SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping bird = constrain(bird, 0U, SEGLEN-1U); - SEGMENT.setPixelColor(bird, c); + SEGMENT.setPixelColor(bird, SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0)); // no palette wrapping } return FRAMETIME; } @@ -4934,7 +4922,7 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1)); byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24); byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64); - CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); + uint32_t color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND); byte xsteps = abs8(x1 - y1) + 1; byte ysteps = abs8(x2 - y2) + 1; @@ -5831,7 +5819,7 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht for (size_t i = 0; i < 8; i++) { int x = beatsin8(12 + i, 2, cols - 3); int y = beatsin8(15 + i, 2, rows - 3); - CRGB color = ColorFromPalette(SEGPALETTE, beatsin8(12 + i, 0, 255), 255); + uint32_t color = ColorFromPalette(SEGPALETTE, beatsin8(12 + i, 0, 255), 255); SEGMENT.addPixelColorXY(x, y, color); if (cols > 24 || rows > 24) { SEGMENT.addPixelColorXY(x+1, y, color); @@ -6725,8 +6713,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. - CRGB color = ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND); // Use the my own palette. - SEGMENT.setPixelColor(i, color); + SEGMENT.setPixelColor(i, ColorFromPalette(myPal, index, volumeSmth*2, LINEARBLEND)); // Use my own palette. } return FRAMETIME; @@ -7532,7 +7519,7 @@ uint16_t mode_2DAkemi(void) { unsigned band = x * cols/8; band = constrain(band, 0, 15); int barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32); - CRGB color = CRGB(SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0)); + uint32_t color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0); for (int y=0; y < barHeight; y++) { SEGMENT.setPixelColorXY(x, rows/2-y, color); @@ -7760,8 +7747,7 @@ uint16_t mode_2Doctopus() { //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1))); unsigned intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); intensity = map((intensity*intensity) & 0xFFFF, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display - CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity); - SEGMENT.setPixelColorXY(x, y, c); + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity)); } } return FRAMETIME; diff --git a/wled00/FX.h b/wled00/FX.h index 989dfbe331..825c722e75 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -851,10 +851,8 @@ class WS2812FX { // 96 bytes return index; }; - uint32_t - now, - timebase, - getPixelColor(uint16_t) const; + uint32_t now, timebase; + uint32_t getPixelColor(unsigned) const; inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } // returns currently valid color (for slot i) AKA SEGCOLOR(); may be blended between two colors while in transition diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index ca28a95869..41fd67319d 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -671,7 +671,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font default: return; } - col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); + uint32_t col = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND); for (int j = 0; j 1) paletteIndex = (i*255)/(virtualLength() -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 = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + palcol.w = gamma8(W(color)); - return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, gamma8(W(color))); + return palcol.color32; } @@ -1419,7 +1420,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); diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 5b948b9c41..404c334495 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -306,7 +306,7 @@ void BusDigital::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { +void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { if (!_valid) return; uint8_t cctWW = 0, cctCW = 0; if (hasWhite()) c = autoWhiteCalc(c); @@ -342,7 +342,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) { } // returns original color if global buffering is enabled, else returns lossly restored color from bus -uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const { +uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { if (!_valid) return 0; if (_data) { size_t offset = pix * getNumberOfChannels(); @@ -501,7 +501,7 @@ BusPwm::BusPwm(BusConfig &bc) DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]); } -void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { +void BusPwm::setPixelColor(unsigned pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c); if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) { @@ -538,7 +538,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) { } //does no index check -uint32_t BusPwm::getPixelColor(uint16_t pix) const { +uint32_t BusPwm::getPixelColor(unsigned pix) const { if (!_valid) return 0; // TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented) switch (_type) { @@ -674,7 +674,7 @@ BusOnOff::BusOnOff(BusConfig &bc) DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); } -void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { +void BusOnOff::setPixelColor(unsigned pix, uint32_t c) { if (pix != 0 || !_valid) return; //only react to first pixel c = autoWhiteCalc(c); uint8_t r = R(c); @@ -684,7 +684,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) { _data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; } -uint32_t BusOnOff::getPixelColor(uint16_t pix) const { +uint32_t BusOnOff::getPixelColor(unsigned pix) const { if (!_valid) return 0; return RGBW32(_data[0], _data[0], _data[0], _data[0]); } @@ -734,7 +734,7 @@ BusNetwork::BusNetwork(BusConfig &bc) DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); } -void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { +void BusNetwork::setPixelColor(unsigned pix, uint32_t c) { if (!_valid || pix >= _len) return; if (_hasWhite) c = autoWhiteCalc(c); if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT @@ -745,7 +745,7 @@ void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) { if (_hasWhite) _data[offset+3] = W(c); } -uint32_t BusNetwork::getPixelColor(uint16_t pix) const { +uint32_t BusNetwork::getPixelColor(unsigned pix) const { if (!_valid || pix >= _len) return 0; unsigned offset = pix * _UDPchannels; return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (hasWhite() ? _data[offset+3] : 0)); @@ -952,7 +952,7 @@ void BusManager::setStatusPixel(uint32_t c) { } } -void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) { +void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) { for (unsigned i = 0; i < numBusses; i++) { unsigned bstart = busses[i]->getStart(); if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue; @@ -975,7 +975,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { Bus::setCCT(cct); } -uint32_t BusManager::getPixelColor(uint16_t pix) { +uint32_t BusManager::getPixelColor(unsigned pix) { for (unsigned i = 0; i < numBusses; i++) { unsigned bstart = busses[i]->getStart(); if (!busses[i]->containsPixel(pix)) continue; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index e96b9de714..1b324d7137 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -82,10 +82,10 @@ class Bus { virtual void show() = 0; virtual bool canShow() const { return true; } virtual void setStatusPixel(uint32_t c) {} - virtual void setPixelColor(uint16_t pix, uint32_t c) = 0; + virtual void setPixelColor(unsigned pix, uint32_t c) = 0; virtual void setBrightness(uint8_t b) { _bri = b; }; virtual void setColorOrder(uint8_t co) {} - virtual uint32_t getPixelColor(uint16_t pix) const { return 0; } + virtual uint32_t getPixelColor(unsigned pix) const { return 0; } virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; } virtual uint16_t getLength() const { return isOk() ? _len : 0; } virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } @@ -203,9 +203,9 @@ class BusDigital : public Bus { bool canShow() const override; void setBrightness(uint8_t b) override; void setStatusPixel(uint32_t c) override; - [[gnu::hot]] void setPixelColor(uint16_t pix, uint32_t c) override; + [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; void setColorOrder(uint8_t colorOrder) override; - [[gnu::hot]] uint32_t getPixelColor(uint16_t pix) const override; + [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; uint8_t getColorOrder() const override { return _colorOrder; } uint8_t getPins(uint8_t* pinArray = nullptr) const override; uint8_t skippedLeds() const override { return _skip; } @@ -251,8 +251,8 @@ class BusPwm : public Bus { BusPwm(BusConfig &bc); ~BusPwm() { cleanup(); } - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; //does no index check + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; //does no index check uint8_t getPins(uint8_t* pinArray = nullptr) const override; uint16_t getFrequency() const override { return _frequency; } void show() override; @@ -278,8 +278,8 @@ class BusOnOff : public Bus { BusOnOff(BusConfig &bc); ~BusOnOff() { cleanup(); } - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; uint8_t getPins(uint8_t* pinArray) const override; void show() override; void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); } @@ -298,8 +298,8 @@ class BusNetwork : public Bus { ~BusNetwork() { cleanup(); } bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out - void setPixelColor(uint16_t pix, uint32_t c) override; - uint32_t getPixelColor(uint16_t pix) const override; + void setPixelColor(unsigned pix, uint32_t c) override; + uint32_t getPixelColor(unsigned pix) const override; uint8_t getPins(uint8_t* pinArray = nullptr) const override; void show() override; void cleanup(); @@ -384,13 +384,13 @@ class BusManager { static void show(); static bool canAllShow(); static void setStatusPixel(uint32_t c); - [[gnu::hot]] static void setPixelColor(uint16_t pix, uint32_t c); + [[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c); static void setBrightness(uint8_t b); // for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} - static uint32_t getPixelColor(uint16_t pix); + [[gnu::hot]] static uint32_t getPixelColor(unsigned pix); static inline int16_t getSegmentCCT() { return Bus::getCCT(); } static Bus* getBus(uint8_t busNr); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 1b6a6f8d91..1c48443717 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -96,7 +96,7 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) } // 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) -CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) +uint32_t ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) { if (blendType == LINEARBLEND_NOWRAP) { index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping @@ -121,7 +121,7 @@ CRGB ColorFromPaletteWLED(const CRGBPalette16& pal, unsigned index, uint8_t brig green1 = (green1 * scale) >> 8; blue1 = (blue1 * scale) >> 8; } - return CRGB((uint8_t)red1, (uint8_t)green1, (uint8_t)blue1); + return RGBW32(red1,green1,blue1,0); } void setRandomColor(byte* rgb) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index ff96cf7490..6568304f80 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -68,6 +68,64 @@ typedef struct WiFiConfig { //colors.cpp #define ColorFromPalette ColorFromPaletteWLED // override fastled version +// CRGBW can be used to manipulate 32bit colors faster. However: if it is passed to functions, it adds overhead compared to a uint32_t color +// use with caution and pay attention to flash size. Usually converting a uint32_t to CRGBW to extract r, g, b, w values is slower than using bitshifts +// it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB +struct CRGBW { + union { + uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB) + uint8_t raw[4]; // Access as an array in the order B, G, R, W + struct { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t w; + }; + }; + + // Default constructor + inline CRGBW() __attribute__((always_inline)) = default; + + // Constructor from a 32-bit color (0xWWRRGGBB) + constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {} + + // Constructor with r, g, b, w values + constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : r(red), g(green), b(blue), w(white) {} + + // Constructor from CRGB + constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : r(rgb.r), g(rgb.g), b(rgb.b), w(0) {} + + // Access as an array + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; } + + // Assignment from 32-bit color + inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; } + + // Assignment from r, g, b, w + inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { r = rgb.r; g = rgb.g; b = rgb.b; w = 0; return *this; } + + // Conversion operator to uint32_t + inline operator uint32_t() const __attribute__((always_inline)) { + return color32; + } + /* + // Conversion operator to CRGB + inline operator CRGB() const __attribute__((always_inline)) { + return CRGB(r, g, b); + } + + CRGBW& scale32 (uint8_t scaledown) // 32bit math + { + if (color32 == 0) return *this; // 2 extra instructions, worth it if called a lot on black (which probably is true) adding check if scaledown is zero adds much more overhead as its 8bit + uint32_t scale = scaledown + 1; + uint32_t rb = (((color32 & 0x00FF00FF) * scale) >> 8) & 0x00FF00FF; // scale red and blue + uint32_t wg = (((color32 & 0xFF00FF00) >> 8) * scale) & 0xFF00FF00; // scale white and green + color32 = rb | wg; + return *this; + }*/ + +}; + struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions union { struct { @@ -106,7 +164,7 @@ class NeoGammaWLEDMethod { [[gnu::hot]] uint32_t color_blend(uint32_t, uint32_t, uint16_t, bool b16=false); [[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]] CRGB ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); +[[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(); inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } From 202901b09f2a8c6da3a453b0d3ed8c8140ea5557 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 28 Sep 2024 15:38:41 +0200 Subject: [PATCH 23/37] bugfix, ESP32 compiler requires the color order to be identical --- wled00/fcn_declare.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 6568304f80..704fae852b 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -74,13 +74,13 @@ typedef struct WiFiConfig { struct CRGBW { union { uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB) - uint8_t raw[4]; // Access as an array in the order B, G, R, W struct { uint8_t b; uint8_t g; uint8_t r; uint8_t w; }; + uint8_t raw[4]; // Access as an array in the order B, G, R, W }; // Default constructor @@ -90,10 +90,10 @@ struct CRGBW { constexpr CRGBW(uint32_t color) __attribute__((always_inline)) : color32(color) {} // Constructor with r, g, b, w values - constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : r(red), g(green), b(blue), w(white) {} + constexpr CRGBW(uint8_t red, uint8_t green, uint8_t blue, uint8_t white = 0) __attribute__((always_inline)) : b(blue), g(green), r(red), w(white) {} // Constructor from CRGB - constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : r(rgb.r), g(rgb.g), b(rgb.b), w(0) {} + constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {} // Access as an array inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; } @@ -102,7 +102,7 @@ struct CRGBW { inline CRGBW& operator=(uint32_t color) __attribute__((always_inline)) { color32 = color; return *this; } // Assignment from r, g, b, w - inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { r = rgb.r; g = rgb.g; b = rgb.b; w = 0; return *this; } + inline CRGBW& operator=(const CRGB& rgb) __attribute__((always_inline)) { b = rgb.b; g = rgb.g; r = rgb.r; w = 0; return *this; } // Conversion operator to uint32_t inline operator uint32_t() const __attribute__((always_inline)) { From c842994df5d5e9120c069c84cba97ba8c686ab0d Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 28 Sep 2024 18:14:43 +0200 Subject: [PATCH 24/37] Pre-calculate virtual - move SEGCOLOR() to Segment class - add SEG_H, SEG_W macros - try to speed up virtualXxxxx() - compile warning fixes --- wled00/FX.cpp | 347 ++++++++++++++++++++++---------------------- wled00/FX.h | 64 ++++---- wled00/FX_2Dfcn.cpp | 146 ++++++++++--------- wled00/FX_fcn.cpp | 123 +++++++++------- wled00/udp.cpp | 54 +++---- wled00/xml.cpp | 4 +- 6 files changed, 367 insertions(+), 371 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 3df91547de..2ec31014f2 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -118,7 +118,7 @@ uint16_t blink(uint32_t color1, uint32_t color2, bool strobe, bool do_palette) { uint32_t color = on ? color1 : color2; if (color == color1 && do_palette) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } } else SEGMENT.fill(color); @@ -300,25 +300,25 @@ uint16_t mode_dynamic(void) { if(SEGENV.call == 0) { //SEGMENT.fill(BLACK); - for (int i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); + for (unsigned i = 0; i < SEGLEN; i++) SEGENV.data[i] = random8(); } uint32_t cycleTime = 50 + (255 - SEGMENT.speed)*15; uint32_t it = strip.now / cycleTime; if (it != SEGENV.step && SEGMENT.speed != 0) //new color { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (random8() <= SEGMENT.intensity) SEGENV.data[i] = random8(); // random color index } SEGENV.step = it; } if (SEGMENT.check1) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.blendPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i]), 16); } } else { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_wheel(SEGENV.data[i])); } } @@ -353,7 +353,7 @@ uint16_t mode_breath(void) { } unsigned lum = 30 + var; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -369,7 +369,7 @@ uint16_t mode_fade(void) { unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10)); unsigned lum = triwave16(counter) >> 8; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum)); } @@ -452,7 +452,7 @@ uint16_t mode_rainbow_cycle(void) { unsigned counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { //intensity/29 = 0 (1/16) 1 (1/8) 2 (1/4) 3 (1/2) 4 (1) 5 (2) 6 (4) 7 (8) 8 (16) uint8_t index = (i * (16 << (SEGMENT.intensity /29)) / SEGLEN) + counter; SEGMENT.setPixelColor(i, SEGMENT.color_wheel(index)); @@ -472,7 +472,7 @@ static uint16_t running(uint32_t color1, uint32_t color2, bool theatre = false) uint32_t it = strip.now / cycleTime; bool usePalette = color1 == SEGCOLOR(0); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { uint32_t col = color2; if (usePalette) color1 = SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0); if (theatre) { @@ -519,7 +519,7 @@ static uint16_t running_base(bool saw, bool dual=false) { unsigned x_scale = SEGMENT.intensity >> 2; uint32_t counter = (strip.now * SEGMENT.speed) >> 9; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned a = i*x_scale - counter; if (saw) { a &= 0xFF; @@ -622,7 +622,7 @@ uint16_t dissolve(uint32_t color) { SEGENV.aux0 = 1; } - for (int j = 0; j <= SEGLEN / 15; j++) { + for (unsigned j = 0; j <= SEGLEN / 15; j++) { if (random8() <= SEGMENT.intensity) { for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times unsigned i = random16(SEGLEN); @@ -684,7 +684,7 @@ static const char _data_FX_MODE_DISSOLVE_RANDOM[] PROGMEM = "Dissolve Rnd@Repeat * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_sparkle(void) { - if (!SEGMENT.check2) for(int i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for(unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } uint32_t cycleTime = 10 + (255 - SEGMENT.speed)*2; @@ -706,7 +706,7 @@ static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!; * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_flash_sparkle(void) { - if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } @@ -727,13 +727,14 @@ static const char _data_FX_MODE_FLASH_SPARKLE[] PROGMEM = "Sparkle Dark@!,!,,,,, * Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/ */ uint16_t mode_hyper_sparkle(void) { - if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) { + if (!SEGMENT.check2) for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } if (strip.now - SEGENV.aux0 > SEGENV.step) { if (random8((255-SEGMENT.intensity) >> 4) == 0) { - for (int i = 0; i < max(1, SEGLEN/3); i++) { + int len = max(1, (int)SEGLEN/3); + for (int i = 0; i < len; i++) { SEGMENT.setPixelColor(random16(SEGLEN), SEGCOLOR(1)); } } @@ -749,7 +750,7 @@ static const char _data_FX_MODE_HYPER_SPARKLE[] PROGMEM = "Sparkle+@!,!,,,,,Over * Strobe effect with different strobe count and pause, controlled by speed. */ uint16_t mode_multi_strobe(void) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } @@ -780,7 +781,7 @@ static const char _data_FX_MODE_MULTI_STROBE[] PROGMEM = "Strobe Mega@!,!;!,!;!; */ uint16_t mode_android(void) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); } @@ -995,10 +996,10 @@ static const char _data_FX_MODE_COLORFUL[] PROGMEM = "Colorful@!,Saturation;1,2, */ uint16_t mode_traffic_light(void) { if (SEGLEN == 1) return mode_static(); - for (int i=0; i < SEGLEN; i++) + for (unsigned i=0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1)); uint32_t mdelay = 500; - for (int i = 0; i < SEGLEN-2 ; i+=3) + for (unsigned i = 0; i < SEGLEN-2 ; i+=3) { switch (SEGENV.aux0) { @@ -1030,7 +1031,7 @@ uint16_t mode_chase_flash(void) { if (SEGLEN == 1) return mode_static(); unsigned flash_step = SEGENV.call % ((FLASH_COUNT * 2) + 1); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0)); } @@ -1226,8 +1227,8 @@ static const char _data_FX_MODE_COMET[] PROGMEM = "Lighthouse@!,Fade rate;!,!;!" */ uint16_t mode_fireworks() { if (SEGLEN == 1) return mode_static(); - const uint16_t width = SEGMENT.is2D() ? SEGMENT.virtualWidth() : SEGMENT.virtualLength(); - const uint16_t height = SEGMENT.virtualHeight(); + const uint16_t width = SEGMENT.is2D() ? SEG_W : SEGLEN; + const uint16_t height = SEG_H; if (SEGENV.call == 0) { SEGENV.aux0 = UINT16_MAX; @@ -1268,8 +1269,8 @@ static const char _data_FX_MODE_FIREWORKS[] PROGMEM = "Fireworks@,Frequency;!,!; //Twinkling LEDs running. Inspired by https://github.com/kitesurfer1404/WS2812FX/blob/master/src/custom/Rain.h uint16_t mode_rain() { if (SEGLEN == 1) return mode_static(); - const unsigned width = SEGMENT.virtualWidth(); - const unsigned height = SEGMENT.virtualHeight(); + const unsigned width = SEG_W; + const unsigned height = SEG_H; SEGENV.step += FRAMETIME; if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) { SEGENV.step = 1; @@ -1283,7 +1284,7 @@ uint16_t mode_rain() { } else { //shift all leds left uint32_t ctemp = SEGMENT.getPixelColor(0); - for (int i = 0; i < SEGLEN - 1; i++) { + for (unsigned i = 0; i < SEGLEN - 1; i++) { SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); } SEGMENT.setPixelColor(SEGLEN -1, ctemp); // wrap around @@ -1314,7 +1315,7 @@ uint16_t mode_fire_flicker(void) { byte b = (SEGCOLOR(0) ); byte lum = (SEGMENT.palette == 0) ? MAX(w, MAX(r, MAX(g, b))) : 255; lum /= (((256-SEGMENT.intensity)/16)+1); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { byte flicker = random8(lum); if (SEGMENT.palette == 0) { SEGMENT.setPixelColor(i, MAX(r - flicker, 0), MAX(g - flicker, 0), MAX(b - flicker, 0), MAX(w - flicker, 0)); @@ -1343,7 +1344,7 @@ uint16_t gradient_base(bool loading) { int p1 = pp-SEGLEN; int p2 = pp+SEGLEN; - for (int i = 0; i < SEGLEN; i++) { + for (int i = 0; i < (int)SEGLEN; i++) { if (loading) { val = abs(((i>pp) ? p2:pp) - i); } else { @@ -1428,7 +1429,7 @@ typedef struct Flasher { uint16_t mode_fairy() { //set every pixel to a 'random' color from palette (using seed so it doesn't change between frames) uint16_t PRNG16 = 5100 + strip.getCurrSegmentId(); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0)); } @@ -1513,7 +1514,7 @@ uint16_t mode_fairytwinkle() { unsigned riseFallTime = 400 + (255-SEGMENT.speed)*3; unsigned maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1); - for (int f = 0; f < SEGLEN; f++) { + for (unsigned f = 0; f < SEGLEN; f++) { unsigned stateTime = now16 - flashers[f].stateStart; //random on/off time reached, switch state if (stateTime > flashers[f].stateDur * 100) { @@ -1558,7 +1559,7 @@ uint16_t tricolor_chase(uint32_t color1, uint32_t color2) { unsigned width = (1 + (SEGMENT.intensity>>4)); // value of 1-16 for each colour unsigned index = it % (width*3); - for (int i = 0; i < SEGLEN; i++, index++) { + for (unsigned i = 0; i < SEGLEN; i++, index++) { if (index > (width*3)-1) index = 0; uint32_t color = color1; @@ -1631,7 +1632,7 @@ uint16_t mode_tricolor_wipe(void) { unsigned ledIndex = (prog * SEGLEN * 3) >> 16; unsigned ledOffset = ledIndex; - for (int i = 0; i < SEGLEN; i++) + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2)); } @@ -1970,8 +1971,8 @@ uint16_t mode_palette() { constexpr float (*cosFunction)(float) = &cos_t; #endif const bool isMatrix = strip.isMatrix; - const int cols = SEGMENT.virtualWidth(); - const int rows = isMatrix ? SEGMENT.virtualHeight() : strip.getActiveSegmentsNum(); + const int cols = SEG_W; + const int rows = isMatrix ? SEG_H : strip.getActiveSegmentsNum(); const int inputShift = SEGMENT.speed; const int inputSize = SEGMENT.intensity; @@ -2084,10 +2085,10 @@ uint16_t mode_fire_2012() { struct virtualStrip { static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) { - const uint8_t ignition = max(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels + const uint8_t ignition = MAX(3,SEGLEN/10); // ignition area: 10% of segment length or minimum 3 pixels // Step 1. Cool down every cell a little - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random8(4); uint8_t minTemp = (i> 8; unsigned h16_128 = hue16 >> 7; @@ -2183,7 +2184,7 @@ static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!"; uint16_t mode_bpm() { uint32_t stp = (strip.now / 20) & 0xFF; uint8_t beat = beatsin8(SEGMENT.speed, 64, 255); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10))); } @@ -2194,7 +2195,7 @@ static const char _data_FX_MODE_BPM[] PROGMEM = "Bpm@!;!;!;;sx=64"; uint16_t mode_fillnoise8() { if (SEGENV.call == 0) SEGENV.step = random16(12345); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } @@ -2209,7 +2210,7 @@ uint16_t mode_noise16_1() { unsigned scale = 320; // the "zoom factor" for the noise SEGENV.step += (1 + SEGMENT.speed/16); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm unsigned shift_y = SEGENV.step/42; // the y position becomes slowly incremented unsigned real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm @@ -2230,7 +2231,7 @@ uint16_t mode_noise16_2() { unsigned scale = 1000; // the "zoom factor" for the noise SEGENV.step += (1 + (SEGMENT.speed >> 1)); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned shift_x = SEGENV.step >> 6; // x as a function of time uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field unsigned noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down @@ -2248,7 +2249,7 @@ uint16_t mode_noise16_3() { unsigned scale = 800; // the "zoom factor" for the noise SEGENV.step += (1 + SEGMENT.speed); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned shift_x = 4223; // no movement along x and y unsigned shift_y = 1234; uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field @@ -2268,7 +2269,7 @@ static const char _data_FX_MODE_NOISE16_3[] PROGMEM = "Noise 3@!;!;!"; //https://github.com/aykevl/ledstrip-spark/blob/master/ledstrip.ino uint16_t mode_noise16_4() { uint32_t stp = (strip.now * SEGMENT.speed) >> 7; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { int index = inoise16(uint32_t(i) << 12, stp); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } @@ -2285,7 +2286,7 @@ uint16_t mode_colortwinkle() { CRGBW col, prev; fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness(); fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness(); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { CRGBW cur = SEGMENT.getPixelColor(i); prev = cur; unsigned index = i >> 3; @@ -2338,7 +2339,7 @@ uint16_t mode_lake() { int wave2 = beatsin8(sp +1, -64,64); int wave3 = beatsin8(sp +2, 0,80); - for (int i = 0; i < SEGLEN; i++) + for (unsigned i = 0; i < SEGLEN; i++) { int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2; uint8_t lum = (index > wave3) ? index - wave3 : 0; @@ -2365,7 +2366,7 @@ uint16_t mode_meteor() { const int max = SEGMENT.palette==5 ? 239 : 255; // "* Colors only" palette blends end with start // fade all leds to colors[1] in LEDs one step - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (random8() <= 255 - SEGMENT.intensity) { int meteorTrailDecay = 128 + random8(127); trail[i] = scale8(trail[i], meteorTrailDecay); @@ -2454,7 +2455,7 @@ uint16_t mode_railway() { if (p0 < 255) pos = p0; } if (SEGENV.aux0) pos = 255 - pos; - for (int i = 0; i < SEGLEN; i += 2) + for (unsigned i = 0; i < SEGLEN; i += 2) { SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(255 - pos, false, false, 255)); // do not use color 1 or 2, always use palette if (i < SEGLEN -1) @@ -2485,7 +2486,7 @@ typedef struct Ripple { #define MAX_RIPPLES 100 #endif static uint16_t ripple_base() { - unsigned maxRipples = min(1 + (SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 + unsigned maxRipples = min(1 + (int)(SEGLEN >> 2), MAX_RIPPLES); // 56 max for 16 segment ESP8266 unsigned dataSize = sizeof(ripple) * maxRipples; if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -2655,7 +2656,7 @@ static uint16_t twinklefox_base(bool cat) unsigned backgroundBrightness = bg.getAverageLight(); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number unsigned myclockoffset16= PRNG16; // use that number as clock offset @@ -2727,8 +2728,8 @@ uint16_t mode_halloween_eyes() }; if (SEGLEN == 1) return mode_static(); - const unsigned maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN; - const unsigned HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5); + const unsigned maxWidth = strip.isMatrix ? SEG_W : SEGLEN; + const unsigned HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEG_W>>4: SEGLEN>>5); const unsigned HALLOWEEN_EYE_WIDTH = HALLOWEEN_EYE_SPACE/2; unsigned eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE; if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short @@ -2751,7 +2752,7 @@ uint16_t mode_halloween_eyes() data.startPos = random16(0, maxWidth - eyeLength - 1); data.color = random8(); - if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices + if (strip.isMatrix) SEGMENT.offset = random16(SEG_H-1); // a hack: reuse offset since it is not used in matrices duration = 128u + random16(SEGMENT.intensity*64u); data.duration = duration; data.state = eyeState::on; @@ -2869,7 +2870,7 @@ uint16_t mode_static_pattern() bool drawingLit = true; unsigned cnt = 0; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, (drawingLit) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : SEGCOLOR(1)); cnt++; if (cnt >= ((drawingLit) ? lit : unlit)) { @@ -2889,7 +2890,7 @@ uint16_t mode_tri_static_pattern() unsigned currSeg = 0; unsigned currSegCount = 0; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if ( currSeg % 3 == 0 ) { SEGMENT.setPixelColor(i, SEGCOLOR(0)); } else if( currSeg % 3 == 1) { @@ -3319,7 +3320,7 @@ uint16_t candle(bool multi) { if (multi && SEGLEN > 1) { //allocate segment data - unsigned dataSize = max(1, SEGLEN -1) *3; //max. 1365 pixels (ESP8266) + unsigned dataSize = max(1, (int)SEGLEN -1) *3; //max. 1365 pixels (ESP8266) if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed } @@ -3378,7 +3379,7 @@ uint16_t candle(bool multi) SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep; } else { - for (int j = 0; j < SEGLEN; j++) { + for (unsigned j = 0; j < SEGLEN; j++) { SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); } @@ -3521,12 +3522,12 @@ uint16_t mode_starburst(void) { if (stars[j].fragment[i] > 0) { float loc = stars[j].fragment[i]; if (mirrored) loc -= (loc-stars[j].pos)*2; - int start = loc - particleSize; - int end = loc + particleSize; + unsigned start = loc - particleSize; + unsigned end = loc + particleSize; if (start < 0) start = 0; if (start == end) end++; if (end > SEGLEN) end = SEGLEN; - for (int p = start; p < end; p++) { + for (unsigned p = start; p < end; p++) { SEGMENT.setPixelColor(p, c); } } @@ -3546,8 +3547,8 @@ static const char _data_FX_MODE_STARBURST[] PROGMEM = "Fireworks Starburst@Chanc uint16_t mode_exploding_fireworks(void) { if (SEGLEN == 1) return mode_static(); - const int cols = SEGMENT.is2D() ? SEGMENT.virtualWidth() : 1; - const int rows = SEGMENT.is2D() ? SEGMENT.virtualHeight() : SEGMENT.virtualLength(); + const int cols = SEGMENT.is2D() ? SEG_W : 1; + const int rows = SEGMENT.is2D() ? SEG_H : SEGLEN; //allocate segment data unsigned maxData = FAIR_DATA_PER_SEG; //ESP8266: 256 ESP32: 640 @@ -3698,7 +3699,7 @@ uint16_t mode_drip(void) unsigned numDrops = 1 + (SEGMENT.intensity >> 6); // 255>>6 = 3 float gravity = -0.0005f - (SEGMENT.speed/50000.0f); - gravity *= max(1, SEGLEN-1); + gravity *= max(1, (int)SEGLEN-1); int sourcedrop = 12; for (unsigned j=0;j= SEGLEN occasionally + unsigned pos = constrain(unsigned(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling } @@ -3821,8 +3822,8 @@ uint16_t mode_tetrix(void) { if (drop->pos > drop->stack) { // fall until top of stack drop->pos -= drop->speed; // may add gravity as: speed += gravity if (int(drop->pos) < int(drop->stack)) drop->pos = drop->stack; - for (int i = int(drop->pos); i < SEGLEN; i++) { - uint32_t col = ipos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); + for (unsigned i = unsigned(drop->pos); i < SEGLEN; i++) { + uint32_t col = i < unsigned(drop->pos)+drop->brick ? SEGMENT.color_from_palette(drop->col, false, false, 0) : SEGCOLOR(1); SEGMENT.setPixelColor(indexToVStrip(i, stripNr), col); } } else { // we hit bottom @@ -3836,7 +3837,7 @@ uint16_t mode_tetrix(void) { drop->brick = 0; // reset brick size (no more growing) if (drop->step > strip.now) { // allow fading of virtual strip - for (int i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend + for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.blendPixelColor(indexToVStrip(i, stripNr), SEGCOLOR(1), 25); // 10% blend } else { drop->stack = 0; // reset brick stack size drop->step = 0; // proceed with next brick @@ -3893,7 +3894,7 @@ uint16_t mode_percent(void) { if (SEGMENT.speed == 255) size = 255; if (percent <= 100) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (i < SEGENV.aux1) { if (SEGMENT.check1) SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,0,100,0,255), false, false, 0)); @@ -3905,7 +3906,7 @@ uint16_t mode_percent(void) { } } } else { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (i < (SEGLEN - SEGENV.aux1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); } @@ -3955,7 +3956,7 @@ uint16_t mode_heartbeat(void) { SEGENV.step = strip.now; } - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8))); } @@ -4049,7 +4050,7 @@ uint16_t mode_pacifica() unsigned basethreshold = beatsin8( 9, 55, 65); unsigned wave = beat8( 7 ); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { CRGB c = CRGB(2, 6, 10); // Render each of four layers, with different scales and speeds, that vary over time c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301)); @@ -4112,7 +4113,7 @@ uint16_t mode_sunrise() { if (SEGMENT.speed > 60) stage = 0xFFFF - stage; //sunset } - for (int i = 0; i <= SEGLEN/2; i++) + for (unsigned i = 0; i <= SEGLEN/2; i++) { //default palette is Fire unsigned wave = triwave16((i * stage) / SEGLEN); @@ -4145,7 +4146,7 @@ static uint16_t phased_base(uint8_t moder) { // We're making si unsigned index = strip.now/64; // Set color rotation speed *phase += SEGMENT.speed/32.0; // You can change the speed of the wave. AKA SPEED (was .4) - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { if (moder == 1) modVal = (inoise8(i*10 + i*10) /16); // Let's randomize our mod length with some Perlin noise. unsigned val = (i+1) * allfreq; // This sets the frequency of the waves. The +1 makes sure that led 0 is used. if (modVal == 0) modVal = 1; @@ -4177,7 +4178,7 @@ uint16_t mode_twinkleup(void) { // A very short twinkle routine unsigned prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work. unsigned pixBri = sin8(ranstart + 16 * strip.now/(256-SEGMENT.speed)); if (random8() > SEGMENT.intensity) pixBri = 0; @@ -4214,7 +4215,7 @@ uint16_t mode_noisepal(void) { // Slow noise if (SEGMENT.palette > 0) palettes[0] = SEGPALETTE; - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i*scale, SEGENV.aux0+i*scale); // Get a value from the noise function. I'm using both x and y axis. SEGMENT.setPixelColor(i, ColorFromPalette(palettes[0], index, 255, LINEARBLEND)); // Use my own palette. } @@ -4237,7 +4238,7 @@ uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tul SEGENV.step += SEGMENT.speed/16; // Speed of animation. unsigned freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal. - for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows: + for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows: int pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub).. //setPixCol(i, i*colorIndex/255, pixBri); SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri)); @@ -4368,7 +4369,7 @@ uint16_t mode_dancing_shadows(void) spotlights[i].lastUpdateTime = time; } - respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (SEGLEN + 2)) + respawn = (spotlights[i].speed > 0.0 && spotlights[i].position > (int)(SEGLEN + 2)) || (spotlights[i].speed < 0.0 && spotlights[i].position < -(spotlights[i].width + 2)); } @@ -4398,7 +4399,7 @@ uint16_t mode_dancing_shadows(void) int start = spotlights[i].position; if (spotlights[i].width <= 1) { - if (start >= 0 && start < SEGLEN) { + if (start >= 0 && start < (int)SEGLEN) { SEGMENT.blendPixelColor(start, color, 128); } } else { @@ -4468,7 +4469,7 @@ uint16_t mode_washing_machine(void) { SEGENV.step += (speed * 2048) / (512 - SEGMENT.speed); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7)); SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); } @@ -4618,7 +4619,7 @@ uint16_t mode_tv_simulator(void) { } // set strip color - for (i = 0; i < SEGLEN; i++) { + for (i = 0; i < (int)SEGLEN; i++) { SEGMENT.setPixelColor(i, r >> 8, g >> 8, b >> 8); // Quantize to 8-bit } @@ -4777,7 +4778,7 @@ uint16_t mode_aurora(void) { if (SEGCOLOR(1)) backlight++; if (SEGCOLOR(2)) backlight++; //Loop through LEDs to determine color - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { CRGB mixedRgb = CRGB(backlight, backlight, backlight); //For each LED we must check each wave if it is "active" at this position. @@ -4824,7 +4825,7 @@ static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixel // Uses beatsin8() + phase shifting. By: Andrew Tuline uint16_t mode_wavesins(void) { - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { uint8_t bri = sin8(strip.now/4 + i * SEGMENT.intensity); uint8_t index = beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider //SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, index, bri, LINEARBLEND)); @@ -4846,8 +4847,8 @@ uint16_t mode_FlowStripe(void) { uint8_t hue = strip.now / (SEGMENT.speed+1); uint32_t t = strip.now / (SEGMENT.intensity/8+1); - for (int i = 0; i < SEGLEN; i++) { - int c = (abs(i - hl) / hl) * 127; + for (unsigned i = 0; i < SEGLEN; i++) { + int c = (abs((int)i - hl) / hl) * 127; c = sin8(c); c = sin8(c / 2 + t); byte b = sin8(c + t/8); @@ -4869,8 +4870,8 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; int x, y; SEGMENT.fadeToBlackBy(16 + (SEGMENT.speed>>3)); // create fading trails @@ -4903,8 +4904,8 @@ static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Ou uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGENV.aux0 = 0; // start with red hue @@ -4955,8 +4956,8 @@ static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Spee uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(64); for (int i = 0; i < cols; i++) { @@ -4976,8 +4977,8 @@ static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2"; uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/512-dna-spiral-variation , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5021,8 +5022,8 @@ static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const int colsCenter = (cols>>1) + (cols%2); const int rowsCenter = (rows>>1) + (rows%2); @@ -5051,8 +5052,8 @@ static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur a uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5085,8 +5086,8 @@ static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y sca uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(16); for (size_t i = 8; i > 0; i--) { @@ -5112,8 +5113,8 @@ typedef struct ColorCount { uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const unsigned dataSize = sizeof(CRGB) * SEGMENT.length(); // using width*height prevents reallocation if mirroring is enabled const int crcBufferLen = 2; //(SEGMENT.width() + SEGMENT.height())*71/100; // roughly sqrt(2)/2 for better repetition detection (Ewowi) @@ -5218,8 +5219,8 @@ static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2 uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const uint32_t a = strip.now / ((SEGMENT.custom3>>1)+1); for (int x = 0; x < cols; x++) { @@ -5250,8 +5251,8 @@ typedef struct Julia { uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (!SEGENV.allocateData(sizeof(julia))) return mode_static(); Julia* julias = reinterpret_cast(SEGENV.data); @@ -5356,8 +5357,8 @@ static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per p uint16_t mode_2DLissajous(void) { // By: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(SEGMENT.intensity); uint_fast16_t phase = (strip.now * (1 + SEGENV.custom3)) /32; // allow user to control rotation speed @@ -5384,8 +5385,8 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,F uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned dataSize = (SEGMENT.length()+7) >> 3; //1 bit per LED for trails if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -5454,8 +5455,8 @@ static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Tra uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; float speed = 0.25f * (1+(SEGMENT.speed>>6)); @@ -5513,8 +5514,8 @@ static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2"; uint16_t mode_2Dnoise(void) { // By Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const unsigned scale = SEGMENT.intensity+2; @@ -5536,8 +5537,8 @@ static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2"; uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(SEGMENT.custom1>>2); uint_fast32_t t = (strip.now * 8) / (256 - SEGMENT.speed); // optimized to avoid float @@ -5576,8 +5577,8 @@ static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fad uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000}; @@ -5627,8 +5628,8 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale; uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5)); uint32_t a = strip.now / (18 - SEGMENT.speed / 16); @@ -5649,8 +5650,8 @@ static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2"; uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5680,8 +5681,8 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g // Modifed by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const uint8_t kBorderWidth = 2; @@ -5711,8 +5712,8 @@ static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Bl uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (!SEGENV.allocateData(sizeof(byte)*(cols+2)*(rows+2))) return mode_static(); //allocation failed byte *bump = reinterpret_cast(SEGENV.data); @@ -5761,8 +5762,8 @@ static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Varian uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -5800,8 +5801,8 @@ static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,S uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek) if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; uint32_t tb = strip.now >> 12; // every ~4s if (tb > SEGENV.step) { @@ -5843,8 +5844,8 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2 uint16_t mode_2Dcrazybees(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; byte n = MIN(MAX_BEES, (rows * cols) / 256 + 1); @@ -5916,8 +5917,8 @@ static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2"; uint16_t mode_2Dghostrider(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; typedef struct Lighter { int16_t gPosX; @@ -6006,8 +6007,8 @@ static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate, uint16_t mode_2Dfloatingblobs(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; typedef struct Blob { float x[MAX_BLOBS], y[MAX_BLOBS]; @@ -6104,8 +6105,8 @@ static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail; uint16_t mode_2Dscrollingtext(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned letterWidth, rotLW; unsigned letterHeight, rotLH; @@ -6205,8 +6206,8 @@ static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Off uint16_t mode_2Ddriftrose(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const float CX = (cols-cols%2)/2.f - .5f; const float CY = (rows-rows%2)/2.f - .5f; @@ -6232,8 +6233,8 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;; uint16_t mode_2Dplasmarotozoom() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned dataSize = SEGMENT.length() + sizeof(float); if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed @@ -6401,8 +6402,8 @@ static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Ma uint16_t mode_2DSwirl(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (SEGENV.call == 0) { SEGMENT.fill(BLACK); @@ -6440,8 +6441,8 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B uint16_t mode_2DWaverly(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; um_data_t *um_data = getAudioData(); float volumeSmth = *(float*) um_data->u_data[0]; @@ -6652,7 +6653,7 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline. SEGENV.aux0 = secondHand; int pixBri = volumeRaw * SEGMENT.intensity / 64; - for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left + for (unsigned i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri)); } @@ -6677,10 +6678,10 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline. float tmpSound2 = volumeSmth * (float)SEGMENT.intensity / 256.0; // Too sensitive. tmpSound2 *= (float)SEGMENT.intensity / 128.0; // Reduce sensitivity/length. - int maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN/2); + unsigned maxLen = mapf(tmpSound2, 0, 127, 0, SEGLEN/2); if (maxLen >SEGLEN/2) maxLen = SEGLEN/2; - for (int i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { + for (unsigned i=(SEGLEN/2-maxLen); i<(SEGLEN/2+maxLen); i++) { uint8_t index = inoise8(i*volumeSmth+SEGENV.aux0, SEGENV.aux1+i*volumeSmth); // Get a value from the noise function. I'm using both x and y axis. SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0)); } @@ -6708,7 +6709,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline. if (SEGENV.call == 0) SEGMENT.fill(BLACK); - for (int i = 0; i < SEGLEN; i++) { + for (unsigned i = 0; i < SEGLEN; i++) { unsigned index = inoise8(i*SEGMENT.speed/64,strip.now*SEGMENT.speed/64*SEGLEN/255); // X location is constant, but we move along the Y at the rate of millis(). By Andrew Tuline. index = (255 - i*256/SEGLEN) * index/(256-SEGMENT.intensity); // Now we need to scale index so that it gets blacker as we get close to one of the ends. // This is a simple y=mx+b equation that's been scaled. index/128 is another scaling. @@ -6735,11 +6736,11 @@ uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline. SEGMENT.fade_out(fadeRate); float tmpSound2 = volumeRaw * 2.0 * (float)SEGMENT.intensity / 255.0; - int maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive. - if (maxLen <0) maxLen = 0; - if (maxLen >SEGLEN) maxLen = SEGLEN; + unsigned maxLen = mapf(tmpSound2, 0, 255, 0, SEGLEN); // map to pixels availeable in current segment // Still a bit too sensitive. + if (maxLen < 0) maxLen = 0; + if (maxLen > SEGLEN) maxLen = SEGLEN; - for (int i=0; i SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left - for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right + for (unsigned i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (unsigned i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; @@ -6803,7 +6804,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline. plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually. plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline. - for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows. + for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows. // updated, similar to "plasma" effect - softhack007 uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2; thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases. @@ -6943,7 +6944,7 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline. SEGENV.step += FRAMETIME; if (SEGENV.step > SPEED_FORMULA_L) { unsigned segLoc = random16(SEGLEN); - SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); + SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, (int)SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16])); ++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16 SEGENV.step = 1; @@ -7006,7 +7007,7 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN. int locn = (log10f((float)FFT_MajorPeak) - 1.78f) * (float)SEGLEN/(MAX_FREQ_LOG10 - 1.78f); // log10 frequency range is from 1.78 to 3.71. Let's scale to SEGLEN. if (locn < 1) locn = 0; // avoid underflow - if (locn >=SEGLEN) locn = SEGLEN-1; + if (locn >= (int)SEGLEN) locn = SEGLEN-1; unsigned pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index. if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow @@ -7159,8 +7160,8 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun // shift the pixels one pixel outwards // if SEGLEN equals 1 these loops won't execute - for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left - for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right + for (unsigned i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left + for (unsigned i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right } return FRAMETIME; @@ -7314,7 +7315,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); } // loop will not execute if SEGLEN equals 1 - for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left + for (unsigned i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left } return FRAMETIME; @@ -7330,8 +7331,8 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; if (!SEGENV.allocateData(cols*sizeof(uint16_t))) return mode_static(); //allocation failed uint16_t *previousBarHeight = reinterpret_cast(SEGENV.data); //array of previous bar heights per frequency band @@ -7383,8 +7384,8 @@ static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,# uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam. if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; int NUMB_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16); int barWidth = (cols / NUMB_BANDS); @@ -7471,8 +7472,8 @@ static uint8_t akemi[] PROGMEM = { uint16_t mode_2DAkemi(void) { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; unsigned counter = (strip.now * ((SEGMENT.speed >> 2) +2)) & 0xFFFF; counter = counter >> 8; @@ -7539,8 +7540,8 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea uint16_t mode_2Ddistortionwaves() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; uint8_t speed = SEGMENT.speed/32; uint8_t scale = SEGMENT.intensity/32; @@ -7594,8 +7595,8 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@ uint16_t mode_2Dsoap() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed @@ -7706,8 +7707,8 @@ static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; uint16_t mode_2Doctopus() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; const uint8_t mapp = 180 / MAX(cols,rows); typedef struct { @@ -7761,8 +7762,8 @@ static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offse uint16_t mode_2Dwavingcell() { if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up - const int cols = SEGMENT.virtualWidth(); - const int rows = SEGMENT.virtualHeight(); + const int cols = SEG_W; + const int rows = SEG_H; uint32_t t = strip.now/(257-SEGMENT.speed); uint8_t aX = SEGMENT.custom1/16 + 9; diff --git a/wled00/FX.h b/wled00/FX.h index 825c722e75..98082c8d8c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -90,11 +90,11 @@ extern byte realtimeMode; // used in getMappedPixelIndex() #define NUM_COLORS 3 /* number of colors per segment */ #define SEGMENT strip._segments[strip.getCurrSegmentId()] #define SEGENV strip._segments[strip.getCurrSegmentId()] -//#define SEGCOLOR(x) strip._segments[strip.getCurrSegmentId()].currentColor(x, strip._segments[strip.getCurrSegmentId()].colors[x]) -//#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength() -#define SEGCOLOR(x) strip.segColor(x) /* saves us a few kbytes of code */ +#define SEGCOLOR(x) Segment::getCurrentColor(x) #define SEGPALETTE Segment::getCurrentPalette() -#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */ +#define SEGLEN Segment::vLength() +#define SEG_W Segment::vWidth() +#define SEG_H Segment::vHeight() #define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) // some common colors @@ -421,7 +421,9 @@ typedef struct Segment { uint16_t _dataLen; static uint16_t _usedSegmentData; - // perhaps this should be per segment, not static + static unsigned _vLength; // 1D dimension used for current effect + static unsigned _vWidth, _vHeight; // 2D dimensions used for current effect + static uint32_t _currentColors[NUM_COLORS]; // colors used for current effect static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) static CRGBPalette16 _randomPalette; // actual random palette static CRGBPalette16 _newRandomPalette; // target random palette @@ -534,14 +536,20 @@ typedef struct Segment { inline uint16_t groupLength() const { return grouping + spacing; } inline uint8_t getLightCapabilities() const { return _capabilities; } - inline static uint16_t getUsedSegmentData() { return _usedSegmentData; } - inline static void addUsedSegmentData(int len) { _usedSegmentData += len; } + inline static uint16_t getUsedSegmentData() { return Segment::_usedSegmentData; } + inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; } #ifndef WLED_DISABLE_MODE_BLEND - inline static void modeBlend(bool blend) { _modeBlend = blend; } + inline static void modeBlend(bool blend) { _modeBlend = blend; } #endif - static void handleRandomPalette(); + inline static unsigned vLength() { return Segment::_vLength; } + inline static unsigned vWidth() { return Segment::_vWidth; } + inline static unsigned vHeight() { return Segment::_vHeight; } + inline static uint32_t getCurrentColor(unsigned i) { return Segment::_currentColors[i]; } // { return i < 3 ? Segment::_currentColors[i] : 0; } inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; } + static void handleRandomPalette(); + + void beginDraw(); // set up parameters for current effect void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); bool setColor(uint8_t slot, uint32_t c); //returns true if changed void setCCT(uint16_t k); @@ -578,14 +586,13 @@ typedef struct Segment { uint8_t currentMode() const; // currently active effect/mode (while in transition) [[gnu::hot]] uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition) CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal); - void setCurrentPalette(); // 1D strip [[gnu::hot]] uint16_t virtualLength() const; - [[gnu::hot]] void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color - inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } - inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } - inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } + [[gnu::hot]] void setPixelColor(int n, uint32_t c, bool unScaled = true); // set relative pixel within segment with color + inline void setPixelColor(unsigned n, uint32_t c, bool unScaled = true) { setPixelColor(int(n), c, unScaled); } + inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } #ifdef WLED_USE_AA_PIXELS void setPixelColor(float i, uint32_t c, bool aa = true); inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } @@ -622,8 +629,8 @@ typedef struct Segment { uint16_t nrOfVStrips() const; // returns number of virtual vertical strips in 2D matrix (used to expand 1D effects into 2D) #ifndef WLED_DISABLE_2D [[gnu::hot]] uint16_t XY(int x, int y); // support function to get relative index within segment - [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color - inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } + [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c, bool unScaled = true); // set relative pixel within segment with color + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c, bool unScaled = true) { setPixelColorXY(int(x), int(y), c, unScaled); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); } @@ -642,8 +649,8 @@ typedef struct Segment { inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur void blur2D(uint8_t blur_amount, bool smear = false); - void blurRow(uint32_t row, fract8 blur_amount, bool smear = false); - void blurCol(uint32_t col, fract8 blur_amount, bool smear = false); + void blurRow(int row, fract8 blur_amount, bool smear = false); + void blurCol(int col, fract8 blur_amount, bool smear = false); void moveX(int8_t delta, bool wrap = false); void moveY(int8_t delta, bool wrap = false); void move(uint8_t dir, uint8_t delta, bool wrap = false); @@ -660,9 +667,9 @@ typedef struct Segment { inline void blur2d(fract8 blur_amount) { blur(blur_amount); } inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } #else - inline uint16_t XY(uint16_t x, uint16_t y) { return x; } - inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } - inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } + inline uint16_t XY(int x, int y) { return x; } + inline void setPixelColorXY(int x, int y, uint32_t c, bool unScaled = true) { setPixelColor(x, c, unScaled); } + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c, bool unScaled = true) { setPixelColor(int(x), c, unScaled); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); } @@ -680,8 +687,8 @@ typedef struct Segment { inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} inline void blur2D(uint8_t blur_amount, bool smear = false) {} - inline void blurRow(uint32_t row, fract8 blur_amount, bool smear = false) {} - inline void blurCol(uint32_t col, fract8 blur_amount, bool smear = false) {} + inline void blurRow(int row, fract8 blur_amount, bool smear = false) {} + inline void blurCol(int col, fract8 blur_amount, bool smear = false) {} inline void moveX(int8_t delta, bool wrap = false) {} inline void moveY(int8_t delta, bool wrap = false) {} inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {} @@ -727,9 +734,6 @@ class WS2812FX { // 96 bytes autoSegments(false), correctWB(false), cctFromRgb(false), - // semi-private (just obscured) used in effect functions through macros - _colors_t{0,0,0}, - _virtualSegmentLength(0), // true private variables _suspend(false), _length(DEFAULT_LED_COUNT), @@ -829,7 +833,7 @@ class WS2812FX { // 96 bytes addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp; inline uint8_t getBrightness() const { return _brightness; } // returns current strip brightness - inline uint8_t getMaxSegments() const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline constexpr unsigned getMaxSegments() { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) inline uint8_t getSegmentsNum() const { return _segments.size(); } // returns currently present segments inline uint8_t getCurrSegmentId() const { return _segment_index; } // returns current segment index (only valid while strip.isServicing()) inline uint8_t getMainSegmentId() const { return _mainSegment; } // returns main segment index @@ -855,7 +859,6 @@ class WS2812FX { // 96 bytes uint32_t getPixelColor(unsigned) const; inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call - inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } // returns currently valid color (for slot i) AKA SEGCOLOR(); may be blended between two colors while in transition const char * getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } @@ -922,11 +925,6 @@ class WS2812FX { // 96 bytes bool cctFromRgb : 1; }; - // using public variables to reduce code size increase due to inline function getSegment() (with bounds checking) - // and color transitions - uint32_t _colors_t[3]; // color used for effect (includes transition) - uint16_t _virtualSegmentLength; - std::vector _segments; friend class Segment; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 41fd67319d..5f61b00940 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -163,23 +163,24 @@ void WS2812FX::setUpMatrix() { // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) uint16_t IRAM_ATTR_YN Segment::XY(int x, int y) { - unsigned width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive) - unsigned height = virtualHeight(); // segment height in logical pixels (is always >= 1) - return isActive() ? (x%width) + (y%height) * width : 0; + 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) + return isActive() ? (x%vW) + (y%vH) * vW : 0; } -void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) +void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col, bool unScaled) { if (!isActive()) return; // not active - if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit + + 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) + if (x >= vW|| y >= vH || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit - uint8_t _bri_t = currentBri(); - if (_bri_t < 255) { - col = color_fade(col, _bri_t); - } + // if color is unscaled + if (unScaled) col = color_fade(col, currentBri()); - if (reverse ) x = virtualWidth() - x - 1; - if (reverse_y) y = virtualHeight() - y - 1; + 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 x *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels @@ -221,11 +222,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) if (!isActive()) return; // not active if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); - - float fX = x * (cols-1); - float fY = y * (rows-1); + float fX = x * (vWidth()-1); + float fY = y * (vHeight()-1); if (aa) { unsigned xL = roundf(fX-0.49f); unsigned xR = roundf(fX+0.49f); @@ -263,9 +261,11 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) // returns RGBW values of pixel uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (!isActive()) return 0; // not active - if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit - if (reverse ) x = virtualWidth() - x - 1; - if (reverse_y) y = virtualHeight() - y - 1; + int vW = vWidth(); + int vH = vHeight(); + if (x >= vW || y >= vH || x<0 || y<0) 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 x *= groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels @@ -274,10 +274,10 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { } // blurRow: perform a blur on a row of a rectangular matrix -void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ +void Segment::blurRow(int row, fract8 blur_amount, bool smear){ if (!isActive() || blur_amount == 0) return; // not active - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); + const int cols = vWidth(); + const int rows = vHeight(); if (row >= rows) return; // blur one row @@ -287,7 +287,7 @@ void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ uint32_t lastnew; uint32_t last; uint32_t curnew = BLACK; - for (unsigned x = 0; x < cols; x++) { + for (int x = 0; x < cols; x++) { uint32_t cur = getPixelColorXY(x, row); uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); @@ -306,10 +306,10 @@ void Segment::blurRow(uint32_t row, fract8 blur_amount, bool smear){ } // blurCol: perform a blur on a column of a rectangular matrix -void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { +void Segment::blurCol(int col, fract8 blur_amount, bool smear) { if (!isActive() || blur_amount == 0) return; // not active - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); + const int cols = vWidth(); + const int rows = vHeight(); if (col >= cols) return; // blur one column @@ -319,7 +319,7 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { uint32_t lastnew; uint32_t last; uint32_t curnew = BLACK; - for (unsigned y = 0; y < rows; y++) { + for (int y = 0; y < rows; y++) { uint32_t cur = getPixelColorXY(col, y); uint32_t part = color_fade(cur, seep); curnew = color_fade(cur, keep); @@ -339,8 +339,8 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { void Segment::blur2D(uint8_t blur_amount, bool smear) { if (!isActive() || blur_amount == 0) return; // not active - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); + const unsigned cols = vWidth(); + const unsigned rows = vHeight(); const uint8_t keep = smear ? 255 : 255 - blur_amount; const uint8_t seep = blur_amount >> (1 + smear); @@ -391,8 +391,8 @@ void Segment::box_blur(unsigned radius, bool smear) { if (!isActive() || radius == 0) return; // not active if (radius > 3) radius = 3; const unsigned d = (1 + 2*radius) * (1 + 2*radius); // averaging divisor - const unsigned cols = virtualWidth(); - const unsigned rows = virtualHeight(); + const unsigned cols = vWidth(); + const unsigned rows = vHeight(); uint16_t *tmpRSum = new uint16_t[cols*rows]; uint16_t *tmpGSum = new uint16_t[cols*rows]; uint16_t *tmpBSum = new uint16_t[cols*rows]; @@ -461,37 +461,37 @@ void Segment::box_blur(unsigned radius, bool smear) { void Segment::moveX(int8_t delta, bool wrap) { if (!isActive()) return; // not active - const int cols = virtualWidth(); - const int rows = virtualHeight(); - if (!delta || abs(delta) >= cols) return; - uint32_t newPxCol[cols]; - for (int y = 0; y < rows; y++) { + 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) + if (!delta || abs(delta) >= vW) return; + uint32_t newPxCol[vW]; + for (int y = 0; y < vH; y++) { if (delta > 0) { - for (int x = 0; x < cols-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y); - for (int x = cols-delta; x < cols; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - cols : x, y); + for (int x = 0; x < vW-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y); + for (int x = vW-delta; x < vW; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - vW : x, y); } else { - for (int x = cols-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y); - for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + cols : x, y); + for (int x = vW-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y); + for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + vW : x, y); } - for (int x = 0; x < cols; x++) setPixelColorXY(x, y, newPxCol[x]); + for (int x = 0; x < vW; x++) setPixelColorXY(x, y, newPxCol[x]); } } void Segment::moveY(int8_t delta, bool wrap) { if (!isActive()) return; // not active - const int cols = virtualWidth(); - const int rows = virtualHeight(); - if (!delta || abs(delta) >= rows) return; - uint32_t newPxCol[rows]; - for (int x = 0; x < cols; x++) { + 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) + if (!delta || abs(delta) >= vH) return; + uint32_t newPxCol[vH]; + for (int x = 0; x < vW; x++) { if (delta > 0) { - for (int y = 0; y < rows-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta)); - for (int y = rows-delta; y < rows; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - rows : y); + for (int y = 0; y < vH-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta)); + for (int y = vH-delta; y < vH; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - vH : y); } else { - for (int y = rows-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta)); - for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + rows : y); + for (int y = vH-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta)); + for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + vH : y); } - for (int y = 0; y < rows; y++) setPixelColorXY(x, y, newPxCol[y]); + for (int y = 0; y < vH; y++) setPixelColorXY(x, y, newPxCol[y]); } } @@ -545,18 +545,20 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, x++; } } else { + // pre-scale color for all pixels + col = color_fade(col, currentBri()); // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; while (y >= x) { - setPixelColorXY(cx+x, cy+y, col); - setPixelColorXY(cx-x, cy+y, col); - setPixelColorXY(cx+x, cy-y, col); - setPixelColorXY(cx-x, cy-y, col); - setPixelColorXY(cx+y, cy+x, col); - setPixelColorXY(cx-y, cy+x, col); - setPixelColorXY(cx+y, cy-x, col); - setPixelColorXY(cx-y, cy-x, col); + setPixelColorXY(cx+x, cy+y, col, false); + setPixelColorXY(cx-x, cy+y, col, false); + setPixelColorXY(cx+x, cy-y, col, false); + setPixelColorXY(cx-x, cy-y, col, false); + setPixelColorXY(cx+y, cy+x, col, false); + setPixelColorXY(cx-y, cy+x, col, false); + setPixelColorXY(cx+y, cy-x, col, false); + setPixelColorXY(cx-y, cy-x, col, false); x++; if (d > 0) { y--; @@ -571,17 +573,19 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) { if (!isActive() || radius == 0) 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) // draw soft bounding circle if (soft) drawCircle(cx, cy, radius, col, soft); + // pre-scale color for all pixels + col = color_fade(col, currentBri()); // fill it - const int cols = virtualWidth(); - const int rows = virtualHeight(); for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { if (x * x + y * y <= radius * radius && - int(cx)+x>=0 && int(cy)+y>=0 && - int(cx)+x= 0 && int(cy)+y >= 0 && + int(cx)+x < vW && int(cy)+y < vH) + setPixelColorXY(cx + x, cy + y, col, false); } } } @@ -589,9 +593,9 @@ void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, //line function void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) { if (!isActive()) return; // not active - const int cols = virtualWidth(); - const int rows = virtualHeight(); - if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; + 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) + if (x0 >= vW || x1 >= vW || y0 >= vH || y1 >= vH) return; const int dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2; // error direction for (;;) { - setPixelColorXY(x0, y0, c); + setPixelColorXY(x0, y0, c, false); if (x0==x1 && y0==y1) break; int e2 = err; if (e2 >-dx) { err -= dy; x0 += sx; } @@ -653,8 +659,6 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, if (!isActive()) return; // not active if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported chr -= 32; // align with font table entries - const int cols = virtualWidth(); - const int rows = virtualHeight(); const int font = w*h; CRGB col = CRGB(color); @@ -681,7 +685,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, case 1: x0 = x + i; y0 = y + j; break; // +90 deg default: x0 = x + (w-1) - j; y0 = y + i; break; // no rotation } - if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen + if (x0 < 0 || x0 >= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen if (((bits>>(j+(8-w))) & 0x01)) { // bit set setPixelColorXY(x0, y0, col); } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index a98755425b..a47a7edc95 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -80,15 +80,18 @@ 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; - +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::_vLength = 0; +unsigned Segment::_vWidth = 0; +unsigned Segment::_vHeight = 0; +uint32_t Segment::_currentColors[NUM_COLORS] = {0,0,0}; 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::_lastPaletteBlend = 0; // in millis (lowest 16 bits only) #ifndef WLED_DISABLE_MODE_BLEND bool Segment::_modeBlend = false; @@ -437,7 +440,21 @@ uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const { #endif } -void Segment::setCurrentPalette() { +// pre-calculate drawing parameters for faster access +void Segment::beginDraw() { + _vWidth = virtualWidth(); + _vHeight = virtualHeight(); + _vLength = virtualLength(); + // adjust gamma for effects + for (unsigned i = 0; i < NUM_COLORS; i++) { + #ifndef WLED_DISABLE_MODE_BLEND + uint32_t col = isInTransition() ? color_blend(_t->_segT._colorT[i], colors[i], progress(), true) : colors[i]; + #else + uint32_t col = isInTransition() ? color_blend(_t->_colorT[i], colors[i], progress(), true) : colors[i]; + #endif + _currentColors[i] = gamma32(col); + } + // load palette into _currentPalette loadPalette(_currentPalette, palette); unsigned prog = progress(); if (strip.paletteFade && prog < 0xFFFFU) { @@ -698,20 +715,21 @@ uint16_t IRAM_ATTR Segment::virtualLength() const { return vLength; } -void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) +void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) { if (!isActive() || i < 0) return; // not active or invalid index #ifndef WLED_DISABLE_2D int vStrip = 0; #endif + 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 >= 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 >= virtualLength()) return; // if pixel would still fall out of segment just exit + if (i >= vL) return; // if pixel would still fall out of segment just exit #else return; #endif @@ -719,22 +737,24 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) #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, currentBri()); switch (map1D2D) { case M12_Pixels: // use all available pixels as a long strip - setPixelColorXY(i % vW, i / vW, col); + setPixelColorXY(i % vW, i / vW, col, false); 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, false); + else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col, false); break; case M12_pArc: // expand in circular fashion from center if (i == 0) - setPixelColorXY(0, 0, col); + setPixelColorXY(0, 0, col, false); else { float r = i; float step = HALF_PI / (2.8284f * r + 4); // we only need (PI/4)/(r/sqrt(2)+1) steps @@ -742,8 +762,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) int x = roundf(sin_t(rad) * r); int y = roundf(cos_t(rad) * r); // exploit symmetry - setPixelColorXY(x, y, col); - setPixelColorXY(y, x, col); + setPixelColorXY(x, y, col, false); + setPixelColorXY(y, x, col, false); } // Bresenham’s Algorithm (may not fill every pixel) //int d = 3 - (2*i); @@ -762,8 +782,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) } break; case M12_pCorner: - for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col); - for (int y = 0; y < i; y++) setPixelColorXY(i, y, col); + for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col, false); + for (int y = 0; y < i; y++) setPixelColorXY(i, y, col, false); break; case M12_sPinwheel: { // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) @@ -802,7 +822,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) int x = posx / Fixed_Scale; int y = posy / Fixed_Scale; // set pixel - if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different + if (x != lastX || y != lastY) setPixelColorXY(x, y, col, false); // only paint if pixel position is different lastX = x; lastY = y; // advance to next position @@ -813,12 +833,12 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) } } 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; } @@ -826,10 +846,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) #endif unsigned len = length(); - uint8_t _bri_t = currentBri(); - if (_bri_t < 255) { - col = color_fade(col, _bri_t); - } + // if color is unscaled + if (unScaled) col = color_fade(col, currentBri()); // expand pixel (taking into account start, grouping, spacing [and offset]) i = i * groupLength(); @@ -907,8 +925,8 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const #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); @@ -961,7 +979,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const } #endif - if (reverse) i = virtualLength() - i - 1; + if (reverse) i = vLength() - i - 1; i *= groupLength(); i += start; // offset/phase @@ -1050,11 +1068,13 @@ 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, currentBri()); for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { - if (is2D()) setPixelColorXY(x, y, c); - else setPixelColor(x, c); + if (is2D()) setPixelColorXY(x, y, c, false); + else setPixelColor(x, c, false); } } @@ -1063,8 +1083,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); @@ -1102,8 +1122,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)); @@ -1126,7 +1146,7 @@ void Segment::blur(uint8_t blur_amount, bool smear) { #endif uint8_t keep = smear ? 255 : 255 - blur_amount; uint8_t seep = blur_amount >> (1 + smear); - unsigned vlength = virtualLength(); + unsigned vlength = vLength(); uint32_t carryover = BLACK; uint32_t lastnew; uint32_t last; @@ -1140,8 +1160,7 @@ void Segment::blur(uint8_t blur_amount, bool smear) { 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; @@ -1156,7 +1175,7 @@ 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); @@ -1179,20 +1198,19 @@ 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 = currentColor(mcol); + uint32_t color = getCurrentColor(mcol < NUM_COLORS ? mcol : 0); // default palette or no RGB support on segment if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { - color = gamma32(color); - return (pbri == 255) ? color : color_fade(color, pbri, true); + 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" CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global - palcol.w = gamma8(W(color)); + palcol.w = W(color); return palcol.color32; } @@ -1359,11 +1377,6 @@ void WS2812FX::service() { 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)); - seg.setCurrentPalette(); // load actual palette // 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); @@ -1375,13 +1388,14 @@ void WS2812FX::service() { // 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. [[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition + seg.beginDraw(); // set up parameters for get/setPixelColor() delay = (*_mode[seg.mode])(); // run new/current mode #ifndef WLED_DISABLE_MODE_BLEND if (modeBlending && seg.mode != tmpMode) { 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) + seg.beginDraw(); // set up parameters for get/setPixelColor() unsigned d2 = (*_mode[tmpMode])(); // run old mode seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state) delay = MIN(delay,d2); // use shortest delay @@ -1397,7 +1411,6 @@ void WS2812FX::service() { } _segment_index++; } - _virtualSegmentLength = 0; _isServicing = false; _triggered = false; diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 60774d7010..a615cefcba 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -416,18 +416,18 @@ void realtimeLock(uint32_t timeoutMs, byte md) start = mainseg.start; stop = mainseg.stop; mainseg.freeze = true; + // if WLED was off and using main segment only, freeze non-main segments so they stay off + if (bri == 0) { + for (size_t s = 0; s < strip.getSegmentsNum(); s++) { + strip.getSegment(s).freeze = true; + } + } } else { start = 0; stop = strip.getLengthTotal(); } // clear strip/segment for (size_t i = start; i < stop; i++) strip.setPixelColor(i,BLACK); - // if WLED was off and using main segment only, freeze non-main segments so they stay off - if (useMainSegmentOnly && bri == 0) { - for (size_t s=0; s < strip.getSegmentsNum(); s++) { - strip.getSegment(s).freeze = true; - } - } } // if strip is off (bri==0) and not already in RTM if (briT == 0 && !realtimeMode && !realtimeOverride) { @@ -510,12 +510,10 @@ void handleNotifications() rgbUdp.read(lbuf, packetSize); realtimeLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION); if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return; - unsigned id = 0; unsigned totalLen = strip.getLengthTotal(); - for (size_t i = 0; i < packetSize -2; i += 3) - { + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor() + for (size_t i = 0, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) { setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0); - id++; if (id >= totalLen) break; } if (!(realtimeMode && useMainSegmentOnly)) strip.show(); return; @@ -595,17 +593,11 @@ void handleNotifications() unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED unsigned totalLen = strip.getLengthTotal(); - for (size_t i = 6; i < tpmPayloadFrameSize + 4U; i += 3) - { - if (id < totalLen) - { - setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - id++; - } - else break; + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor() + for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) { + setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); } - if (tpmPacketCount == numPackets) //reset packet count and show if all packets were received - { + if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received tpmPacketCount = 0; strip.show(); } @@ -629,6 +621,7 @@ void handleNotifications() if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return; unsigned totalLen = strip.getLengthTotal(); + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor() if (udpIn[0] == 1 && packetSize > 5) //warls { for (size_t i = 2; i < packetSize -3; i += 4) @@ -637,39 +630,29 @@ void handleNotifications() } } else if (udpIn[0] == 2 && packetSize > 4) //drgb { - unsigned id = 0; - for (size_t i = 2; i < packetSize -2; i += 3) + for (size_t i = 2, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) { setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - - id++; if (id >= totalLen) break; } } else if (udpIn[0] == 3 && packetSize > 6) //drgbw { - unsigned id = 0; - for (size_t i = 2; i < packetSize -3; i += 4) + for (size_t i = 2, id = 0; i < packetSize -3 && id < totalLen; i += 4, id++) { setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); - - id++; if (id >= totalLen) break; } } else if (udpIn[0] == 4 && packetSize > 7) //dnrgb { unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); - for (size_t i = 4; i < packetSize -2; i += 3) + for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 3, id++) { - if (id >= totalLen) break; setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); - id++; } } else if (udpIn[0] == 5 && packetSize > 8) //dnrgbw { unsigned id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); - for (size_t i = 4; i < packetSize -2; i += 4) + for (size_t i = 4; i < packetSize -2 && id < totalLen; i += 4, id++) { - if (id >= totalLen) break; setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); - id++; } } strip.show(); @@ -705,8 +688,7 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w) w = gamma8(w); } if (useMainSegmentOnly) { - Segment &seg = strip.getMainSegment(); - if (pix10) return; + char nS[32]; if (subPage == SUBPAGE_MENU) { @@ -259,8 +260,6 @@ void getSettingsJS(byte subPage, Print& settingsScript) if (subPage == SUBPAGE_LEDS) { - char nS[32]; - appendGPIOinfo(settingsScript); settingsScript.print(SET_F("d.ledTypes=")); settingsScript.print(BusManager::getLEDTypesJSONString().c_str()); settingsScript.print(";"); @@ -399,7 +398,6 @@ void getSettingsJS(byte subPage, Print& settingsScript) if (subPage == SUBPAGE_SYNC) { - [[maybe_unused]] char nS[32]; printSetFormValue(settingsScript,PSTR("UP"),udpPort); printSetFormValue(settingsScript,PSTR("U2"),udpPort2); #ifndef WLED_DISABLE_ESPNOW From 9114867578477e4cf5fa652769ea0690503c5f10 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 28 Sep 2024 18:48:43 +0200 Subject: [PATCH 25/37] Fix compiler error --- wled00/FX.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.h b/wled00/FX.h index 98082c8d8c..b4aaf3c48c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -833,7 +833,7 @@ class WS2812FX { // 96 bytes addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp; inline uint8_t getBrightness() const { return _brightness; } // returns current strip brightness - inline constexpr unsigned getMaxSegments() { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) + inline static constexpr unsigned getMaxSegments() { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value) inline uint8_t getSegmentsNum() const { return _segments.size(); } // returns currently present segments inline uint8_t getCurrSegmentId() const { return _segment_index; } // returns current segment index (only valid while strip.isServicing()) inline uint8_t getMainSegmentId() const { return _mainSegment; } // returns main segment index From ffbc8c5f709461f96c557e5dc9162c8f3e5c6004 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 29 Sep 2024 13:55:00 +0200 Subject: [PATCH 26/37] Reverting addition of `bool unScale`, added new improvements and fixes - Added pre-calculation for segment brightness: stored in _segBri. The impact on FPS is not huge but measurable (~1-2FPS in my test conditions) - Removed `bool unScaled` from `setPixelColor()` function again (it has no/minimal impact on speed but huge impact on flash usage: +850 bytes) - Removed negative checking in `setPixelColorXY()` and replaced it with a local typecast to unsigned, saves a few instructions (tested and working) - Changed int8_t to int in `moveX()` and `moveY()` - Removed a few functions from IRAM as they are now not called for every pixel but only once per segment update - Removed a `virtualWidth()` call from `ripple_base()` - Bugfix in `mode_colortwinkle()` --- wled00/FX.cpp | 4 ++-- wled00/FX.h | 26 ++++++++++++------------- wled00/FX_2Dfcn.cpp | 40 ++++++++++++++++---------------------- wled00/FX_fcn.cpp | 47 ++++++++++++++++++++++----------------------- 4 files changed, 55 insertions(+), 62 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 2ec31014f2..77167b02d1 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -2294,7 +2294,7 @@ uint16_t mode_colortwinkle() { bool fadeUp = bitRead(SEGENV.data[index], bitNum); if (fadeUp) { - CRGBW incrementalColor = color_fade(col, fadeUpAmount, true); + CRGBW incrementalColor = color_fade(cur, fadeUpAmount, true); col = color_add(cur, incrementalColor); if (col.r == 255 || col.g == 255 || col.b == 255) { @@ -2528,7 +2528,7 @@ static uint16_t ripple_base() { } else {//randomly create new wave if (random16(IBN + 10000) <= (SEGMENT.intensity >> (SEGMENT.is2D()*3))) { ripples[i].state = 1; - ripples[i].pos = SEGMENT.is2D() ? ((random8(SEGENV.virtualWidth())<<8) | (random8(SEGENV.virtualHeight()))) : random16(SEGLEN); + ripples[i].pos = SEGMENT.is2D() ? ((random8(SEG_W)<<8) | (random8(SEG_H))) : random16(SEGLEN); ripples[i].color = random8(); //color } } diff --git a/wled00/FX.h b/wled00/FX.h index b4aaf3c48c..8a452cfcbc 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -420,8 +420,8 @@ typedef struct Segment { }; uint16_t _dataLen; static uint16_t _usedSegmentData; - - static unsigned _vLength; // 1D dimension used for current effect + static uint8_t _segBri; // Current brightness of segment + static unsigned _vLength; // 1D dimension used for current effect static unsigned _vWidth, _vHeight; // 2D dimensions used for current effect static uint32_t _currentColors[NUM_COLORS]; // colors used for current effect static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) @@ -546,7 +546,7 @@ typedef struct Segment { inline static unsigned vHeight() { return Segment::_vHeight; } inline static uint32_t getCurrentColor(unsigned i) { return Segment::_currentColors[i]; } // { return i < 3 ? Segment::_currentColors[i] : 0; } inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; } - + inline static uint8_t getCurrentBrightness() { return Segment::_segBri; } static void handleRandomPalette(); void beginDraw(); // set up parameters for current effect @@ -589,8 +589,8 @@ typedef struct Segment { // 1D strip [[gnu::hot]] uint16_t virtualLength() const; - [[gnu::hot]] void setPixelColor(int n, uint32_t c, bool unScaled = true); // set relative pixel within segment with color - inline void setPixelColor(unsigned n, uint32_t c, bool unScaled = true) { setPixelColor(int(n), c, unScaled); } + [[gnu::hot]] void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color + inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } #ifdef WLED_USE_AA_PIXELS @@ -629,8 +629,8 @@ typedef struct Segment { uint16_t nrOfVStrips() const; // returns number of virtual vertical strips in 2D matrix (used to expand 1D effects into 2D) #ifndef WLED_DISABLE_2D [[gnu::hot]] uint16_t XY(int x, int y); // support function to get relative index within segment - [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c, bool unScaled = true); // set relative pixel within segment with color - inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c, bool unScaled = true) { setPixelColorXY(int(x), int(y), c, unScaled); } + [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); } @@ -651,8 +651,8 @@ typedef struct Segment { void blur2D(uint8_t blur_amount, bool smear = false); void blurRow(int row, fract8 blur_amount, bool smear = false); void blurCol(int col, fract8 blur_amount, bool smear = false); - void moveX(int8_t delta, bool wrap = false); - void moveY(int8_t delta, bool wrap = false); + void moveX(int delta, bool wrap = false); + void moveY(int delta, bool wrap = false); void move(uint8_t dir, uint8_t delta, bool wrap = false); void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } @@ -668,8 +668,8 @@ typedef struct Segment { inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } #else inline uint16_t XY(int x, int y) { return x; } - inline void setPixelColorXY(int x, int y, uint32_t c, bool unScaled = true) { setPixelColor(x, c, unScaled); } - inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c, bool unScaled = true) { setPixelColor(int(x), c, unScaled); } + inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); } @@ -689,8 +689,8 @@ typedef struct Segment { inline void blur2D(uint8_t blur_amount, bool smear = false) {} inline void blurRow(int row, fract8 blur_amount, bool smear = false) {} inline void blurCol(int col, fract8 blur_amount, bool smear = false) {} - inline void moveX(int8_t delta, bool wrap = false) {} - inline void moveY(int8_t delta, bool wrap = false) {} + inline void moveX(int delta, bool wrap = false) {} + inline void moveY(int delta, bool wrap = false) {} inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {} inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {} inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {} diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 5f61b00940..bee3175972 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -168,16 +168,16 @@ uint16_t IRAM_ATTR_YN Segment::XY(int x, int y) return isActive() ? (x%vW) + (y%vH) * vW : 0; } -void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col, bool unScaled) +void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) { 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) - if (x >= vW|| y >= vH || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit + if (unsigned(x) >= vW || unsigned(y) >= vH) return; // if pixel would fall out of virtual segment just exit - // if color is unscaled - if (unScaled) col = color_fade(col, currentBri()); + if(getCurrentBrightness() < 255) + col = color_fade(col, getCurrentBrightness()); // scale brightness if (reverse ) x = vW - x - 1; if (reverse_y) y = vH - y - 1; @@ -459,7 +459,7 @@ void Segment::box_blur(unsigned radius, bool smear) { delete[] tmpWSum; } -void Segment::moveX(int8_t delta, bool wrap) { +void Segment::moveX(int delta, bool wrap) { 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) @@ -477,7 +477,7 @@ void Segment::moveX(int8_t delta, bool wrap) { } } -void Segment::moveY(int8_t delta, bool wrap) { +void Segment::moveY(int delta, bool wrap) { 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) @@ -545,20 +545,18 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, x++; } } else { - // pre-scale color for all pixels - col = color_fade(col, currentBri()); // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; while (y >= x) { - setPixelColorXY(cx+x, cy+y, col, false); - setPixelColorXY(cx-x, cy+y, col, false); - setPixelColorXY(cx+x, cy-y, col, false); - setPixelColorXY(cx-x, cy-y, col, false); - setPixelColorXY(cx+y, cy+x, col, false); - setPixelColorXY(cx-y, cy+x, col, false); - setPixelColorXY(cx+y, cy-x, col, false); - setPixelColorXY(cx-y, cy-x, col, false); + setPixelColorXY(cx+x, cy+y, col); + setPixelColorXY(cx-x, cy+y, col); + setPixelColorXY(cx+x, cy-y, col); + setPixelColorXY(cx-x, cy-y, col); + setPixelColorXY(cx+y, cy+x, col); + setPixelColorXY(cx-y, cy+x, col); + setPixelColorXY(cx+y, cy-x, col); + setPixelColorXY(cx-y, cy-x, col); x++; if (d > 0) { y--; @@ -577,15 +575,13 @@ void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, const int vH = vHeight(); // segment height in logical pixels (is always >= 1) // draw soft bounding circle if (soft) drawCircle(cx, cy, radius, col, soft); - // pre-scale color for all pixels - col = color_fade(col, currentBri()); // fill it for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { if (x * x + y * y <= radius * radius && int(cx)+x >= 0 && int(cy)+y >= 0 && int(cx)+x < vW && int(cy)+y < vH) - setPixelColorXY(cx + x, cy + y, col, false); + setPixelColorXY(cx + x, cy + y, col); } } } @@ -633,12 +629,10 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 if (steep) std::swap(x,y); // restore if steep } } else { - // pre-scale color for all pixels - c = color_fade(c, currentBri()); // Bresenham's algorithm int err = (dx>dy ? dx : -dy)/2; // error direction for (;;) { - setPixelColorXY(x0, y0, c, false); + setPixelColorXY(x0, y0, c); if (x0==x1 && y0==y1) break; int e2 = err; if (e2 >-dx) { err -= dy; x0 += sx; } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index a47a7edc95..9edd7a282c 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -86,6 +86,9 @@ uint16_t Segment::maxHeight = 1; unsigned Segment::_vLength = 0; unsigned Segment::_vWidth = 0; unsigned Segment::_vHeight = 0; +uint8_t Segment::_segBri = 0; +//uint8_t Segment::_currentBrightness2 = 0; +//uint8_t Segment::_currentBrightness3 = 0; uint32_t Segment::_currentColors[NUM_COLORS] = {0,0,0}; CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black); CRGBPalette16 Segment::_randomPalette = generateRandomPalette(); // was CRGBPalette16(DEFAULT_COLOR); @@ -413,7 +416,7 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { } #endif -uint8_t IRAM_ATTR Segment::currentBri(bool useCct) const { +uint8_t Segment::currentBri(bool useCct) const { unsigned prog = progress(); if (prog < 0xFFFFU) { unsigned curBri = (useCct ? cct : (on ? opacity : 0)) * prog; @@ -445,6 +448,7 @@ 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 @@ -624,21 +628,21 @@ void Segment::setPalette(uint8_t pal) { } // 2D matrix -uint16_t IRAM_ATTR Segment::virtualWidth() const { +uint16_t 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 { +uint16_t 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 { +uint16_t Segment::nrOfVStrips() const { unsigned vLen = 1; #ifndef WLED_DISABLE_2D if (is2D() && map1D2D == M12_pBar) vLen = virtualWidth(); @@ -683,7 +687,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(); @@ -715,7 +719,7 @@ uint16_t IRAM_ATTR Segment::virtualLength() const { return vLength; } -void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) +void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) { if (!isActive() || i < 0) return; // not active or invalid index #ifndef WLED_DISABLE_2D @@ -739,22 +743,20 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) if (is2D()) { 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, currentBri()); switch (map1D2D) { case M12_Pixels: // use all available pixels as a long strip - setPixelColorXY(i % vW, i / vW, col, false); + setPixelColorXY(i % vW, i / vW, 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, false); - else for (int x = 0; x < vW; x++) setPixelColorXY(x, vH - i - 1, col, false); + 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) - setPixelColorXY(0, 0, col, false); + setPixelColorXY(0, 0, col); else { float r = i; float step = HALF_PI / (2.8284f * r + 4); // we only need (PI/4)/(r/sqrt(2)+1) steps @@ -762,8 +764,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) int x = roundf(sin_t(rad) * r); int y = roundf(cos_t(rad) * r); // exploit symmetry - setPixelColorXY(x, y, col, false); - setPixelColorXY(y, x, col, false); + setPixelColorXY(x, y, col); + setPixelColorXY(y, x, col); } // Bresenham’s Algorithm (may not fill every pixel) //int d = 3 - (2*i); @@ -782,8 +784,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) } break; case M12_pCorner: - for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col, false); - for (int y = 0; y < i; y++) setPixelColorXY(i, y, col, false); + for (int x = 0; x <= i; x++) setPixelColorXY(x, i, col); + for (int y = 0; y < i; y++) setPixelColorXY(i, y, col); break; case M12_sPinwheel: { // i = angle --> 0 - 296 (Big), 0 - 192 (Medium), 0 - 72 (Small) @@ -822,7 +824,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) int x = posx / Fixed_Scale; int y = posy / Fixed_Scale; // set pixel - if (x != lastX || y != lastY) setPixelColorXY(x, y, col, false); // only paint if pixel position is different + if (x != lastX || y != lastY) setPixelColorXY(x, y, col); // only paint if pixel position is different lastX = x; lastY = y; // advance to next position @@ -846,9 +848,8 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col, bool unScaled) #endif unsigned len = length(); - // if color is unscaled - if (unScaled) col = color_fade(col, currentBri()); - + if(getCurrentBrightness() < 255) //!!! test without this if + col = color_fade(col, getCurrentBrightness()); // scale brightness // expand pixel (taking into account start, grouping, spacing [and offset]) i = i * groupLength(); if (reverse) { // is segment reversed? @@ -1070,11 +1071,9 @@ void Segment::fill(uint32_t c) { if (!isActive()) return; // not active 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, currentBri()); for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) { - if (is2D()) setPixelColorXY(x, y, c, false); - else setPixelColor(x, c, false); + if (is2D()) setPixelColorXY(x, y, c); + else setPixelColor(x, c); } } From 336da25463ff13485da9bd2b1d825f1647368221 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 29 Sep 2024 14:14:07 +0200 Subject: [PATCH 27/37] Private global _colorScaled --- wled00/FX.h | 15 ++++++++------- wled00/FX_2Dfcn.cpp | 20 ++++++++++++++++++-- wled00/FX_fcn.cpp | 18 +++++++++++++----- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 8a452cfcbc..763dc2639d 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -420,10 +420,11 @@ typedef struct Segment { }; uint16_t _dataLen; static uint16_t _usedSegmentData; - static uint8_t _segBri; // Current brightness of segment + static uint8_t _segBri; // brightness of segment for current effect static unsigned _vLength; // 1D dimension used for current effect static unsigned _vWidth, _vHeight; // 2D dimensions used for current effect static uint32_t _currentColors[NUM_COLORS]; // colors used for current effect + static bool _colorScaled; // color has been scaled prior to setPixelColor() call static CRGBPalette16 _currentPalette; // palette used for current effect (includes transition, used in color_from_palette()) static CRGBPalette16 _randomPalette; // actual random palette static CRGBPalette16 _newRandomPalette; // target random palette @@ -590,9 +591,9 @@ typedef struct Segment { // 1D strip [[gnu::hot]] uint16_t virtualLength() const; [[gnu::hot]] void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color - inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } - inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } - inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } + inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); } + inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } #ifdef WLED_USE_AA_PIXELS void setPixelColor(float i, uint32_t c, bool aa = true); inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); } @@ -630,7 +631,7 @@ typedef struct Segment { #ifndef WLED_DISABLE_2D [[gnu::hot]] uint16_t XY(int x, int y); // support function to get relative index within segment [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color - inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); } @@ -668,8 +669,8 @@ typedef struct Segment { inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } #else inline uint16_t XY(int x, int y) { return x; } - inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } - inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } + inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } + inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); } diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index bee3175972..8804cdbb63 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -176,8 +176,8 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) const int vH = vHeight(); // segment height in logical pixels (is always >= 1) if (unsigned(x) >= vW || unsigned(y) >= vH) return; // if pixel would fall out of virtual segment just exit - if(getCurrentBrightness() < 255) - col = color_fade(col, getCurrentBrightness()); // scale brightness + // if color is unscaled + if (!_colorScaled) col = color_fade(col, _segBri); if (reverse ) x = vW - x - 1; if (reverse_y) y = vH - y - 1; @@ -545,6 +545,9 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, x++; } } else { + // pre-scale color for all pixels + col = color_fade(col, _segBri); + _colorScaled = true; // Bresenham’s Algorithm int d = 3 - (2*radius); int y = radius, x = 0; @@ -565,6 +568,7 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, d += 4 * x + 6; } } + _colorScaled = false; } } @@ -575,6 +579,9 @@ void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, const int vH = vHeight(); // segment height in logical pixels (is always >= 1) // draw soft bounding circle if (soft) drawCircle(cx, cy, radius, col, soft); + // pre-scale color for all pixels + col = color_fade(col, _segBri); + _colorScaled = true; // fill it for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { @@ -584,6 +591,7 @@ void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, setPixelColorXY(cx + x, cy + y, col); } } + _colorScaled = false; } //line function @@ -629,6 +637,9 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 if (steep) std::swap(x,y); // restore if steep } } else { + // pre-scale color for all pixels + c = color_fade(c, _segBri); + _colorScaled = true; // Bresenham's algorithm int err = (dx>dy ? dx : -dy)/2; // error direction for (;;) { @@ -638,6 +649,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3 if (e2 >-dx) { err -= dy; x0 += sx; } if (e2 < dy) { err += dx; y0 += sy; } } + _colorScaled = false; } } @@ -670,6 +682,9 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, default: return; } uint32_t col = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND); + // pre-scale color for all pixels + col = color_fade(col, _segBri); + _colorScaled = true; for (int j = 0; j= 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 @@ -834,6 +836,7 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) break; } } + _colorScaled = false; return; } else if (Segment::maxHeight != 1 && (width() == 1 || height() == 1)) { if (start < Segment::maxWidth*Segment::maxHeight) { @@ -848,8 +851,9 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) #endif unsigned len = length(); - if(getCurrentBrightness() < 255) //!!! test without this if - col = color_fade(col, getCurrentBrightness()); // scale brightness + // if color is unscaled + if (!_colorScaled) col = color_fade(col, _segBri); + // expand pixel (taking into account start, grouping, spacing [and offset]) i = i * groupLength(); if (reverse) { // is segment reversed? @@ -1071,10 +1075,14 @@ void Segment::fill(uint32_t c) { if (!isActive()) return; // not active 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; } /* From 0ae73296cffaff3089893734c2621174347de7ad Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 29 Sep 2024 15:19:37 +0200 Subject: [PATCH 28/37] Update comment --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 86f190ad5c..232994e731 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -442,7 +442,7 @@ uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const { #endif } -// pre-calculate drawing parameters for faster access +// pre-calculate drawing parameters for faster access (based on the idea from @softhack007 from MM fork) void Segment::beginDraw() { _vWidth = virtualWidth(); _vHeight = virtualHeight(); From ee380c5377b1f73e2a8da2247bb77288b9670899 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 30 Sep 2024 16:35:40 +0200 Subject: [PATCH 29/37] Replace uint16_t with unsigned for segment data swap if statements in color_fade --- wled00/FX.h | 8 ++++---- wled00/FX_fcn.cpp | 4 ++-- wled00/colors.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 763dc2639d..065daa86d5 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -400,7 +400,7 @@ typedef struct Segment { uint32_t _stepT; uint32_t _callT; uint8_t *_dataT; - uint16_t _dataLenT; + unsigned _dataLenT; TemporarySegmentData() : _dataT(nullptr) // just in case... , _dataLenT(0) @@ -418,8 +418,8 @@ typedef struct Segment { uint8_t _reserved : 4; }; }; - uint16_t _dataLen; - static uint16_t _usedSegmentData; + unsigned _dataLen; + static unsigned _usedSegmentData; static uint8_t _segBri; // brightness of segment for current effect static unsigned _vLength; // 1D dimension used for current effect static unsigned _vWidth, _vHeight; // 2D dimensions used for current effect @@ -537,7 +537,7 @@ typedef struct Segment { inline uint16_t groupLength() const { return grouping + spacing; } inline uint8_t getLightCapabilities() const { return _capabilities; } - inline static uint16_t getUsedSegmentData() { return Segment::_usedSegmentData; } + inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; } inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; } #ifndef WLED_DISABLE_MODE_BLEND inline static void modeBlend(bool blend) { _modeBlend = blend; } diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 232994e731..13e9e73bb9 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -80,7 +80,7 @@ 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[] +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; @@ -433,7 +433,7 @@ uint8_t Segment::currentMode() const { return mode; } -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; #ifndef WLED_DISABLE_MODE_BLEND return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot]; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 1c48443717..c059ea9db4 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -77,8 +77,8 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { - if (c1 == BLACK || amount == 0) return BLACK; if (amount == 255) return c1; + if (c1 == BLACK || amount == 0) return BLACK; uint32_t scaledcolor; // color order is: W R G B from MSB to LSB uint32_t scale = amount; // 32bit for faster calculation uint32_t addRemains = 0; From ba3a61f6236f316e3b03e3cf3bd92c79b0a1ce8f Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 2 Oct 2024 20:14:25 +0200 Subject: [PATCH 30/37] Reduced code size by: - removing WS2812FX::setMode() - removing WS2812FX::setColor() - removing floating point in transition - color handling modification in set.cpp - replaced uint8_t with unsigned in function parameters - inlined WS2812FX::isUpdating() - (MAY BE BREAKING) alexa & smartnest update --- usermods/smartnest/usermod_smartnest.h | 2 +- wled00/FX.h | 29 +++++++--------- wled00/FX_2Dfcn.cpp | 2 +- wled00/FX_fcn.cpp | 46 +++----------------------- wled00/alexa.cpp | 4 +-- wled00/bus_manager.h | 1 + wled00/led.cpp | 21 ++++++------ wled00/set.cpp | 45 ++++++++++++------------- wled00/udp.cpp | 6 ++-- wled00/wled.cpp | 10 +++--- wled00/wled.h | 1 - 11 files changed, 61 insertions(+), 106 deletions(-) diff --git a/usermods/smartnest/usermod_smartnest.h b/usermods/smartnest/usermod_smartnest.h index 92d524c88a..9d21ef2e73 100644 --- a/usermods/smartnest/usermod_smartnest.h +++ b/usermods/smartnest/usermod_smartnest.h @@ -49,7 +49,7 @@ class Smartnest : public Usermod void setColor(int r, int g, int b) { - strip.setColor(0, r, g, b); + strip.getMainSegment().setColor(0, RGBW32(r, g, b, 0)); stateUpdated(CALL_MODE_DIRECT_CHANGE); char msg[18] {}; sprintf(msg, "rgb(%d,%d,%d)", r, g, b); diff --git a/wled00/FX.h b/wled00/FX.h index 065daa86d5..14c2681620 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -30,6 +30,7 @@ #include #include "const.h" +#include "bus_manager.h" #define FASTLED_INTERNAL //remove annoying pragma messages #define USE_GET_MILLISECOND_TIMER @@ -654,7 +655,7 @@ typedef struct Segment { void blurCol(int col, fract8 blur_amount, bool smear = false); void moveX(int delta, bool wrap = false); void moveY(int delta, bool wrap = false); - void move(uint8_t dir, uint8_t delta, bool wrap = false); + void move(unsigned dir, unsigned delta, bool wrap = false); void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false); @@ -781,25 +782,22 @@ class WS2812FX { // 96 bytes #endif finalizeInit(), // initialises strip components service(), // executes effect functions when due and calls strip.show() - setMode(uint8_t segid, uint8_t m), // sets effect/mode for given segment (high level API) - setColor(uint8_t slot, uint32_t c), // sets color (in slot) for given segment (high level API) setCCT(uint16_t k), // sets global CCT (either in relative 0-255 value or in K) setBrightness(uint8_t b, bool direct = false), // sets strip brightness setRange(uint16_t i, uint16_t i2, uint32_t col), // used for clock overlay purgeSegments(), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint) setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), - setMainSegmentId(uint8_t n), + setMainSegmentId(unsigned n = 0), resetSegments(), // marks all segments for reset makeAutoSegments(bool forceReset = false), // will create segments based on configured outputs fixInvalidSegments(), // fixes incorrect segment configuration setPixelColor(unsigned n, uint32_t c), // paints absolute strip pixel with index n and color c show(), // initiates LED output - setTargetFps(uint8_t fps), + setTargetFps(unsigned fps), setupEffectData(); // add default effects to the list; defined in FX.cpp inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); } inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); } - inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); } inline void setPixelColor(unsigned n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } inline void setPixelColor(unsigned n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); } inline void fill(uint32_t c) { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline) @@ -815,9 +813,9 @@ class WS2812FX { // 96 bytes checkSegmentAlignment(), hasRGBWBus() const, hasCCTBus() const, - isUpdating() const, // return true if the strip is being sent pixel updates - deserializeMap(uint8_t n=0); + deserializeMap(unsigned n = 0); + inline bool isUpdating() const { return !BusManager::canAllShow(); } // return true if the strip is being sent pixel updates inline bool isServicing() const { return _isServicing; } // returns true if strip.service() is executing inline bool hasWhiteChannel() const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset) @@ -844,9 +842,9 @@ class WS2812FX { // 96 bytes uint16_t getLengthPhysical() const, - getLengthTotal() const, // will include virtual/nonexistent pixels in matrix - getFps() const; + getLengthTotal() const; // will include virtual/nonexistent pixels in matrix + inline uint16_t getFps() const { return (millis() - _lastShow > 2000) ? 0 : _cumulativeFps +1; } // Returns the refresh rate of the LED strip inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms) inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant) inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H) @@ -859,15 +857,12 @@ class WS2812FX { // 96 bytes uint32_t now, timebase; uint32_t getPixelColor(unsigned) const; - inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call + inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call - const char * - getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); } + const char *getModeData(unsigned id = 0) const { return (id && id < _modeCount) ? _modeData[id] : PSTR("Solid"); } + inline const char **getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data - const char ** - getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data - - Segment& getSegment(uint8_t id); + Segment& getSegment(unsigned id); inline Segment& getFirstSelectedSeg() { return _segments[getFirstSelectedSegId()]; } // returns reference to first segment that is "selected" inline Segment& getMainSegment() { return _segments[getMainSegmentId()]; } // returns reference to main segment inline Segment* getSegments() { return &(_segments[0]); } // returns pointer to segment vector structure (warning: use carefully) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 8804cdbb63..9b39b4c8fc 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -499,7 +499,7 @@ void Segment::moveY(int delta, bool wrap) { // @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down // @param delta number of pixels to move // @param wrap around -void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { +void Segment::move(unsigned dir, unsigned delta, bool wrap) { if (delta==0) return; switch (dir) { case 0: moveX( delta, wrap); break; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 13e9e73bb9..7927269ed6 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1464,49 +1464,11 @@ void WS2812FX::show() { _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) { +void WS2812FX::setTargetFps(unsigned 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 - } -} - -//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::setCCT(uint16_t k) { for (segment &seg : _segments) { if (seg.isActive() && seg.isSelected()) { @@ -1553,7 +1515,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; @@ -1629,7 +1591,7 @@ void WS2812FX::purgeSegments() { } } -Segment& WS2812FX::getSegment(uint8_t id) { +Segment& WS2812FX::getSegment(unsigned id) { return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors } @@ -1844,7 +1806,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]; diff --git a/wled00/alexa.cpp b/wled00/alexa.cpp index b108f294b3..81b9ec3469 100644 --- a/wled00/alexa.cpp +++ b/wled00/alexa.cpp @@ -126,10 +126,10 @@ void onAlexaChange(EspalexaDevice* dev) } else { colorKtoRGB(k, rgbw); } - strip.setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3])); + strip.getMainSegment().setColor(0, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3])); } else { uint32_t color = dev->getRGB(); - strip.setColor(0, color); + strip.getMainSegment().setColor(0, color); } stateUpdated(CALL_MODE_ALEXA); } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 1b324d7137..e25a068498 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -6,6 +6,7 @@ */ #include "const.h" +#include "pin_manager.h" #include //colors.cpp diff --git a/wled00/led.cpp b/wled00/led.cpp index 9de0495b45..5439fbb686 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -80,6 +80,7 @@ byte scaledBri(byte in) void applyBri() { if (!realtimeMode || !arlsForceMaxBri) { + //DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld); strip.setBrightness(scaledBri(briT)); } } @@ -144,7 +145,6 @@ void stateUpdated(byte callMode) { if (transitionActive) { briOld = briT; - tperLast = 0; } else strip.setTransitionMode(true); // force all segments to transition mode transitionActive = true; @@ -184,22 +184,21 @@ void handleTransitions() updateInterfaces(interfaceUpdateCallMode); if (transitionActive && strip.getTransition() > 0) { - float tper = (millis() - transitionStartTime)/(float)strip.getTransition(); - if (tper >= 1.0f) { + int ti = millis() - transitionStartTime; + int tr = strip.getTransition(); + if (ti/tr) { strip.setTransitionMode(false); // stop all transitions // restore (global) transition time if not called from UDP notifier or single/temporary transition from JSON (also playlist) if (jsonTransitionOnce) strip.setTransition(transitionDelay); transitionActive = false; jsonTransitionOnce = false; - tperLast = 0; applyFinalBri(); return; } - if (tper - tperLast < 0.004f) return; // less than 1 bit change (1/255) - tperLast = tper; - briT = briOld + ((bri - briOld) * tper); - - applyBri(); + byte briTO = briT; + int deltaBri = (int)bri - (int)briOld; + briT = briOld + (deltaBri * ti / tr); + if (briTO != briT) applyBri(); } } @@ -234,8 +233,8 @@ void handleNightlight() colNlT[1] = effectSpeed; colNlT[2] = effectPalette; - strip.setMode(strip.getFirstSelectedSegId(), FX_MODE_STATIC); // make sure seg runtime is reset if it was in sunrise mode - effectCurrent = FX_MODE_SUNRISE; + strip.getFirstSelectedSeg().setMode(FX_MODE_STATIC); // make sure seg runtime is reset if it was in sunrise mode + effectCurrent = FX_MODE_SUNRISE; // colorUpdated() will take care of assigning that to all selected segments effectSpeed = nightlightDelayMins; effectPalette = 0; if (effectSpeed > 60) effectSpeed = 60; //currently limited to 60 minutes diff --git a/wled00/set.cpp b/wled00/set.cpp index 9b7aa92a52..cf3a07dd02 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -837,8 +837,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) } // temporary values, write directly to segments, globals are updated by setValuesFromFirstSelectedSeg() - uint32_t col0 = selseg.colors[0]; - uint32_t col1 = selseg.colors[1]; + uint32_t col0 = selseg.colors[0]; + uint32_t col1 = selseg.colors[1]; + uint32_t col2 = selseg.colors[2]; byte colIn[4] = {R(col0), G(col0), B(col0), W(col0)}; byte colInSec[4] = {R(col1), G(col1), B(col1), W(col1)}; byte effectIn = selseg.mode; @@ -919,7 +920,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) //set brightness updateVal(req.c_str(), "&A=", &bri); - bool col0Changed = false, col1Changed = false; + bool col0Changed = false, col1Changed = false, col2Changed = false; //set colors col0Changed |= updateVal(req.c_str(), "&R=", &colIn[0]); col0Changed |= updateVal(req.c_str(), "&G=", &colIn[1]); @@ -976,7 +977,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) } //set color from HEX or 32bit DEC - byte tmpCol[4]; pos = req.indexOf(F("CL=")); if (pos > 0) { colorFromDecOrHexString(colIn, (char*)req.substring(pos + 3).c_str()); @@ -989,10 +989,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) } pos = req.indexOf(F("C3=")); if (pos > 0) { + byte tmpCol[4]; colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str()); - uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]); + col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]); selseg.setColor(2, col2); // defined above (SS= or main) - if (!singleSegment) strip.setColor(2, col2); // will set color to all active & selected segments + col2Changed = true; } //set to random hue SR=0->1st SR=1->2nd @@ -1003,29 +1004,22 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) col0Changed |= (!sec); col1Changed |= sec; } - //swap 2nd & 1st - pos = req.indexOf(F("SC")); - if (pos > 0) { - byte temp; - for (unsigned i=0; i<4; i++) { - temp = colIn[i]; - colIn[i] = colInSec[i]; - colInSec[i] = temp; - } - col0Changed = col1Changed = true; - } - // apply colors to selected segment, and all selected segments if applicable if (col0Changed) { - uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]); - selseg.setColor(0, colIn0); - if (!singleSegment) strip.setColor(0, colIn0); // will set color to all active & selected segments + col0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]); + selseg.setColor(0, col0); } if (col1Changed) { - uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]); - selseg.setColor(1, colIn1); - if (!singleSegment) strip.setColor(1, colIn1); // will set color to all active & selected segments + col1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]); + selseg.setColor(1, col1); + } + + //swap 2nd & 1st + pos = req.indexOf(F("SC")); + if (pos > 0) { + std::swap(col0,col1); + col0Changed = col1Changed = true; } bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false; @@ -1055,6 +1049,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) if (speedChanged) seg.speed = speedIn; if (intensityChanged) seg.intensity = intensityIn; if (paletteChanged) seg.setPalette(paletteIn); + if (col0Changed) seg.setColor(0, col0); + if (col1Changed) seg.setColor(1, col1); + if (col2Changed) seg.setColor(2, col2); if (custom1Changed) seg.custom1 = custom1In; if (custom2Changed) seg.custom2 = custom2In; if (custom3Changed) seg.custom3 = custom3In; diff --git a/wled00/udp.cpp b/wled00/udp.cpp index a615cefcba..0ff39f1109 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -234,12 +234,12 @@ void parseNotifyPacket(uint8_t *udpIn) { //apply colors from notification to main segment, only if not syncing full segments if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) { // primary color, only apply white if intented (version > 0) - strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0)); + strip.getMainSegment().setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0)); if (version > 1) { - strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color + strip.getMainSegment().setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color } if (version > 6) { - strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color + strip.getMainSegment().setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color if (version > 9 && udpIn[37] < 255) { // valid CCT/Kelvin value unsigned cct = udpIn[38]; if (udpIn[37] > 0) { //Kelvin diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 39e0d250be..a80808a782 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -221,6 +221,7 @@ void WLED::loop() strip.finalizeInit(); // also loads default ledmap if present if (aligned) strip.makeAutoSegments(); else strip.fixInvalidSegments(); + BusManager::setBrightness(bri); // fix re-initialised bus' brightness doSerializeConfig = true; } if (loadLedmap >= 0) { @@ -571,10 +572,11 @@ void WLED::beginStrip() } else { // fix for #3196 if (bootPreset > 0) { - bool oldTransition = fadeTransition; // workaround if transitions are enabled - fadeTransition = false; // ignore transitions temporarily - strip.setColor(0, BLACK); // set all segments black - fadeTransition = oldTransition; // restore transitions + // set all segments black (no transition) + for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { + Segment &seg = strip.getSegment(i); + if (seg.isActive()) seg.colors[0] = BLACK; + } col[0] = col[1] = col[2] = col[3] = 0; // needed for colorUpdated() } briLast = briS; bri = 0; diff --git a/wled00/wled.h b/wled00/wled.h index 052f29b29f..f8021e92b6 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -584,7 +584,6 @@ WLED_GLOBAL bool transitionActive _INIT(false); WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration WLED_GLOBAL uint16_t transitionDelayDefault _INIT(750); // default transition time (stored in cfg.json) WLED_GLOBAL unsigned long transitionStartTime; -WLED_GLOBAL float tperLast _INIT(0.0f); // crossfade transition progress, 0.0f - 1.0f WLED_GLOBAL bool jsonTransitionOnce _INIT(false); // flag to override transitionDelay (playlist, JSON API: "live" & "seg":{"i"} & "tt") WLED_GLOBAL uint8_t randomPaletteChangeTime _INIT(5); // amount of time [s] between random palette changes (min: 1s, max: 255s) WLED_GLOBAL bool useHarmonicRandomPalette _INIT(true); // use *harmonic* random palette generation (nicer looking) or truly random From a15c391e6c00dd1b81f4e3116aa87a81628a17e8 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 3 Oct 2024 21:19:34 +0200 Subject: [PATCH 31/37] Improvement to `setPixelColorXY` and some flash optimisations - changes to `setPixelColorXY` give an extra FPS, some checks and the loops are only done when needed, additional function call is still faster (force inlining it gives negligible speed boost but eats more flash) - commented out the unused `boxBlur` function - code size improvemnts (also faster) in `moveX()` and `moveY()` by only copying whats required and avoiding code duplications - consolidated the `blur()` functions by enabling asymmetrical blur2D() to replace `blurRow` and `blurCol` - compiler warning fixes (explicit unsigned casts) --- wled00/FX.h | 19 ++- wled00/FX_2Dfcn.cpp | 286 +++++++++++++++++++++----------------------- wled00/FX_fcn.cpp | 2 +- 3 files changed, 143 insertions(+), 164 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index 14c2681620..c06332c765 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -457,6 +457,8 @@ typedef struct Segment { {} } *_t; + [[gnu::hot]] void _setPixelColorXY_raw(int& x, int& y, uint32_t& col); // set pixel without mapping (internal use only) + public: Segment(uint16_t sStart=0, uint16_t sStop=30) : @@ -617,12 +619,10 @@ typedef struct Segment { // 2D Blur: shortcuts for bluring columns or rows only (50% faster than full 2D blur) inline void blurCols(fract8 blur_amount, bool smear = false) { // blur all columns - const unsigned cols = virtualWidth(); - for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount, smear); + blur2D(0, blur_amount, smear); } inline void blurRows(fract8 blur_amount, bool smear = false) { // blur all rows - const unsigned rows = virtualHeight(); - for ( unsigned i = 0; i < rows; i++) blurRow(i, blur_amount, smear); + blur2D(blur_amount, 0, smear); } // 2D matrix @@ -649,10 +649,8 @@ typedef struct Segment { inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(r,g,b,w), preserveCR); } inline void addPixelColorXY(int x, int y, CRGB c, bool preserveCR = true) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), preserveCR); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); } - void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur - void blur2D(uint8_t blur_amount, bool smear = false); - void blurRow(int row, fract8 blur_amount, bool smear = false); - void blurCol(int col, fract8 blur_amount, bool smear = false); + //void box_blur(unsigned r = 1U, bool smear = false); // 2D box blur + void blur2D(uint8_t blur_x, uint8_t blur_y, bool smear = false); void moveX(int delta, bool wrap = false); void moveY(int delta, bool wrap = false); void move(unsigned dir, unsigned delta, bool wrap = false); @@ -666,7 +664,6 @@ typedef struct Segment { inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline void wu_pixel(uint32_t x, uint32_t y, CRGB c); - inline void blur2d(fract8 blur_amount) { blur(blur_amount); } inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } #else inline uint16_t XY(int x, int y) { return x; } @@ -687,8 +684,8 @@ typedef struct Segment { inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool saturate = false) { addPixelColor(x, RGBW32(r,g,b,w), saturate); } inline void addPixelColorXY(int x, int y, CRGB c, bool saturate = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), saturate); } inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } - inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} - inline void blur2D(uint8_t blur_amount, bool smear = false) {} + //inline void box_blur(unsigned i, bool vertical, fract8 blur_amount) {} + inline void blur2D(uint8_t blur_x, uint8_t blur_y, bool smear = false) {} inline void blurRow(int row, fract8 blur_amount, bool smear = false) {} inline void blurCol(int col, fract8 blur_amount, bool smear = false) {} inline void moveX(int delta, bool wrap = false) {} diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 9b39b4c8fc..3ad061a8cf 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -168,13 +168,34 @@ uint16_t IRAM_ATTR_YN Segment::XY(int x, int y) return isActive() ? (x%vW) + (y%vH) * vW : 0; } +// raw setColor function without checks (checks are done in setPixelColorXY()) +void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(int& x, int& y, uint32_t& col) +{ +#ifndef WLED_DISABLE_MODE_BLEND + // if blending modes, blend with underlying pixel + if (_modeBlend) col = color_blend(strip.getPixelColorXY(start + x, startY + y), col, 0xFFFFU - progress(), true); +#endif + strip.setPixelColorXY(start + x, startY + y, col); + if (mirror) { //set the corresponding horizontally mirrored pixel + if (transpose) strip.setPixelColorXY(start + x, startY + height() - y - 1, col); + else strip.setPixelColorXY(start + width() - x - 1, startY + y, col); + } + if (mirror_y) { //set the corresponding vertically mirrored pixel + if (transpose) strip.setPixelColorXY(start + width() - x - 1, startY + y, col); + else strip.setPixelColorXY(start + x, startY + height() - y - 1, col); + } + if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel + strip.setPixelColorXY(start + width() - x - 1, startY + height() - y - 1, col); + } +} + void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) { 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) - if (unsigned(x) >= vW || unsigned(y) >= vH) return; // if pixel would fall out of virtual segment just exit + if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) return; // if pixel would fall out of virtual segment just exit // if color is unscaled if (!_colorScaled) col = color_fade(col, _segBri); @@ -182,36 +203,28 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) 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 - x *= groupLength(); // expand to physical pixels - y *= groupLength(); // expand to physical pixels - int W = width(); - int H = height(); - - int yY = y; - for (int j = 0; j < grouping; j++) { // groupping vertically - if (yY >= H) break; - int xX = x; - for (int g = 0; g < grouping; g++) { // groupping horizontally - if (xX >= W) break; // we have reached X dimension's end -#ifndef WLED_DISABLE_MODE_BLEND - // if blending modes, blend with underlying pixel - if (_modeBlend) col = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true); -#endif - strip.setPixelColorXY(start + xX, startY + yY, col); - if (mirror) { //set the corresponding horizontally mirrored pixel - if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); - else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); + unsigned groupLen = groupLength(); + + if(groupLen > 1) + { + int W = width(); + int H = height(); + x *= groupLen; // expand to physical pixels + y *= groupLen; // expand to physical pixels + int yY = y; + for (int j = 0; j < grouping; j++) { // groupping vertically + if (yY >= H) break; + int xX = x; + for (int g = 0; g < grouping; g++) { // groupping horizontally + if (xX >= W) break; // we have reached X dimension's end + _setPixelColorXY_raw(xX, yY, col); + xX++; } - if (mirror_y) { //set the corresponding vertically mirrored pixel - if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col); - else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col); - } - if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel - strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, col); - } - xX++; + yY++; } - yY++; + } + else { + _setPixelColorXY_raw(x, y, col); } } @@ -263,7 +276,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { if (!isActive()) return 0; // not active int vW = vWidth(); int vH = vHeight(); - if (x >= vW || y >= vH || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit + if (unsigned(x) >= unsigned(vW) || unsigned(y) >= unsigned(vH)) 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 @@ -273,119 +286,62 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const { return strip.getPixelColorXY(start + x, startY + y); } -// blurRow: perform a blur on a row of a rectangular matrix -void Segment::blurRow(int row, fract8 blur_amount, bool smear){ - if (!isActive() || blur_amount == 0) return; // not active - const int cols = vWidth(); - const int rows = vHeight(); - - if (row >= rows) return; - // blur one row - uint8_t keep = smear ? 255 : 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - uint32_t carryover = BLACK; - uint32_t lastnew; - uint32_t last; - uint32_t curnew = BLACK; - for (int x = 0; x < cols; x++) { - uint32_t cur = getPixelColorXY(x, row); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (x > 0) { - 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) setPixelColorXY(x - 1, row, prev); - } else // first pixel - setPixelColorXY(x, row, curnew); - lastnew = curnew; - last = cur; // save original value for comparison on next iteration - carryover = part; - } - setPixelColorXY(cols-1, row, curnew); // set last pixel -} - -// blurCol: perform a blur on a column of a rectangular matrix -void Segment::blurCol(int col, fract8 blur_amount, bool smear) { - if (!isActive() || blur_amount == 0) return; // not active - const int cols = vWidth(); - const int rows = vHeight(); - - if (col >= cols) return; - // blur one column - uint8_t keep = smear ? 255 : 255 - blur_amount; - uint8_t seep = blur_amount >> 1; - uint32_t carryover = BLACK; - uint32_t lastnew; - uint32_t last; - uint32_t curnew = BLACK; - for (int y = 0; y < rows; y++) { - uint32_t cur = getPixelColorXY(col, y); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (y > 0) { - 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) setPixelColorXY(col, y - 1, prev); - } else // first pixel - setPixelColorXY(col, y, curnew); - lastnew = curnew; - last = cur; //save original value for comparison on next iteration - carryover = part; - } - setPixelColorXY(col, rows - 1, curnew); -} - -void Segment::blur2D(uint8_t blur_amount, bool smear) { - if (!isActive() || blur_amount == 0) return; // not active +// 2D blurring, can be asymmetrical +void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) { + if (!isActive()) return; // not active const unsigned cols = vWidth(); const unsigned rows = vHeight(); - - const uint8_t keep = smear ? 255 : 255 - blur_amount; - const uint8_t seep = blur_amount >> (1 + smear); uint32_t lastnew; uint32_t last; - for (unsigned row = 0; row < rows; row++) { - uint32_t carryover = BLACK; - uint32_t curnew = BLACK; - for (unsigned x = 0; x < cols; x++) { - uint32_t cur = getPixelColorXY(x, row); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (x > 0) { - 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) setPixelColorXY(x - 1, row, prev); - } else setPixelColorXY(x, row, curnew); // first pixel - lastnew = curnew; - last = cur; // save original value for comparison on next iteration - carryover = part; + if(blur_x) { + const uint8_t keepx = smear ? 255 : 255 - blur_x; + const uint8_t seepx = blur_x >> (1 + smear); + for (unsigned row = 0; row < rows; row++) { // blur rows (x direction) + uint32_t carryover = BLACK; + uint32_t curnew = BLACK; + for (unsigned x = 0; x < cols; x++) { + uint32_t cur = getPixelColorXY(x, row); + uint32_t part = color_fade(cur, seepx); + curnew = color_fade(cur, keepx); + if (x > 0) { + 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) setPixelColorXY(x - 1, row, prev); + } else setPixelColorXY(x, row, curnew); // first pixel + lastnew = curnew; + last = cur; // save original value for comparison on next iteration + carryover = part; + } + setPixelColorXY(cols-1, row, curnew); // set last pixel } - setPixelColorXY(cols-1, row, curnew); // set last pixel } - for (unsigned col = 0; col < cols; col++) { - uint32_t carryover = BLACK; - uint32_t curnew = BLACK; - for (unsigned y = 0; y < rows; y++) { - uint32_t cur = getPixelColorXY(col, y); - uint32_t part = color_fade(cur, seep); - curnew = color_fade(cur, keep); - if (y > 0) { - 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) setPixelColorXY(col, y - 1, prev); - } else setPixelColorXY(col, y, curnew); // first pixel - lastnew = curnew; - last = cur; //save original value for comparison on next iteration - carryover = part; + if(blur_y) { + const uint8_t keepy = smear ? 255 : 255 - blur_y; + const uint8_t seepy = blur_y >> (1 + smear); + for (unsigned col = 0; col < cols; col++) { + uint32_t carryover = BLACK; + uint32_t curnew = BLACK; + for (unsigned y = 0; y < rows; y++) { + uint32_t cur = getPixelColorXY(col, y); + uint32_t part = color_fade(cur, seepy); + curnew = color_fade(cur, keepy); + if (y > 0) { + 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) setPixelColorXY(col, y - 1, prev); + } else setPixelColorXY(col, y, curnew); // first pixel + lastnew = curnew; + last = cur; //save original value for comparison on next iteration + carryover = part; + } + setPixelColorXY(col, rows - 1, curnew); } - setPixelColorXY(col, rows - 1, curnew); } } +/* // 2D Box blur void Segment::box_blur(unsigned radius, bool smear) { if (!isActive() || radius == 0) return; // not active @@ -458,43 +414,69 @@ void Segment::box_blur(unsigned radius, bool smear) { delete[] tmpBSum; delete[] tmpWSum; } - +*/ void Segment::moveX(int delta, bool wrap) { - if (!isActive()) return; // not active + if (!isActive() || !delta) 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) - if (!delta || abs(delta) >= vW) return; + int absDelta = abs(delta); + if (absDelta >= vW) return; uint32_t newPxCol[vW]; + int newDelta; + int stop = vW; + int start = 0; + if(wrap) newDelta = (delta + vW) % vW; // +cols in case delta < 0 + else { + if(delta < 0) start = -delta; + stop = vW - absDelta; + newDelta = delta > 0 ? delta : 0; + } for (int y = 0; y < vH; y++) { - if (delta > 0) { - for (int x = 0; x < vW-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y); - for (int x = vW-delta; x < vW; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - vW : x, y); - } else { - for (int x = vW-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y); - for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + vW : x, y); + for (int x = 0; x < stop; x++) { + int srcX; + if (wrap) { + srcX = (x + newDelta) % vW; // Wrap using modulo when `wrap` is true + } else { + srcX = x + newDelta; + } + newPxCol[x] = getPixelColorXY(srcX, y); } - for (int x = 0; x < vW; x++) setPixelColorXY(x, y, newPxCol[x]); + for (int x = 0; x < stop; x++) setPixelColorXY(x + start, y, newPxCol[x]); } } void Segment::moveY(int delta, bool wrap) { - if (!isActive()) return; // not active + if (!isActive() || !delta) 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) - if (!delta || abs(delta) >= vH) return; + int absDelta = abs(delta); + if (absDelta >= vH) return; uint32_t newPxCol[vH]; + int newDelta; + int stop = vH; + int start = 0; + if(wrap) newDelta = (delta + vH) % vH; // +rows in case delta < 0 + else { + if(delta < 0) start = -delta; + stop = vH - absDelta; + newDelta = delta > 0 ? delta : 0; + } for (int x = 0; x < vW; x++) { - if (delta > 0) { - for (int y = 0; y < vH-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta)); - for (int y = vH-delta; y < vH; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - vH : y); - } else { - for (int y = vH-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta)); - for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + vH : y); + for (int y = 0; y < stop; y++) { + int srcY; + if (wrap) { + srcY = (y + newDelta) % vH; // Wrap using modulo when `wrap` is true + } else { + srcY = y + newDelta; + } + newPxCol[y] = getPixelColorXY(x, srcY); } - for (int y = 0; y < vH; y++) setPixelColorXY(x, y, newPxCol[y]); + for (int y = 0; y < stop; y++) setPixelColorXY(x, y + start, newPxCol[y]); } } +// TODO: check if it works, used in FX rain and 2D spaceships + // move() - move all pixels in desired direction delta number of pixels // @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down // @param delta number of pixels to move diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7927269ed6..613b41fadf 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1146,7 +1146,7 @@ void Segment::blur(uint8_t blur_amount, bool smear) { #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; } From ca062140f3c17eb64928a5b47566bb7599828cb1 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 3 Oct 2024 19:39:39 +0000 Subject: [PATCH 32/37] removed todo. --- wled00/FX_2Dfcn.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 3ad061a8cf..6b3e6ef11a 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -475,8 +475,6 @@ void Segment::moveY(int delta, bool wrap) { } } -// TODO: check if it works, used in FX rain and 2D spaceships - // move() - move all pixels in desired direction delta number of pixels // @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down // @param delta number of pixels to move From eb5ad232a0e8dd5623051bc8d03c399fba318291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Sat, 5 Oct 2024 23:31:31 +0200 Subject: [PATCH 33/37] Minor tweaks and whitespace --- wled00/FX_2Dfcn.cpp | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 6b3e6ef11a..0e31083dfa 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -195,6 +195,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) 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 // if color is unscaled @@ -205,8 +206,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed unsigned groupLen = groupLength(); - if(groupLen > 1) - { + if (groupLen > 1) { int W = width(); int H = height(); x *= groupLen; // expand to physical pixels @@ -222,8 +222,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) } yY++; } - } - else { + } else { _setPixelColorXY_raw(x, y, col); } } @@ -293,7 +292,7 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) { const unsigned rows = vHeight(); uint32_t lastnew; uint32_t last; - if(blur_x) { + if (blur_x) { const uint8_t keepx = smear ? 255 : 255 - blur_x; const uint8_t seepx = blur_x >> (1 + smear); for (unsigned row = 0; row < rows; row++) { // blur rows (x direction) @@ -316,7 +315,7 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) { setPixelColorXY(cols-1, row, curnew); // set last pixel } } - if(blur_y) { + if (blur_y) { const uint8_t keepy = smear ? 255 : 255 - blur_y; const uint8_t seepy = blur_y >> (1 + smear); for (unsigned col = 0; col < cols; col++) { @@ -425,20 +424,16 @@ void Segment::moveX(int delta, bool wrap) { int newDelta; int stop = vW; int start = 0; - if(wrap) newDelta = (delta + vW) % vW; // +cols in case delta < 0 + if (wrap) newDelta = (delta + vW) % vW; // +cols in case delta < 0 else { - if(delta < 0) start = -delta; + if (delta < 0) start = absDelta; stop = vW - absDelta; newDelta = delta > 0 ? delta : 0; } for (int y = 0; y < vH; y++) { for (int x = 0; x < stop; x++) { - int srcX; - if (wrap) { - srcX = (x + newDelta) % vW; // Wrap using modulo when `wrap` is true - } else { - srcX = x + newDelta; - } + int srcX = x + newDelta; + if (wrap) srcX %= vW; // Wrap using modulo when `wrap` is true newPxCol[x] = getPixelColorXY(srcX, y); } for (int x = 0; x < stop; x++) setPixelColorXY(x + start, y, newPxCol[x]); @@ -455,20 +450,16 @@ void Segment::moveY(int delta, bool wrap) { int newDelta; int stop = vH; int start = 0; - if(wrap) newDelta = (delta + vH) % vH; // +rows in case delta < 0 + if (wrap) newDelta = (delta + vH) % vH; // +rows in case delta < 0 else { - if(delta < 0) start = -delta; + if (delta < 0) start = absDelta; stop = vH - absDelta; newDelta = delta > 0 ? delta : 0; } for (int x = 0; x < vW; x++) { for (int y = 0; y < stop; y++) { - int srcY; - if (wrap) { - srcY = (y + newDelta) % vH; // Wrap using modulo when `wrap` is true - } else { - srcY = y + newDelta; - } + int srcY = y + newDelta; + if (wrap) srcY %= vH; // Wrap using modulo when `wrap` is true newPxCol[y] = getPixelColorXY(x, srcY); } for (int y = 0; y < stop; y++) setPixelColorXY(x, y + start, newPxCol[y]); From be64930ebb97b554e3e03c6c178269cec791053a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Mon, 7 Oct 2024 16:50:51 +0200 Subject: [PATCH 34/37] Indentation and shadowed variable. --- wled00/FX_2Dfcn.cpp | 6 +++--- wled00/colors.cpp | 48 ++++++++++++++++++++++----------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 0e31083dfa..5a7dc76d3b 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -652,9 +652,9 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font default: return; } - uint32_t col = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND); + uint32_t c = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND); // pre-scale color for all pixels - col = color_fade(col, _segBri); + c = color_fade(c, _segBri); _colorScaled = true; for (int j = 0; j= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen if (((bits>>(j+(8-w))) & 0x01)) { // bit set - setPixelColorXY(x0, y0, col); + setPixelColorXY(x0, y0, c); } } _colorScaled = false; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index c059ea9db4..27c9c82891 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -98,30 +98,30 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) // 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) { - if (blendType == LINEARBLEND_NOWRAP) { - index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping - } - unsigned hi4 = byte(index) >> 4; - const CRGB* entry = (CRGB*)( (uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); - unsigned red1 = entry->r; - unsigned green1 = entry->g; - unsigned blue1 = entry->b; - if (blendType != NOBLEND) { - if (hi4 == 15) entry = &(pal[0]); - else ++entry; - unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 - unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max - red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; - green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; - blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; - } - if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted - uint32_t scale = brightness + 1; // adjust for rounding (bitshift) - red1 = (red1 * scale) >> 8; - green1 = (green1 * scale) >> 8; - blue1 = (blue1 * scale) >> 8; - } - return RGBW32(red1,green1,blue1,0); + if (blendType == LINEARBLEND_NOWRAP) { + index = (index*240) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping + } + unsigned hi4 = byte(index) >> 4; + const CRGB* entry = (CRGB*)((uint8_t*)(&(pal[0])) + (hi4 * sizeof(CRGB))); + unsigned red1 = entry->r; + unsigned green1 = entry->g; + unsigned blue1 = entry->b; + if (blendType != NOBLEND) { + if (hi4 == 15) entry = &(pal[0]); + else ++entry; + unsigned f2 = ((index & 0x0F) << 4) + 1; // +1 so we scale by 256 as a max value, then result can just be shifted by 8 + unsigned f1 = (257 - f2); // f2 is 1 minimum, so this is 256 max + red1 = (red1 * f1 + (unsigned)entry->r * f2) >> 8; + green1 = (green1 * f1 + (unsigned)entry->g * f2) >> 8; + blue1 = (blue1 * f1 + (unsigned)entry->b * f2) >> 8; + } + if (brightness < 255) { // note: zero checking could be done to return black but that is hardly ever used so it is omitted + uint32_t scale = brightness + 1; // adjust for rounding (bitshift) + red1 = (red1 * scale) >> 8; + green1 = (green1 * scale) >> 8; + blue1 = (blue1 * scale) >> 8; + } + return RGBW32(red1,green1,blue1,0); } void setRandomColor(byte* rgb) From 210191b251f0ebdca0d5d12f21278b03fe404755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Mon, 7 Oct 2024 20:19:07 +0200 Subject: [PATCH 35/37] Fix for realtime drawing on main segment --- wled00/e131.cpp | 4 ++++ wled00/udp.cpp | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 7c074759e7..bc26a0639e 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -39,6 +39,7 @@ void handleDDPPacket(e131_packet_t* p) { realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP); if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) { + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); for (unsigned i = start; i < stop; i++, c += ddpChannelsPerLed) { setRealtimePixel(i, data[c], data[c+1], data[c+2], ddpChannelsPerLed >3 ? data[c+3] : 0); } @@ -147,6 +148,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return; wChannel = (availDMXLen > 3) ? e131_data[dataOffset+3] : 0; + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); for (unsigned i = 0; i < totalLen; i++) setRealtimePixel(i, e131_data[dataOffset+0], e131_data[dataOffset+1], e131_data[dataOffset+2], wChannel); break; @@ -164,6 +166,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ strip.setBrightness(bri, true); } + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); for (unsigned i = 0; i < totalLen; i++) setRealtimePixel(i, e131_data[dataOffset+1], e131_data[dataOffset+2], e131_data[dataOffset+3], wChannel); break; @@ -308,6 +311,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ } } + if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); if (!is4Chan) { for (unsigned i = previousLeds; i < ledsTotal; i++) { setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 0ff39f1109..a6a0f6aa2f 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -687,10 +687,11 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w) b = gamma8(b); w = gamma8(w); } + uint32_t col = RGBW32(r,g,b,w); if (useMainSegmentOnly) { - strip.getMainSegment().setPixelColor(pix, r, g, b, w); // this expects that strip.getMainSegment().beginDraw() has been called in handleNotification() + strip.getMainSegment().setPixelColor(pix, col); // this expects that strip.getMainSegment().beginDraw() has been called in handleNotification() } else { - strip.setPixelColor(pix, r, g, b, w); + strip.setPixelColor(pix, col); } } } From ef1e24cec26cdb05b7a342e7a432f03692ca521e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Sat, 9 Nov 2024 10:42:49 +0100 Subject: [PATCH 36/37] Bugfix & code reduction - correctly clear segment spacing change - renamed Segment::setUp() to Segment::setGeometry() - removed WS2812FX::setSegment() - removed obsolete/unfunctional word clock usermod .cpp file --- .../usermod_word_clock_matrix.h | 128 ++++---- .../word-clock-matrix/word-clock-matrix.cpp | 305 ------------------ wled00/FX.h | 6 +- wled00/FX_fcn.cpp | 34 +- wled00/json.cpp | 16 +- wled00/set.cpp | 4 +- wled00/udp.cpp | 15 +- 7 files changed, 99 insertions(+), 409 deletions(-) delete mode 100644 usermods/word-clock-matrix/word-clock-matrix.cpp diff --git a/usermods/word-clock-matrix/usermod_word_clock_matrix.h b/usermods/word-clock-matrix/usermod_word_clock_matrix.h index 506c1275ef..82499c0ce1 100644 --- a/usermods/word-clock-matrix/usermod_word_clock_matrix.h +++ b/usermods/word-clock-matrix/usermod_word_clock_matrix.h @@ -31,14 +31,14 @@ class WordClockMatrix : public Usermod //strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); //select first two segments (background color + FX settable) - WS2812FX::Segment &seg = strip.getSegment(0); + Segment &seg = strip.getSegment(0); seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF))); strip.getSegment(0).setOption(0, false); strip.getSegment(0).setOption(2, false); //other segments are text for (int i = 1; i < 10; i++) { - WS2812FX::Segment &seg = strip.getSegment(i); + Segment &seg = strip.getSegment(i); seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF))); strip.getSegment(i).setOption(0, true); strip.setBrightness(64); @@ -80,61 +80,61 @@ class WordClockMatrix : public Usermod void displayTime(byte hour, byte minute) { bool isToHour = false; //true if minute > 30 - strip.setSegment(0, 0, 64); // background - strip.setSegment(1, 0, 2); //It is + strip.getSegment(0).setGeometry(0, 64); // background + strip.getSegment(1).setGeometry(0, 2); //It is - strip.setSegment(2, 0, 0); - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //past - strip.setSegment(6, 0, 0); //to - strip.setSegment(8, 0, 0); //disable o'clock + strip.getSegment(2).setGeometry(0, 0); + strip.getSegment(3).setGeometry(0, 0); //disable minutes + strip.getSegment(4).setGeometry(0, 0); //past + strip.getSegment(6).setGeometry(0, 0); //to + strip.getSegment(8).setGeometry(0, 0); //disable o'clock if (hour < 24) //valid time, display { if (minute == 30) { - strip.setSegment(2, 3, 6); //half - strip.setSegment(3, 0, 0); //minutes + strip.getSegment(2).setGeometry(3, 6); //half + strip.getSegment(3).setGeometry(0, 0); //minutes } else if (minute == 15 || minute == 45) { - strip.setSegment(3, 0, 0); //minutes + strip.getSegment(3).setGeometry(0, 0); //minutes } else if (minute == 10) { - //strip.setSegment(5, 6, 8); //ten + //strip.getSegment(5).setGeometry(6, 8); //ten } else if (minute == 5) { - //strip.setSegment(5, 16, 18); //five + //strip.getSegment(5).setGeometry(16, 18); //five } else if (minute == 0) { - strip.setSegment(3, 0, 0); //minutes + strip.getSegment(3).setGeometry(0, 0); //minutes //hourChime(); } else { - strip.setSegment(3, 18, 22); //minutes + strip.getSegment(3).setGeometry(18, 22); //minutes } //past or to? if (minute == 0) { //full hour - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //disable past - strip.setSegment(6, 0, 0); //disable to - strip.setSegment(8, 60, 64); //o'clock + strip.getSegment(3).setGeometry(0, 0); //disable minutes + strip.getSegment(4).setGeometry(0, 0); //disable past + strip.getSegment(6).setGeometry(0, 0); //disable to + strip.getSegment(8).setGeometry(60, 64); //o'clock } else if (minute > 34) { - //strip.setSegment(6, 22, 24); //to + //strip.getSegment(6).setGeometry(22, 24); //to //minute = 60 - minute; isToHour = true; } else { - //strip.setSegment(4, 24, 27); //past + //strip.getSegment(4).setGeometry(24, 27); //past //isToHour = false; } } @@ -143,68 +143,68 @@ class WordClockMatrix : public Usermod if (minute <= 4) { - strip.setSegment(3, 0, 0); //nothing - strip.setSegment(5, 0, 0); //nothing - strip.setSegment(6, 0, 0); //nothing - strip.setSegment(8, 60, 64); //o'clock + strip.getSegment(3).setGeometry(0, 0); //nothing + strip.getSegment(5).setGeometry(0, 0); //nothing + strip.getSegment(6).setGeometry(0, 0); //nothing + strip.getSegment(8).setGeometry(60, 64); //o'clock } else if (minute <= 9) { - strip.setSegment(5, 16, 18); // five past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(16, 18); // five past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 14) { - strip.setSegment(5, 6, 8); // ten past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(6, 8); // ten past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 19) { - strip.setSegment(5, 8, 12); // quarter past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(8, 12); // quarter past + strip.getSegment(3).setGeometry(0, 0); //minutes + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 24) { - strip.setSegment(5, 12, 16); // twenty past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(12, 16); // twenty past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 29) { - strip.setSegment(5, 12, 18); // twenty-five past - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(12, 18); // twenty-five past + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 34) { - strip.setSegment(5, 3, 6); // half past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past + strip.getSegment(5).setGeometry(3, 6); // half past + strip.getSegment(3).setGeometry(0, 0); //minutes + strip.getSegment(4).setGeometry(24, 27); //past } else if (minute <= 39) { - strip.setSegment(5, 12, 18); // twenty-five to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(12, 18); // twenty-five to + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 44) { - strip.setSegment(5, 12, 16); // twenty to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(12, 16); // twenty to + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 49) { - strip.setSegment(5, 8, 12); // quarter to - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(8, 12); // quarter to + strip.getSegment(3).setGeometry(0, 0); //minutes + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 54) { - strip.setSegment(5, 6, 8); // ten to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(6, 8); // ten to + strip.getSegment(6).setGeometry(22, 24); //to } else if (minute <= 59) { - strip.setSegment(5, 16, 18); // five to - strip.setSegment(6, 22, 24); //to + strip.getSegment(5).setGeometry(16, 18); // five to + strip.getSegment(6).setGeometry(22, 24); //to } //hours @@ -220,45 +220,45 @@ class WordClockMatrix : public Usermod switch (hour) { case 1: - strip.setSegment(7, 27, 29); + strip.getSegment(7).setGeometry(27, 29); break; //one case 2: - strip.setSegment(7, 35, 37); + strip.getSegment(7).setGeometry(35, 37); break; //two case 3: - strip.setSegment(7, 29, 32); + strip.getSegment(7).setGeometry(29, 32); break; //three case 4: - strip.setSegment(7, 32, 35); + strip.getSegment(7).setGeometry(32, 35); break; //four case 5: - strip.setSegment(7, 37, 40); + strip.getSegment(7).setGeometry(37, 40); break; //five case 6: - strip.setSegment(7, 43, 45); + strip.getSegment(7).setGeometry(43, 45); break; //six case 7: - strip.setSegment(7, 40, 43); + strip.getSegment(7).setGeometry(40, 43); break; //seven case 8: - strip.setSegment(7, 45, 48); + strip.getSegment(7).setGeometry(45, 48); break; //eight case 9: - strip.setSegment(7, 48, 50); + strip.getSegment(7).setGeometry(48, 50); break; //nine case 10: - strip.setSegment(7, 54, 56); + strip.getSegment(7).setGeometry(54, 56); break; //ten case 11: - strip.setSegment(7, 50, 54); + strip.getSegment(7).setGeometry(50, 54); break; //eleven case 12: - strip.setSegment(7, 56, 60); + strip.getSegment(7).setGeometry(56, 60); break; //twelve } selectWordSegments(true); - applyMacro(1); + applyPreset(1); } void timeOfDay() diff --git a/usermods/word-clock-matrix/word-clock-matrix.cpp b/usermods/word-clock-matrix/word-clock-matrix.cpp deleted file mode 100644 index 67c5b1e472..0000000000 --- a/usermods/word-clock-matrix/word-clock-matrix.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include "wled.h" -/* - * This v1 usermod file allows you to add own functionality to WLED more easily - * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality - * EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h) - * If you just need 8 bytes, use 2551-2559 (you do not need to increase EEPSIZE) - * - * Consider the v2 usermod API if you need a more advanced feature set! - */ - - -uint8_t minuteLast = 99; -int dayBrightness = 128; -int nightBrightness = 16; - -//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t) - -//gets called once at boot. Do all initialization that doesn't depend on network here -void userSetup() -{ -saveMacro(14, "A=128", false); -saveMacro(15, "A=64", false); -saveMacro(16, "A=16", false); - -saveMacro(1, "&FX=0&R=255&G=255&B=255", false); - -//strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); - - //select first two segments (background color + FX settable) - Segment &seg = strip.getSegment(0); - seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF))); - strip.getSegment(0).setOption(0, false); - strip.getSegment(0).setOption(2, false); - //other segments are text - for (int i = 1; i < 10; i++) - { - Segment &seg = strip.getSegment(i); - seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF))); - strip.getSegment(i).setOption(0, true); - strip.setBrightness(128); - } -} - -//gets called every time WiFi is (re-)connected. Initialize own network interfaces here -void userConnected() -{ -} - -void selectWordSegments(bool state) -{ - for (int i = 1; i < 10; i++) - { - //Segment &seg = strip.getSegment(i); - strip.getSegment(i).setOption(0, state); - // strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true); - //seg.mode = 12; - //seg.palette = 1; - //strip.setBrightness(255); - } - strip.getSegment(0).setOption(0, !state); -} - -void hourChime() -{ - //strip.resetSegments(); - selectWordSegments(true); - colorUpdated(CALL_MODE_FX_CHANGED); - //savePreset(255); - selectWordSegments(false); - //strip.getSegment(0).setOption(0, true); - strip.getSegment(0).setOption(2, true); - applyPreset(12); - colorUpdated(CALL_MODE_FX_CHANGED); -} - -void displayTime(byte hour, byte minute) -{ - bool isToHour = false; //true if minute > 30 - strip.setSegment(0, 0, 64); // background - strip.setSegment(1, 0, 2); //It is - - strip.setSegment(2, 0, 0); - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //past - strip.setSegment(6, 0, 0); //to - strip.setSegment(8, 0, 0); //disable o'clock - - if (hour < 24) //valid time, display - { - if (minute == 30) - { - strip.setSegment(2, 3, 6); //half - strip.setSegment(3, 0, 0); //minutes - } - else if (minute == 15 || minute == 45) - { - strip.setSegment(3, 0, 0); //minutes - } - else if (minute == 10) - { - //strip.setSegment(5, 6, 8); //ten - } - else if (minute == 5) - { - //strip.setSegment(5, 16, 18); //five - } - else if (minute == 0) - { - strip.setSegment(3, 0, 0); //minutes - //hourChime(); - } - else - { - strip.setSegment(3, 18, 22); //minutes - } - - //past or to? - if (minute == 0) - { //full hour - strip.setSegment(3, 0, 0); //disable minutes - strip.setSegment(4, 0, 0); //disable past - strip.setSegment(6, 0, 0); //disable to - strip.setSegment(8, 60, 64); //o'clock - } - else if (minute > 34) - { - //strip.setSegment(6, 22, 24); //to - //minute = 60 - minute; - isToHour = true; - } - else - { - //strip.setSegment(4, 24, 27); //past - //isToHour = false; - } - } - else - { //temperature display - } - - //byte minuteRem = minute %10; - - if (minute <= 4) - { - strip.setSegment(3, 0, 0); //nothing - strip.setSegment(5, 0, 0); //nothing - strip.setSegment(6, 0, 0); //nothing - strip.setSegment(8, 60, 64); //o'clock - } - else if (minute <= 9) - { - strip.setSegment(5, 16, 18); // five past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 14) - { - strip.setSegment(5, 6, 8); // ten past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 19) - { - strip.setSegment(5, 8, 12); // quarter past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 24) - { - strip.setSegment(5, 12, 16); // twenty past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 29) - { - strip.setSegment(5, 12, 18); // twenty-five past - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 34) - { - strip.setSegment(5, 3, 6); // half past - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(4, 24, 27); //past - } - else if (minute <= 39) - { - strip.setSegment(5, 12, 18); // twenty-five to - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 44) - { - strip.setSegment(5, 12, 16); // twenty to - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 49) - { - strip.setSegment(5, 8, 12); // quarter to - strip.setSegment(3, 0, 0); //minutes - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 54) - { - strip.setSegment(5, 6, 8); // ten to - strip.setSegment(6, 22, 24); //to - } - else if (minute <= 59) - { - strip.setSegment(5, 16, 18); // five to - strip.setSegment(6, 22, 24); //to - } - - //hours - if (hour > 23) - return; - if (isToHour) - hour++; - if (hour > 12) - hour -= 12; - if (hour == 0) - hour = 12; - - switch (hour) - { - case 1: - strip.setSegment(7, 27, 29); - break; //one - case 2: - strip.setSegment(7, 35, 37); - break; //two - case 3: - strip.setSegment(7, 29, 32); - break; //three - case 4: - strip.setSegment(7, 32, 35); - break; //four - case 5: - strip.setSegment(7, 37, 40); - break; //five - case 6: - strip.setSegment(7, 43, 45); - break; //six - case 7: - strip.setSegment(7, 40, 43); - break; //seven - case 8: - strip.setSegment(7, 45, 48); - break; //eight - case 9: - strip.setSegment(7, 48, 50); - break; //nine - case 10: - strip.setSegment(7, 54, 56); - break; //ten - case 11: - strip.setSegment(7, 50, 54); - break; //eleven - case 12: - strip.setSegment(7, 56, 60); - break; //twelve - } - -selectWordSegments(true); -applyMacro(1); -} - -void timeOfDay() { -// NOT USED: use timed macros instead - //Used to set brightness dependant of time of day - lights dimmed at night - - //monday to thursday and sunday - - if ((weekday(localTime) == 6) | (weekday(localTime) == 7)) { - if (hour(localTime) > 0 | hour(localTime) < 8) { - strip.setBrightness(nightBrightness); - } - else { - strip.setBrightness(dayBrightness); - } - } - else { - if (hour(localTime) < 6 | hour(localTime) >= 22) { - strip.setBrightness(nightBrightness); - } - else { - strip.setBrightness(dayBrightness); - } - } -} - -//loop. You can use "if (WLED_CONNECTED)" to check for successful connection -void userLoop() -{ - if (minute(localTime) != minuteLast) - { - updateLocalTime(); - //timeOfDay(); - minuteLast = minute(localTime); - displayTime(hour(localTime), minute(localTime)); - if (minute(localTime) == 0){ - hourChime(); - } - if (minute(localTime) == 1){ - //turn off background segment; - strip.getSegment(0).setOption(2, false); - //applyPreset(255); - } - } -} diff --git a/wled00/FX.h b/wled00/FX.h index c06332c765..d56c0fa99b 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -539,6 +539,7 @@ typedef struct Segment { inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels inline uint16_t groupLength() const { return grouping + spacing; } inline uint8_t getLightCapabilities() const { return _capabilities; } + inline void deactivate() { setGeometry(0,0); } inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; } inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; } @@ -554,14 +555,14 @@ typedef struct Segment { static void handleRandomPalette(); void beginDraw(); // set up parameters for current effect - void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1); + void setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t m12=0); bool setColor(uint8_t slot, uint32_t c); //returns true if changed void setCCT(uint16_t k); void setOpacity(uint8_t o); void setOption(uint8_t n, bool val); void setMode(uint8_t fx, bool loadDefaults = false); void setPalette(uint8_t pal); - uint8_t differs(Segment& b) const; + uint8_t differs(const Segment& b) const; void refreshLightCapabilities(); // runtime data functions @@ -783,7 +784,6 @@ class WS2812FX { // 96 bytes setBrightness(uint8_t b, bool direct = false), // sets strip brightness setRange(uint16_t i, uint16_t i2, uint32_t col), // used for clock overlay purgeSegments(), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint) - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1), setMainSegmentId(unsigned n = 0), resetSegments(), // marks all segments for reset makeAutoSegments(bool forceReset = false), // will create segments based on configured outputs diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 613b41fadf..69b0c028a6 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -489,8 +489,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 @@ -498,11 +500,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; @@ -511,6 +521,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); @@ -993,7 +1004,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; @@ -1595,19 +1606,6 @@ 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 diff --git a/wled00/json.cpp b/wled00/json.cpp index 0df7294c85..64b9ddd8db 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -34,7 +34,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) //DEBUG_PRINTLN(F("-- JSON deserialize segment.")); Segment& seg = strip.getSegment(id); //DEBUG_PRINTF_P(PSTR("-- Original segment: %p (%p)\n"), &seg, seg.data); - Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor) + const Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor) //DEBUG_PRINTF_P(PSTR("-- Duplicate segment: %p (%p)\n"), &prev, prev.data); int start = elem["start"] | seg.start; @@ -96,17 +96,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) uint16_t of = seg.offset; uint8_t soundSim = elem["si"] | seg.soundSim; uint8_t map1D2D = elem["m12"] | seg.map1D2D; - - if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps - - seg.map1D2D = constrain(map1D2D, 0, 7); + uint8_t set = elem[F("set")] | seg.set; + seg.set = constrain(set, 0, 3); seg.soundSim = constrain(soundSim, 0, 3); - uint8_t set = elem[F("set")] | seg.set; - seg.set = constrain(set, 0, 3); - - int len = 1; - if (stop > start) len = stop - start; + int len = (stop > start) ? stop - start : 1; int offset = elem[F("of")] | INT32_MAX; if (offset != INT32_MAX) { int offsetAbs = abs(offset); @@ -117,7 +111,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) if (stop > start && of > len -1) of = len -1; // update segment (delete if necessary) - seg.setUp(start, stop, grp, spc, of, startY, stopY); // strip needs to be suspended for this to work without issues + seg.setGeometry(start, stop, grp, spc, of, startY, stopY, map1D2D); // strip needs to be suspended for this to work without issues if (newSeg) seg.refreshLightCapabilities(); // fix for #3403 diff --git a/wled00/set.cpp b/wled00/set.cpp index cf3a07dd02..15981d30dc 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -874,7 +874,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) if (pos > 0) { spcI = std::max(0,getNumVal(&req, pos)); } - strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY); + strip.suspend(); // must suspend strip operations before changing geometry + selseg.setGeometry(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY, selseg.map1D2D); + strip.resume(); pos = req.indexOf(F("RV=")); //Segment reverse if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0'; diff --git a/wled00/udp.cpp b/wled00/udp.cpp index a6a0f6aa2f..47398bc8a2 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -260,11 +260,12 @@ void parseNotifyPacket(uint8_t *udpIn) { // are we syncing bounds and slave has more active segments than master? if (receiveSegmentBounds && numSrcSegs < strip.getActiveSegmentsNum()) { DEBUG_PRINTLN(F("Removing excessive segments.")); - for (size_t i=strip.getSegmentsNum(); i>numSrcSegs; i--) { - if (strip.getSegment(i).isActive()) { - strip.setSegment(i-1,0,0); // delete segment - } + strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" + for (size_t i=strip.getSegmentsNum(); i>numSrcSegs && i>0; i--) { + Segment &seg = strip.getSegment(i-1); + if (seg.isActive()) seg.deactivate(); // delete segment } + strip.resume(); } size_t inactiveSegs = 0; for (size_t i = 0; i < numSrcSegs && i < strip.getMaxSegments(); i++) { @@ -300,7 +301,7 @@ void parseNotifyPacket(uint8_t *udpIn) { if (!receiveSegmentOptions) { DEBUG_PRINTF_P(PSTR("Set segment w/o options: %d [%d,%d;%d,%d]\n"), id, (int)start, (int)stop, (int)startY, (int)stopY); strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" - selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); + selseg.setGeometry(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY, selseg.map1D2D); strip.resume(); continue; // we do receive bounds, but not options } @@ -342,12 +343,12 @@ void parseNotifyPacket(uint8_t *udpIn) { if (receiveSegmentBounds) { DEBUG_PRINTF_P(PSTR("Set segment w/ options: %d [%d,%d;%d,%d]\n"), id, (int)start, (int)stop, (int)startY, (int)stopY); strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" - selseg.setUp(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY); + selseg.setGeometry(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY, selseg.map1D2D); strip.resume(); } else { DEBUG_PRINTF_P(PSTR("Set segment grouping: %d [%d,%d]\n"), id, (int)udpIn[5+ofs], (int)udpIn[6+ofs]); strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case" - selseg.setUp(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY); + selseg.setGeometry(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY, selseg.map1D2D); strip.resume(); } } From 0a05611e1d5a8dcccf515ba616042692197258f6 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Tue, 26 Nov 2024 20:59:36 +0100 Subject: [PATCH 37/37] more improvements to setPixelColor - code is a bit cleaner and faster as well - chaning array access to pointer access in bus_manager makes it a few instructions faster - changed getNumberOfPins and getNumberOfChannels to return 32bit values, saving the unnecessary 8bit conversion --- wled00/FX_2Dfcn.cpp | 41 +++++++++++++++++++++-------------------- wled00/bus_manager.cpp | 26 ++++++++++++++++---------- wled00/bus_manager.h | 6 +++--- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index adb9f8bcad..ba0c69322c 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -156,21 +156,26 @@ uint16_t IRAM_ATTR_YN Segment::XY(int x, int y) // raw setColor function without checks (checks are done in setPixelColorXY()) void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(int& x, int& y, uint32_t& col) { + const int baseX = start + x; + const int baseY = startY + y; #ifndef WLED_DISABLE_MODE_BLEND // if blending modes, blend with underlying pixel - if (_modeBlend) col = color_blend(strip.getPixelColorXY(start + x, startY + y), col, 0xFFFFU - progress(), true); + if (_modeBlend) col = color_blend(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress(), true); #endif - strip.setPixelColorXY(start + x, startY + y, col); - if (mirror) { //set the corresponding horizontally mirrored pixel - if (transpose) strip.setPixelColorXY(start + x, startY + height() - y - 1, col); - else strip.setPixelColorXY(start + width() - x - 1, startY + y, col); - } - if (mirror_y) { //set the corresponding vertically mirrored pixel - if (transpose) strip.setPixelColorXY(start + width() - x - 1, startY + y, col); - else strip.setPixelColorXY(start + x, startY + height() - y - 1, col); - } - if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel - strip.setPixelColorXY(start + width() - x - 1, startY + height() - y - 1, col); + strip.setPixelColorXY(baseX, baseY, col); + + // Apply mirroring + if (mirror || mirror_y) { + auto setMirroredPixel = [&](int mx, int my) { + strip.setPixelColorXY(mx, my, col); + }; + + const int mirrorX = start + width() - x - 1; + const int mirrorY = startY + height() - y - 1; + + if (mirror) setMirroredPixel(transpose ? baseX : mirrorX, transpose ? mirrorY : baseY); + if (mirror_y) setMirroredPixel(transpose ? mirrorX : baseX, transpose ? baseY : mirrorY); + if (mirror && mirror_y) setMirroredPixel(mirrorX, mirrorY); } } @@ -196,16 +201,12 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) int H = height(); x *= groupLen; // expand to physical pixels y *= groupLen; // expand to physical pixels - int yY = y; - for (int j = 0; j < grouping; j++) { // groupping vertically - if (yY >= H) break; - int xX = x; - for (int g = 0; g < grouping; g++) { // groupping horizontally - if (xX >= W) break; // we have reached X dimension's end + const int maxY = std::min(y + grouping, H); + const int maxX = std::min(x + grouping, W); + for (int yY = y; yY < maxY; yY++) { + for (int xX = x; xX < maxX; xX++) { _setPixelColorXY_raw(xX, yY, col); - xX++; } - yY++; } } else { _setPixelColorXY_raw(x, y, col); diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 404c334495..941135497f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -308,20 +308,20 @@ void BusDigital::setStatusPixel(uint32_t c) { void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { if (!_valid) return; - uint8_t cctWW = 0, cctCW = 0; if (hasWhite()) c = autoWhiteCalc(c); if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT if (_data) { size_t offset = pix * getNumberOfChannels(); + uint8_t* dataptr = _data + offset; if (hasRGB()) { - _data[offset++] = R(c); - _data[offset++] = G(c); - _data[offset++] = B(c); + *dataptr++ = R(c); + *dataptr++ = G(c); + *dataptr++ = B(c); } - if (hasWhite()) _data[offset++] = W(c); + if (hasWhite()) *dataptr++ = W(c); // unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT // we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio) - if (hasCCT()) _data[offset] = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it + if (hasCCT()) *dataptr = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it } else { if (_reversed) pix = _len - pix -1; pix += _skip; @@ -336,8 +336,14 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break; } } - if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); - PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW); + uint16_t wwcw = 0; + if (hasCCT()) { + uint8_t cctWW = 0, cctCW = 0; + Bus::calculateCCT(c, cctWW, cctCW); + wwcw = (cctCW<<8) | cctWW; + } + + PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw); } } @@ -345,7 +351,7 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) { uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { if (!_valid) return 0; if (_data) { - size_t offset = pix * getNumberOfChannels(); + const size_t offset = pix * getNumberOfChannels(); uint32_t c; if (!hasRGB()) { c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]); @@ -356,7 +362,7 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { } else { if (_reversed) pix = _len - pix -1; pix += _skip; - unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); + const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder); uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri); if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs unsigned r = R(c); diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index e25a068498..49077f27c6 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -110,7 +110,7 @@ class Bus { inline void setStart(uint16_t start) { _start = start; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } - inline uint8_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } + inline uint32_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } inline uint16_t getStart() const { return _start; } inline uint8_t getType() const { return _type; } inline bool isOk() const { return _valid; } @@ -119,8 +119,8 @@ class Bus { inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes - static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK - static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } + static constexpr uint32_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK + static constexpr uint32_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr bool hasRGB(uint8_t type) { return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF); }