Merge pull request #139 from MoonModules/upstream_patch_clusterf

AC minor patch cluster
* Anti-Aliased Lines and Circles
* replacing deprecated functions in ws.cpp
* using global brightness in analogue clock overlay
* fix for palette names (custom palettes)
* bugfix for UI: when resizing the window, it always jumped to the Colors tab
* UI: add webpage shortcuts for tabs
This commit is contained in:
Frank
2024-07-13 01:13:53 +02:00
committed by GitHub
10 changed files with 242 additions and 181 deletions

View File

@@ -76,7 +76,7 @@ private:
bool sensorFound = false; bool sensorFound = false;
// Home Assistant and MQTT // Home Assistant and MQTT
String mqttLuminanceTopic = FPSTR(""); String mqttLuminanceTopic;
bool mqttInitialized = false; bool mqttInitialized = false;
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages

View File

@@ -626,7 +626,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if ((_relay[i].pin<0 && !usePcf8574) || !_relay[i].external) continue; if ((_relay[i].pin<0 && !usePcf8574) || !_relay[i].external) continue;
uiDomString = F("Relay "); uiDomString += i; uiDomString = F("Relay "); uiDomString += i;
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value infoArr = user.createNestedArray(uiDomString); // timer value
uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({"); uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
uiDomString += FPSTR(_name); uiDomString += FPSTR(_name);

View File

@@ -2551,7 +2551,7 @@ uint16_t ripple_base()
uint16_t cx = rippleorigin >> 8; uint16_t cx = rippleorigin >> 8;
uint16_t cy = rippleorigin & 0xFF; uint16_t cy = rippleorigin & 0xFF;
uint8_t mag = scale8(sin8((propF>>2)), amp); 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 } else
#endif #endif
{ {
@@ -6271,8 +6271,8 @@ uint16_t mode_2Dfloatingblobs(void) {
} }
} }
uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); 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); if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c);
else SEGMENT.setPixelColorXY(blob->x[i], blob->y[i], c); else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c);
// move x // 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)); 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)); else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f));

View File

@@ -122,6 +122,10 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.
#define PURPLE (uint32_t)0x400080 #define PURPLE (uint32_t)0x400080
#define ORANGE (uint32_t)0xFF3000 #define ORANGE (uint32_t)0xFF3000
#define PINK (uint32_t)0xFF1493 #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 ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F #define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY (uint32_t)0x2F4F4F #define DARKSLATEGREY (uint32_t)0x2F4F4F
@@ -614,11 +618,11 @@ typedef struct Segment {
// 1D strip // 1D strip
uint16_t virtualLength(void) const; uint16_t virtualLength(void) const;
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color 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 inline 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, 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, 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); } 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); }
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, 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 uint32_t __attribute__((pure)) getPixelColor(int i); // WLEDMM attribute added
// 1D support functions (some implement 2D as well) // 1D support functions (some implement 2D as well)
void blur(uint8_t, bool smear = false); void blur(uint8_t, bool smear = false);
@@ -626,10 +630,10 @@ typedef struct Segment {
void fade_out(uint8_t r); void fade_out(uint8_t r);
void fadeToBlackBy(uint8_t fadeBy); void fadeToBlackBy(uint8_t fadeBy);
void blendPixelColor(int n, uint32_t color, uint8_t blend); 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, 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 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
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, 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); void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos); 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); 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(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, 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(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 //#ifdef WLED_USE_AA_PIXELS
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true, bool fast=true); 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); } 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); uint32_t __attribute__((pure)) getPixelColorXY(int x, int y);
// 2D support functions // 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, 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, 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 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
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, 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 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 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); 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 moveX(int8_t delta, bool wrap = false);
void moveY(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 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 drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); 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 drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
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 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, 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, 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 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 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); void nscale8(uint8_t scale);
bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx
#else #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(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, 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(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 //#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, 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); } 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 moveX(int8_t delta, bool wrap = false) {}
inline void moveY(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 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 drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} inline void drawCircle(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, CRGB c) {} 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, 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 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) {} 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) {}

View File

@@ -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]) // 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 void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
const uint_fast16_t cols = virtualWidth(); if (!isActive() || blur_amount == 0) return; // not active
const uint_fast16_t rows = virtualHeight(); const int cols = virtualWidth();
const uint_fast16_t dim1 = vertical ? rows : cols; const int rows = virtualHeight();
const uint_fast16_t dim2 = vertical ? cols : rows; const int dim1 = vertical ? rows : cols;
const int dim2 = vertical ? cols : rows;
if (i >= dim2) return; if (i >= dim2) return;
const float seep = blur_amount/255.f; const float seep = blur_amount/255.f;
const float keep = 3.f - 2.f*seep; const float keep = 3.f - 2.f*seep;
// 1D box blur // 1D box blur
CRGB tmp[dim1]; uint32_t out[dim1], in[dim1];
for (uint_fast16_t j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
uint_fast16_t x = vertical ? i : j; int x = vertical ? i : j;
uint_fast16_t y = vertical ? j : i; int y = vertical ? j : i;
int_fast16_t xp = vertical ? x : x-1; // "signed" to prevent underflow in[j] = getPixelColorXY(x, y);
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);
} }
for (uint_fast16_t j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
uint_fast16_t x = vertical ? i : j; uint32_t curr = in[j];
uint_fast16_t y = vertical ? j : i; uint32_t prev = j > 0 ? in[j-1] : BLACK;
setPixelColorXY((int)x, (int)y, tmp[j]); 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) { 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) { void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
if (!isActive()) return; // not active if (!isActive() || radius == 0) return; // not active
// Bresenhams Algorithm if (soft) {
int d = 3 - (2*radius); // Xiaolin Wus algorithm
int y = radius, x = 0; int rsq = radius*radius;
while (y >= x) { int x = 0;
setPixelColorXY(cx+x, cy+y, col); int y = radius;
setPixelColorXY(cx-x, cy+y, col); unsigned oldFade = 0;
setPixelColorXY(cx+x, cy-y, col); while (x < y) {
setPixelColorXY(cx-x, cy-y, col); float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
setPixelColorXY(cx+y, cy+x, col); unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
setPixelColorXY(cx-y, cy+x, col); if (oldFade > fade) y--;
setPixelColorXY(cx+y, cy-x, col); oldFade = fade;
setPixelColorXY(cx-y, cy-x, col); setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
x++; setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
if (d > 0) { setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
y--; setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
d += 4 * (x - y) + 10; setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true));
} else { setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true));
d += 4 * x + 6; 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 {
// Bresenhams 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 // 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) { void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
if (!isActive()) return; // not active if (!isActive() || radius == 0) return; // not active
const uint16_t cols = virtualWidth(); // draw soft bounding circle
const uint16_t rows = virtualHeight(); if (soft) drawCircle(cx, cy, radius, col, soft);
for (int16_t y = -radius; y <= radius; y++) { // fill it
for (int16_t x = -radius; x <= radius; x++) { 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 && if (x * x + y * y <= radius * radius &&
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 && int16_t(cx)+x>=0 && int16_t(cy)+y>=0 &&
int16_t(cx)+x<cols && int16_t(cy)+y<rows) int16_t(cx)+x<cols && int16_t(cy)+y<rows)
@@ -582,20 +598,57 @@ void Segment::nscale8(uint8_t scale) { //WLEDMM: use fast types
} }
//line function //line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { 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 if (!isActive()) return; // not active
const uint16_t cols = virtualWidth(); const int cols = virtualWidth();
const uint16_t rows = virtualHeight(); const int rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
int16_t err = (dx>dy ? dx : -dy)/2, e2; const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
for (;;) {
setPixelColorXY(x0,y0,c); // single pixel (line length == 0)
if (x0==x1 && y0==y1) break; if (dx+dy == 0) {
e2 = err; setPixelColorXY(x0, y0, c);
if (e2 >-dx) { err -= dy; x0 += sx; } return;
if (e2 < dy) { err += dx; y0 += sy; } }
if (soft) {
// Xiaolin Wus algorithm
const bool steep = dy > 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; }
}
} }
} }

View File

@@ -247,6 +247,7 @@ function onLoad()
selectSlot(0); selectSlot(0);
updateTablinks(0); updateTablinks(0);
handleLocationHash();
pmtLS = localStorage.getItem('wledPmt'); pmtLS = localStorage.getItem('wledPmt');
// Load initial data // Load initial data
@@ -279,7 +280,6 @@ function updateTablinks(tabI)
{ {
var tablinks = gEBCN("tablinks"); var tablinks = gEBCN("tablinks");
for (var i of tablinks) i.classList.remove('active'); for (var i of tablinks) i.classList.remove('active');
if (pcMode) return;
tablinks[tabI].classList.add('active'); tablinks[tabI].classList.add('active');
} }
@@ -290,6 +290,21 @@ function openTab(tabI, force = false)
_C.classList.toggle('smooth', false); _C.classList.toggle('smooth', false);
_C.style.setProperty('--i', iSlide); _C.style.setProperty('--i', iSlide);
updateTablinks(tabI); 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; var timeout;
@@ -3615,12 +3630,11 @@ function togglePcMode(fromB = false)
if (fromB) { if (fromB) {
pcModeA = !pcModeA; pcModeA = !pcModeA;
localStorage.setItem('pcm', pcModeA); localStorage.setItem('pcm', pcModeA);
openTab(0, true);
} }
pcMode = (wW >= 1024) && pcModeA; pcMode = (wW >= 1024) && pcModeA;
if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape 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() 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('buttonPcm').className = (pcMode) ? "active":"";
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto"; gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
sCol('--bh', gId('bot').clientHeight + "px"); sCol('--bh', gId('bot').clientHeight + "px");
@@ -3652,6 +3666,7 @@ size();
_C.style.setProperty('--n', N); _C.style.setProperty('--n', N);
window.addEventListener('resize', size, true); window.addEventListener('resize', size, true);
window.addEventListener('hashchange', handleLocationHash);
_C.addEventListener('mousedown', lock, false); _C.addEventListener('mousedown', lock, false);
_C.addEventListener('touchstart', lock, false); _C.addEventListener('touchstart', lock, false);

View File

@@ -340,8 +340,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.fill(BLACK); // WLEDMM why now? seg.fill(BLACK); // WLEDMM why now?
} }
uint16_t start = 0, stop = 0; start = 0, stop = 0;
byte set = 0; //0 nothing set, 1 start set, 2 range set set = 0; //0 nothing set, 1 start set, 2 range set
for (size_t i = 0; i < iarr.size(); i++) { for (size_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) { if(iarr[i].is<JsonInteger>()) {
@@ -1554,10 +1554,20 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
uint16_t used = strip.getLengthTotal(); 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 uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
char buffer[2000]; #ifndef WLED_DISABLE_2D
strcpy_P(buffer, PSTR("{\"leds\":[")); if (strip.isMatrix) {
obuf = buffer; // ignore anything behid matrix (i.e. extra strip)
olen = 9; 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) 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)); g = qadd8(w, G(c));
b = qadd8(w, B(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; buf--; // remove last comma
oappend((const char*)F("],\"n\":")); buf += sprintf_P(buf, PSTR("],\"n\":%d"), n);
oappendi(n); (*buf++) = '}';
oappend("}"); (*buf++) = 0;
if (request) { if (request) {
request->send(200, "application/json", buffer); request->send(200, "application/json", toString(std::move(buffer)));
} }
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
else { else {
wsc->text(obuf, olen); wsc->text(toString(std::move(buffer)));
} }
#endif #endif
return true; return true;
} }
#endif #endif

View File

@@ -25,11 +25,11 @@ void _overlayAnalogClock()
{ {
if (secondPixel < analogClock12pixel) if (secondPixel < analogClock12pixel)
{ {
strip.setRange(analogClock12pixel, overlayMax, 0xFF0000); strip.setRange(analogClock12pixel, overlayMax, color_fade(0xFF0000, bri));
strip.setRange(overlayMin, secondPixel, 0xFF0000); strip.setRange(overlayMin, secondPixel, color_fade(0xFF0000, bri));
} else } else
{ {
strip.setRange(analogClock12pixel, secondPixel, 0xFF0000); strip.setRange(analogClock12pixel, secondPixel, color_fade(0xFF0000, bri));
} }
} }
if (analogClock5MinuteMarks) if (analogClock5MinuteMarks)
@@ -38,12 +38,12 @@ void _overlayAnalogClock()
{ {
int pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i); int pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i);
if (pix > overlayMax) pix -= overlaySize; if (pix > overlayMax) pix -= overlaySize;
strip.setPixelColor(pix, 0x00FFAA); strip.setPixelColor(pix, color_fade(0x00FFAA, bri));
} }
} }
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, color_fade(0xFF0000, bri));
strip.setPixelColor(minutePixel, 0x00FF00); strip.setPixelColor(minutePixel, color_fade(0x00FF00, bri));
strip.setPixelColor(hourPixel, 0x0000FF); strip.setPixelColor(hourPixel, color_fade(0x0000FF, bri));
} }

View File

@@ -267,8 +267,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
} else return 0; } else return 0;
} }
if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) { if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) {
snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode); snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode);
dest[maxLen-1] = '\0'; dest[maxLen-1] = '\0';
return strlen(dest); return strlen(dest);
} }

View File

@@ -108,7 +108,6 @@ void sendDataWs(AsyncWebSocketClient * client)
{ {
DEBUG_PRINTF("sendDataWs\n"); DEBUG_PRINTF("sendDataWs\n");
if (!ws.count()) return; if (!ws.count()) return;
AsyncWebSocketMessageBuffer * buffer;
if (!requestJSONBufferLock(12)) { if (!requestJSONBufferLock(12)) {
if (client) { if (client) {
@@ -138,17 +137,7 @@ void sendDataWs(AsyncWebSocketClient * client)
// DEBUG_PRINTF("%s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM // DEBUG_PRINTF("%s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM
#endif #endif
if (len < 1) return; // WLEDMM do not allocate 0 size buffer if (len < 1) return; // WLEDMM do not allocate 0 size buffer
AsyncWebSocketBuffer buffer(len);
// 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
#ifdef ESP8266 #ifdef ESP8266
size_t heap2 = ESP.getFreeHeap(); size_t heap2 = ESP.getFreeHeap();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(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.")); USER_PRINTLN(F("WS buffer allocation failed."));
ws.closeAll(1013); //code 1013 = temporary overload, try again later ws.closeAll(1013); //code 1013 = temporary overload, try again later
ws.cleanupClients(0); //disconnect all clients to release memory ws.cleanupClients(0); //disconnect all clients to release memory
ws._cleanBuffers();
errorFlag = ERR_LOW_WS_MEM; errorFlag = ERR_LOW_WS_MEM;
return; //out of memory return; //out of memory
} }
serializeJson(doc, (char *)buffer.data(), len);
buffer->lock();
serializeJson(doc, (char *)buffer->get(), len);
DEBUG_PRINT(F("Sending WS data ")); DEBUG_PRINT(F("Sending WS data "));
if (client) { if (client) {
client->text(buffer); client->text(std::move(buffer));
DEBUG_PRINTLN(F("to a single client.")); DEBUG_PRINTLN(F("to a single client."));
} else { } else {
ws.textAll(buffer); ws.textAll(std::move(buffer));
DEBUG_PRINTLN(F("to multiple clients.")); DEBUG_PRINTLN(F("to multiple clients."));
} }
buffer->unlock();
ws._cleanBuffers();
releaseJSONBufferLock(); releaseJSONBufferLock();
} }
@@ -227,32 +211,21 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
#endif #endif
size_t pos = (strip.isMatrix ? 4 : 2); size_t pos = (strip.isMatrix ? 4 : 2);
size_t bufSize = pos + (used/n)*3; 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 if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen
uint8_t* buffer = wsBuf->get(); AsyncWebSocketBuffer wsBuf(bufSize);
if (!buffer) return false; //out of memory if (!wsBuf) {
USER_PRINTLN(F("WS buffer allocation failed."));
errorFlag = ERR_LOW_WS_MEM;
return false; //out of memory
}
uint8_t* buffer = reinterpret_cast<uint8_t*>(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[0] = 'L';
buffer[1] = 1; //version buffer[1] = 1; //version
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
@@ -290,9 +263,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
} }
} }
wsc->binary(wsBuf); wsc->binary(std::move(wsBuf));
wsBuf->unlock(); // un-protect buffer
ws._cleanBuffers(); // cleans up if the message is not added to any clients.
return true; return true;
} }