Fix for transpose.

blur1d() using weighted box blur.
This commit is contained in:
Blaz Kristan 2022-05-28 19:23:16 +02:00
parent 461cc1d5a8
commit 8b73a7375a
4 changed files with 446 additions and 282 deletions

File diff suppressed because it is too large Load Diff

View File

@ -306,8 +306,8 @@ class WS2812FX {
inline bool getOption(uint8_t n) { return ((options >> n) & 0x01); }
inline bool isSelected() { return getOption(0); }
inline bool isActive() { return stop > start; }
inline uint16_t width() { return getOption(SEG_OPTION_TRANSPOSED) ? stopY - startY : stop - start; }
inline uint16_t height() { return getOption(SEG_OPTION_TRANSPOSED) ? stop - start : stopY - startY; }
inline uint16_t width() { return /*getOption(SEG_OPTION_TRANSPOSED) ? stopY - startY :*/ stop - start; }
inline uint16_t height() { return /*getOption(SEG_OPTION_TRANSPOSED) ? stop - start :*/ stopY - startY; }
inline uint16_t length() { return width(); }
inline uint16_t groupLength() { return grouping + spacing; }
inline uint8_t getLightCapabilities() { return _capabilities; }

View File

@ -110,8 +110,7 @@ void WS2812FX::setUpMatrix() {
uint16_t IRAM_ATTR WS2812FX::XY(uint16_t x, uint16_t y) {
uint16_t width = SEGMENT.virtualWidth(); // segment width in logical pixels
uint16_t height = SEGMENT.virtualHeight(); // segment height in logical pixels
/*
// it may be unnecessary to perform transpose since pixels should be always addressed using XY() function
if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) {
uint16_t t;
// swap X & Y if segment transposed
@ -119,7 +118,6 @@ uint16_t IRAM_ATTR WS2812FX::XY(uint16_t x, uint16_t y) {
// swap width & height if segment transposed
t = width; width = height; height = t;
}
*/
return (x%width) + (y%height) * width;
}
@ -206,14 +204,17 @@ void WS2812FX::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// blurRow: perform a blur on a row of a rectangular matrix
void WS2812FX::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) {
const uint16_t width = SEGMENT.virtualWidth();
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
if (row >= height) return;
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
if (row >= rows) return;
// blur one row
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < width; x++) {
for (uint16_t x = 0; x < cols; x++) {
CRGB cur = leds ? leds[XY(x,row)] : col_to_crgb(getPixelColorXY(x, row));
CRGB part = cur;
part.nscale8(seep);
@ -232,14 +233,17 @@ void WS2812FX::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) {
// blurCol: perform a blur on a column of a rectangular matrix
void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
const uint16_t width = SEGMENT.virtualWidth();
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
if (col >= width) return;
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
if (col >= cols) return;
// blur one column
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < height; i++) {
for (uint16_t i = 0; i < rows; i++) {
CRGB cur = leds ? leds[XY(col,i)] : col_to_crgb(getPixelColorXY(col, i));
CRGB part = cur;
part.nscale8(seep);
@ -271,16 +275,23 @@ void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
// it can be used to (slowly) clear the LEDs to black.
void WS2812FX::blur1d(CRGB* leds, fract8 blur_amount) {
uint16_t height = SEGMENT.virtualHeight();
for (uint16_t y = 0; y < height; y++) blurRow(y, blur_amount, leds);
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
//const uint16_t cols = isTransposed ? height : width;
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount, leds);
}
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void WS2812FX::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds) {
const uint16_t width = SEGMENT.virtualWidth();
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const uint16_t dim1 = vertical ? height : width;
const uint16_t dim2 = vertical ? width : height;
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
const uint16_t dim1 = vertical ? rows : cols;
const uint16_t dim2 = vertical ? cols : rows;
if (i >= dim2) return;
const float seep = blur_amount/255.f;
const float keep = 3.f - 2.f*seep;
@ -313,23 +324,29 @@ void WS2812FX::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds)
void WS2812FX::blur2d(CRGB* leds, fract8 blur_amount) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
for (uint16_t i = 0; i < height; i++) blurRow(i, blur_amount, leds); // blur all rows
for (uint16_t k = 0; k < width; k++) blurCol(k, blur_amount, leds); // blur all columns
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = !isTransposed ? height : width;
const uint16_t cols = !isTransposed ? width : height;
for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount, leds); // blur all rows
for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount, leds); // blur all columns
}
void WS2812FX::moveX(CRGB *leds, int8_t delta) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
if (delta) {
if (delta > 0) {
for (uint8_t y = 0; y < height; y++) {
for (uint8_t x = 0; x < width-1; x++) {
for (uint8_t y = 0; y < rows; y++) {
for (uint8_t x = 0; x < cols-1; x++) {
leds[XY(x, y)] = leds[XY(x + delta, y)];
}
}
} else {
for (uint8_t y = 0; y < height; y++) {
for (uint8_t x = width - 1; x > 0; x--) {
for (uint8_t y = 0; y < rows; y++) {
for (uint8_t x = cols - 1; x > 0; x--) {
leds[XY(x, y)] = leds[XY(x + delta, y)];
}
}
@ -340,16 +357,19 @@ void WS2812FX::moveX(CRGB *leds, int8_t delta) {
void WS2812FX::moveY(CRGB *leds, int8_t delta) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
if (delta) {
if (delta > 0) {
for (uint8_t x = 0; x < width; x++) {
for (uint8_t y = 0; y < height-1; y++) {
for (uint8_t x = 0; x < cols; x++) {
for (uint8_t y = 0; y < rows-1; y++) {
leds[XY(x, y)] = leds[XY(x, y + delta)];
}
}
} else {
for (uint8_t x = 0; x < width; x++) {
for (uint8_t y = height - 1; y > 0; y--) {
for (uint8_t x = 0; x < cols; x++) {
for (uint8_t y = rows - 1; y > 0; y--) {
leds[XY(x, y)] = leds[XY(x, y + delta)];
}
}
@ -357,11 +377,13 @@ void WS2812FX::moveY(CRGB *leds, int8_t delta) {
}
}
void WS2812FX::fill_solid(CRGB* leds, CRGB color) {
const uint16_t w = SEGMENT.virtualWidth();
const uint16_t h = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (leds) leds[XY(x,y)] = color;
else setPixelColorXY(x, y, color);
}
@ -369,11 +391,16 @@ void WS2812FX::fill_solid(CRGB* leds, CRGB color) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void WS2812FX::fill_circle(CRGB* leds, uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
for (int16_t y = -radius; y <= radius; y++) {
for (int16_t 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<SEGMENT.virtualWidth() && int16_t(cy)+y<SEGMENT.virtualHeight())
int16_t(cx)+x<cols && int16_t(cy)+y<rows)
leds[XY(cx + x, cy + y)] += col;
}
}
@ -384,22 +411,34 @@ void WS2812FX::fadeToBlackBy(CRGB* leds, uint8_t fadeBy) {
}
void WS2812FX::nscale8(CRGB* leds, uint8_t scale) {
const uint16_t w = SEGMENT.virtualWidth();
const uint16_t h = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (leds) leds[XY(x,y)].nscale8(scale);
else setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x, y)).nscale8(scale));
}
}
void WS2812FX::setPixels(CRGB* leds) {
const uint16_t w = SEGMENT.virtualWidth();
const uint16_t h = SEGMENT.virtualHeight();
for (uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) setPixelColorXY(x, y, leds[XY(x,y)]);
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) setPixelColorXY(x, y, leds[XY(x,y)]);
}
//line function
void WS2812FX::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
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;
int16_t err = (dx>dy ? dx : -dy)/2, e2;
@ -3490,17 +3529,20 @@ static const unsigned char console_font_6x8[] PROGMEM = {
};
void WS2812FX::drawCharacter(unsigned char chr, int16_t x, int16_t y, CRGB color, CRGB *leds) {
const uint16_t width = SEGMENT.virtualWidth();
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
for (uint8_t i = 0; i<8; i++) { // character height
int16_t y0 = y + i;
if (y0 < 0) continue; // drawing off-screen
if (y0 >= height) break; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen
uint8_t bits = pgm_read_byte_near(&console_font_6x8[(chr * 8) + i]);
for (uint8_t j = 0; j<6; j++) { // character width
int16_t x0 = x + 5 - j;
if ((x0 >= 0 || x0 < width) && ((bits>>(j+1)) & 0x01)) { // bit set & drawing on-screen
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+1)) & 0x01)) { // bit set & drawing on-screen
if (leds) leds[XY(x0,y0)] = color;
else setPixelColorXY(x0, y0, color);
}
@ -3508,7 +3550,6 @@ void WS2812FX::drawCharacter(unsigned char chr, int16_t x, int16_t y, CRGB color
}
}
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
void WS2812FX::wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
// extract the fractional parts and derive their inverses

View File

@ -890,9 +890,12 @@ uint32_t IRAM_ATTR WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint1
* Fills segment with color
*/
void WS2812FX::fill(uint32_t c) {
uint16_t w = SEGMENT.virtualWidth(); // same as SEGLEN
uint16_t h = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (isMatrix) setPixelColorXY(x, y, c);
else setPixelColor(x, c);
}
@ -910,8 +913,12 @@ void WS2812FX::blendPixelColor(uint16_t n, uint32_t color, uint8_t blend)
* fade out function, higher rate = quicker fade
*/
void WS2812FX::fade_out(uint8_t rate) {
uint16_t w = SEGMENT.virtualWidth(); // same as SEGLEN
uint16_t h = SEGMENT.virtualHeight();
const uint16_t width = SEGMENT.virtualWidth(); // same as SEGLEN
const uint16_t height = SEGMENT.virtualHeight();
const bool isTransposed = SEGMENT.getOption(SEG_OPTION_TRANSPOSED);
const uint16_t rows = isTransposed ? width : height;
const uint16_t cols = isTransposed ? height : width;
rate = (255-rate) >> 1;
float mappedRate = float(rate) +1.1;
@ -921,7 +928,7 @@ void WS2812FX::fade_out(uint8_t rate) {
int g2 = G(color);
int b2 = B(color);
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
color = isMatrix ? getPixelColorXY(x, y) : getPixelColor(x);
int w1 = W(color);
int r1 = R(color);