diff --git a/platformio.ini b/platformio.ini index a7485244cd..57ed240ebe 100644 --- a/platformio.ini +++ b/platformio.ini @@ -137,7 +137,6 @@ upload_speed = 115200 # ------------------------------------------------------------------------------ lib_compat_mode = strict lib_deps = - fastled/FastLED @ 3.6.0 IRremoteESP8266 @ 2.8.2 makuna/NeoPixelBus @ 2.8.3 #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta @@ -225,7 +224,6 @@ lib_deps_compat = ESPAsyncTCP @ 1.2.2 ESPAsyncUDP ESP8266PWM - fastled/FastLED @ 3.6.0 IRremoteESP8266 @ 2.8.2 makuna/NeoPixelBus @ 2.7.9 https://github.com/blazoncek/QuickESPNow.git#optional-debug diff --git a/usermods/Analog_Clock/Analog_Clock.cpp b/usermods/Analog_Clock/Analog_Clock.cpp index 970ba72241..d268b340a6 100644 --- a/usermods/Analog_Clock/Analog_Clock.cpp +++ b/usermods/Analog_Clock/Analog_Clock.cpp @@ -116,7 +116,7 @@ class AnalogClockUsermod : public Usermod { ); } - static inline uint32_t scale32(uint32_t c, fract8 scale) { + static inline uint32_t scale32(uint32_t c, uint8_t scale) { return RGBW32( scale8(R(c), scale), scale8(G(c), scale), diff --git a/usermods/audioreactive/audio_reactive.cpp b/usermods/audioreactive/audio_reactive.cpp index 4b3520562d..e13e6bae52 100644 --- a/usermods/audioreactive/audio_reactive.cpp +++ b/usermods/audioreactive/audio_reactive.cpp @@ -1994,12 +1994,12 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) { case 2: b = map(x, 0, 255, 0, NUM_GEQ_CHANNELS/2); // convert palette position to lower half of freq band hsv = CHSV(fftResult[b], 255, x); - hsv2rgb_rainbow(hsv, value); // convert to R,G,B + value = hsv; // convert to R,G,B break; case 1: b = map(x, 1, 255, 0, 10); // convert palette position to lower half of freq band hsv = CHSV(fftResult[b], 255, map(fftResult[b], 0, 255, 30, 255)); // pick hue - hsv2rgb_rainbow(hsv, value); // convert to R,G,B + value = hsv; // convert to R,G,B break; default: if (x == 1) { diff --git a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp index 0a485152f1..43ac2bf1bc 100644 --- a/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp +++ b/usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp @@ -96,14 +96,14 @@ class RotaryEncoderBrightnessColor : public Usermod fastled_col.red = colPri[0]; fastled_col.green = colPri[1]; fastled_col.blue = colPri[2]; - prim_hsv = rgb2hsv_approximate(fastled_col); + prim_hsv = rgb2hsv(fastled_col); new_val = (int16_t)prim_hsv.h + fadeAmount; 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 prim_hsv.h = (byte)new_val; - hsv2rgb_rainbow(prim_hsv, fastled_col); + fastled_col = prim_hsv ; colPri[0] = fastled_col.red; colPri[1] = fastled_col.green; colPri[2] = fastled_col.blue; @@ -121,14 +121,14 @@ class RotaryEncoderBrightnessColor : public Usermod fastled_col.red = colPri[0]; fastled_col.green = colPri[1]; fastled_col.blue = colPri[2]; - prim_hsv = rgb2hsv_approximate(fastled_col); + prim_hsv = rgb2hsv(fastled_col); new_val = (int16_t)prim_hsv.h - fadeAmount; 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 prim_hsv.h = (byte)new_val; - hsv2rgb_rainbow(prim_hsv, fastled_col); + fastled_col = prim_hsv; colPri[0] = fastled_col.red; colPri[1] = fastled_col.green; colPri[2] = fastled_col.blue; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index f439e4c664..2bb984521c 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -13,6 +13,8 @@ #include "wled.h" #include "FX.h" #include "fcn_declare.h" +#include "colors.h" +#include "prng.h" #if !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D)) #include "FXparticleSystem.h" @@ -68,17 +70,14 @@ //#define MAX_FREQUENCY 5120 //#define MAX_FREQ_LOG10 3.71f +static PRNG prng(hw_random()); // pseudo-random number generator class, seed = hardware random number + // effect utility functions uint8_t sin_gap(uint16_t in) { if (in & 0x100) return 0; return sin8_t(in + 192); // correct phase shift of sine so that it starts and stops at 0 } -uint16_t triwave16(uint16_t in) { - if (in < 0x8000) return in *2; - return 0xFFFF - (in - 0x8000)*2; -} - /* * Generates a tristate square wave w/ attac & decay * @param x input value 0-255 @@ -1759,30 +1758,30 @@ static const char _data_FX_MODE_MULTI_COMET[] PROGMEM = "Multi Comet@!,Fade;!,!; */ uint16_t mode_random_chase(void) { if (SEGENV.call == 0) { - SEGENV.step = RGBW32(random8(), random8(), random8(), 0); - SEGENV.aux0 = random16(); + SEGENV.step = RGBW32(prng.random8(), prng.random8(), prng.random8(), 0); + SEGENV.aux0 = prng.random16(); } - unsigned prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function + unsigned prevSeed = prng.getSeed(); // save seed so we can restore it at the end of the function uint32_t cycleTime = 25 + (3 * (uint32_t)(255 - SEGMENT.speed)); uint32_t it = strip.now / cycleTime; uint32_t color = SEGENV.step; - random16_set_seed(SEGENV.aux0); + prng.setSeed(SEGENV.aux0); for (int i = SEGLEN -1; i >= 0; i--) { - uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8(); - uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8(); - uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8(); + uint8_t r = prng.random8(6) != 0 ? (color >> 16 & 0xFF) : prng.random8(); + uint8_t g = prng.random8(6) != 0 ? (color >> 8 & 0xFF) : prng.random8(); + uint8_t b = prng.random8(6) != 0 ? (color & 0xFF) : prng.random8(); color = RGBW32(r, g, b, 0); SEGMENT.setPixelColor(i, color); if (i == SEGLEN -1U && SEGENV.aux1 != (it & 0xFFFFU)) { //new first color in next frame SEGENV.step = color; - SEGENV.aux0 = random16_get_seed(); + SEGENV.aux0 = prng.getSeed(); } } SEGENV.aux1 = it & 0xFFFF; - random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG + prng.setSeed(prevSeed); // restore original seed so other effects can use "random" PRNG return FRAMETIME; } static const char _data_FX_MODE_RANDOM_CHASE[] PROGMEM = "Stream 2@!;;"; @@ -2270,8 +2269,8 @@ uint16_t mode_colortwinkle() { if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed 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(); + uint8_t fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness(); + uint8_t fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness(); for (unsigned i = 0; i < SEGLEN; i++) { CRGBW cur = SEGMENT.getPixelColor(i); prev = cur; @@ -2545,7 +2544,7 @@ static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wav // // TwinkleFOX: Twinkling 'holiday' lights that fade in and out. // Colors are chosen from a palette. Read more about this effect using the link above! -static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) +static CRGBW twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) { // Overall twinkle speed (changed) unsigned ticks = ms / SEGENV.aux0; @@ -2580,7 +2579,7 @@ static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) } unsigned hue = slowcycle8 - salt; - CRGB c; + CRGBW c; if (bright > 0) { c = ColorFromPalette(SEGPALETTE, hue, bright, NOBLEND); if (!SEGMENT.check1) { @@ -2595,7 +2594,7 @@ static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) } } } else { - c = CRGB::Black; + c = 0; // black } return c; } @@ -2618,14 +2617,14 @@ static uint16_t twinklefox_base(bool cat) else SEGENV.aux0 = 22 + ((100 - SEGMENT.speed) >> 1); // Set up the background color, "bg". - CRGB bg = CRGB(SEGCOLOR(1)); + CRGBW bg = SEGCOLOR(1); unsigned bglight = bg.getAverageLight(); if (bglight > 64) { - bg.nscale8_video(16); // very bright, so scale to 1/16th + bg = color_fade(bg, 16, true); // very bright, so scale to 1/16th } else if (bglight > 16) { - bg.nscale8_video(64); // not that bright, so scale to 1/4th + bg = color_fade(bg, 64, true); // not that bright, so scale to 1/4th } else { - bg.nscale8_video(86); // dim, scale to 1/3rd. + bg = color_fade(bg, 86, true); // dim, scale to 1/3rd. } unsigned backgroundBrightness = bg.getAverageLight(); @@ -2643,18 +2642,18 @@ static uint16_t twinklefox_base(bool cat) // We now have the adjusted 'clock' for this pixel, now we call // the function that computes what color the pixel should be based // on the "brightness = f( time )" idea. - CRGB c = twinklefox_one_twinkle(myclock30, myunique8, cat); + CRGBW c = twinklefox_one_twinkle(myclock30, myunique8, cat); unsigned cbright = c.getAverageLight(); int deltabright = cbright - backgroundBrightness; - if (deltabright >= 32 || (!bg)) { + if (deltabright >= 32 || (bg==0)) { // If the new pixel is significantly brighter than the background color, // use the new color. 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 - SEGMENT.setPixelColor(i, color_blend(RGBW32(bg.r,bg.g,bg.b,0), RGBW32(c.r,c.g,c.b,0), uint8_t(deltabright * 8))); + SEGMENT.setPixelColor(i, color_blend(bg, c, uint8_t(deltabright * 8))); } else { // if the new pixel is not at all brighter than the background color, // just use the background color. @@ -4146,17 +4145,17 @@ static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!" uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. - 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. + unsigned prevSeed = prng.getSeed(); // save seed so we can restore it at the end of the function + prng.setSeed(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 (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 ranstart = prng.random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work. unsigned pixBri = sin8_t(ranstart + 16 * strip.now/(256-SEGMENT.speed)); - if (random8() > SEGMENT.intensity) pixBri = 0; - SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri)); + if (prng.random8() > SEGMENT.intensity) pixBri = 0; + SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(prng.random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri)); } - random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG + prng.setSeed(prevSeed); // restore original seed so other effects can use "random" PRNG return FRAMETIME; } static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;;m12=0"; @@ -4178,6 +4177,7 @@ uint16_t mode_noisepal(void) { // Slow noise SEGENV.step = strip.now; unsigned baseI = hw_random8(); + //palettes[1] = CRGBPalette16(CHSV(baseI+hw_random8(64), 255, hw_random8(128,255)), CHSV(baseI+128, 255, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 192, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 255, hw_random8(128,255))); palettes[1] = CRGBPalette16(CHSV(baseI+hw_random8(64), 255, hw_random8(128,255)), CHSV(baseI+128, 255, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 192, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 255, hw_random8(128,255))); } @@ -4921,7 +4921,7 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so uint8_t rate = j * 255 / steps; byte dx = lerp8by8(x1, y1, rate); byte dy = lerp8by8(x2, y2, rate); - //SEGMENT.setPixelColorXY(dx, dy, grad ? color.nscale8_video(255-rate) : color); // use addPixelColorXY for different look + //SEGMENT.setPixelColorXY(dx, dy, grad ? color_fade(color, (255-rate), true) : color); // use addPixelColorXY for different look SEGMENT.addPixelColorXY(dx, dy, color); // use setPixelColorXY for different look if (grad) SEGMENT.fadePixelColorXY(dx, dy, rate); } @@ -5696,8 +5696,7 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi uint8_t someVal = SEGMENT.speed/4; // Was 25. for (int j = 0; j < (rows + 2); j++) { for (int i = 0; i < (cols + 2); i++) { - //byte col = (inoise8_raw(i * someVal, j * someVal, t)) / 2; - byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 0x7F) / 3; + byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 127) >> 2; // about +/- 32 bump[index++] = col; } } @@ -5824,9 +5823,9 @@ uint16_t mode_2Dcrazybees(void) { int8_t deltaX, deltaY, signX, signY, error; void aimed(uint16_t w, uint16_t h) { //random16_set_seed(millis()); - aimX = random8(0, w); - aimY = random8(0, h); - hue = random8(); + aimX = prng.random8(0, w); + aimY = prng.random8(0, h); + hue = prng.random8(); deltaX = abs(aimX - posX); deltaY = abs(aimY - posY); signX = posX < aimX ? 1 : -1; @@ -5839,10 +5838,10 @@ uint16_t mode_2Dcrazybees(void) { bee_t *bee = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - random16_set_seed(strip.now); + prng.setSeed(strip.now); for (size_t i = 0; i < n; i++) { - bee[i].posX = random8(0, cols); - bee[i].posY = random8(0, rows); + bee[i].posX = prng.random8(0, cols); + bee[i].posY = prng.random8(0, rows); bee[i].aimed(cols, rows); } } @@ -6013,7 +6012,7 @@ uint16_t mode_2Dfloatingblobs(void) { // Bounce balls around for (size_t i = 0; i < Amount; i++) { - if (SEGENV.step < strip.now) blob->color[i] = add8(blob->color[i], 4); // slowly change color + if (SEGENV.step < strip.now) blob->color[i] += 4; // slowly change color // change radius if needed if (blob->grow[i]) { // enlarge radius until it is >= 4 @@ -7509,7 +7508,7 @@ static void soapPixels(bool isRow, uint8_t *noise3d, CRGB *pixels) { else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[indxA]*3); if ((zF >= 0) && (zF < tCR)) PixelB = pixels[indxB]; else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[indxB]*3); - ledsbuff[j] = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); + ledsbuff[j] = (PixelA.nscale8(ease8InOutCubic(255 - fraction))) + (PixelB.nscale8(ease8InOutCubic(fraction))); } for (int j = 0; j < tCR; j++) { CRGB c = ledsbuff[j]; @@ -7862,7 +7861,7 @@ uint16_t mode_particlefireworks(void) { emitparticles = hw_random16(SEGMENT.intensity >> 2) + (SEGMENT.intensity >> 2) + 5; // defines the size of the explosion #endif - if (random16() & 1) { // 50% chance for circular explosion + if (hw_random() & 1) { // 50% chance for circular explosion circularexplosion = true; speed = 2 + hw_random16(3) + ((SEGMENT.intensity >> 6)); currentspeed = speed; @@ -9727,7 +9726,7 @@ uint16_t mode_particleBalance(void) { if (SEGMENT.check3) // random, use perlin noise xgravity = ((int16_t)perlin8(SEGENV.aux0) - 128); else // sinusoidal - xgravity = (int16_t)cos8(SEGENV.aux0) - 128;//((int32_t)(SEGMENT.custom3 << 2) * cos8(SEGENV.aux0) + xgravity = (int16_t)cos8_t(SEGENV.aux0) - 128;//((int32_t)(SEGMENT.custom3 << 2) * cos8(SEGENV.aux0) // scale the force xgravity = (xgravity * ((SEGMENT.custom3+1) << 2)) / 128; // xgravity: -127 to +127 PartSys->applyForce(xgravity); @@ -10081,6 +10080,7 @@ uint16_t mode_particle1DsonicStream(void) { uint32_t baseBin = SEGMENT.custom3 >> 1; // 0 - 15 map(SEGMENT.custom3, 0, 31, 0, 14); loudness = fftResult[baseBin];// + fftResult[baseBin + 1]; + int mids = sqrt32_bw((int)fftResult[5] + (int)fftResult[6] + (int)fftResult[7] + (int)fftResult[8] + (int)fftResult[9] + (int)fftResult[10]); // average the mids, bin 5 is ~500Hz, bin 10 is ~2kHz (see audio_reactive.h) if (baseBin > 12) loudness = loudness << 2; // double loudness for high frequencies (better detecion) diff --git a/wled00/FX.h b/wled00/FX.h index 6481ff7572..7e3e2a85c2 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -18,13 +18,9 @@ #include #include "wled.h" - #include "const.h" #include "bus_manager.h" - -#define FASTLED_INTERNAL //remove annoying pragma messages -#define USE_GET_MILLISECOND_TIMER -#include "FastLED.h" +#include "colors.h" #define DEFAULT_BRIGHTNESS (uint8_t)127 #define DEFAULT_MODE (uint8_t)0 @@ -42,11 +38,6 @@ #define MAX(a,b) ((a)>(b)?(a):(b)) #endif -//color mangling macros -#ifndef RGBW32 -#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() @@ -512,7 +503,7 @@ typedef struct Segment { uint16_t _dur; // -> here is one byte of padding Transition(uint16_t dur=750) - : _palT(CRGBPalette16(CRGB::Black)) + : _palT(CRGBPalette16()) , _prevPaletteBlends(0) , _start(millis()) , _dur(dur) @@ -693,10 +684,10 @@ typedef struct Segment { [[gnu::hot]] uint32_t color_wheel(uint8_t pos) const; // 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 + inline void blurCols(uint8_t blur_amount, bool smear = false) { // blur all columns blur2D(0, blur_amount, smear); } - inline void blurRows(fract8 blur_amount, bool smear = false) { // blur all rows + inline void blurRows(uint8_t blur_amount, bool smear = false) { // blur all rows blur2D(blur_amount, 0, smear); } @@ -767,10 +758,10 @@ 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 box_blur(unsigned i, bool vertical, uint8_t 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 blurRow(int row, uint8_t blur_amount, bool smear = false) {} + inline void blurCol(int col, uint8_t blur_amount, bool smear = 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) {} diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index db8d5a308f..be00255fa0 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -13,6 +13,7 @@ #include "FX.h" #include "FXparticleSystem.h" // TODO: better define the required function (mem service) in FX.h? #include "palettes.h" +#include "colors.h" /* Custom per-LED mapping has moved! @@ -76,7 +77,7 @@ unsigned Segment::_vHeight = 0; uint8_t Segment::_segBri = 0; uint32_t Segment::_currentColors[NUM_COLORS] = {0,0,0}; bool Segment::_colorScaled = false; -CRGBPalette16 Segment::_currentPalette = CRGBPalette16(CRGB::Black); +CRGBPalette16 Segment::_currentPalette = CRGBPalette16(); 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 diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index cff5342565..a17e96c2c0 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -577,7 +577,7 @@ void ParticleSystem2D::ParticleSys_render() { if (blendingStyle == BLEND_STYLE_FADE && SEGMENT.isInTransition() && lastRender + (strip.getFrameTime() >> 1) > strip.now) // fixes speedup during transitions TODO: find a better solution return; lastRender = strip.now; - CRGB baseRGB; + CRGBW baseRGB; uint32_t brightness; // particle brightness, fades if dying static bool useAdditiveTransfer = false; // use add instead of set for buffer transferring (must persist between calls) bool isNonFadeTransition = (pmem->inTransition || pmem->finalTransfer) && blendingStyle != BLEND_STYLE_FADE; @@ -648,21 +648,18 @@ void ParticleSystem2D::ParticleSys_render() { if (fireIntesity) { // fire mode brightness = (uint32_t)particles[i].ttl * (3 + (fireIntesity >> 5)) + 20; brightness = min(brightness, (uint32_t)255); - baseRGB = ColorFromPaletteWLED(SEGPALETTE, brightness, 255); + baseRGB = ColorFromPalette(SEGPALETTE, brightness, 255); } else { brightness = min((particles[i].ttl << 1), (int)255); - baseRGB = ColorFromPaletteWLED(SEGPALETTE, particles[i].hue, 255); + baseRGB = ColorFromPalette(SEGPALETTE, particles[i].hue, 255); if (particles[i].sat < 255) { - CHSV32 baseHSV; - rgb2hsv((uint32_t((byte(baseRGB.r) << 16) | (byte(baseRGB.g) << 8) | (byte(baseRGB.b)))), baseHSV); // convert to HSV + CHSV32 baseHSV = baseRGB; baseHSV.s = particles[i].sat; // set the saturation - uint32_t tempcolor; - hsv2rgb(baseHSV, tempcolor); // convert back to RGB - baseRGB = (CRGB)tempcolor; + hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB } } - renderParticle(i, brightness, baseRGB, particlesettings.wrapX, particlesettings.wrapY); + renderParticle(i, brightness, CRGB(baseRGB), particlesettings.wrapX, particlesettings.wrapY); } if (particlesize > 1) { @@ -1532,7 +1529,7 @@ void ParticleSystem1D::ParticleSys_render() { if (blendingStyle == BLEND_STYLE_FADE && SEGMENT.isInTransition() && lastRender + (strip.getFrameTime() >> 1) > strip.now) // fixes speedup during transitions TODO: find a better solution return; lastRender = strip.now; - CRGB baseRGB; + CRGBW baseRGB; uint32_t brightness; // particle brightness, fades if dying // bool useAdditiveTransfer; // use add instead of set for buffer transferring bool isNonFadeTransition = (pmem->inTransition || pmem->finalTransfer) && blendingStyle != BLEND_STYLE_FADE; @@ -1579,19 +1576,16 @@ void ParticleSystem1D::ParticleSys_render() { // generate RGB values for particle brightness = min(particles[i].ttl << 1, (int)255); - baseRGB = ColorFromPaletteWLED(SEGPALETTE, particles[i].hue, 255); + baseRGB = ColorFromPalette(SEGPALETTE, particles[i].hue, 255); if (advPartProps) { //saturation is advanced property in 1D system if (advPartProps[i].sat < 255) { - CHSV32 baseHSV; - rgb2hsv((uint32_t((byte(baseRGB.r) << 16) | (byte(baseRGB.g) << 8) | (byte(baseRGB.b)))), baseHSV); // convert to HSV + CHSV32 baseHSV = baseRGB; baseHSV.s = advPartProps[i].sat; // set the saturation - uint32_t tempcolor; - hsv2rgb(baseHSV, tempcolor); // convert back to RGB - baseRGB = (CRGB)tempcolor; + hsv2rgb_spectrum(baseHSV, baseRGB); // convert back to RGB } } - renderParticle(i, brightness, baseRGB, particlesettings.wrap); + renderParticle(i, brightness, CRGB(baseRGB), particlesettings.wrap); } // apply smear-blur to rendered frame if (globalSmear > 0) { diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index cee34c2ea2..5cc445da67 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -24,25 +24,14 @@ #include "bus_manager.h" #include "bus_wrapper.h" #include +#include "colors.h" extern bool cctICused; extern bool useParallelI2S; -//colors.cpp -uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); - //udp.cpp uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri=255, bool isRGBW=false); - -//color mangling macros -#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) -#define R(c) (byte((c) >> 16)) -#define G(c) (byte((c) >> 8)) -#define B(c) (byte(c)) -#define W(c) (byte((c) >> 24)) - - static ColorOrderMap _colorOrderMap = {}; bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { diff --git a/wled00/colors.cpp b/wled00/colors.cpp index ba38d5a151..589696ca67 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -1,12 +1,21 @@ #include "wled.h" +#include "fcn_declare.h" +#include "colors.h" /* * Color conversion & utility methods */ /* - * color blend function, based on FastLED blend function - * the calculation for each color is: result = (A*(amountOfA) + A + B*(amountOfB) + B) / 256 with amountOfA = 255 - amountOfB + * FastLED Reference + * ----------------- + * functions in this file derived from FastLED @ 3.6.0 (https://github.com/FastLED/FastLED) are marked with a comment containing "derived from FastLED" + * those functions are therefore licensed under the MIT license See /src/dependencies/fastled/LICENSE.txt for details. + */ + +/* + * color blend function + * the calculation for each color is: result = (C1*(256-blend)+C2+C2*blend) / 256 */ uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { // min / max blend checking is omitted: calls with 0 or 255 are rare, checking lowers overall performance @@ -25,8 +34,7 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { * original idea: https://github.com/wled-dev/WLED/pull/2465 by https://github.com/Proto-molecule * speed optimisations by @dedehai */ -uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) -{ +uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) { if (c1 == BLACK) return c2; if (c2 == BLACK) return c1; const uint32_t TWO_CHANNEL_MASK = 0x00FF00FF; // mask for R and B channels or W and G if negated @@ -38,15 +46,10 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) uint32_t g = wg & 0xFFFF; if (preserveCR) { // preserve color ratios - 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 - //max = g > max ? g : max; - //max = b > max ? b : max; - //max = w > max ? w : max; - if (max > 255) { - const uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead + // find min/max value. note: faster than using min/max functions or on par + uint32_t maxval = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); + if (maxval > 255) { + const uint32_t scale = (uint32_t(255)<<8) / maxval; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead rb = ((rb * scale) >> 8) & TWO_CHANNEL_MASK; wg = (wg * scale) & ~TWO_CHANNEL_MASK; } else wg <<= 8; //shift white and green back to correct position @@ -64,9 +67,7 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool preserveCR) * fades color toward black * if using "video" method the resulting color will never become black unless it is already black */ - -uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) -{ +uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) { 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 @@ -86,8 +87,24 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) return scaledcolor; } -// 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) + +/* + color adjustment in HSV color space (converts RGB to HSV and back), color conversions are not 100% accurate + note: inputs are 32bit to speed up the function, useful input value ranges are -255 to +255 + note2: if only one hue change is needed, use CRGBW.adjust_hue() instead (much faster) +*/ +__attribute__((optimize("O3"))) void adjust_color(CRGBW& rgb, int32_t hueShift, int32_t valueChange, int32_t satChange) { + if(rgb.color32 == 0 && valueChange <= 0) return; // black and no value change -> return black + CHSV32 hsv; + rgb2hsv(rgb, hsv); //convert to HSV + hsv.h += (hueShift << 8); // shift hue (hue is 16 bits) + hsv.s = (int)hsv.s + satChange < 0 ? 0 : ((int)hsv.s + satChange > 255 ? 255 : (int)hsv.s + satChange); + hsv.v = (int)hsv.v + valueChange < 0 ? 0 : ((int)hsv.v + valueChange > 255 ? 255 : (int)hsv.v + valueChange); + hsv2rgb_spectrum(hsv, rgb); // convert back to RGB +} + +// derived from FastLED: replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) +uint32_t ColorFromPalette(const CRGBPalette16& pal, unsigned index, uint8_t brightness, TBlendType blendType) { if (blendType == LINEARBLEND_NOWRAP) { index = (index * 0xF0) >> 8; // Blend range is affected by lo4 blend of values, remap to avoid wrapping @@ -232,64 +249,97 @@ CRGBPalette16 generateRandomPalette() // generate fully random palette CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255))); } -void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0) -{ - 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; +// convert HSV (16bit hue) to RGB (32bit with white = 0), optimized for speed +__attribute__((optimize("O2"))) void hsv2rgb_spectrum(const CHSV32& hsv, CRGBW& rgb) { + unsigned p, q, t; + unsigned region = ((unsigned)hsv.h * 6) >> 16; // h / (65536 / 6) + unsigned remainder = (hsv.h - (region * 10923)) * 6; // 10923 = (65536 / 6) + + // check for zero saturation + if (hsv.s == 0) { + rgb.r = rgb.g = rgb.b = hsv.v; + return; } - region = h / 10923; // 65536 / 6 = 10923 - remainder = (h - (region * 10923)) * 6; - p = (v * (255 - s)) >> 8; - q = (v * (255 - ((s * remainder) >> 16))) >> 8; - t = (v * (255 - ((s * (65535 - remainder)) >> 16))) >> 8; + + p = (hsv.v * (255 - hsv.s)) >> 8; + q = (hsv.v * (255 - ((hsv.s * remainder) >> 16))) >> 8; + t = (hsv.v * (255 - ((hsv.s * (65535 - remainder)) >> 16))) >> 8; switch (region) { case 0: - rgb = v << 16 | t << 8 | p; break; + rgb.r = hsv.v; + rgb.g = t; + rgb.b = p; + break; case 1: - rgb = q << 16 | v << 8 | p; break; + rgb.r = q; + rgb.g = hsv.v; + rgb.b = p; + break; case 2: - rgb = p << 16 | v << 8 | t; break; + rgb.r = p; + rgb.g = hsv.v; + rgb.b = t; + break; case 3: - rgb = p << 16 | q << 8 | v; break; + rgb.r = p; + rgb.g = q; + rgb.b = hsv.v; + break; case 4: - rgb = t << 16 | p << 8 | v; break; + rgb.r = t; + rgb.g = p; + rgb.b = hsv.v; + break; default: - rgb = v << 16 | p << 8 | q; break; + rgb.r = hsv.v; + rgb.g = p; + rgb.b = 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; +// CHSV to CRGB, dumb conversion: slower so this should not be used in time critical code, use rainbow version instead +void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb) { + CHSV32 hsv32(hsv); + CRGBW rgb32; + hsv2rgb_spectrum(hsv32, rgb32); + rgb = CRGB(rgb32); +} + +// convert RGB to HSV (16bit hue), not 100% color accurate. note: using "O3" makes it ~5% faster at minimal flash cost (~20 bytes) +__attribute__((optimize("O3"))) void rgb2hsv(const CRGBW& rgb, CHSV32& hsv) { + int32_t r = rgb.r; // note: using 32bit variables tested faster than 8bit + int32_t g = rgb.g; + int32_t b = rgb.b; 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 + // find min/max value. note: faster than using min/max functions (lets compiler optimize more when using "O3"), other variants (nested ifs, xor) tested slower + maxval = (r > g) ? ((r > b) ? r : b) : ((g > b) ? g : b); + if (maxval == 0) { + hsv.hsv32 = 0; + return; // black, avoids division by zero + } + minval = (r < g) ? ((r < b) ? r : b) : ((g < b) ? g : b); hsv.v = maxval; delta = maxval - minval; - hsv.s = (255 * delta) / maxval; - if (hsv.s == 0) return; // gray value + hsv.s = delta == maxval ? 255 : (255 * delta) / maxval; // faster on fully saturated colors, slightly slower otherwise + // note: early return if s==0 is omitted here to increase speed as gray values are rarely used 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; } +CHSV rgb2hsv(const CRGB c) { // CRGB to CHSV + CHSV32 hsv; + rgb2hsv(CRGBW(c), hsv); + return CHSV(hsv); +} + 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); + CRGBW crgb; + hsv2rgb_spectrum(CHSV32(hue, sat, 255), crgb); + rgb[0] = crgb.r; + rgb[1] = crgb.g; + rgb[2] = crgb.b; } //get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html) diff --git a/wled00/colors.h b/wled00/colors.h new file mode 100644 index 0000000000..e7afe720f3 --- /dev/null +++ b/wled00/colors.h @@ -0,0 +1,169 @@ +#ifndef WLED_COLORS_H +#define WLED_COLORS_H +#include "src/dependencies/fastled/fastled_fcn.h" +/* + Note on color types and conversions: + - WLED uses 32bit colors (RGBW), if possible, use CRGBW instead of CRGB for better performance (no conversion in setPixelColor) + - use CRGB if RAM usage is of concern (i.e. for larger color arrays) + - direct conversion (assignment or construction) from CHSV/CHSV32 to CRGB/CRGBW use the "rainbow" method (nicer colors, see fastled documentation) + - converting CRGB(W) to HSV32 color is quite accurate but still not 100% (but much more accurate than fastled's "hsv2rgb_approximate" function) + - when converting CRGB(W) to HSV32 and back, "hsv2rgb_spectrum" preserves the colors better than the _rainbow version + - to manipulate an RGB color in HSV space, use the adjust_color function or the CRGBW.adjust_hue method + + Some functions in this file are derived from FastLED (https://github.com/FastLED/FastLED) licensed under the MIT license. + See /src/dependencies/fastled/LICENSE.txt for details. +*/ + +// 32bit color mangling macros +#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) +#define R(c) (byte((c) >> 16)) +#define G(c) (byte((c) >> 8)) +#define B(c) (byte(c)) +#define W(c) (byte((c) >> 24)) + +struct CRGBW; +struct CHSV32; + +// similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) +class NeoGammaWLEDMethod { + public: + [[gnu::hot]] static uint8_t Correct(uint8_t value); // apply Gamma to single channel + [[gnu::hot]] static uint32_t Correct32(uint32_t color); // apply Gamma to RGBW32 color (WLED specific, not used by NPB) + static void calcGammaTable(float gamma); // re-calculates & fills gamma table + static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; } // get value from Gamma table (WLED specific, not used by NPB) + private: + static uint8_t gammaT[]; +}; +#define gamma32(c) NeoGammaWLEDMethod::Correct32(c) +#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) +[[gnu::hot, gnu::pure]] uint32_t color_blend(uint32_t c1, uint32_t c2 , uint8_t blend); +inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); }; +[[gnu::hot, gnu::pure]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false); +[[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); +void adjust_color(CRGBW& rgb, int32_t hueShift, int32_t valueChange, int32_t satChange); +[[gnu::hot, gnu::pure]] uint32_t ColorFromPalette(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); +CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette); +CRGBPalette16 generateRandomPalette(); + + +void hsv2rgb_spectrum(const CHSV32& hsv, CRGBW& rgb); +void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb); +void rgb2hsv(const CRGBW& rgb, CHSV32& hsv); +CHSV rgb2hsv(const CRGB c); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); +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 +void colorRGBtoXY(const byte* rgb, float* xy); // only defined if huesync disabled TODO +void colorFromDecOrHexString(byte* rgb, const char* in); +bool colorFromHexString(byte* rgb, const char* in); +uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); +uint16_t approximateKelvinFromRGB(uint32_t rgb); +void setRandomColor(byte* rgb); + +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 hsv32; // 32bit access + }; + inline CHSV32() __attribute__((always_inline)) = default; // default constructor + + // allow construction from hue, saturation, and 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 + + // construction from a 32bit rgb color (white channel is ignored) + inline CHSV32(const CRGBW& rgb) __attribute__((always_inline)); + + inline CHSV32& operator= (const CRGBW& rgb) __attribute__((always_inline)); // assignment from 32bit rgb color (white channel is ignored) +}; + +// 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) + 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 (matches 32 bit colors) + }; + + // 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)) : b(blue), g(green), r(red), w(white) {} + + // Constructor from CRGB + constexpr CRGBW(CRGB rgb) __attribute__((always_inline)) : b(rgb.b), g(rgb.g), r(rgb.r), w(0) {} + + // Constructor from CHSV32 + inline CRGBW(CHSV32 hsv) __attribute__((always_inline)) { hsv2rgb_rainbow(hsv.h, hsv.s, hsv.v, raw, true); } + + // Constructor from CHSV + inline CRGBW(CHSV hsv) __attribute__((always_inline)) { hsv2rgb_rainbow(hsv.h<<8, hsv.s, hsv.v, raw, true); } + + // 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 CHSV32 + inline CRGBW& operator=(CHSV32 hsv) __attribute__((always_inline)) { hsv2rgb_rainbow(hsv.h, hsv.s, hsv.v, raw, true); return *this; } + + // Assignment from CHSV + inline CRGBW& operator=(CHSV hsv) __attribute__((always_inline)) { hsv2rgb_rainbow(hsv.h<<8, hsv.s, hsv.v, raw, true); return *this; } + + // Assignment from r, g, b, w + 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)) { + return color32; + } + + // adjust hue: input range is 256 for full color cycle, input can be negative + inline void adjust_hue(int hueshift) __attribute__((always_inline)) { + CHSV32 hsv = *this; + hsv.h += hueshift << 8; + hsv2rgb_spectrum(hsv, *this); + } + + // get the average of the R, G, B and W values + uint8_t getAverageLight() const { + return (r + g + b + w) >> 2; + } +}; + +inline CHSV32::CHSV32(const CRGBW& rgb) { + rgb2hsv(rgb, *this); +} + +inline CHSV32& CHSV32::operator= (const CRGBW& rgb) { // assignment from 32bit rgb color (white channel is ignored) + rgb2hsv(rgb, *this); + return *this; +} + + +#endif \ No newline at end of file diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index ff2443648d..ed76b8cdf6 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -2,6 +2,8 @@ #ifndef WLED_FCN_DECLARE_H #define WLED_FCN_DECLARE_H +#include "colors.h" + /* * All globally accessible functions are declared here */ @@ -69,124 +71,6 @@ typedef struct WiFiConfig { } } wifi_config; -//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) - 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 - 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)) : b(blue), g(green), r(red), w(white) {} - - // Constructor from CRGB - 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]; } - - // 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)) { 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)) { - 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 { - 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: - [[gnu::hot]] static uint8_t Correct(uint8_t value); // apply Gamma to single channel - [[gnu::hot]] static uint32_t Correct32(uint32_t color); // apply Gamma to RGBW32 color (WLED specific, not used by NPB) - static void calcGammaTable(float gamma); // re-calculates & fills gamma table - static inline uint8_t rawGamma8(uint8_t val) { return gammaT[val]; } // get value from Gamma table (WLED specific, not used by NPB) - private: - static uint8_t gammaT[]; -}; -#define gamma32(c) NeoGammaWLEDMethod::Correct32(c) -#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c) -[[gnu::hot, gnu::pure]] uint32_t color_blend(uint32_t c1, uint32_t c2 , uint8_t blend); -inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return color_blend(c1, c2, b >> 8); }; -[[gnu::hot, gnu::pure]] uint32_t color_add(uint32_t, uint32_t, bool preserveCR = false); -[[gnu::hot, gnu::pure]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false); -[[gnu::hot, gnu::pure]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); -CRGBPalette16 generateHarmonicRandomPalette(const 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 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 -void colorRGBtoXY(const byte* rgb, float* xy); // only defined if huesync disabled TODO -void colorFromDecOrHexString(byte* rgb, const char* in); -bool colorFromHexString(byte* rgb, const char* in); -uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); -uint16_t approximateKelvinFromRGB(uint32_t rgb); -void setRandomColor(byte* rgb); - //dmx_output.cpp void initDMXOutput(); void handleDMXOutput(); @@ -509,9 +393,13 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL int16_t extractModeDefaults(uint8_t mode, const char *segVar); void checkSettingsPIN(const char *pin); uint16_t crc16(const unsigned char* data_p, size_t length); -uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0); -uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0); -uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0); +uint16_t beat88(uint16_t beats_per_minute_88, uint32_t timebase = 0); +uint16_t beat16(uint16_t beats_per_minute, uint32_t timebase = 0); +uint8_t beat8(uint16_t beats_per_minute, uint32_t timebase = 0); +uint16_t beatsin88_t(uint16_t beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0); +uint16_t beatsin16_t(uint16_t beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0); +uint8_t beatsin8_t(uint16_t beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0); + um_data_t* simulateSound(uint8_t simulationId); void enumerateLedmaps(); [[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos); @@ -569,6 +457,9 @@ void clearEEPROM(); #endif //wled_math.cpp +#define sin_t sin_approx +#define cos_t cos_approx +#define tan_t tan_approx //float cos_t(float phi); // use float math //float sin_t(float phi); //float tan_t(float x); @@ -586,9 +477,6 @@ template T atan_t(T x); float floor_t(float x); float fmod_t(float num, float denom); uint32_t sqrt32_bw(uint32_t x); -#define sin_t sin_approx -#define cos_t cos_approx -#define tan_t tan_approx /* #include // standard math functions. use a lot of flash @@ -601,6 +489,7 @@ uint32_t sqrt32_bw(uint32_t x); #define fmod_t fmodf #define floor_t floorf */ + //wled_serial.cpp void handleSerial(); void updateBaudRate(uint32_t rate); diff --git a/wled00/ir.cpp b/wled00/ir.cpp index 9f7950a2fb..4ea42ba454 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -2,6 +2,7 @@ #ifndef WLED_DISABLE_INFRARED #include "ir_codes.h" +#include "colors.h" /* * Infrared sensor support for several generic RGB remotes and custom JSON remote @@ -128,22 +129,18 @@ 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(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 - prim_hsv.h = (byte)new_val; - hsv2rgb_rainbow(prim_hsv, fastled_col); + CRGBW newcolor = CRGBW(sseg.colors[0]); + newcolor.adjust_hue(amount); + newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { Segment& seg = strip.getSegment(i); if (!seg.isActive() || !seg.isSelected()) continue; - seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0])); + seg.colors[0] = newcolor.color32; } setValuesFromFirstSelectedSeg(); } else { - strip.getMainSegment().colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0])); + strip.getMainSegment().colors[0] = newcolor.color32; setValuesFromMainSeg(); } } @@ -172,20 +169,21 @@ 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(fastled_col); - int16_t new_val = (int16_t) prim_hsv.s + amount; + + CHSV32 prim_hsv = CRGBW(sseg.colors[0]); + int32_t new_val = (int32_t)prim_hsv.s + amount; prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255 - hsv2rgb_rainbow(prim_hsv, fastled_col); + CRGBW newcolor = prim_hsv; + newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { Segment& seg = strip.getSegment(i); if (!seg.isActive() || !seg.isSelected()) continue; - seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0])); + seg.colors[0] = newcolor; } setValuesFromFirstSelectedSeg(); } else { - strip.getMainSegment().colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0])); + strip.getMainSegment().colors[0] = newcolor; setValuesFromMainSeg(); } } diff --git a/wled00/palettes.h b/wled00/palettes.h index c84c1fb97a..c6b558c8a3 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -3,17 +3,141 @@ * 4 bytes per color: index, red, green, blue */ +#ifndef PalettesWLED_h +#define PalettesWLED_h + +#include "src/dependencies/fastled/fastled_fcn.h" + +// FastLED Palettes +// ---------------- +// Palettes imported from FastLED @ 3.6.0 (https://github.com/FastLED/FastLED) are licensed under the MIT license +// See /src/dependencies/fastled/LICENSE.txt for details + +// Cloudy color palette +const TProgmemRGBPalette16 CloudColors_p PROGMEM = { + CRGB::Blue, + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + CRGB::DarkBlue, + + CRGB::Blue, + CRGB::DarkBlue, + CRGB::SkyBlue, + CRGB::SkyBlue, + + CRGB::LightBlue, + CRGB::White, + CRGB::LightBlue, + CRGB::SkyBlue +}; + +// Lava color palette +const TProgmemRGBPalette16 LavaColors_p PROGMEM = { + CRGB::Black, + CRGB::Maroon, + CRGB::Black, + CRGB::Maroon, + + CRGB::DarkRed, + CRGB::DarkRed, + CRGB::Maroon, + CRGB::DarkRed, + + CRGB::DarkRed, + CRGB::DarkRed, + CRGB::Red, + CRGB::Orange, + + CRGB::White, + CRGB::Orange, + CRGB::Red, + CRGB::DarkRed +}; + +// Ocean colors, blues and whites +const TProgmemRGBPalette16 OceanColors_p PROGMEM = { + CRGB::MidnightBlue, + CRGB::DarkBlue, + CRGB::MidnightBlue, + CRGB::Navy, + + CRGB::DarkBlue, + CRGB::MediumBlue, + CRGB::SeaGreen, + CRGB::Teal, + + CRGB::CadetBlue, + CRGB::Blue, + CRGB::DarkCyan, + CRGB::CornflowerBlue, + + CRGB::Aquamarine, + CRGB::SeaGreen, + CRGB::Aqua, + CRGB::LightSkyBlue +}; + +// Forest colors, greens +const TProgmemRGBPalette16 ForestColors_p PROGMEM = { + CRGB::DarkGreen, + CRGB::DarkGreen, + CRGB::DarkOliveGreen, + CRGB::DarkGreen, + + CRGB::Green, + CRGB::ForestGreen, + CRGB::OliveDrab, + CRGB::Green, + + CRGB::SeaGreen, + CRGB::MediumAquamarine, + CRGB::LimeGreen, + CRGB::YellowGreen, + + CRGB::LightGreen, + CRGB::LawnGreen, + CRGB::MediumAquamarine, + CRGB::ForestGreen +}; + +// HSV Rainbow +const TProgmemRGBPalette16 RainbowColors_p PROGMEM = { + 0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00, + 0xABAB00, 0x56D500, 0x00FF00, 0x00D52A, + 0x00AB55, 0x0056AA, 0x0000FF, 0x2A00D5, + 0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B}; + +// HSV Rainbow colors with alternatating stripes of black +const TProgmemRGBPalette16 RainbowStripeColors_p PROGMEM = { + 0xFF0000, 0x000000, 0xAB5500, 0x000000, + 0xABAB00, 0x000000, 0x00FF00, 0x000000, + 0x00AB55, 0x000000, 0x0000FF, 0x000000, + 0x5500AB, 0x000000, 0xAB0055, 0x000000}; + +// HSV color ramp: blue, purple, pink, red, orange, yellow (and back). +// Basically, everything but the greens, which tend to make +// people's skin look unhealthy. This palette is good for +// lighting at a club or party, where it'll be shining on people. +const TProgmemRGBPalette16 PartyColors_p PROGMEM = { + 0x5500AB, 0x84007C, 0xB5004B, 0xE5001B, + 0xE81700, 0xB84700, 0xAB7700, 0xABAB00, + 0xAB5500, 0xDD2200, 0xF2000E, 0xC2003E, + 0x8F0071, 0x5F00A1, 0x2F00D0, 0x0007F9}; +// end of FastLED palettes + + // From ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb -// Unfortunately, these are stored in RAM! // Gradient palette "ib_jul01_gp", originally from // http://soliton.vm.bytemark.co.uk/pub/cpt-city/ing/xmas/tn/ib_jul01.png.index.html // converted for FastLED with gammas (2.6, 2.2, 2.5) // Size: 16 bytes of program space. -#ifndef PalettesWLED_h -#define PalettesWLED_h - const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, 94, 1, 29, 18, diff --git a/wled00/prng.h b/wled00/prng.h new file mode 100644 index 0000000000..da0a84f124 --- /dev/null +++ b/wled00/prng.h @@ -0,0 +1,23 @@ +#include "wled.h" + +// Simple and fast Pseudo-Random-Number-Generator for 16bit and 8bit random numbers +// allows the same sequence of random numbers to be generated by setting the 16bit-seed +// values found by brute-force test algorithm: sequence has no repetitions and good randomness for its simplicity +class PRNG { +private: + uint16_t seed; +public: + PRNG(uint16_t initialSeed = 0x1234) : seed(initialSeed) {} + void setSeed(uint16_t s) { seed = s; } + uint16_t getSeed() const { return seed; } + uint16_t random16() { + seed = seed * 3001 + 31683; + seed ^= seed >> 7; + return seed; + } + uint16_t random16(uint16_t lim) { return ((uint32_t)random16() * lim) >> 16; } + uint16_t random16(uint16_t min, uint16_t lim) { uint16_t delta = lim - min; return random16(delta) + min; } + uint8_t random8() { return random16(); } + uint8_t random8(uint8_t lim) { return (uint8_t)(((uint16_t)random8() * lim) >> 8); } + uint8_t random8(uint8_t min, uint8_t lim) { uint8_t delta = lim - min; return random8(delta) + min; } +}; diff --git a/wled00/src/dependencies/fastled/LICENSE.txt b/wled00/src/dependencies/fastled/LICENSE.txt new file mode 100644 index 0000000000..ebe476330b --- /dev/null +++ b/wled00/src/dependencies/fastled/LICENSE.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 FastLED + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wled00/src/dependencies/fastled/fastled_fcn.cpp b/wled00/src/dependencies/fastled/fastled_fcn.cpp new file mode 100644 index 0000000000..87449db406 --- /dev/null +++ b/wled00/src/dependencies/fastled/fastled_fcn.cpp @@ -0,0 +1,259 @@ +#include "fastled_fcn.h" + +// Code originally from FastLED version 3.6.0. Optimized for WLED use by @dedehai +// Licensed unter MIT license, see LICENSE.txt for details + +// convert HSV (16bit hue) to RGB (24bit), optimized for speed (integer types and function arguments were very carefully selected for best performance) +// this does the same as the FastLED hsv2rgb_rainbow function but with 16bit hue and optimizations for use with CRGB as well as CRGBW +// note: this function is used when converting CHSV->CRGB or CHSV32->CRGBW by assignment or constructor, there is no need to call it explicitly +__attribute__((optimize("O2"))) void hsv2rgb_rainbow(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW) { + uint8_t hue = h>>8; + uint8_t sat = s; + uint32_t val = v; + uint32_t offset = h & 0x1FFF; // 0..31 + uint32_t third16 = (offset * 21846); // offset16 = offset * 1/3<<16 + uint8_t third = third16 >> 21; // max = 85 + uint8_t r, g, b; // note: making these 32bit is significantly slower + + if (!(hue & 0x80)) { + if (!(hue & 0x40)) { // section 0-1 + if (!(hue & 0x20)) { + r = 255 - third; + g = third; + b = 0; + } else { + r = 171; + g = 85 + third; + b = 0; + } + } else { // section 2-3 + if (!(hue & 0x20)) { + uint8_t twothirds = third16 >> 20; // max=170 + r = 171 - twothirds; + g = 170 + third; + b = 0; + } else { + r = 0; + g = 255 - third; + b = third; + } + } + } else { // section 4-7 + if (!(hue & 0x40)) { + if (!(hue & 0x20)) { + r = 0; + uint8_t twothirds = third16 >> 20; // max=170 + g = 171 - twothirds; + b = 85 + twothirds; + } else { + r = third; + g = 0; + b = 255 - third; + } + } else { + if (!(hue & 0x20)) { + r = 85 + third; + g = 0; + b = 171 - third; + } else { + r = 170 + third; + g = 0; + b = 85 - third; + } + } + } + + // scale down colors if desaturated and add the brightness_floor to r, g, and b. + if (sat != 255) { + if (sat == 0) { + r = 255; + g = 255; + b = 255; + } else { + //we know sat is < 255 and > 1, lets use that: scale8video is always +1, so drop the conditional + uint32_t desat = 255 - sat; + desat = (desat * desat); // scale8_video(desat, desat) but more accurate, dropped the "+1" for speed: visual difference is negligible + uint8_t brightness_floor = desat >> 8; + uint32_t satscale = 0xFFFF - desat; + if (r) r = ((r * satscale) >> 16); + if (g) g = ((g * satscale) >> 16); + if (b) b = ((b * satscale) >> 16); + + r += brightness_floor; + g += brightness_floor; + b += brightness_floor; + } + } + + // scale everything down if value < 255. + if (val != 255) { + if (val == 0) { + r = 0; + g = 0; + b = 0; + } else { + val = val*val + 512; // = scale8_video(val,val)+2; + if (r) r = ((r * val) >> 16) + 1; + if (g) g = ((g * val) >> 16) + 1; + if (b) b = ((b * val) >> 16) + 1; + } + } + if(isRGBW) { + rgbdata[0] = b; + rgbdata[1] = g; + rgbdata[2] = r; + //rgbdata[3] = 0; // white + } else { + rgbdata[0] = r; + rgbdata[1] = g; + rgbdata[2] = b; + } +} + + +// black body radiation to RGB +CRGB HeatColor(uint8_t temperature) { + CRGB heatcolor; + uint8_t t192 = (((int)temperature * 191) >> 8) + (temperature ? 1 : 0); // scale down, but keep 1 as minimum + // calculate a value that ramps up from zero to 255 in each 'third' of the scale. + uint8_t heatramp = t192 & 0x3F; // 0..63 + heatramp <<= 2; // scale up to 0..252 + heatcolor.r = 255; + heatcolor.b = 0; + if(t192 & 0x80) { // we're in the hottest third + heatcolor.g = 255; // full green + heatcolor.b = heatramp; // ramp up blue + } else if(t192 & 0x40) { // we're in the middle third + heatcolor.g = heatramp; // ramp up green + } else { // we're in the coolest third + heatcolor.r = heatramp; // ramp up red + heatcolor.g = 0; // no green + } + return heatcolor; +} + +// CRGB color fill functions (from fastled, used for color palettes) +void fill_solid_RGB(CRGB* colors, uint32_t num, const CRGB& c1) { + for(uint32_t i = 0; i < num; i++) { + colors[i] = c1; + } +} + +// fill CRGB array with a color gradient +void fill_gradient_RGB(CRGB* colors, uint32_t startpos, CRGB startcolor, uint32_t endpos, CRGB endcolor) { + if(endpos < startpos) { // if the points are in the wrong order, flip them + unsigned t = endpos; + CRGB tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + int rdistance = endcolor.r - startcolor.r; + int gdistance = endcolor.g - startcolor.g; + int bdistance = endcolor.b - startcolor.b; + + int divisor = endpos - startpos; + divisor = divisor == 0 ? 1 : divisor; // prevent division by zero + + int rdelta = (rdistance << 16) / divisor; + int gdelta = (gdistance << 16) / divisor; + int bdelta = (bdistance << 16) / divisor; + + int rshifted = startcolor.r << 16; + int gshifted = startcolor.g << 16; + int bshifted = startcolor.b << 16; + + for (unsigned i = startpos; i <= endpos; i++) { + colors[i] = CRGB(rshifted >> 16, gshifted >> 16, bshifted >> 16); + rshifted += rdelta; + gshifted += gdelta; + bshifted += bdelta; + } +} + +void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c2) { + uint32_t last = num - 1; + fill_gradient_RGB(colors, 0, c1, last, c2); +} + +void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c2, const CRGB& c3) { + uint32_t half = (num / 2); + uint32_t last = num - 1; + fill_gradient_RGB(colors, 0, c1, half, c2); + fill_gradient_RGB(colors, half, c2, last, c3); +} + +void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) { + uint32_t onethird = (num / 3); + uint32_t twothirds = ((num * 2) / 3); + uint32_t last = num - 1; + fill_gradient_RGB(colors, 0, c1, onethird, c2); + fill_gradient_RGB(colors, onethird, c2, twothirds, c3); + fill_gradient_RGB(colors, twothirds, c3, last, c4); +} + +// palette blending +void nblendPaletteTowardPalette(CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges) { + uint8_t* p1; + uint8_t* p2; + uint32_t changes = 0; + p1 = (uint8_t*)current.entries; + p2 = (uint8_t*)target.entries; + const uint32_t totalChannels = sizeof(CRGBPalette16); + for (uint32_t i = 0; i < totalChannels; ++i) { + if (p1[i] == p2[i]) continue; // if the values are equal, no changes are needed + if (p1[i] < p2[i]) { ++p1[i]; ++changes; } // if the current value is less than the target, increase it by one + if (p1[i] > p2[i]) { // if the current value is greater than the target, increase it by one (or two if it's still greater). + --p1[i]; ++changes; + if (p1[i] > p2[i]) + --p1[i]; + } + if(changes >= maxChanges) + break; + } +} + +// cubic ease function (S-curve: 3x^2 - 2x^3) +// 8-bit +uint8_t ease8InOutCubic(uint8_t i) { + uint32_t ii = ((uint32_t)i * i) >> 8; + uint32_t factor = (3 << 8) - (((uint32_t)i << 1)); // 3 - 2i + return (ii * factor) >> 8; +} +// 16-bit +uint16_t ease16InOutCubic(uint16_t i) { + uint32_t ii = ((uint32_t)i * i) >> 16; + uint32_t factor = (3 << 16) - (((uint32_t)i << 1)); // 3 - 2i + return (ii * factor) >> 16; +} + +// quadradic ease function (S-curve: x^2) +uint8_t ease8InOutQuad(uint8_t i) +{ + uint32_t j = i; + if (j & 0x80) j = 255 - j; // mirror if > 127 + uint32_t jj = (j * j) >> 7; + return (i & 0x80) ? (255 - jj) : jj; +} + +// triangular wave generator +uint8_t triwave8(uint8_t in) { + if (in & 0x80) in = 255 - in; + return in << 1; +} + +uint16_t triwave16(uint16_t in) { + if (in < 0x8000) return in *2; + return 0xFFFF - (in - 0x8000)*2; +} + +// quadratic waveform generator. Spends just a little more time at the limits than "sine" does. +uint8_t quadwave8(uint8_t in) { + return ease8InOutQuad(triwave8(in)); +} + +// cubic waveform generator. Spends visibly more time at the limits than "sine" does. +uint8_t cubicwave8(uint8_t in) { + return ease8InOutCubic(triwave8(in)); +} diff --git a/wled00/src/dependencies/fastled/fastled_fcn.h b/wled00/src/dependencies/fastled/fastled_fcn.h new file mode 100644 index 0000000000..6c18144f6d --- /dev/null +++ b/wled00/src/dependencies/fastled/fastled_fcn.h @@ -0,0 +1,784 @@ +#ifndef FASTLED_FCN_H +#define FASTLED_FCN_H + +#include +#include // for mem operations + +// Code originally from FastLED version 3.6.0. Optimized for WLED use by @dedehai +// Licensed unter MIT license, see LICENSE.txt for details + +// inline math functions +__attribute__ ((always_inline)) inline uint8_t scale8(uint8_t i, uint8_t scale ) { return ((int)i * (1 + (int)scale)) >> 8; } +__attribute__ ((always_inline)) inline uint8_t scale8_video(uint8_t i, uint8_t scale ) { return (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0); } +__attribute__ ((always_inline)) inline uint16_t scale16(uint16_t i, uint16_t scale ) { return ((uint32_t)i * (1 + (uint32_t)scale)) >> 16; } +__attribute__ ((always_inline)) inline uint8_t qadd8(uint8_t i, uint8_t j) { unsigned t = i + j; return t > 255 ? 255 : t; } +__attribute__ ((always_inline)) inline uint8_t qsub8(uint8_t i, uint8_t j) { int t = i - j; return t < 0 ? 0 : t; } +__attribute__ ((always_inline)) inline uint8_t qmul8(uint8_t i, uint8_t j) { unsigned p = (unsigned)i * (unsigned)j; return p > 255 ? 255 : p; } +__attribute__ ((always_inline)) inline int8_t abs8(int8_t i) { return i < 0 ? -i : i; } +__attribute__ ((always_inline)) inline int8_t lerp8by8(uint8_t a, uint8_t b, uint8_t frac) { return a + ((((int32_t)b - (int32_t)a) * ((int32_t)frac+1)) >> 8); } + +//forward declarations +struct CRGB; +struct CHSV; +class CRGBPalette16; + +typedef uint32_t TProgmemRGBPalette16[16]; +typedef uint8_t TDynamicRGBGradientPalette_byte; // Byte of an RGB gradient entry, stored in dynamic (heap) memory +typedef const TDynamicRGBGradientPalette_byte *TDynamicRGBGradientPalette_bytes; // Pointer to bytes of an RGB gradient, stored in dynamic (heap) memory +typedef TDynamicRGBGradientPalette_bytes TDynamicRGBGradientPalettePtr; // Alias of ::TDynamicRGBGradientPalette_bytes +typedef const uint8_t TProgmemRGBGradientPalette_byte; +typedef const TProgmemRGBGradientPalette_byte *TProgmemRGBGradientPalette_bytes; +typedef TProgmemRGBGradientPalette_bytes TProgmemRGBGradientPalettePtr; + +// color interpolation options for palette +typedef enum { + NOBLEND=0, // No interpolation between palette entries + LINEARBLEND=1, // Linear interpolation between palette entries, with wrap-around from end to the beginning again + LINEARBLEND_NOWRAP=2 // Linear interpolation between palette entries, but no wrap-around +} TBlendType; + +typedef union { + struct { + uint8_t index; // index of the color entry in the gradient + uint8_t r; + uint8_t g; + uint8_t b; + }; + uint32_t dword; // values packed as 32-bit + uint8_t bytes[4]; // values as an array +} TRGBGradientPaletteEntryUnion; + +// function prototypes +void hsv2rgb_rainbow(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW); +CRGB HeatColor(uint8_t temperature); // black body radiation +void fill_solid_RGB(CRGB* colors, uint32_t num, const CRGB& c1) ; +void fill_gradient_RGB(CRGB* colors, uint32_t startpos, CRGB startcolor, uint32_t endpos, CRGB endcolor); +void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c2); +void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c2, const CRGB& c3); +void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4); +void nblendPaletteTowardPalette(CRGBPalette16& current, CRGBPalette16& target, uint8_t maxChanges); + +uint8_t ease8InOutCubic(uint8_t i); +uint16_t ease16InOutCubic(uint16_t i); +uint8_t ease8InOutQuad(uint8_t i); +uint8_t triwave8(uint8_t in); +uint16_t triwave16(uint16_t in); +uint8_t quadwave8(uint8_t in); +uint8_t cubicwave8(uint8_t in); + +// Representation of an HSV pixel (hue, saturation, value (aka brightness)). +struct CHSV { + union { + struct { + union { + uint8_t hue; + uint8_t h; + }; + union { + uint8_t saturation; + uint8_t sat; + uint8_t s; + }; + union { + uint8_t value; + uint8_t val; + uint8_t v; + }; + }; + uint8_t raw[3]; // order is: hue [0], saturation [1], value [2] + }; + + inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) { + return raw[x]; + } + + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { + return raw[x]; + } + + // default constructor + // @warning default values are UNITIALIZED! + inline CHSV() __attribute__((always_inline)) = default; + + // allow construction from hue, saturation, and value + inline CHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) + : h(ih), s(is), v(iv) { } + + // allow copy construction + inline CHSV(const CHSV& rhs) __attribute__((always_inline)) = default; + + // allow copy construction + inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) = default; + + // assign new HSV values + inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) { + h = ih; + s = is; + v = iv; + return *this; + } +}; + +// representation of an RGB pixel (Red, Green, Blue) +struct CRGB { + union { + struct { + union { + uint8_t r; + uint8_t red; + }; + union { + uint8_t g; + uint8_t green; + }; + union { + uint8_t b; + uint8_t blue; + }; + }; + uint8_t raw[3]; // order is: 0 = red, 1 = green, 2 = blue + }; + + inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) { return raw[x]; } + + // array access operator to index into the CRGB object + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) { return raw[x]; } + + // default constructor (uninitialized) + inline CRGB() __attribute__((always_inline)) = default; + + // allow construction from red, green, and blue + inline CRGB(uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline)) + : r(ir), g(ig), b(ib) { } + + // allow construction from 32-bit (really 24-bit) bit 0xRRGGBB color code + inline CRGB(uint32_t colorcode) __attribute__((always_inline)) + : r(uint8_t(colorcode>>16)), g(uint8_t(colorcode>>8)), b(uint8_t(colorcode)) { } + + // allow copy construction + inline CRGB(const CRGB& rhs) __attribute__((always_inline)) = default; + + // allow construction from a CHSV color + inline CRGB(const CHSV& rhs) __attribute__((always_inline)) { + hsv2rgb_rainbow(rhs.h<<8, rhs.s, rhs.v, raw, false); + } + + // allow assignment from hue, saturation, and value + inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline)) { + hsv2rgb_rainbow(hue<<8, sat, val, raw, false); return *this; + } + + // allow assignment from just a hue, sat and val are set to max + inline CRGB& setHue (uint8_t hue) __attribute__((always_inline)) { + hsv2rgb_rainbow(hue<<8, 255, 255, raw, false); return *this; + } + + // allow assignment from HSV color + inline CRGB& operator= (const CHSV& rhs) __attribute__((always_inline)) { + hsv2rgb_rainbow(rhs.h<<8, rhs.s, rhs.v, raw, false); return *this; + } + // allow assignment from one RGB struct to another + inline CRGB& operator= (const CRGB& rhs) __attribute__((always_inline)) = default; + + // allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code + inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline)) { + r = uint8_t(colorcode>>16); + g = uint8_t(colorcode>>8); + b = uint8_t(colorcode); + return *this; + } + + // allow assignment from red, green, and blue + inline CRGB& setRGB (uint8_t nr, uint8_t ng, uint8_t nb) __attribute__((always_inline)) { + r = nr; + g = ng; + b = nb; + return *this; + } + + // allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code + inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) { + r = uint8_t(colorcode>>16); + g = uint8_t(colorcode>>8); + b = uint8_t(colorcode); + return *this; + } + + // add one CRGB to another, saturating at 0xFF for each channel + inline CRGB& operator+= (const CRGB& rhs) { + r = qadd8(r, rhs.r); + g = qadd8(g, rhs.g); + b = qadd8(b, rhs.b); + return *this; + } + + // add a constant to each channel, saturating at 0xFF + inline CRGB& addToRGB (uint8_t d) { + r = qadd8(r, d); + g = qadd8(g, d); + b = qadd8(b, d); + return *this; + } + + // subtract one CRGB from another, saturating at 0x00 for each channel + inline CRGB& operator-= (const CRGB& rhs) { + r = qsub8(r, rhs.r); + g = qsub8(g, rhs.g); + b = qsub8(b, rhs.b); + return *this; + } + + // subtract a constant from each channel, saturating at 0x00 + inline CRGB& subtractFromRGB(uint8_t d) { + r = qsub8(r, d); + g = qsub8(g, d); + b = qsub8(b, d); + return *this; + } + + // subtract a constant of '1' from each channel, saturating at 0x00 + inline CRGB& operator-- () __attribute__((always_inline)) { + subtractFromRGB(1); + return *this; + } + + // operator-- + inline CRGB operator-- (int) __attribute__((always_inline)) { + CRGB retval(*this); + --(*this); + return retval; + } + + // add a constant of '1' to each channel, saturating at 0xFF + inline CRGB& operator++ () __attribute__((always_inline)) { + addToRGB(1); + return *this; + } + + // operator++ + inline CRGB operator++ (int) __attribute__((always_inline)) { + CRGB retval(*this); + ++(*this); + return retval; + } + + // divide each of the channels by a constant + inline CRGB& operator/= (uint8_t d) { + r /= d; + g /= d; + b /= d; + return *this; + } + + // right shift each of the channels by a constant + inline CRGB& operator>>= (uint8_t d) { + r >>= d; + g >>= d; + b >>= d; + return *this; + } + + // multiply each of the channels by a constant, saturating each channel at 0xFF. + inline CRGB& operator*= (uint8_t d) { + r = qmul8(r, d); + g = qmul8(g, d); + b = qmul8(b, d); + return *this; + } + + // scale down a RGB to N/256ths of its current brightness (will not scale all the way to black) + inline CRGB& nscale8_video(uint8_t scaledown) { + uint8_t nonzeroscale = (scaledown != 0) ? 1 : 0; + r = (r == 0) ? 0 : (((int)r * (int)(scaledown)) >> 8) + nonzeroscale; + g = (g == 0) ? 0 : (((int)g * (int)(scaledown)) >> 8) + nonzeroscale; + b = (b == 0) ? 0 : (((int)b * (int)(scaledown)) >> 8) + nonzeroscale; + return *this; + } + + // scale down a RGB to N/256ths of its current brightness (can scale to black) + inline CRGB& nscale8(uint8_t scaledown) { + uint32_t scale_fixed = scaledown + 1; + r = (((uint32_t)r) * scale_fixed) >> 8; + g = (((uint32_t)g) * scale_fixed) >> 8; + b = (((uint32_t)b) * scale_fixed) >> 8; + return *this; + } + + inline CRGB& nscale8(const CRGB& scaledown) { + r = ::scale8(r, scaledown.r); + g = ::scale8(g, scaledown.g); + b = ::scale8(b, scaledown.b); + return *this; + } + + // return a CRGB object that is a scaled down version of this object + inline CRGB scale8(uint8_t scaledown) const { + CRGB out = *this; + uint32_t scale_fixed = scaledown + 1; + out.r = (((uint32_t)out.r) * scale_fixed) >> 8; + out.g = (((uint32_t)out.g) * scale_fixed) >> 8; + out.b = (((uint32_t)out.b) * scale_fixed) >> 8; + return out; + } + + // return a CRGB object that is a scaled down version of this object + inline CRGB scale8(const CRGB& scaledown) const { + CRGB out; + out.r = ::scale8(r, scaledown.r); + out.g = ::scale8(g, scaledown.g); + out.b = ::scale8(b, scaledown.b); + return out; + } + + // fadeToBlackBy is a synonym for nscale8(), as a fade instead of a scale + inline CRGB& fadeToBlackBy(uint8_t fadefactor) { + uint32_t scale_fixed = 256 - fadefactor; + r = (((uint32_t)r) * scale_fixed) >> 8; + g = (((uint32_t)g) * scale_fixed) >> 8; + b = (((uint32_t)b) * scale_fixed) >> 8; + return *this; + } + + // "or" operator brings each channel up to the higher of the two values + inline CRGB& operator|=(const CRGB& rhs) { + if (rhs.r > r) r = rhs.r; + if (rhs.g > g) g = rhs.g; + if (rhs.b > b) b = rhs.b; + return *this; + } + + inline CRGB& operator|=(uint8_t d) { + if (d > r) r = d; + if (d > g) g = d; + if (d > b) b = d; + return *this; + } + + // "and" operator brings each channel down to the lower of the two values + inline CRGB& operator&=(const CRGB& rhs) { + if (rhs.r < r) r = rhs.r; + if (rhs.g < g) g = rhs.g; + if (rhs.b < b) b = rhs.b; + return *this; + } + + inline CRGB& operator&=(uint8_t d) { + if (d < r) r = d; + if (d < g) g = d; + if (d < b) b = d; + return *this; + } + + // this allows testing a CRGB for zero-ness + inline explicit operator bool() const __attribute__((always_inline)) { + return r || g || b; + } + + // converts a CRGB to a 32-bit color with white = 0 + inline explicit operator uint32_t() const { + return (uint32_t{r} << 16) | + (uint32_t{g} << 8) | + uint32_t{b}; + } + + // invert each channel + inline CRGB operator-() const { + CRGB retval; + retval.r = 255 - r; + retval.g = 255 - g; + retval.b = 255 - b; + return retval; + } + + // get the average of the R, G, and B values + inline uint8_t getAverageLight() const { + return ((r + g + b) * 21846) >> 16; // x*21846>>16 is equal to "divide by 3" + } + + typedef enum { + AliceBlue=0xF0F8FF, + Amethyst=0x9966CC, + AntiqueWhite=0xFAEBD7, + Aqua=0x00FFFF, + Aquamarine=0x7FFFD4, + Azure=0xF0FFFF, + Beige=0xF5F5DC, + Bisque=0xFFE4C4, + Black=0x000000, + BlanchedAlmond=0xFFEBCD, + Blue=0x0000FF, + BlueViolet=0x8A2BE2, + Brown=0xA52A2A, + BurlyWood=0xDEB887, + CadetBlue=0x5F9EA0, + Chartreuse=0x7FFF00, + Chocolate=0xD2691E, + Coral=0xFF7F50, + CornflowerBlue=0x6495ED, + Cornsilk=0xFFF8DC, + Crimson=0xDC143C, + Cyan=0x00FFFF, + DarkBlue=0x00008B, + DarkCyan=0x008B8B, + DarkGoldenrod=0xB8860B, + DarkGray=0xA9A9A9, + DarkGrey=0xA9A9A9, + DarkGreen=0x006400, + DarkKhaki=0xBDB76B, + DarkMagenta=0x8B008B, + DarkOliveGreen=0x556B2F, + DarkOrange=0xFF8C00, + DarkOrchid=0x9932CC, + DarkRed=0x8B0000, + DarkSalmon=0xE9967A, + DarkSeaGreen=0x8FBC8F, + DarkSlateBlue=0x483D8B, + DarkSlateGray=0x2F4F4F, + DarkSlateGrey=0x2F4F4F, + DarkTurquoise=0x00CED1, + DarkViolet=0x9400D3, + DeepPink=0xFF1493, + DeepSkyBlue=0x00BFFF, + DimGray=0x696969, + DimGrey=0x696969, + DodgerBlue=0x1E90FF, + FireBrick=0xB22222, + FloralWhite=0xFFFAF0, + ForestGreen=0x228B22, + Fuchsia=0xFF00FF, + Gainsboro=0xDCDCDC, + GhostWhite=0xF8F8FF, + Gold=0xFFD700, + Goldenrod=0xDAA520, + Gray=0x808080, + Grey=0x808080, + Green=0x008000, + GreenYellow=0xADFF2F, + Honeydew=0xF0FFF0, + HotPink=0xFF69B4, + IndianRed=0xCD5C5C, + Indigo=0x4B0082, + Ivory=0xFFFFF0, + Khaki=0xF0E68C, + Lavender=0xE6E6FA, + LavenderBlush=0xFFF0F5, + LawnGreen=0x7CFC00, + LemonChiffon=0xFFFACD, + LightBlue=0xADD8E6, + LightCoral=0xF08080, + LightCyan=0xE0FFFF, + LightGoldenrodYellow=0xFAFAD2, + LightGreen=0x90EE90, + LightGrey=0xD3D3D3, + LightPink=0xFFB6C1, + LightSalmon=0xFFA07A, + LightSeaGreen=0x20B2AA, + LightSkyBlue=0x87CEFA, + LightSlateGray=0x778899, + LightSlateGrey=0x778899, + LightSteelBlue=0xB0C4DE, + LightYellow=0xFFFFE0, + Lime=0x00FF00, + LimeGreen=0x32CD32, + Linen=0xFAF0E6, + Magenta=0xFF00FF, + Maroon=0x800000, + MediumAquamarine=0x66CDAA, + MediumBlue=0x0000CD, + MediumOrchid=0xBA55D3, + MediumPurple=0x9370DB, + MediumSeaGreen=0x3CB371, + MediumSlateBlue=0x7B68EE, + MediumSpringGreen=0x00FA9A, + MediumTurquoise=0x48D1CC, + MediumVioletRed=0xC71585, + MidnightBlue=0x191970, + MintCream=0xF5FFFA, + MistyRose=0xFFE4E1, + Moccasin=0xFFE4B5, + NavajoWhite=0xFFDEAD, + Navy=0x000080, + OldLace=0xFDF5E6, + Olive=0x808000, + OliveDrab=0x6B8E23, + Orange=0xFFA500, + OrangeRed=0xFF4500, + Orchid=0xDA70D6, + PaleGoldenrod=0xEEE8AA, + PaleGreen=0x98FB98, + PaleTurquoise=0xAFEEEE, + PaleVioletRed=0xDB7093, + PapayaWhip=0xFFEFD5, + PeachPuff=0xFFDAB9, + Peru=0xCD853F, + Pink=0xFFC0CB, + Plaid=0xCC5533, + Plum=0xDDA0DD, + PowderBlue=0xB0E0E6, + Purple=0x800080, + Red=0xFF0000, + RosyBrown=0xBC8F8F, + RoyalBlue=0x4169E1, + SaddleBrown=0x8B4513, + Salmon=0xFA8072, + SandyBrown=0xF4A460, + SeaGreen=0x2E8B57, + Seashell=0xFFF5EE, + Sienna=0xA0522D, + Silver=0xC0C0C0, + SkyBlue=0x87CEEB, + SlateBlue=0x6A5ACD, + SlateGray=0x708090, + SlateGrey=0x708090, + Snow=0xFFFAFA, + SpringGreen=0x00FF7F, + SteelBlue=0x4682B4, + Tan=0xD2B48C, + Teal=0x008080, + Thistle=0xD8BFD8, + Tomato=0xFF6347, + Turquoise=0x40E0D0, + Violet=0xEE82EE, + Wheat=0xF5DEB3, + White=0xFFFFFF, + WhiteSmoke=0xF5F5F5, + Yellow=0xFFFF00, + YellowGreen=0x9ACD32, + FairyLight=0xFFE42D, // LED RGB color that roughly approximates the color of incandescent fairy lights + FairyLightNCC=0xFF9D2A // if using no color correction, use this + } HTMLColorCode; +}; + +__attribute__((always_inline)) inline CRGB operator+(const CRGB& p1, const CRGB& p2) { + return CRGB(qadd8(p1.r, p2.r), qadd8(p1.g, p2.g), qadd8(p1.b, p2.b)); +} + +__attribute__((always_inline)) inline CRGB operator-(const CRGB& p1, const CRGB& p2) { + return CRGB(qsub8(p1.r, p2.r), qsub8(p1.g, p2.g), qsub8(p1.b, p2.b)); +} + +__attribute__((always_inline)) inline bool operator== (const CRGB& lhs, const CRGB& rhs) { + return (lhs.r == rhs.r) && (lhs.g == rhs.g) && (lhs.b == rhs.b); +} + +__attribute__((always_inline)) inline bool operator!= (const CRGB& lhs, const CRGB& rhs) { + return !(lhs == rhs); +} + +// RGB color palette with 16 discrete values +class CRGBPalette16 { +public: + CRGB entries[16]; + CRGBPalette16() { + memset(entries, 0, sizeof(entries)); // default constructor: set all to black + } + + // Create palette from 16 CRGB values + CRGBPalette16(const CRGB& c00, const CRGB& c01, const CRGB& c02, const CRGB& c03, + const CRGB& c04, const CRGB& c05, const CRGB& c06, const CRGB& c07, + const CRGB& c08, const CRGB& c09, const CRGB& c10, const CRGB& c11, + const CRGB& c12, const CRGB& c13, const CRGB& c14, const CRGB& c15) { + entries[0] = c00; entries[1] = c01; entries[2] = c02; entries[3] = c03; + entries[4] = c04; entries[5] = c05; entries[6] = c06; entries[7] = c07; + entries[8] = c08; entries[9] = c09; entries[10] = c10; entries[11] = c11; + entries[12] = c12; entries[13] = c13; entries[14] = c14; entries[15] = c15; + }; + + // Copy constructor + CRGBPalette16(const CRGBPalette16& rhs) { + memmove((void*)&(entries[0]), &(rhs.entries[0]), sizeof(entries)); + } + + // Create palette from array of CRGB colors + CRGBPalette16(const CRGB rhs[16]) { + memmove((void*)&(entries[0]), &(rhs[0]), sizeof(entries)); + } + + // Copy assignment operator + CRGBPalette16& operator=(const CRGBPalette16& rhs) { + memmove((void*)&(entries[0]), &(rhs.entries[0]), sizeof(entries)); + return *this; + } + + // Create palette from array of CRGB colors + CRGBPalette16& operator=(const CRGB rhs[16]) { + memmove((void*)&(entries[0]), &(rhs[0]), sizeof(entries)); + return *this; + } + + // Create palette from palette stored in PROGMEM + CRGBPalette16(const TProgmemRGBPalette16& rhs) { + for (int i = 0; i < 16; ++i) { + entries[i] = *(const uint32_t*)(rhs + i); + } + } + + // Copy assignment operator for PROGMEM palette + CRGBPalette16& operator=(const TProgmemRGBPalette16& rhs) { + for (int i = 0; i < 16; ++i) { + entries[i] = *(const uint32_t*)(rhs + i); + } + return *this; + } + + // Equality operator + bool operator==(const CRGBPalette16& rhs) const { + const uint8_t* p = (const uint8_t*)(&(this->entries[0])); + const uint8_t* q = (const uint8_t*)(&(rhs.entries[0])); + if (p == q) return true; + for (unsigned i = 0; i < (sizeof(entries)); ++i) { + if (*p != *q) return false; + ++p; + ++q; + } + return true; + } + + // Inequality operator + bool operator!=(const CRGBPalette16& rhs) const { + return !(*this == rhs); + } + + // Array subscript operator + inline CRGB& operator[](uint8_t x) __attribute__((always_inline)) { + return entries[x]; + } + + // Array subscript operator (const) + inline const CRGB& operator[](uint8_t x) const __attribute__((always_inline)) { + return entries[x]; + } + + // Array subscript operator + inline CRGB& operator[](int x) __attribute__((always_inline)) { + return entries[(uint8_t)x]; + } + + // Array subscript operator (const) + inline const CRGB& operator[](int x) const __attribute__((always_inline)) { + return entries[(uint8_t)x]; + } + + // Get the underlying pointer to the CRGB entries making up the palette + operator CRGB*() { + return &(entries[0]); + } + + // Create palette from a single CRGB color + CRGBPalette16(const CRGB& c1) { + fill_solid_RGB(&(entries[0]), 16, c1); + } + + // Create palette from two CRGB colors + CRGBPalette16(const CRGB& c1, const CRGB& c2) { + fill_gradient_RGB(&(entries[0]), 16, c1, c2); + } + + // Create palette from three CRGB colors + CRGBPalette16(const CRGB& c1, const CRGB& c2, const CRGB& c3) { + fill_gradient_RGB(&(entries[0]), 16, c1, c2, c3); + } + + // Create palette from four CRGB colors + CRGBPalette16(const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) { + fill_gradient_RGB(&(entries[0]), 16, c1, c2, c3, c4); + } + + // Creates a palette from a gradient palette in PROGMEM. + // + // Gradient palettes are loaded into CRGBPalettes in such a way + // that, if possible, every color represented in the gradient palette + // is also represented in the CRGBPalette, this may not preserve original + // color spacing, but will try to not omit small color bands. + + CRGBPalette16(TProgmemRGBGradientPalette_bytes progpal) { + *this = progpal; + } + + CRGBPalette16& operator=(TProgmemRGBGradientPalette_bytes progpal) { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + int count = 0; + do { + u.dword = *(const uint32_t*)(progent + count); + ++count; + } while (u.index != 255); + + int lastSlotUsed = -1; + + u.dword = *(const uint32_t*)(progent); + CRGB rgbstart(u.r, u.g, u.b); + + int indexstart = 0; + int istart8 = 0; + int iend8 = 0; + while (indexstart < 255) { + ++progent; + u.dword = *(const uint32_t*)(progent); + int indexend = u.index; + CRGB rgbend(u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if (count < 16) { + if ((istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if (iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB(&(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + + // Creates a palette from a gradient palette in dynamic (heap) memory. + CRGBPalette16& loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes gpal) { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + unsigned count = 0; + do { + u = *(ent + count); + ++count; + } while (u.index != 255); + + int lastSlotUsed = -1; + + u = *ent; + CRGB rgbstart(u.r, u.g, u.b); + + int indexstart = 0; + int istart8 = 0; + int iend8 = 0; + while (indexstart < 255) { + ++ent; + u = *ent; + int indexend = u.index; + CRGB rgbend(u.r, u.g, u.b); + istart8 = indexstart / 16; + iend8 = indexend / 16; + if (count < 16) { + if ((istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) { + istart8 = lastSlotUsed + 1; + if (iend8 < istart8) { + iend8 = istart8; + } + } + lastSlotUsed = iend8; + } + fill_gradient_RGB(&(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } +}; + +#endif \ No newline at end of file diff --git a/wled00/util.cpp b/wled00/util.cpp index ac8a162073..2e1c459576 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -1,7 +1,7 @@ #include "wled.h" #include "fcn_declare.h" #include "const.h" - +#include "src/dependencies/fastled/fastled_fcn.h" //helper to get int value at a position in string int getNumVal(const String* req, uint16_t pos) @@ -376,39 +376,59 @@ uint16_t crc16(const unsigned char* data_p, size_t length) { return crc; } -// fastled beatsin: 1:1 replacements to remove the use of fastled sin16() +// FastLED Reference +// ----------------- +// The following beat functions derived from FastLED @ 3.6.0 (https://github.com/FastLED/FastLED) are licensed under the MIT license +// See /src/dependencies/fastled/LICENSE.txt for details + +// Generates a 16-bit "sawtooth" wave at a given BPM, with BPM specified in Q8.8 fixed-point format: +// for 120 BPM it would be 120*256 = 30720. If you just want to specify "120", use beat16() or beat8(). +// timebase is the time offset of the wave from the millis() timer +uint16_t beat88(uint16_t beats_per_minute_88, uint32_t timebase) { + return ((millis() - timebase) * beats_per_minute_88 * 280) >> 16; +} + +// Generates a 16-bit "sawtooth" wave at a given BPM +uint16_t beat16(uint16_t beats_per_minute, uint32_t timebase) { + if (beats_per_minute < 256) beats_per_minute <<= 8; + return beat88(beats_per_minute, timebase); +} + +/// Generates an 8-bit "sawtooth" wave at a given BPM +uint8_t beat8(uint16_t beats_per_minute, uint32_t timebase) { + return beat16(beats_per_minute, timebase) >> 8; +} + // Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details. -uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset) -{ - uint16_t beat = beat88( beats_per_minute_88, timebase); - uint16_t beatsin (sin16_t( beat + phase_offset) + 32768); - uint16_t rangewidth = highest - lowest; - uint16_t scaledbeat = scale16( beatsin, rangewidth); - uint16_t result = lowest + scaledbeat; - return result; +uint16_t beatsin88_t(uint16_t beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset) { + uint16_t beat = beat88(beats_per_minute_88, timebase); + uint16_t beatsin = (sin16_t( beat + phase_offset) + 32768); + uint16_t rangewidth = highest - lowest; + uint16_t scaledbeat = scale16(beatsin, rangewidth); + uint16_t result = lowest + scaledbeat; + return result; } // Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details. -uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset) -{ - uint16_t beat = beat16( beats_per_minute, timebase); - uint16_t beatsin = (sin16_t( beat + phase_offset) + 32768); - uint16_t rangewidth = highest - lowest; - uint16_t scaledbeat = scale16( beatsin, rangewidth); - uint16_t result = lowest + scaledbeat; - return result; +uint16_t beatsin16_t(uint16_t beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset) { + uint16_t beat = beat16(beats_per_minute, timebase); + uint16_t beatsin = sin16_t(beat + phase_offset) + 32768; + uint16_t rangewidth = highest - lowest; + uint16_t scaledbeat = scale16(beatsin, rangewidth); + uint16_t result = lowest + scaledbeat; + return result; } // Generates an 8-bit sine wave at a given BPM that oscillates within a given range. see fastled for details. -uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset) -{ - uint8_t beat = beat8( beats_per_minute, timebase); - uint8_t beatsin = sin8_t( beat + phase_offset); - uint8_t rangewidth = highest - lowest; - uint8_t scaledbeat = scale8( beatsin, rangewidth); - uint8_t result = lowest + scaledbeat; - return result; +uint8_t beatsin8_t(uint16_t beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset) { + uint8_t beat = beat8(beats_per_minute, timebase); + uint8_t beatsin = sin8_t(beat + phase_offset); + uint8_t rangewidth = highest - lowest; + uint8_t scaledbeat = scale8(beatsin, rangewidth); + uint8_t result = lowest + scaledbeat; + return result; } +// end of FastLED functions /////////////////////////////////////////////////////////////////////////////// // Begin simulateSound (to enable audio enhanced effects to display something) @@ -635,7 +655,7 @@ static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) } // Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! -static inline __attribute__((always_inline)) int32_t gradient1D(uint32_t x0, int32_t dx) { +static int32_t gradient1D(uint32_t x0, int32_t dx) { uint32_t h = x0 * 0x27D4EB2D; h ^= h >> 15; h *= 0x92C3412B; diff --git a/wled00/wled.cpp b/wled00/wled.cpp index cc338d23f2..7f89168591 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -513,10 +513,6 @@ void WLED::setup() DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap()); #endif - // Seed FastLED random functions with an esp random value, which already works properly at this point. - const uint32_t seed32 = hw_random(); - random16_set_seed((uint16_t)seed32); - #if WLED_WATCHDOG_TIMEOUT > 0 enableWatchdog(); #endif diff --git a/wled00/wled.h b/wled00/wled.h index 8926967ff9..5fe6c01536 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -184,8 +184,9 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; #define FASTLED_INTERNAL //remove annoying pragma messages #define USE_GET_MILLISECOND_TIMER -#include "FastLED.h" + #include "const.h" +#include "colors.h" #include "fcn_declare.h" #include "NodeStruct.h" #include "pin_manager.h" @@ -1037,13 +1038,6 @@ WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0); //macro to convert F to const #define SET_F(x) (const char*)F(x) -//color mangling macros -#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) -#define R(c) (byte((c) >> 16)) -#define G(c) (byte((c) >> 8)) -#define B(c) (byte(c)) -#define W(c) (byte((c) >> 24)) - class WLED { public: WLED(); diff --git a/wled00/wled_math.cpp b/wled00/wled_math.cpp index 43c593080e..8b2d927b22 100644 --- a/wled00/wled_math.cpp +++ b/wled00/wled_math.cpp @@ -244,3 +244,4 @@ uint32_t sqrt32_bw(uint32_t x) { } return res; } +