diff --git a/usermods/BH1750_v2/usermod_bh1750.h b/usermods/BH1750_v2/usermod_bh1750.h index 5b5fa25f..a6e7d653 100644 --- a/usermods/BH1750_v2/usermod_bh1750.h +++ b/usermods/BH1750_v2/usermod_bh1750.h @@ -76,7 +76,7 @@ private: bool sensorFound = false; // Home Assistant and MQTT - String mqttLuminanceTopic = FPSTR(""); + String mqttLuminanceTopic; bool mqttInitialized = false; bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index cf0aca22..dd47e767 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -626,7 +626,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) { for (int i=0; i> 8; uint16_t cy = rippleorigin & 0xFF; uint8_t mag = scale8(sin8((propF>>2)), amp); - if (propI > 0) SEGMENT.draw_circle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag)); + if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true); } else #endif { @@ -6271,8 +6271,8 @@ uint16_t mode_2Dfloatingblobs(void) { } } uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); - if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->x[i], blob->y[i], roundf(blob->r[i]), c); - else SEGMENT.setPixelColorXY(blob->x[i], blob->y[i], c); + if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c); + else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c); // move x if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f)); diff --git a/wled00/FX.h b/wled00/FX.h index b8864db4..4772dbe7 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -122,6 +122,10 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn. #define PURPLE (uint32_t)0x400080 #define ORANGE (uint32_t)0xFF3000 #define PINK (uint32_t)0xFF1493 +#define GREY (uint32_t)0x808080 +#define GRAY GREY +#define DARKGREY (uint32_t)0x333333 +#define DARKGRAY DARKGREY #define ULTRAWHITE (uint32_t)0xFFFFFFFF #define DARKSLATEGRAY (uint32_t)0x2F4F4F #define DARKSLATEGREY (uint32_t)0x2F4F4F @@ -614,11 +618,11 @@ typedef struct Segment { // 1D strip uint16_t virtualLength(void) const; void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color - void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline - void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline + inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline void setPixelColor(float i, uint32_t c, bool aa = true); - 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); } - void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } + 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); } + inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); } uint32_t __attribute__((pure)) getPixelColor(int i); // WLEDMM attribute added // 1D support functions (some implement 2D as well) void blur(uint8_t, bool smear = false); @@ -626,10 +630,10 @@ typedef struct Segment { void fade_out(uint8_t r); void fadeToBlackBy(uint8_t fadeBy); void blendPixelColor(int n, uint32_t color, uint8_t blend); - void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } + inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } void addPixelColor(int n, uint32_t color, bool fast = false); - void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline - void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline + 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); } // automatically inline + inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline void fadePixelColor(uint16_t n, uint8_t fade); uint8_t get_random_wheel_index(uint8_t pos); uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); @@ -665,6 +669,7 @@ typedef struct Segment { 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)); } //#ifdef WLED_USE_AA_PIXELS void setPixelColorXY(float x, float y, uint32_t c, bool aa = true, bool fast=true); inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } @@ -673,10 +678,10 @@ typedef struct Segment { uint32_t __attribute__((pure)) getPixelColorXY(int x, int y); // 2D support functions void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); - 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 blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); } void addPixelColorXY(int x, int y, uint32_t color, bool fast = false); - 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); } // automatically 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 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); } // automatically inline + inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade); void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight) void blurRow(uint32_t row, fract8 blur_amount, bool smear = false); @@ -684,18 +689,20 @@ typedef struct Segment { 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); - void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); - void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); - void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); - void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic 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) { 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); + inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); } + void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false); + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft); } // automatic inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0); - void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline + inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0); - void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.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) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline void wu_pixel(uint32_t x, uint32_t y, CRGB c); - void blur1d(fract8 blur_amount); // blur all rows in 1 dimension + //void blur1d(fract8 blur_amount); // blur all rows in 1 dimension void blur2d(fract8 blur_amount) { blur(blur_amount); } - void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } + inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } void nscale8(uint8_t scale); bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx #else @@ -704,6 +711,7 @@ typedef struct Segment { 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)); } //#ifdef WLED_USE_AA_PIXELS inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); } @@ -722,9 +730,12 @@ typedef struct Segment { 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) {} - inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} - inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} - inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} + 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) {} + inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {} + inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {} + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false) {} + inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} 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) {} diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 1d217ff0..061d8c19 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -427,56 +427,38 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) { } // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) -void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { //WLEDMM: use fast types - const uint_fast16_t cols = virtualWidth(); - const uint_fast16_t rows = virtualHeight(); - const uint_fast16_t dim1 = vertical ? rows : cols; - const uint_fast16_t dim2 = vertical ? cols : rows; +void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { + if (!isActive() || blur_amount == 0) return; // not active + const int cols = virtualWidth(); + const int rows = virtualHeight(); + const int dim1 = vertical ? rows : cols; + const int dim2 = vertical ? cols : rows; if (i >= dim2) return; const float seep = blur_amount/255.f; const float keep = 3.f - 2.f*seep; // 1D box blur - CRGB tmp[dim1]; - for (uint_fast16_t j = 0; j < dim1; j++) { - uint_fast16_t x = vertical ? i : j; - uint_fast16_t y = vertical ? j : i; - int_fast16_t xp = vertical ? x : x-1; // "signed" to prevent underflow - int_fast16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow - uint_fast16_t xn = vertical ? x : x+1; - uint_fast16_t yn = vertical ? y+1 : y; - CRGB curr = getPixelColorXY(x,y); - CRGB prev = (xp<0 || yp<0) ? CRGB::Black : getPixelColorXY(xp,yp); - CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : getPixelColorXY(xn,yn); - uint16_t r, g, b; - r = (curr.r*keep + (prev.r + next.r)*seep) / 3; - g = (curr.g*keep + (prev.g + next.g)*seep) / 3; - b = (curr.b*keep + (prev.b + next.b)*seep) / 3; - tmp[j] = CRGB(r,g,b); + uint32_t out[dim1], in[dim1]; + for (int j = 0; j < dim1; j++) { + int x = vertical ? i : j; + int y = vertical ? j : i; + in[j] = getPixelColorXY(x, y); } - for (uint_fast16_t j = 0; j < dim1; j++) { - uint_fast16_t x = vertical ? i : j; - uint_fast16_t y = vertical ? j : i; - setPixelColorXY((int)x, (int)y, tmp[j]); + for (int j = 0; j < dim1; j++) { + uint32_t curr = in[j]; + uint32_t prev = j > 0 ? in[j-1] : BLACK; + uint32_t next = j < dim1-1 ? in[j+1] : BLACK; + uint8_t r, g, b, w; + r = (R(curr)*keep + (R(prev) + R(next))*seep) / 3; + g = (G(curr)*keep + (G(prev) + G(next))*seep) / 3; + b = (B(curr)*keep + (B(prev) + B(next))*seep) / 3; + w = (W(curr)*keep + (W(prev) + W(next))*seep) / 3; + out[j] = RGBW32(r,g,b,w); + } + for (int j = 0; j < dim1; j++) { + int x = vertical ? i : j; + int y = vertical ? j : i; + setPixelColorXY(x, y, out[j]); } -} - -// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. -// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors. -// -// 0 = no spread at all -// 64 = moderate spreading -// 172 = maximum smooth, even spreading -// -// 173..255 = wider spreading, but increasing flicker -// -// Total light is NOT entirely conserved, so many repeated -// calls to 'blur' will also result in the light fading, -// eventually all the way to black; this is by design so that -// it can be used to (slowly) clear the LEDs to black. - -void Segment::blur1d(fract8 blur_amount) { //WLEDMM: use fast types - const uint_fast16_t rows = virtualHeight(); - for (uint_fast16_t y = 0; y < rows; y++) blurRow(y, blur_amount); } void Segment::moveX(int8_t delta, bool wrap) { @@ -533,37 +515,71 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { } } -void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { - if (!isActive()) return; // not active - // 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); - x++; - if (d > 0) { - y--; - d += 4 * (x - y) + 10; - } else { - d += 4 * x + 6; +void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) { + if (!isActive() || radius == 0) return; // not active + if (soft) { + // Xiaolin Wu’s algorithm + int rsq = radius*radius; + int x = 0; + int y = radius; + unsigned oldFade = 0; + while (x < y) { + float yf = sqrtf(float(rsq - x*x)); // needs to be floating point + unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep + if (oldFade > fade) y--; + oldFade = fade; + setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true)); + setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true)); + setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true)); + setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true)); + setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true)); + setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true)); + setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true)); + setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true)); + setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true)); + setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true)); + setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true)); + setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true)); + setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true)); + setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true)); + setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true)); + setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true)); + x++; + } + } else { + // 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); + x++; + if (d > 0) { + y--; + d += 4 * (x - y) + 10; + } else { + d += 4 * x + 6; + } } } } // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs -void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { - if (!isActive()) return; // not active - const uint16_t cols = virtualWidth(); - const uint16_t rows = virtualHeight(); - for (int16_t y = -radius; y <= radius; y++) { - for (int16_t x = -radius; x <= radius; x++) { +void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) { + if (!isActive() || radius == 0) return; // not active + // draw soft bounding circle + if (soft) drawCircle(cx, cy, radius, col, soft); + // 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 && int16_t(cx)+x>=0 && int16_t(cy)+y>=0 && int16_t(cx)+x= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; - const int16_t dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2; - for (;;) { - setPixelColorXY(x0,y0,c); - if (x0==x1 && y0==y1) break; - e2 = err; - if (e2 >-dx) { err -= dy; x0 += sx; } - if (e2 < dy) { err += dx; y0 += sy; } + + const int dx = abs(x1-x0), sx = x0 dx; + if (steep) { + // we need to go along longest dimension + std::swap(x0,y0); + std::swap(x1,y1); + } + if (x0 > x1) { + // we need to go in increasing fashion + std::swap(x0,x1); + std::swap(y0,y1); + } + float gradient = x1-x0 == 0 ? 1.0f : float(y1-y0) / float(x1-x0); + float intersectY = y0; + for (int x = x0; x <= x1; x++) { + unsigned keep = float(0xFFFF) * (intersectY-int(intersectY)); // how much color to keep + unsigned seep = 0xFFFF - keep; // how much background to keep + int y = int(intersectY); + if (steep) std::swap(x,y); // temporarily swap if steep + // pixel coverage is determined by fractional part of y co-ordinate + setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep, true)); + setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep, true)); + intersectY += gradient; + if (steep) std::swap(x,y); // restore if steep + } + } else { + // Bresenham's algorithm + int err = (dx>dy ? dx : -dy)/2; // error direction + for (;;) { + setPixelColorXY(x0, y0, c); + if (x0==x1 && y0==y1) break; + int e2 = err; + if (e2 >-dx) { err -= dy; x0 += sx; } + if (e2 < dy) { err += dx; y0 += sy; } + } } } diff --git a/wled00/data/index.js b/wled00/data/index.js index 1fcbfcaf..e89ad78e 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -247,6 +247,7 @@ function onLoad() selectSlot(0); updateTablinks(0); + handleLocationHash(); pmtLS = localStorage.getItem('wledPmt'); // Load initial data @@ -279,7 +280,6 @@ function updateTablinks(tabI) { var tablinks = gEBCN("tablinks"); for (var i of tablinks) i.classList.remove('active'); - if (pcMode) return; tablinks[tabI].classList.add('active'); } @@ -290,6 +290,21 @@ function openTab(tabI, force = false) _C.classList.toggle('smooth', false); _C.style.setProperty('--i', iSlide); updateTablinks(tabI); + switch (tabI) { + case 0: window.location.hash = "Colors"; break; + case 1: window.location.hash = "Effects"; break; + case 2: window.location.hash = "Segments"; break; + case 3: window.location.hash = "Presets"; break; + } +} + +function handleLocationHash() { + switch (window.location.hash) { + case "#Colors": openTab(0); break; + case "#Effects": openTab(1); break; + case "#Segments": openTab(2); break; + case "#Presets": openTab(3); break; + } } var timeout; @@ -3615,12 +3630,11 @@ function togglePcMode(fromB = false) if (fromB) { pcModeA = !pcModeA; localStorage.setItem('pcm', pcModeA); + openTab(0, true); } pcMode = (wW >= 1024) && pcModeA; if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size() - openTab(0, true); - updateTablinks(0); gId('buttonPcm').className = (pcMode) ? "active":""; gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto"; sCol('--bh', gId('bot').clientHeight + "px"); @@ -3652,6 +3666,7 @@ size(); _C.style.setProperty('--n', N); window.addEventListener('resize', size, true); +window.addEventListener('hashchange', handleLocationHash); _C.addEventListener('mousedown', lock, false); _C.addEventListener('touchstart', lock, false); diff --git a/wled00/json.cpp b/wled00/json.cpp index 5456b455..444f0e4d 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -340,8 +340,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId) seg.fill(BLACK); // WLEDMM why now? } - uint16_t start = 0, stop = 0; - byte set = 0; //0 nothing set, 1 start set, 2 range set + start = 0, stop = 0; + set = 0; //0 nothing set, 1 start set, 2 range set for (size_t i = 0; i < iarr.size(); i++) { if(iarr[i].is()) { @@ -1554,10 +1554,20 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) uint16_t used = strip.getLengthTotal(); uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS - char buffer[2000]; - strcpy_P(buffer, PSTR("{\"leds\":[")); - obuf = buffer; - olen = 9; +#ifndef WLED_DISABLE_2D + if (strip.isMatrix) { + // ignore anything behid matrix (i.e. extra strip) + used = Segment::maxWidth*Segment::maxHeight; // always the size of matrix (more or less than strip.getLengthTotal()) + n = 1; + if (used > MAX_LIVE_LEDS) n = 2; + if (used > MAX_LIVE_LEDS*4) n = 4; + } +#endif + + DynamicBuffer buffer(9 + (9*MAX_LIVE_LEDS) + 7 + 5 + 6 + 5 + 6 + 5 + 2); + char* buf = buffer.data(); // assign buffer for oappnd() functions + strncpy_P(buffer.data(), PSTR("{\"leds\":["), buffer.size()); + buf += 9; // sizeof(PSTR()) from last line for (size_t i= 0; i < used; i += n) { @@ -1575,20 +1585,21 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient) g = qadd8(w, G(c)); b = qadd8(w, B(c)); } - olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0)); + buf += sprintf_P(buf, PSTR("\"%06X\","), RGBW32(r,g,b,0)); } - olen -= 1; - oappend((const char*)F("],\"n\":")); - oappendi(n); - oappend("}"); + buf--; // remove last comma + buf += sprintf_P(buf, PSTR("],\"n\":%d"), n); + (*buf++) = '}'; + (*buf++) = 0; + if (request) { - request->send(200, "application/json", buffer); + request->send(200, "application/json", toString(std::move(buffer))); } #ifdef WLED_ENABLE_WEBSOCKETS else { - wsc->text(obuf, olen); + wsc->text(toString(std::move(buffer))); } - #endif + #endif return true; } #endif diff --git a/wled00/overlay.cpp b/wled00/overlay.cpp index cfee7e81..6ffc6c8e 100644 --- a/wled00/overlay.cpp +++ b/wled00/overlay.cpp @@ -25,11 +25,11 @@ void _overlayAnalogClock() { if (secondPixel < analogClock12pixel) { - strip.setRange(analogClock12pixel, overlayMax, 0xFF0000); - strip.setRange(overlayMin, secondPixel, 0xFF0000); + strip.setRange(analogClock12pixel, overlayMax, color_fade(0xFF0000, bri)); + strip.setRange(overlayMin, secondPixel, color_fade(0xFF0000, bri)); } else { - strip.setRange(analogClock12pixel, secondPixel, 0xFF0000); + strip.setRange(analogClock12pixel, secondPixel, color_fade(0xFF0000, bri)); } } if (analogClock5MinuteMarks) @@ -38,12 +38,12 @@ void _overlayAnalogClock() { int pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i); if (pix > overlayMax) pix -= overlaySize; - strip.setPixelColor(pix, 0x00FFAA); + strip.setPixelColor(pix, color_fade(0x00FFAA, bri)); } } - if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); - strip.setPixelColor(minutePixel, 0x00FF00); - strip.setPixelColor(hourPixel, 0x0000FF); + if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, color_fade(0xFF0000, bri)); + strip.setPixelColor(minutePixel, color_fade(0x00FF00, bri)); + strip.setPixelColor(hourPixel, color_fade(0x0000FF, bri)); } diff --git a/wled00/util.cpp b/wled00/util.cpp index 1bcf2ad0..87e14c2c 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -267,8 +267,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe } else return 0; } - if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) { - snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode); + if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) { + snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode); dest[maxLen-1] = '\0'; return strlen(dest); } diff --git a/wled00/ws.cpp b/wled00/ws.cpp index 5b8bda42..22c5afd2 100644 --- a/wled00/ws.cpp +++ b/wled00/ws.cpp @@ -108,7 +108,6 @@ void sendDataWs(AsyncWebSocketClient * client) { DEBUG_PRINTF("sendDataWs\n"); if (!ws.count()) return; - AsyncWebSocketMessageBuffer * buffer; if (!requestJSONBufferLock(12)) { if (client) { @@ -138,17 +137,7 @@ void sendDataWs(AsyncWebSocketClient * client) // DEBUG_PRINTF("%s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM #endif if (len < 1) return; // WLEDMM do not allocate 0 size buffer - - // WLEDMM use exceptions to catch out-of-memory errors - #if __cpp_exceptions - try{ - buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266 - } catch(...) { - buffer = nullptr; - } - #else - buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266 - #endif + AsyncWebSocketBuffer buffer(len); #ifdef ESP8266 size_t heap2 = ESP.getFreeHeap(); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); @@ -161,24 +150,19 @@ void sendDataWs(AsyncWebSocketClient * client) USER_PRINTLN(F("WS buffer allocation failed.")); ws.closeAll(1013); //code 1013 = temporary overload, try again later ws.cleanupClients(0); //disconnect all clients to release memory - ws._cleanBuffers(); errorFlag = ERR_LOW_WS_MEM; return; //out of memory } - - buffer->lock(); - serializeJson(doc, (char *)buffer->get(), len); + serializeJson(doc, (char *)buffer.data(), len); DEBUG_PRINT(F("Sending WS data ")); if (client) { - client->text(buffer); + client->text(std::move(buffer)); DEBUG_PRINTLN(F("to a single client.")); } else { - ws.textAll(buffer); + ws.textAll(std::move(buffer)); DEBUG_PRINTLN(F("to multiple clients.")); } - buffer->unlock(); - ws._cleanBuffers(); releaseJSONBufferLock(); } @@ -227,32 +211,21 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" #endif size_t pos = (strip.isMatrix ? 4 : 2); size_t bufSize = pos + (used/n)*3; - - if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen - //AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize); - // WLEDMM protect against exceptions due to low memory - AsyncWebSocketMessageBuffer * wsBuf = nullptr; -#if __cpp_exceptions - try{ -#endif - wsBuf = ws.makeBuffer(bufSize); -#if __cpp_exceptions - } catch(...) { -#else - if (wsBuf == nullptr) { // 8266 does not support exceptions -#endif - wsBuf = nullptr; - USER_PRINTLN(F("WS buffer allocation failed.")); - //ws.closeAll(1013); //code 1013 = temporary overload, try again later - //ws.cleanupClients(0); //disconnect all clients to release memory - ws._cleanBuffers(); - } - if (!wsBuf) return false; //out of memory - uint8_t* buffer = wsBuf->get(); - if (!buffer) return false; //out of memory + if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen + AsyncWebSocketBuffer wsBuf(bufSize); + if (!wsBuf) { + USER_PRINTLN(F("WS buffer allocation failed.")); + errorFlag = ERR_LOW_WS_MEM; + return false; //out of memory + } + uint8_t* buffer = reinterpret_cast(wsBuf.data()); + if (!buffer) { + USER_PRINTLN(F("WS buffer allocation failed.")); + errorFlag = ERR_LOW_WS_MEM; + return false; //out of memory + } - wsBuf->lock(); // protect buffer from being cleaned by another WS instance buffer[0] = 'L'; buffer[1] = 1; //version #ifndef WLED_DISABLE_2D @@ -290,9 +263,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static" } } - wsc->binary(wsBuf); - wsBuf->unlock(); // un-protect buffer - ws._cleanBuffers(); // cleans up if the message is not added to any clients. + wsc->binary(std::move(wsBuf)); return true; }