From dec3fe5e4127d493412f2e48993f8430d129fe94 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Mon, 17 Mar 2025 20:06:22 +0100 Subject: [PATCH 01/15] removed fastled dependencies - copied relevant functions - optimized some of them for ESP32 --- platformio.ini | 4 +- usermods/Analog_Clock/Analog_Clock.h | 2 +- wled00/FX.cpp | 92 +- wled00/FX.h | 20 +- wled00/FX_fcn.cpp | 2 +- wled00/bus_manager.cpp | 13 +- wled00/colors.cpp | 245 ++++- wled00/colors.h | 1276 ++++++++++++++++++++++++++ wled00/fcn_declare.h | 185 ++-- wled00/ir.cpp | 31 +- wled00/palettes.h | 119 +++ wled00/util.cpp | 85 +- wled00/wled.cpp | 4 - wled00/wled.h | 10 +- wled00/wled_math.cpp | 23 + 15 files changed, 1861 insertions(+), 250 deletions(-) create mode 100644 wled00/colors.h diff --git a/platformio.ini b/platformio.ini index 5b1b212656..1eb6719c0d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -136,7 +136,7 @@ upload_speed = 115200 # ------------------------------------------------------------------------------ lib_compat_mode = strict lib_deps = - fastled/FastLED @ 3.6.0 + ;fastled/FastLED @ 3.6.0 IRremoteESP8266 @ 2.8.2 makuna/NeoPixelBus @ 2.8.3 #https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta @@ -232,7 +232,7 @@ lib_deps_compat = ESPAsyncTCP @ 1.2.2 ESPAsyncUDP ESP8266PWM - fastled/FastLED @ 3.6.0 + ;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.h b/usermods/Analog_Clock/Analog_Clock.h index 9d82f7670c..175888aa0e 100644 --- a/usermods/Analog_Clock/Analog_Clock.h +++ b/usermods/Analog_Clock/Analog_Clock.h @@ -117,7 +117,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/wled00/FX.cpp b/wled00/FX.cpp index e5132da57b..027e16541a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -13,6 +13,7 @@ #include "wled.h" #include "FX.h" #include "fcn_declare.h" +#include "colors.h" #if !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D)) #include "FXparticleSystem.h" @@ -68,17 +69,14 @@ //#define MAX_FREQUENCY 5120 //#define MAX_FREQ_LOG10 3.71f +PRNG prng; // pseudo-random number generator class + // 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 @@ -1760,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@!;;"; @@ -2271,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; @@ -2546,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; @@ -2581,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) { @@ -2596,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; } @@ -2619,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(); @@ -2644,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. @@ -4147,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"; @@ -4179,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))); } @@ -4922,8 +4921,9 @@ 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 + color_fade(color, (255-rate), true); if (grad) SEGMENT.fadePixelColorXY(dx, dy, rate); } @@ -5824,9 +5824,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 +5839,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 +6013,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] = blob->color[i] + 4; // slowly change color // change radius if needed if (blob->grow[i]) { // enlarge radius until it is >= 4 @@ -7509,7 +7509,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]; @@ -7818,7 +7818,7 @@ uint16_t mode_particlefireworks(void) { else if (PartSys->sources[j].source.vy < 0) { // rocket is exploded and time is up (ttl=0 and negative speed), relaunch it PartSys->sources[j].source.y = PS_P_RADIUS; // start from bottom PartSys->sources[j].source.x = (PartSys->maxX >> 2) + hw_random(PartSys->maxX >> 1); // centered half - PartSys->sources[j].source.vy = (SEGMENT.custom3) + random16(SEGMENT.custom1 >> 3) + 5; // rocket speed TODO: need to adjust for segment height + PartSys->sources[j].source.vy = (SEGMENT.custom3) + hw_random16(SEGMENT.custom1 >> 3) + 5; // rocket speed TODO: need to adjust for segment height PartSys->sources[j].source.vx = hw_random16(7) - 3; // not perfectly straight up PartSys->sources[j].source.sat = 30; // low saturation -> exhaust is off-white PartSys->sources[j].source.ttl = hw_random16(SEGMENT.custom1) + (SEGMENT.custom1 >> 1); // set fuse time @@ -7862,7 +7862,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; @@ -7888,7 +7888,7 @@ uint16_t mode_particlefireworks(void) { counter = 0; speed += 3 + ((SEGMENT.intensity >> 6)); // increase speed to form a second wave PartSys->sources[j].source.hue += hueincrement; // new color for next circle - PartSys->sources[j].source.sat = min((uint16_t)150, random16()); + PartSys->sources[j].source.sat = min((uint16_t)150, hw_random16()); } angle += angleincrement; // set angle for next particle } @@ -9723,7 +9723,7 @@ uint16_t mode_particleBalance(void) { if (SEGMENT.check3) // random, use perlin noise xgravity = ((int16_t)inoise8(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); @@ -10050,7 +10050,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 = sqrt16((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) + 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 3544e1fa7f..c0a26caed2 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,10 +38,12 @@ #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() @@ -510,7 +508,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) @@ -689,10 +687,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); } @@ -763,10 +761,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 7bd875ad0d..595fde823b 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -76,7 +76,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/bus_manager.cpp b/wled00/bus_manager.cpp index 6423a436b1..07a5444877 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)) - - bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { if (count() >= WLED_MAX_COLOR_ORDER_MAPPINGS || len == 0 || (colorOrder & 0x0F) > COL_ORDER_MAX) return false; // upper nibble contains W swap information _mappings.push_back({start,len,colorOrder}); diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 7bfd71c292..e6f7d239fd 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -1,5 +1,6 @@ #include "wled.h" - +#include "fcn_declare.h" +//#include "colors.h" // todo: needed? its already included in fcn declare -> seems to compile fine without this include -> remove? /* * Color conversion & utility methods */ @@ -259,6 +260,139 @@ void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB } } +inline CRGB hsv2rgb(const CHSV& hsv) { // CHSV to CRGB + CHSV32 hsv32(hsv); + uint32_t rgb; + hsv2rgb(hsv32, rgb); + return CRGB(rgb); +} + + +/// @cond +#define K255 255 +#define K171 171 +#define K170 170 +#define K85 85 +/// @endcond + +// rainbow spectrum, adapted from fastled //!!! TODO: check and optimized this function + +void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { + uint8_t hue = hsv.hue; + uint8_t sat = hsv.sat; + uint8_t val = hsv.val; + uint8_t offset = hue & 0x1F; // 0..31 + uint8_t offset8 = offset << 3; // offset8 = offset * 8 + uint8_t third = scale8( offset8, (256 / 3)); // max = 85 + uint8_t r, g, b; + if( ! (hue & 0x80) ) { + // 0XX + if( ! (hue & 0x40) ) { + // 00X + //section 0-1 + if( ! (hue & 0x20) ) { + // 000 + //case 0: // R -> O + r = K255 - third; + g = third; + b = 0; + } else { + r = K171; + g = K85 + third ; + b = 0; + } + } else { + //01X + // section 2-3 + if( ! (hue & 0x20) ) { + + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + r = K171 - twothirds; + g = K170 + third; + b = 0; + + } else { + // 011 + // case 3: // G -> A + r = 0; + g = K255 - third; + b = third; + } + } + } else { + // section 4-7 + // 1XX + if( ! (hue & 0x40) ) { + // 10X + if( ! ( hue & 0x20) ) { + // 100 + //case 4: // A -> B + r = 0; + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + g = K171 - twothirds; //K170? + b = K85 + twothirds; + } else { + // 101 + //case 5: // B -> P + r = third; + g = 0; + b = K255 - third; + } + } else { + if( ! (hue & 0x20) ) { + // 110 + //case 6: // P -- K + r = K85 + third; + g = 0; + b = K171 - third; + } else { + // 111 + //case 7: // K -> R + r = K170 + third; + g = 0; + b = K85 - third; + } + } + } + // Scale down colors if we're desaturated at all + // and add the brightness_floor to r, g, and b. + if( sat != 255 ) { + if( sat == 0) { + r = 255; b = 255; g = 255; + } else { + uint8_t desat = 255 - sat; + desat = scale8_video( desat, desat); + uint8_t satscale = 255 - desat; + if( r ) r = scale8( r, satscale) + 1; + if( g ) g = scale8( g, satscale) + 1; + if( b ) b = scale8( b, satscale) + 1; + + uint8_t brightness_floor = desat; + r += brightness_floor; + g += brightness_floor; + b += brightness_floor; + } + } + + // Now scale everything down if we're at value < 255. + if( val != 255 ) { + val = scale8_video( val, val); + if( val == 0 ) { + r=0; g=0; b=0; + } else { + if( r ) r = scale8( r, val) + 1; + if( g ) g = scale8( g, val) + 1; + if( b ) b = scale8( b, val) + 1; + } + } + + rgb.r = r; + rgb.g = g; + rgb.b = b; +} + 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; @@ -280,6 +414,12 @@ void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), else hsv.h = 43690 + (10923 * (r - g)) / delta; } +inline CHSV rgb2hsv(const CRGB c) { // CRGB to CHSV + CHSV32 hsv; + rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), 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); @@ -335,6 +475,27 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins } } +// black body radiation to RGB (from fastled) +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; +} + #ifndef WLED_DISABLE_HUESYNC void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) { @@ -543,3 +704,85 @@ uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct32(uint32_t color) b = gammaT[b]; return RGBW32(r, g, b, w); } + +// 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 + uint32_t t = endpos; + CRGB tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; + } + int32_t rdistance = endcolor.r - startcolor.r; + int32_t gdistance = endcolor.g - startcolor.g; + int32_t bdistance = endcolor.b - startcolor.b; + + int32_t divisor = endpos - startpos; + divisor = divisor == 0 ? 1 : divisor; // prevent division by zero + + int32_t rdelta = (rdistance << 16) / divisor; + int32_t gdelta = (gdistance << 16) / divisor; + int32_t bdelta = (bdistance << 16) / divisor; + + int32_t rshifted = startcolor.r << 16; + int32_t gshifted = startcolor.g << 16; + int32_t bshifted = startcolor.b << 16; + + for (int32_t 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; + } +} \ No newline at end of file diff --git a/wled00/colors.h b/wled00/colors.h new file mode 100644 index 0000000000..f15d64280b --- /dev/null +++ b/wled00/colors.h @@ -0,0 +1,1276 @@ +#ifndef WLED_COLORS_H +#define WLED_COLORS_H + +// note: some functions/structs have been copied from fastled library, modified and optimized for WLED + +#define ColorFromPalette ColorFromPaletteWLED // override fastled version //!!!todo: rename + +// 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)) + +//forward declarations +struct CRGB; +struct CHSV; +struct CRGBW; +struct CHSV32; +class CRGBPalette16; +uint8_t scale8( uint8_t i, uint8_t scale ); +uint8_t qadd8( uint8_t i, uint8_t j); +uint8_t qsub8( uint8_t i, uint8_t j); +int8_t abs8(int8_t i); + +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; ///< CRGB::red channel value of the color entry + uint8_t g; ///< CRGB::green channel value of the color entry + uint8_t b; ///< CRGB::blue channel value of the color entry + }; + uint32_t dword; ///< values as a packed 32-bit double word + uint8_t bytes[4]; ///< values as an array +} TRGBGradientPaletteEntryUnion; + +// 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(); + +void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); +CRGB hsv2rgb(const CHSV& hsv); +void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb); +void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); +void rgb2hsv(const uint32_t rgbw, CHSV32& hsv); +CHSV rgb2hsv(const CRGB c); +void colorKtoRGB(uint16_t kelvin, byte* rgb); +void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb +CRGB HeatColor(uint8_t temperature); // black body radiation +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); +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); + +// 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 + /// @param x the index to retrieve (0-2) + /// @returns the CRGB::raw value for the given index + inline const uint8_t& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return raw[x]; + } + + /// Default constructor + /// @warning Default values are UNITIALIZED! + inline CRGB() __attribute__((always_inline)) = default; + + /// Allow construction from red, green, and blue + /// @param ir input red value + /// @param ig input green value + /// @param ib input blue value + 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 + /// @param colorcode a packed 24 bit color code + inline CRGB( uint32_t colorcode) __attribute__((always_inline)) + : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) + { + } +/* + /// Allow construction from a LEDColorCorrection enum + /// @param colorcode an LEDColorCorrect enumeration value + inline CRGB( LEDColorCorrection colorcode) __attribute__((always_inline)) + : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) + { + + } + + /// Allow construction from a ColorTemperature enum + /// @param colorcode an ColorTemperature enumeration value + inline CRGB( ColorTemperature colorcode) __attribute__((always_inline)) + : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) + { + + } +*/ + /// 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, *this); + } + /// Allow assignment from hue, saturation, and value + inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline)) + { + hsv2rgb_rainbow( CHSV(hue, sat, val), *this); + return *this; + } + + /// Allow assignment from just a hue. + /// Saturation and value (brightness) are set automatically to max. + /// @param hue color hue + inline CRGB& setHue (uint8_t hue) __attribute__((always_inline)) + { + hsv2rgb_rainbow( CHSV(hue, 255, 255), *this); + return *this; + } + + /// Allow assignment from HSV color + inline CRGB& operator= (const CHSV& rhs) __attribute__((always_inline)) + { + hsv2rgb_rainbow(rhs, *this); + 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 + /// @param colorcode a packed 24 bit color code + inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline)) + { + r = (colorcode >> 16) & 0xFF; + g = (colorcode >> 8) & 0xFF; + b = (colorcode >> 0) & 0xFF; + return *this; + } + + /// Allow assignment from red, green, and blue + /// @param nr new red value + /// @param ng new green value + /// @param nb new blue value + 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 + /// @param colorcode a packed 24 bit color code + inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) + { + r = (colorcode >> 16) & 0xFF; + g = (colorcode >> 8) & 0xFF; + b = (colorcode >> 0) & 0xFF; + 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. + /// @note This is NOT an operator+= overload because the compiler + /// can't usefully decide when it's being passed a 32-bit + /// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue) + 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. + /// @note This is NOT an operator+= overload because the compiler + /// can't usefully decide when it's being passed a 32-bit + /// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue) + 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; + } + + /// @copydoc operator-- + inline CRGB operator-- (int ) __attribute__((always_inline)) + { + CRGB retval(*this); + --(*this); + return retval; + } + + /// Add a constant of '1' from each channel, saturating at 0xFF + inline CRGB& operator++ () __attribute__((always_inline)) + { + addToRGB(1); + return *this; + } + + /// @copydoc 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 ) + { + unsigned red = (unsigned)r * (unsigned)d; + unsigned green = (unsigned)r * (unsigned)d; + unsigned blue = (unsigned)r * (unsigned)d; + if( red > 255) red = 255; + if( green > 255) green = 255; + if( blue > 255) blue = 255; + r = red; + g = green; + b = blue; + return *this; + } + + 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; + } + +/* + /// fadeLightBy is a synonym for nscale8_video(), as a fade instead of a scale + /// @param fadefactor the amount to fade, sent to nscale8_video() as (255 - fadefactor) + inline CRGB& fadeLightBy (uint8_t fadefactor ) + { + nscale8x3_video( r, g, b, 255 - fadefactor); + return *this; + } +*/ + /// Scale down a RGB to N/256ths of its current brightness, using + /// "plain math" dimming rules. "Plain math" dimming rules means that the low light + /// levels may dim all the way to 100% black. + /// @see nscale8x3 + 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; + } + + /// Scale down a RGB to N/256ths of its current brightness, using + /// "plain math" dimming rules. "Plain math" dimming rules means that the low light + /// levels may dim all the way to 100% black. + /// @see ::scale8 + 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 + /// @param fadefactor the amount to fade, sent to nscale8() as (255 - fadefactor) + 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; + } + + /// @copydoc operator|= + 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; + } + + /// @copydoc operator&= + 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 having an alpha of 255. + inline explicit operator uint32_t() const + { + return uint32_t{0xff000000} | + (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" + } + + /// Predefined RGB colors + typedef enum { + AliceBlue=0xF0F8FF, ///< @htmlcolorblock{F0F8FF} + Amethyst=0x9966CC, ///< @htmlcolorblock{9966CC} + AntiqueWhite=0xFAEBD7, ///< @htmlcolorblock{FAEBD7} + Aqua=0x00FFFF, ///< @htmlcolorblock{00FFFF} + Aquamarine=0x7FFFD4, ///< @htmlcolorblock{7FFFD4} + Azure=0xF0FFFF, ///< @htmlcolorblock{F0FFFF} + Beige=0xF5F5DC, ///< @htmlcolorblock{F5F5DC} + Bisque=0xFFE4C4, ///< @htmlcolorblock{FFE4C4} + Black=0x000000, ///< @htmlcolorblock{000000} + BlanchedAlmond=0xFFEBCD, ///< @htmlcolorblock{FFEBCD} + Blue=0x0000FF, ///< @htmlcolorblock{0000FF} + BlueViolet=0x8A2BE2, ///< @htmlcolorblock{8A2BE2} + Brown=0xA52A2A, ///< @htmlcolorblock{A52A2A} + BurlyWood=0xDEB887, ///< @htmlcolorblock{DEB887} + CadetBlue=0x5F9EA0, ///< @htmlcolorblock{5F9EA0} + Chartreuse=0x7FFF00, ///< @htmlcolorblock{7FFF00} + Chocolate=0xD2691E, ///< @htmlcolorblock{D2691E} + Coral=0xFF7F50, ///< @htmlcolorblock{FF7F50} + CornflowerBlue=0x6495ED, ///< @htmlcolorblock{6495ED} + Cornsilk=0xFFF8DC, ///< @htmlcolorblock{FFF8DC} + Crimson=0xDC143C, ///< @htmlcolorblock{DC143C} + Cyan=0x00FFFF, ///< @htmlcolorblock{00FFFF} + DarkBlue=0x00008B, ///< @htmlcolorblock{00008B} + DarkCyan=0x008B8B, ///< @htmlcolorblock{008B8B} + DarkGoldenrod=0xB8860B, ///< @htmlcolorblock{B8860B} + DarkGray=0xA9A9A9, ///< @htmlcolorblock{A9A9A9} + DarkGrey=0xA9A9A9, ///< @htmlcolorblock{A9A9A9} + DarkGreen=0x006400, ///< @htmlcolorblock{006400} + DarkKhaki=0xBDB76B, ///< @htmlcolorblock{BDB76B} + DarkMagenta=0x8B008B, ///< @htmlcolorblock{8B008B} + DarkOliveGreen=0x556B2F, ///< @htmlcolorblock{556B2F} + DarkOrange=0xFF8C00, ///< @htmlcolorblock{FF8C00} + DarkOrchid=0x9932CC, ///< @htmlcolorblock{9932CC} + DarkRed=0x8B0000, ///< @htmlcolorblock{8B0000} + DarkSalmon=0xE9967A, ///< @htmlcolorblock{E9967A} + DarkSeaGreen=0x8FBC8F, ///< @htmlcolorblock{8FBC8F} + DarkSlateBlue=0x483D8B, ///< @htmlcolorblock{483D8B} + DarkSlateGray=0x2F4F4F, ///< @htmlcolorblock{2F4F4F} + DarkSlateGrey=0x2F4F4F, ///< @htmlcolorblock{2F4F4F} + DarkTurquoise=0x00CED1, ///< @htmlcolorblock{00CED1} + DarkViolet=0x9400D3, ///< @htmlcolorblock{9400D3} + DeepPink=0xFF1493, ///< @htmlcolorblock{FF1493} + DeepSkyBlue=0x00BFFF, ///< @htmlcolorblock{00BFFF} + DimGray=0x696969, ///< @htmlcolorblock{696969} + DimGrey=0x696969, ///< @htmlcolorblock{696969} + DodgerBlue=0x1E90FF, ///< @htmlcolorblock{1E90FF} + FireBrick=0xB22222, ///< @htmlcolorblock{B22222} + FloralWhite=0xFFFAF0, ///< @htmlcolorblock{FFFAF0} + ForestGreen=0x228B22, ///< @htmlcolorblock{228B22} + Fuchsia=0xFF00FF, ///< @htmlcolorblock{FF00FF} + Gainsboro=0xDCDCDC, ///< @htmlcolorblock{DCDCDC} + GhostWhite=0xF8F8FF, ///< @htmlcolorblock{F8F8FF} + Gold=0xFFD700, ///< @htmlcolorblock{FFD700} + Goldenrod=0xDAA520, ///< @htmlcolorblock{DAA520} + Gray=0x808080, ///< @htmlcolorblock{808080} + Grey=0x808080, ///< @htmlcolorblock{808080} + Green=0x008000, ///< @htmlcolorblock{008000} + GreenYellow=0xADFF2F, ///< @htmlcolorblock{ADFF2F} + Honeydew=0xF0FFF0, ///< @htmlcolorblock{F0FFF0} + HotPink=0xFF69B4, ///< @htmlcolorblock{FF69B4} + IndianRed=0xCD5C5C, ///< @htmlcolorblock{CD5C5C} + Indigo=0x4B0082, ///< @htmlcolorblock{4B0082} + Ivory=0xFFFFF0, ///< @htmlcolorblock{FFFFF0} + Khaki=0xF0E68C, ///< @htmlcolorblock{F0E68C} + Lavender=0xE6E6FA, ///< @htmlcolorblock{E6E6FA} + LavenderBlush=0xFFF0F5, ///< @htmlcolorblock{FFF0F5} + LawnGreen=0x7CFC00, ///< @htmlcolorblock{7CFC00} + LemonChiffon=0xFFFACD, ///< @htmlcolorblock{FFFACD} + LightBlue=0xADD8E6, ///< @htmlcolorblock{ADD8E6} + LightCoral=0xF08080, ///< @htmlcolorblock{F08080} + LightCyan=0xE0FFFF, ///< @htmlcolorblock{E0FFFF} + LightGoldenrodYellow=0xFAFAD2, ///< @htmlcolorblock{FAFAD2} + LightGreen=0x90EE90, ///< @htmlcolorblock{90EE90} + LightGrey=0xD3D3D3, ///< @htmlcolorblock{D3D3D3} + LightPink=0xFFB6C1, ///< @htmlcolorblock{FFB6C1} + LightSalmon=0xFFA07A, ///< @htmlcolorblock{FFA07A} + LightSeaGreen=0x20B2AA, ///< @htmlcolorblock{20B2AA} + LightSkyBlue=0x87CEFA, ///< @htmlcolorblock{87CEFA} + LightSlateGray=0x778899, ///< @htmlcolorblock{778899} + LightSlateGrey=0x778899, ///< @htmlcolorblock{778899} + LightSteelBlue=0xB0C4DE, ///< @htmlcolorblock{B0C4DE} + LightYellow=0xFFFFE0, ///< @htmlcolorblock{FFFFE0} + Lime=0x00FF00, ///< @htmlcolorblock{00FF00} + LimeGreen=0x32CD32, ///< @htmlcolorblock{32CD32} + Linen=0xFAF0E6, ///< @htmlcolorblock{FAF0E6} + Magenta=0xFF00FF, ///< @htmlcolorblock{FF00FF} + Maroon=0x800000, ///< @htmlcolorblock{800000} + MediumAquamarine=0x66CDAA, ///< @htmlcolorblock{66CDAA} + MediumBlue=0x0000CD, ///< @htmlcolorblock{0000CD} + MediumOrchid=0xBA55D3, ///< @htmlcolorblock{BA55D3} + MediumPurple=0x9370DB, ///< @htmlcolorblock{9370DB} + MediumSeaGreen=0x3CB371, ///< @htmlcolorblock{3CB371} + MediumSlateBlue=0x7B68EE, ///< @htmlcolorblock{7B68EE} + MediumSpringGreen=0x00FA9A, ///< @htmlcolorblock{00FA9A} + MediumTurquoise=0x48D1CC, ///< @htmlcolorblock{48D1CC} + MediumVioletRed=0xC71585, ///< @htmlcolorblock{C71585} + MidnightBlue=0x191970, ///< @htmlcolorblock{191970} + MintCream=0xF5FFFA, ///< @htmlcolorblock{F5FFFA} + MistyRose=0xFFE4E1, ///< @htmlcolorblock{FFE4E1} + Moccasin=0xFFE4B5, ///< @htmlcolorblock{FFE4B5} + NavajoWhite=0xFFDEAD, ///< @htmlcolorblock{FFDEAD} + Navy=0x000080, ///< @htmlcolorblock{000080} + OldLace=0xFDF5E6, ///< @htmlcolorblock{FDF5E6} + Olive=0x808000, ///< @htmlcolorblock{808000} + OliveDrab=0x6B8E23, ///< @htmlcolorblock{6B8E23} + Orange=0xFFA500, ///< @htmlcolorblock{FFA500} + OrangeRed=0xFF4500, ///< @htmlcolorblock{FF4500} + Orchid=0xDA70D6, ///< @htmlcolorblock{DA70D6} + PaleGoldenrod=0xEEE8AA, ///< @htmlcolorblock{EEE8AA} + PaleGreen=0x98FB98, ///< @htmlcolorblock{98FB98} + PaleTurquoise=0xAFEEEE, ///< @htmlcolorblock{AFEEEE} + PaleVioletRed=0xDB7093, ///< @htmlcolorblock{DB7093} + PapayaWhip=0xFFEFD5, ///< @htmlcolorblock{FFEFD5} + PeachPuff=0xFFDAB9, ///< @htmlcolorblock{FFDAB9} + Peru=0xCD853F, ///< @htmlcolorblock{CD853F} + Pink=0xFFC0CB, ///< @htmlcolorblock{FFC0CB} + Plaid=0xCC5533, ///< @htmlcolorblock{CC5533} + Plum=0xDDA0DD, ///< @htmlcolorblock{DDA0DD} + PowderBlue=0xB0E0E6, ///< @htmlcolorblock{B0E0E6} + Purple=0x800080, ///< @htmlcolorblock{800080} + Red=0xFF0000, ///< @htmlcolorblock{FF0000} + RosyBrown=0xBC8F8F, ///< @htmlcolorblock{BC8F8F} + RoyalBlue=0x4169E1, ///< @htmlcolorblock{4169E1} + SaddleBrown=0x8B4513, ///< @htmlcolorblock{8B4513} + Salmon=0xFA8072, ///< @htmlcolorblock{FA8072} + SandyBrown=0xF4A460, ///< @htmlcolorblock{F4A460} + SeaGreen=0x2E8B57, ///< @htmlcolorblock{2E8B57} + Seashell=0xFFF5EE, ///< @htmlcolorblock{FFF5EE} + Sienna=0xA0522D, ///< @htmlcolorblock{A0522D} + Silver=0xC0C0C0, ///< @htmlcolorblock{C0C0C0} + SkyBlue=0x87CEEB, ///< @htmlcolorblock{87CEEB} + SlateBlue=0x6A5ACD, ///< @htmlcolorblock{6A5ACD} + SlateGray=0x708090, ///< @htmlcolorblock{708090} + SlateGrey=0x708090, ///< @htmlcolorblock{708090} + Snow=0xFFFAFA, ///< @htmlcolorblock{FFFAFA} + SpringGreen=0x00FF7F, ///< @htmlcolorblock{00FF7F} + SteelBlue=0x4682B4, ///< @htmlcolorblock{4682B4} + Tan=0xD2B48C, ///< @htmlcolorblock{D2B48C} + Teal=0x008080, ///< @htmlcolorblock{008080} + Thistle=0xD8BFD8, ///< @htmlcolorblock{D8BFD8} + Tomato=0xFF6347, ///< @htmlcolorblock{FF6347} + Turquoise=0x40E0D0, ///< @htmlcolorblock{40E0D0} + Violet=0xEE82EE, ///< @htmlcolorblock{EE82EE} + Wheat=0xF5DEB3, ///< @htmlcolorblock{F5DEB3} + White=0xFFFFFF, ///< @htmlcolorblock{FFFFFF} + WhiteSmoke=0xF5F5F5, ///< @htmlcolorblock{F5F5F5} + Yellow=0xFFFF00, ///< @htmlcolorblock{FFFF00} + YellowGreen=0x9ACD32, ///< @htmlcolorblock{9ACD32} + + // LED RGB color that roughly approximates + // the color of incandescent fairy lights, + // assuming that you're using FastLED + // color correction on your LEDs (recommended). + FairyLight=0xFFE42D, ///< @htmlcolorblock{FFE42D} + + // If you are using no color correction, use this + FairyLightNCC=0xFF9D2A ///< @htmlcolorblock{FFE42D} + + } 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); +} + +/* +/// HSV color palette with 16 discrete values +class CHSVPalette16 { +public: + CHSV entries[16]; ///< the color entries that make up the palette + + /// @copydoc CHSV::CHSV() + CHSVPalette16() {}; + + /// Create palette from 16 CHSV values + CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, + const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, + const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, + const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& 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 + CHSVPalette16( const CHSVPalette16& rhs) + { + memmove( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + } + + /// @copydoc CHSVPalette16(const CHSVPalette16& rhs) + CHSVPalette16& operator=( const CHSVPalette16& rhs) + { + memmove( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); + return *this; + } + + /// Create palette from palette stored in PROGMEM + CHSVPalette16( const TProgmemHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + } + + /// @copydoc CHSVPalette16(const TProgmemHSVPalette16&) + CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); + entries[i].hue = xyz.red; + entries[i].sat = xyz.green; + entries[i].val = xyz.blue; + } + return *this; + } + + /// Array access operator to index into the gradient entries + /// @param x the index to retrieve + /// @returns reference to an entry in the palette's color array + /// @note This does not perform any interpolation like ColorFromPalette(), + /// it accesses the underlying entries that make up the gradient. Beware + /// of bounds issues! + inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc operator[] + inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc operator[] + inline CHSV& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// @copydoc operator[] + inline const CHSV& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CHSV*() + { + return &(entries[0]); + } + + /// Check if two palettes have the same color entries + bool operator==( const CHSVPalette16 &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( uint8_t i = 0; i < (sizeof( entries)); ++i) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + + /// Check if two palettes do not have the same color entries + bool operator!=( const CHSVPalette16 &rhs) const + { + return !( *this == rhs); + } + + /// Create palette filled with one color + /// @param c1 the color to fill the palette with + CHSVPalette16( const CHSV& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + + /// Create palette with a gradient from one color to another + /// @param c1 the starting color for the gradient + /// @param c2 the end color for the gradient + CHSVPalette16( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 16, c1, c2); + } + + /// Create palette with three-color gradient + /// @param c1 the starting color for the gradient + /// @param c2 the middle color for the gradient + /// @param c3 the end color for the gradient + CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3); + } + + /// Create palette with four-color gradient + /// @param c1 the starting color for the gradient + /// @param c2 the first middle color for the gradient + /// @param c3 the second middle color for the gradient + /// @param c4 the end color for the gradient + CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); + } + +}; +*/ + +/// 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)); + } + /// @copydoc CRGBPalette16(const CRGBPalette16&) + 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 CHSV palette + CRGBPalette16( const CHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + }*/ + +/* + /// Create palette from array of CHSV colors + CRGBPalette16( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + }*/ + + /* + /// @copydoc CRGBPalette16(const CHSVPalette16&) + CRGBPalette16& operator=( const CHSVPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion + } + return *this; + } + + + /// Create palette from array of CHSV colors + CRGBPalette16& operator=( const CHSV rhs[16]) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = rhs[i]; // implicit HSV-to-RGB conversion + } + return *this; + }*/ + + + /// Create palette from palette stored in PROGMEM + CRGBPalette16( const TProgmemRGBPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = *(const uint32_t*)(rhs + i); + } + } + /// @copydoc CRGBPalette16(const TProgmemRGBPalette16&) + CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) + { + for( uint8_t i = 0; i < 16; ++i) { + entries[i] = *(const uint32_t*)( rhs + i); + } + return *this; + } + + /// @copydoc CHSVPalette16::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( uint8_t i = 0; i < (sizeof( entries)); ++i) { + if( *p != *q) return false; + ++p; + ++q; + } + return true; + } + /// @copydoc CHSVPalette16::operator!= + bool operator!=( const CRGBPalette16 &rhs) const + { + return !( *this == rhs); + } + /// @copydoc CHSVPalette16::operator[] + inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) + { + return entries[x]; + } + /// @copydoc CHSVPalette16::operator[] + inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) + { + return entries[x]; + } + + /// @copydoc CHSVPalette16::operator[] + inline CRGB& operator[] (int x) __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + /// @copydoc CHSVPalette16::operator[] + inline const CRGB& operator[] (int x) const __attribute__((always_inline)) + { + return entries[(uint8_t)x]; + } + + /// Get the underlying pointer to the CHSV entries making up the palette + operator CRGB*() + { + return &(entries[0]); + } + +/* + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) + CRGBPalette16( const CHSV& c1) + { + fill_solid( &(entries[0]), 16, c1); + } + + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) + CRGBPalette16( const CHSV& c1, const CHSV& c2) + { + fill_gradient( &(entries[0]), 16, c1, c2); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) + { + fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); + } +*/ + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) + CRGBPalette16( const CRGB& c1) + { + fill_solid_RGB(&(entries[0]), 16, c1); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) + CRGBPalette16( const CRGB& c1, const CRGB& c2) + { + fill_gradient_RGB(&(entries[0]), 16, c1, c2); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) + CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3) + { + fill_gradient_RGB(&(entries[0]), 16, c1, c2, c3); + } + /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) + 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. + /// + /// For example, consider a gradient palette that is all black except + /// for a single, one-element-wide (1/256th!) spike of red in the middle: + /// @code + /// 0, 0,0,0 + /// 124, 0,0,0 + /// 125, 255,0,0 // one 1/256th-palette-wide red stripe + /// 126, 0,0,0 + /// 255, 0,0,0 + /// @endcode + /// A naive conversion of this 256-element palette to a 16-element palette + /// might accidentally completely eliminate the red spike, rendering the + /// palette completely black. + /// + /// However, the conversions provided here would attempt to include a + /// the red stripe in the output, more-or-less as faithfully as possible. + /// So in this case, the resulting CRGBPalette16 palette would have a red + /// stripe in the middle which was 1/16th of a palette wide -- the + /// narrowest possible in a CRGBPalette16. + /// + /// This means that the relative width of stripes in a CRGBPalette16 + /// will be, by definition, different from the widths in the gradient + /// palette. This code attempts to preserve "all the colors", rather than + /// the exact stripe widths at the expense of dropping some colors. + + CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal ) + { + *this = progpal; + } + /// @copydoc CRGBPalette16(TProgmemRGBGradientPalette_bytes) + CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal ) + { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u.dword = *(const uint32_t*)(progent + count); + ++count; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + u.dword = *(const uint32_t*)( progent); + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t 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); //!!! todo: implement fill gradient function + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + + /// Creates a palette from a gradient palette in dynamic (heap) memory. + /// @copydetails CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) + CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) + { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u = *(ent + count); + ++count; + } while ( u.index != 255); + + int8_t lastSlotUsed = -1; + + + u = *ent; + CRGB rgbstart( u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t 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); //!!! todo: implement fill gradient function + indexstart = indexend; + rgbstart = rgbend; + } + return *this; + } + +}; + + +// 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) {} + + // 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; + } + + //inline bool operator==(const CRGBW& clr) const __attribute__((always_inline)) { + // return color32 == clr.color32; + //} + + //inline bool operator==(const uint32_t clr) const __attribute__((always_inline)) { + // return color32 == clr; + //} + /* + // 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; + }*/ + + /// Get the average of the R, G, B and W values + uint8_t getAverageLight( ) const { + return (r + g + b + w) >> 2; + } + +}; + +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 + 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 uint32_t rgb) __attribute__((always_inline)) { + rgb2hsv(rgb, *this); + } + inline CHSV32& operator= (const uint32_t& rgb) __attribute__((always_inline)) { // 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 6584d524e9..764fca4997 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 */ @@ -68,124 +70,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(); @@ -508,9 +392,17 @@ 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); +uint8_t triwave8(uint8_t in); +uint16_t triwave16(uint16_t in); +uint8_t quadwave8(uint8_t in); +uint8_t cubicwave8(uint8_t in); + um_data_t* simulateSound(uint8_t simulationId); void enumerateLedmaps(); [[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos); @@ -559,6 +451,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); @@ -576,9 +471,27 @@ 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 +uint8_t ease8InOutCubic(uint8_t i); +uint16_t ease16InOutCubic(uint16_t i); +uint8_t ease8InOutQuad(uint8_t i); + +// inline math functions +__attribute__ ((always_inline)) inline uint8_t scale8(uint8_t i, uint8_t scale ) { return ((uint32_t)i * (1 + (uint32_t)scale)) >> 8; } +__attribute__ ((always_inline)) inline uint8_t scale8_video(uint8_t i, uint8_t scale ) { return (((uint32_t)i * (uint32_t)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 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); } + +inline uint8_t inoise8(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement +inline uint8_t inoise8_raw(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement +inline uint8_t inoise8(uint16_t x,uint16_t y) { return 0; } // dummy, needs replacement +inline uint8_t inoise8(uint16_t x) { return 0; } // dummy, needs replacement +inline uint8_t inoise16(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement +inline uint8_t inoise16(uint16_t x,uint16_t y) { return 0; } // dummy, needs replacement +inline uint8_t inoise16(uint16_t x) { return 0; } // dummy, needs replacement + /* #include // standard math functions. use a lot of flash @@ -591,6 +504,30 @@ uint32_t sqrt32_bw(uint32_t x); #define fmod_t fmodf #define floor_t floorf */ + +// PRNG for 16bit and 8bit random numbers used by some effects (fastled replacement) +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() { + uint32_t s = seed; + s *= 0x9E37; + s ^= s >> 11; + seed = (s & 0xFFFF) ^ (s >> 16); + 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() >> 8; } + 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; } +}; +extern PRNG prng; + //wled_serial.cpp void handleSerial(); void updateBaudRate(uint32_t rate); diff --git a/wled00/ir.cpp b/wled00/ir.cpp index 9f7950a2fb..832c698a32 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,20 @@ 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); + CHSV32 prim_hsv = sseg.colors[0]; + prim_hsv.h += (amount<<8); + CRGBW newcolor; + hsv2rgb(prim_hsv, newcolor.color32); + 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 +171,22 @@ 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 = 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; + hsv2rgb(prim_hsv, newcolor.color32); + 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..2e9f3a7f89 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -14,6 +14,125 @@ #ifndef PalettesWLED_h #define PalettesWLED_h +// 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}; + +/// Alias of RainbowStripeColors_p +#define RainbowStripesColors_p RainbowStripeColors_p + +/// 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}; + const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, 94, 1, 29, 18, diff --git a/wled00/util.cpp b/wled00/util.cpp index 16af85e712..ade44d5d8e 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -376,38 +376,73 @@ 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 beat per minute and beatsin replacements + +// 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; +} + +// 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/wled.cpp b/wled00/wled.cpp index bafdf58a93..8c7e4e21fe 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -514,10 +514,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 0b65a95085..0a8ccedf2b 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 "FastLED.h" //TODO:remove #include "const.h" +#include "colors.h" #include "fcn_declare.h" #include "NodeStruct.h" #include "pin_manager.h" @@ -1026,13 +1027,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..cb9f0b1b3b 100644 --- a/wled00/wled_math.cpp +++ b/wled00/wled_math.cpp @@ -244,3 +244,26 @@ uint32_t sqrt32_bw(uint32_t x) { } return res; } + +// 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; +} From 8ae04c180ccbd969f4fa60fa0d0b001c3c0e24e6 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Tue, 18 Mar 2025 19:50:10 +0100 Subject: [PATCH 02/15] added perlin functions from PR, code cleanup. work in progress --- usermods/audioreactive/audio_reactive.h | 2 +- wled00/FX.cpp | 2 +- wled00/colors.cpp | 215 ++-- wled00/colors.h | 1510 +++++++++-------------- wled00/fcn_declare.h | 15 +- wled00/util.cpp | 175 +++ 6 files changed, 854 insertions(+), 1065 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index e6b620098a..d4b4b2d597 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -870,7 +870,7 @@ class AudioReactive : public Usermod { const int AGC_preset = (soundAgc > 0)? (soundAgc-1): 0; // make sure the _compiler_ knows this value will not change while we are inside the function #ifdef WLED_DISABLE_SOUND - micIn = inoise8(millis(), millis()); // Simulated analog read + micIn = perlin8(millis(), millis()); // Simulated analog read micDataReal = micIn; #else #ifdef ARDUINO_ARCH_ESP32 diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 027e16541a..1420a863f9 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5697,7 +5697,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; bump[index++] = col; } } diff --git a/wled00/colors.cpp b/wled00/colors.cpp index e6f7d239fd..e904f10ca7 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -267,130 +267,105 @@ inline CRGB hsv2rgb(const CHSV& hsv) { // CHSV to CRGB return CRGB(rgb); } - -/// @cond -#define K255 255 -#define K171 171 -#define K170 170 -#define K85 85 -/// @endcond - // rainbow spectrum, adapted from fastled //!!! TODO: check and optimized this function - void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { - uint8_t hue = hsv.hue; - uint8_t sat = hsv.sat; - uint8_t val = hsv.val; - uint8_t offset = hue & 0x1F; // 0..31 - uint8_t offset8 = offset << 3; // offset8 = offset * 8 - uint8_t third = scale8( offset8, (256 / 3)); // max = 85 - uint8_t r, g, b; - if( ! (hue & 0x80) ) { - // 0XX - if( ! (hue & 0x40) ) { - // 00X - //section 0-1 - if( ! (hue & 0x20) ) { - // 000 - //case 0: // R -> O - r = K255 - third; - g = third; - b = 0; - } else { - r = K171; - g = K85 + third ; - b = 0; - } - } else { - //01X - // section 2-3 - if( ! (hue & 0x20) ) { - - //uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 - r = K171 - twothirds; - g = K170 + third; - b = 0; - - } else { - // 011 - // case 3: // G -> A - r = 0; - g = K255 - third; - b = third; - } - } + uint8_t hue = hsv.hue; + uint8_t sat = hsv.sat; + uint8_t val = hsv.val; + uint8_t offset = hue & 0x1F; // 0..31 + uint8_t offset8 = offset << 3; // offset8 = offset * 8 + uint8_t third = scale8(offset8, (256 / 3)); // max = 85 + uint8_t r, g, b; + + 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 = (third << 1); + uint8_t twothirds = scale8(offset8, ((256 * 2) / 3)); // 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 = (third << 1); + uint8_t twothirds = scale8(offset8, ((256 * 2) / 3)); // max=170 + g = 171 - twothirds; // K170? + b = 85 + twothirds; + } else { + r = third; + g = 0; + b = 255 - third; + } } else { - // section 4-7 - // 1XX - if( ! (hue & 0x40) ) { - // 10X - if( ! ( hue & 0x20) ) { - // 100 - //case 4: // A -> B - r = 0; - //uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 - g = K171 - twothirds; //K170? - b = K85 + twothirds; - } else { - // 101 - //case 5: // B -> P - r = third; - g = 0; - b = K255 - third; - } - } else { - if( ! (hue & 0x20) ) { - // 110 - //case 6: // P -- K - r = K85 + third; - g = 0; - b = K171 - third; - } else { - // 111 - //case 7: // K -> R - r = K170 + third; - g = 0; - b = K85 - third; - } - } + if (!(hue & 0x20)) { + r = 85 + third; + g = 0; + b = 171 - third; + } else { + r = 170 + third; + g = 0; + b = 85 - third; + } } - // Scale down colors if we're desaturated at all - // and add the brightness_floor to r, g, and b. - if( sat != 255 ) { - if( sat == 0) { - r = 255; b = 255; g = 255; - } else { - uint8_t desat = 255 - sat; - desat = scale8_video( desat, desat); - uint8_t satscale = 255 - desat; - if( r ) r = scale8( r, satscale) + 1; - if( g ) g = scale8( g, satscale) + 1; - if( b ) b = scale8( b, satscale) + 1; - - uint8_t brightness_floor = desat; - r += brightness_floor; - g += brightness_floor; - b += brightness_floor; - } + } + + // scale down colors if we're desaturated at all + // and add the brightness_floor to r, g, and b. + if (sat != 255) { + if (sat == 0) { + r = 255; + b = 255; + g = 255; + } else { + uint8_t desat = 255 - sat; + desat = scale8_video(desat, desat); + uint8_t satscale = 255 - desat; + if (r) r = scale8(r, satscale) + 1; + if (g) g = scale8(g, satscale) + 1; + if (b) b = scale8(b, satscale) + 1; + + uint8_t brightness_floor = desat; + r += brightness_floor; + g += brightness_floor; + b += brightness_floor; } + } - // Now scale everything down if we're at value < 255. - if( val != 255 ) { - val = scale8_video( val, val); - if( val == 0 ) { - r=0; g=0; b=0; - } else { - if( r ) r = scale8( r, val) + 1; - if( g ) g = scale8( g, val) + 1; - if( b ) b = scale8( b, val) + 1; - } + // Now scale everything down if we're at value < 255. + if (val != 255) { + val = scale8_video(val, val); + if (val == 0) { + r = 0; + g = 0; + b = 0; + } else { + if (r) r = scale8(r, val) + 1; + if (g) g = scale8(g, val) + 1; + if (b) b = scale8(b, val) + 1; } + } - rgb.r = r; - rgb.g = g; - rgb.b = b; + rgb.r = r; + rgb.g = g; + rgb.b = b; } void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version @@ -484,10 +459,10 @@ CRGB HeatColor(uint8_t temperature) { heatramp <<= 2; // scale up to 0..252 heatcolor.r = 255; heatcolor.b = 0; - if( t192 & 0x80) { // we're in the hottest third + 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 + } 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 @@ -777,12 +752,12 @@ void nblendPaletteTowardPalette(CRGBPalette16& current, CRGBPalette16& target, u 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). + 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) + if(changes >= maxChanges) break; } } \ No newline at end of file diff --git a/wled00/colors.h b/wled00/colors.h index f15d64280b..58d7e5fb4b 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -18,9 +18,9 @@ struct CHSV; struct CRGBW; struct CHSV32; class CRGBPalette16; -uint8_t scale8( uint8_t i, uint8_t scale ); -uint8_t qadd8( uint8_t i, uint8_t j); -uint8_t qsub8( uint8_t i, uint8_t j); +uint8_t scale8(uint8_t i, uint8_t scale); +uint8_t qadd8(uint8_t i, uint8_t j); +uint8_t qsub8(uint8_t i, uint8_t j); int8_t abs8(int8_t i); typedef uint32_t TProgmemRGBPalette16[16]; @@ -127,18 +127,16 @@ struct CHSV { 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) - { - } + inline CHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) + : h(ih), s(is), v(iv) { } - /// Allow copy construction + // allow copy construction inline CHSV(const CHSV& rhs) __attribute__((always_inline)) = default; - /// Allow copy construction + // allow copy construction inline CHSV& operator= (const CHSV& rhs) __attribute__((always_inline)) = default; - /// Assign new HSV values + // assign new HSV values inline CHSV& setHSV(uint8_t ih, uint8_t is, uint8_t iv) __attribute__((always_inline)) { h = ih; s = is; @@ -147,7 +145,7 @@ struct CHSV { } }; -// Representation of an RGB pixel (Red, Green, Blue) +// representation of an RGB pixel (Red, Green, Blue) struct CRGB { union { struct { @@ -167,1020 +165,679 @@ struct CRGB { 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]; - } + inline uint8_t& operator[] (uint8_t x) __attribute__((always_inline)) { return raw[x]; } - /// Array access operator to index into the CRGB object - /// @param x the index to retrieve (0-2) - /// @returns the CRGB::raw value for the given index - inline const uint8_t& operator[] (uint8_t x) const __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 - /// @warning Default values are UNITIALIZED! + // default constructor (uninitialized) inline CRGB() __attribute__((always_inline)) = default; - /// Allow construction from red, green, and blue - /// @param ir input red value - /// @param ig input green value - /// @param ib input blue value - 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 - /// @param colorcode a packed 24 bit color code - inline CRGB( uint32_t colorcode) __attribute__((always_inline)) - : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) - { - } -/* - /// Allow construction from a LEDColorCorrection enum - /// @param colorcode an LEDColorCorrect enumeration value - inline CRGB( LEDColorCorrection colorcode) __attribute__((always_inline)) - : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) - { + // 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(R(colorcode)), g(G(colorcode)), b(B(colorcode)) { } - /// Allow construction from a ColorTemperature enum - /// @param colorcode an ColorTemperature enumeration value - inline CRGB( ColorTemperature colorcode) __attribute__((always_inline)) - : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF) - { - - } -*/ - /// Allow copy construction + // allow copy construction inline CRGB(const CRGB& rhs) __attribute__((always_inline)) = default; - /// Allow construction from a CHSV color + // allow construction from a CHSV color inline CRGB(const CHSV& rhs) __attribute__((always_inline)) { - hsv2rgb_rainbow( rhs, *this); + hsv2rgb_rainbow(rhs, *this); } - /// Allow assignment from hue, saturation, and value + // allow assignment from hue, saturation, and value inline CRGB& setHSV (uint8_t hue, uint8_t sat, uint8_t val) __attribute__((always_inline)) { - hsv2rgb_rainbow( CHSV(hue, sat, val), *this); - return *this; + hsv2rgb_rainbow(CHSV(hue, sat, val), *this); + return *this; } - /// Allow assignment from just a hue. - /// Saturation and value (brightness) are set automatically to max. - /// @param hue color hue + // Allow assignment from just a hue, sat and val are set to max inline CRGB& setHue (uint8_t hue) __attribute__((always_inline)) { - hsv2rgb_rainbow( CHSV(hue, 255, 255), *this); - return *this; + hsv2rgb_rainbow(CHSV(hue, 255, 255), *this); + return *this; } /// Allow assignment from HSV color inline CRGB& operator= (const CHSV& rhs) __attribute__((always_inline)) { - hsv2rgb_rainbow(rhs, *this); - return *this; + hsv2rgb_rainbow(rhs, *this); + return *this; } - /// Allow assignment from one RGB struct to another + // 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 - /// @param colorcode a packed 24 bit color code - inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline)) - { - r = (colorcode >> 16) & 0xFF; - g = (colorcode >> 8) & 0xFF; - b = (colorcode >> 0) & 0xFF; - return *this; + // allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code + inline CRGB& operator= (const uint32_t colorcode) __attribute__((always_inline)) { + r = R(colorcode); + g = G(colorcode); + b = B(colorcode); + return *this; } - /// Allow assignment from red, green, and blue - /// @param nr new red value - /// @param ng new green value - /// @param nb new blue value - 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 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; } - +#define R(c) (byte((c) >> 16)) +#define G(c) (byte((c) >> 8)) +#define B(c) (byte(c)) /// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code - /// @param colorcode a packed 24 bit color code - inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) - { - r = (colorcode >> 16) & 0xFF; - g = (colorcode >> 8) & 0xFF; - b = (colorcode >> 0) & 0xFF; - return *this; + inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) { + r = R(colorcode); + g = G(colorcode); + b = B(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 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. - /// @note This is NOT an operator+= overload because the compiler - /// can't usefully decide when it's being passed a 32-bit - /// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue) - inline CRGB& addToRGB (uint8_t d ) - { - r = qadd8( r, d); - g = qadd8( g, d); - b = qadd8( b, d); - 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 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. - /// @note This is NOT an operator+= overload because the compiler - /// can't usefully decide when it's being passed a 32-bit - /// constant (e.g. CRGB::Red) and an 8-bit one (CRGB::Blue) - inline CRGB& subtractFromRGB(uint8_t d ) - { - r = qsub8( r, d); - g = qsub8( g, d); - b = qsub8( b, d); - 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; + // subtract a constant of '1' from each channel, saturating at 0x00 + inline CRGB& operator-- () __attribute__((always_inline)) { + subtractFromRGB(1); + return *this; } - /// @copydoc operator-- - inline CRGB operator-- (int ) __attribute__((always_inline)) - { - CRGB retval(*this); - --(*this); - return retval; + // operator-- + inline CRGB operator-- (int) __attribute__((always_inline)) { + CRGB retval(*this); + --(*this); + return retval; } - /// Add a constant of '1' from each channel, saturating at 0xFF - inline CRGB& operator++ () __attribute__((always_inline)) - { - addToRGB(1); - return *this; + // add a constant of '1' to each channel, saturating at 0xFF + inline CRGB& operator++ () __attribute__((always_inline)) { + addToRGB(1); + return *this; } - /// @copydoc operator++ - inline CRGB operator++ (int ) __attribute__((always_inline)) - { - CRGB retval(*this); - ++(*this); - return retval; + // 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; + // 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; + // 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 ) - { - unsigned red = (unsigned)r * (unsigned)d; - unsigned green = (unsigned)r * (unsigned)d; - unsigned blue = (unsigned)r * (unsigned)d; - if( red > 255) red = 255; - if( green > 255) green = 255; - if( blue > 255) blue = 255; - r = red; - g = green; - b = blue; - return *this; + // multiply each of the channels by a constant, saturating each channel at 0xFF. + inline CRGB& operator*= (uint8_t d) { + unsigned red = (unsigned)r * (unsigned)d; + unsigned green = (unsigned)r * (unsigned)d; + unsigned blue = (unsigned)r * (unsigned)d; + if(red > 255) red = 255; + if(green > 255) green = 255; + if(blue > 255) blue = 255; + r = red; + g = green; + b = blue; + 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; + 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; } -/* - /// fadeLightBy is a synonym for nscale8_video(), as a fade instead of a scale - /// @param fadefactor the amount to fade, sent to nscale8_video() as (255 - fadefactor) - inline CRGB& fadeLightBy (uint8_t fadefactor ) - { - nscale8x3_video( r, g, b, 255 - fadefactor); - return *this; - } -*/ - /// Scale down a RGB to N/256ths of its current brightness, using - /// "plain math" dimming rules. "Plain math" dimming rules means that the low light - /// levels may dim all the way to 100% black. - /// @see nscale8x3 - 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; - } + // 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; + } - /// Scale down a RGB to N/256ths of its current brightness, using - /// "plain math" dimming rules. "Plain math" dimming rules means that the low light - /// levels may dim all the way to 100% black. - /// @see ::scale8 - inline CRGB& nscale8 (const CRGB & scaledown ) - { - r = ::scale8(r, scaledown.r); - g = ::scale8(g, scaledown.g); - b = ::scale8(b, scaledown.b); - 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(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; - } + /// 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 - /// @param fadefactor the amount to fade, sent to nscale8() as (255 - fadefactor) - 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; - } + /// fadeToBlackBy is a synonym for nscale8(), as a fade instead of a scale + /// @param fadefactor the amount to fade, sent to nscale8() as (255 - fadefactor) + 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; - } + /// "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; + } - /// @copydoc operator|= - inline CRGB& operator|= (uint8_t d ) - { - if( d > r) r = d; - if( d > g) g = d; - if( d > b) b = d; - return *this; - } + /// @copydoc operator|= + 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; - } + /// "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; + } - /// @copydoc operator&= - inline CRGB& operator&= (uint8_t d ) - { - if( d < r) r = d; - if( d < g) g = d; - if( d < b) b = d; - return *this; - } + /// @copydoc operator&= + 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; - } + /// 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 having an alpha of 255. - inline explicit operator uint32_t() const - { - return uint32_t{0xff000000} | - (uint32_t{r} << 16) | - (uint32_t{g} << 8) | - uint32_t{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; - } + // 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" - } + // 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" + } - /// Predefined RGB colors - typedef enum { - AliceBlue=0xF0F8FF, ///< @htmlcolorblock{F0F8FF} - Amethyst=0x9966CC, ///< @htmlcolorblock{9966CC} - AntiqueWhite=0xFAEBD7, ///< @htmlcolorblock{FAEBD7} - Aqua=0x00FFFF, ///< @htmlcolorblock{00FFFF} - Aquamarine=0x7FFFD4, ///< @htmlcolorblock{7FFFD4} - Azure=0xF0FFFF, ///< @htmlcolorblock{F0FFFF} - Beige=0xF5F5DC, ///< @htmlcolorblock{F5F5DC} - Bisque=0xFFE4C4, ///< @htmlcolorblock{FFE4C4} - Black=0x000000, ///< @htmlcolorblock{000000} - BlanchedAlmond=0xFFEBCD, ///< @htmlcolorblock{FFEBCD} - Blue=0x0000FF, ///< @htmlcolorblock{0000FF} - BlueViolet=0x8A2BE2, ///< @htmlcolorblock{8A2BE2} - Brown=0xA52A2A, ///< @htmlcolorblock{A52A2A} - BurlyWood=0xDEB887, ///< @htmlcolorblock{DEB887} - CadetBlue=0x5F9EA0, ///< @htmlcolorblock{5F9EA0} - Chartreuse=0x7FFF00, ///< @htmlcolorblock{7FFF00} - Chocolate=0xD2691E, ///< @htmlcolorblock{D2691E} - Coral=0xFF7F50, ///< @htmlcolorblock{FF7F50} - CornflowerBlue=0x6495ED, ///< @htmlcolorblock{6495ED} - Cornsilk=0xFFF8DC, ///< @htmlcolorblock{FFF8DC} - Crimson=0xDC143C, ///< @htmlcolorblock{DC143C} - Cyan=0x00FFFF, ///< @htmlcolorblock{00FFFF} - DarkBlue=0x00008B, ///< @htmlcolorblock{00008B} - DarkCyan=0x008B8B, ///< @htmlcolorblock{008B8B} - DarkGoldenrod=0xB8860B, ///< @htmlcolorblock{B8860B} - DarkGray=0xA9A9A9, ///< @htmlcolorblock{A9A9A9} - DarkGrey=0xA9A9A9, ///< @htmlcolorblock{A9A9A9} - DarkGreen=0x006400, ///< @htmlcolorblock{006400} - DarkKhaki=0xBDB76B, ///< @htmlcolorblock{BDB76B} - DarkMagenta=0x8B008B, ///< @htmlcolorblock{8B008B} - DarkOliveGreen=0x556B2F, ///< @htmlcolorblock{556B2F} - DarkOrange=0xFF8C00, ///< @htmlcolorblock{FF8C00} - DarkOrchid=0x9932CC, ///< @htmlcolorblock{9932CC} - DarkRed=0x8B0000, ///< @htmlcolorblock{8B0000} - DarkSalmon=0xE9967A, ///< @htmlcolorblock{E9967A} - DarkSeaGreen=0x8FBC8F, ///< @htmlcolorblock{8FBC8F} - DarkSlateBlue=0x483D8B, ///< @htmlcolorblock{483D8B} - DarkSlateGray=0x2F4F4F, ///< @htmlcolorblock{2F4F4F} - DarkSlateGrey=0x2F4F4F, ///< @htmlcolorblock{2F4F4F} - DarkTurquoise=0x00CED1, ///< @htmlcolorblock{00CED1} - DarkViolet=0x9400D3, ///< @htmlcolorblock{9400D3} - DeepPink=0xFF1493, ///< @htmlcolorblock{FF1493} - DeepSkyBlue=0x00BFFF, ///< @htmlcolorblock{00BFFF} - DimGray=0x696969, ///< @htmlcolorblock{696969} - DimGrey=0x696969, ///< @htmlcolorblock{696969} - DodgerBlue=0x1E90FF, ///< @htmlcolorblock{1E90FF} - FireBrick=0xB22222, ///< @htmlcolorblock{B22222} - FloralWhite=0xFFFAF0, ///< @htmlcolorblock{FFFAF0} - ForestGreen=0x228B22, ///< @htmlcolorblock{228B22} - Fuchsia=0xFF00FF, ///< @htmlcolorblock{FF00FF} - Gainsboro=0xDCDCDC, ///< @htmlcolorblock{DCDCDC} - GhostWhite=0xF8F8FF, ///< @htmlcolorblock{F8F8FF} - Gold=0xFFD700, ///< @htmlcolorblock{FFD700} - Goldenrod=0xDAA520, ///< @htmlcolorblock{DAA520} - Gray=0x808080, ///< @htmlcolorblock{808080} - Grey=0x808080, ///< @htmlcolorblock{808080} - Green=0x008000, ///< @htmlcolorblock{008000} - GreenYellow=0xADFF2F, ///< @htmlcolorblock{ADFF2F} - Honeydew=0xF0FFF0, ///< @htmlcolorblock{F0FFF0} - HotPink=0xFF69B4, ///< @htmlcolorblock{FF69B4} - IndianRed=0xCD5C5C, ///< @htmlcolorblock{CD5C5C} - Indigo=0x4B0082, ///< @htmlcolorblock{4B0082} - Ivory=0xFFFFF0, ///< @htmlcolorblock{FFFFF0} - Khaki=0xF0E68C, ///< @htmlcolorblock{F0E68C} - Lavender=0xE6E6FA, ///< @htmlcolorblock{E6E6FA} - LavenderBlush=0xFFF0F5, ///< @htmlcolorblock{FFF0F5} - LawnGreen=0x7CFC00, ///< @htmlcolorblock{7CFC00} - LemonChiffon=0xFFFACD, ///< @htmlcolorblock{FFFACD} - LightBlue=0xADD8E6, ///< @htmlcolorblock{ADD8E6} - LightCoral=0xF08080, ///< @htmlcolorblock{F08080} - LightCyan=0xE0FFFF, ///< @htmlcolorblock{E0FFFF} - LightGoldenrodYellow=0xFAFAD2, ///< @htmlcolorblock{FAFAD2} - LightGreen=0x90EE90, ///< @htmlcolorblock{90EE90} - LightGrey=0xD3D3D3, ///< @htmlcolorblock{D3D3D3} - LightPink=0xFFB6C1, ///< @htmlcolorblock{FFB6C1} - LightSalmon=0xFFA07A, ///< @htmlcolorblock{FFA07A} - LightSeaGreen=0x20B2AA, ///< @htmlcolorblock{20B2AA} - LightSkyBlue=0x87CEFA, ///< @htmlcolorblock{87CEFA} - LightSlateGray=0x778899, ///< @htmlcolorblock{778899} - LightSlateGrey=0x778899, ///< @htmlcolorblock{778899} - LightSteelBlue=0xB0C4DE, ///< @htmlcolorblock{B0C4DE} - LightYellow=0xFFFFE0, ///< @htmlcolorblock{FFFFE0} - Lime=0x00FF00, ///< @htmlcolorblock{00FF00} - LimeGreen=0x32CD32, ///< @htmlcolorblock{32CD32} - Linen=0xFAF0E6, ///< @htmlcolorblock{FAF0E6} - Magenta=0xFF00FF, ///< @htmlcolorblock{FF00FF} - Maroon=0x800000, ///< @htmlcolorblock{800000} - MediumAquamarine=0x66CDAA, ///< @htmlcolorblock{66CDAA} - MediumBlue=0x0000CD, ///< @htmlcolorblock{0000CD} - MediumOrchid=0xBA55D3, ///< @htmlcolorblock{BA55D3} - MediumPurple=0x9370DB, ///< @htmlcolorblock{9370DB} - MediumSeaGreen=0x3CB371, ///< @htmlcolorblock{3CB371} - MediumSlateBlue=0x7B68EE, ///< @htmlcolorblock{7B68EE} - MediumSpringGreen=0x00FA9A, ///< @htmlcolorblock{00FA9A} - MediumTurquoise=0x48D1CC, ///< @htmlcolorblock{48D1CC} - MediumVioletRed=0xC71585, ///< @htmlcolorblock{C71585} - MidnightBlue=0x191970, ///< @htmlcolorblock{191970} - MintCream=0xF5FFFA, ///< @htmlcolorblock{F5FFFA} - MistyRose=0xFFE4E1, ///< @htmlcolorblock{FFE4E1} - Moccasin=0xFFE4B5, ///< @htmlcolorblock{FFE4B5} - NavajoWhite=0xFFDEAD, ///< @htmlcolorblock{FFDEAD} - Navy=0x000080, ///< @htmlcolorblock{000080} - OldLace=0xFDF5E6, ///< @htmlcolorblock{FDF5E6} - Olive=0x808000, ///< @htmlcolorblock{808000} - OliveDrab=0x6B8E23, ///< @htmlcolorblock{6B8E23} - Orange=0xFFA500, ///< @htmlcolorblock{FFA500} - OrangeRed=0xFF4500, ///< @htmlcolorblock{FF4500} - Orchid=0xDA70D6, ///< @htmlcolorblock{DA70D6} - PaleGoldenrod=0xEEE8AA, ///< @htmlcolorblock{EEE8AA} - PaleGreen=0x98FB98, ///< @htmlcolorblock{98FB98} - PaleTurquoise=0xAFEEEE, ///< @htmlcolorblock{AFEEEE} - PaleVioletRed=0xDB7093, ///< @htmlcolorblock{DB7093} - PapayaWhip=0xFFEFD5, ///< @htmlcolorblock{FFEFD5} - PeachPuff=0xFFDAB9, ///< @htmlcolorblock{FFDAB9} - Peru=0xCD853F, ///< @htmlcolorblock{CD853F} - Pink=0xFFC0CB, ///< @htmlcolorblock{FFC0CB} - Plaid=0xCC5533, ///< @htmlcolorblock{CC5533} - Plum=0xDDA0DD, ///< @htmlcolorblock{DDA0DD} - PowderBlue=0xB0E0E6, ///< @htmlcolorblock{B0E0E6} - Purple=0x800080, ///< @htmlcolorblock{800080} - Red=0xFF0000, ///< @htmlcolorblock{FF0000} - RosyBrown=0xBC8F8F, ///< @htmlcolorblock{BC8F8F} - RoyalBlue=0x4169E1, ///< @htmlcolorblock{4169E1} - SaddleBrown=0x8B4513, ///< @htmlcolorblock{8B4513} - Salmon=0xFA8072, ///< @htmlcolorblock{FA8072} - SandyBrown=0xF4A460, ///< @htmlcolorblock{F4A460} - SeaGreen=0x2E8B57, ///< @htmlcolorblock{2E8B57} - Seashell=0xFFF5EE, ///< @htmlcolorblock{FFF5EE} - Sienna=0xA0522D, ///< @htmlcolorblock{A0522D} - Silver=0xC0C0C0, ///< @htmlcolorblock{C0C0C0} - SkyBlue=0x87CEEB, ///< @htmlcolorblock{87CEEB} - SlateBlue=0x6A5ACD, ///< @htmlcolorblock{6A5ACD} - SlateGray=0x708090, ///< @htmlcolorblock{708090} - SlateGrey=0x708090, ///< @htmlcolorblock{708090} - Snow=0xFFFAFA, ///< @htmlcolorblock{FFFAFA} - SpringGreen=0x00FF7F, ///< @htmlcolorblock{00FF7F} - SteelBlue=0x4682B4, ///< @htmlcolorblock{4682B4} - Tan=0xD2B48C, ///< @htmlcolorblock{D2B48C} - Teal=0x008080, ///< @htmlcolorblock{008080} - Thistle=0xD8BFD8, ///< @htmlcolorblock{D8BFD8} - Tomato=0xFF6347, ///< @htmlcolorblock{FF6347} - Turquoise=0x40E0D0, ///< @htmlcolorblock{40E0D0} - Violet=0xEE82EE, ///< @htmlcolorblock{EE82EE} - Wheat=0xF5DEB3, ///< @htmlcolorblock{F5DEB3} - White=0xFFFFFF, ///< @htmlcolorblock{FFFFFF} - WhiteSmoke=0xF5F5F5, ///< @htmlcolorblock{F5F5F5} - Yellow=0xFFFF00, ///< @htmlcolorblock{FFFF00} - YellowGreen=0x9ACD32, ///< @htmlcolorblock{9ACD32} - - // LED RGB color that roughly approximates - // the color of incandescent fairy lights, - // assuming that you're using FastLED - // color correction on your LEDs (recommended). - FairyLight=0xFFE42D, ///< @htmlcolorblock{FFE42D} - - // If you are using no color correction, use this - FairyLightNCC=0xFF9D2A ///< @htmlcolorblock{FFE42D} - - } HTMLColorCode; + 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)); + 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)); + 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); + 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); + return !(lhs == rhs); } -/* -/// HSV color palette with 16 discrete values -class CHSVPalette16 { +// RGB color palette with 16 discrete values +class CRGBPalette16 { public: - CHSV entries[16]; ///< the color entries that make up the palette - - /// @copydoc CHSV::CHSV() - CHSVPalette16() {}; - - /// Create palette from 16 CHSV values - CHSVPalette16( const CHSV& c00,const CHSV& c01,const CHSV& c02,const CHSV& c03, - const CHSV& c04,const CHSV& c05,const CHSV& c06,const CHSV& c07, - const CHSV& c08,const CHSV& c09,const CHSV& c10,const CHSV& c11, - const CHSV& c12,const CHSV& c13,const CHSV& c14,const CHSV& 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 - CHSVPalette16( const CHSVPalette16& rhs) - { - memmove( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); - } - - /// @copydoc CHSVPalette16(const CHSVPalette16& rhs) - CHSVPalette16& operator=( const CHSVPalette16& rhs) - { - memmove( (void *) &(entries[0]), &(rhs.entries[0]), sizeof( entries)); - return *this; - } - - /// Create palette from palette stored in PROGMEM - CHSVPalette16( const TProgmemHSVPalette16& rhs) - { - for( uint8_t i = 0; i < 16; ++i) { - CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); - entries[i].hue = xyz.red; - entries[i].sat = xyz.green; - entries[i].val = xyz.blue; - } - } + CRGB entries[16]; + CRGBPalette16() { + memset(entries, 0, sizeof(entries)); // default constructor: set all to black + } - /// @copydoc CHSVPalette16(const TProgmemHSVPalette16&) - CHSVPalette16& operator=( const TProgmemHSVPalette16& rhs) - { - for( uint8_t i = 0; i < 16; ++i) { - CRGB xyz = FL_PGM_READ_DWORD_NEAR( rhs + i); - entries[i].hue = xyz.red; - entries[i].sat = xyz.green; - entries[i].val = xyz.blue; - } - return *this; - } + // 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; + }; - /// Array access operator to index into the gradient entries - /// @param x the index to retrieve - /// @returns reference to an entry in the palette's color array - /// @note This does not perform any interpolation like ColorFromPalette(), - /// it accesses the underlying entries that make up the gradient. Beware - /// of bounds issues! - inline CHSV& operator[] (uint8_t x) __attribute__((always_inline)) - { - return entries[x]; - } + // Copy constructor + CRGBPalette16(const CRGBPalette16& rhs) { + memmove((void*)&(entries[0]), &(rhs.entries[0]), sizeof(entries)); + } - /// @copydoc operator[] - inline const CHSV& operator[] (uint8_t x) const __attribute__((always_inline)) - { - return entries[x]; - } + // Create palette from array of CRGB colors + CRGBPalette16(const CRGB rhs[16]) { + memmove((void*)&(entries[0]), &(rhs[0]), sizeof(entries)); + } - /// @copydoc operator[] - inline CHSV& operator[] (int x) __attribute__((always_inline)) - { - return entries[(uint8_t)x]; - } + // Copy assignment operator + CRGBPalette16& operator=(const CRGBPalette16& rhs) { + memmove((void*)&(entries[0]), &(rhs.entries[0]), sizeof(entries)); + return *this; + } - /// @copydoc operator[] - inline const CHSV& operator[] (int x) const __attribute__((always_inline)) - { - return entries[(uint8_t)x]; - } + // Create palette from array of CRGB colors + CRGBPalette16& operator=(const CRGB rhs[16]) { + memmove((void*)&(entries[0]), &(rhs[0]), sizeof(entries)); + return *this; + } - /// Get the underlying pointer to the CHSV entries making up the palette - operator CHSV*() - { - return &(entries[0]); + // 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); } + } - /// Check if two palettes have the same color entries - bool operator==( const CHSVPalette16 &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( uint8_t i = 0; i < (sizeof( entries)); ++i) { - if( *p != *q) return false; - ++p; - ++q; - } - return true; + // 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; + } - /// Check if two palettes do not have the same color entries - bool operator!=( const CHSVPalette16 &rhs) const - { - return !( *this == rhs); - } + // 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 (int i = 0; i < (sizeof(entries)); ++i) { + if (*p != *q) return false; + ++p; + ++q; + } + return true; + } - /// Create palette filled with one color - /// @param c1 the color to fill the palette with - CHSVPalette16( const CHSV& c1) - { - fill_solid( &(entries[0]), 16, c1); - } + // Inequality operator + bool operator!=(const CRGBPalette16& rhs) const { + return !(*this == rhs); + } - /// Create palette with a gradient from one color to another - /// @param c1 the starting color for the gradient - /// @param c2 the end color for the gradient - CHSVPalette16( const CHSV& c1, const CHSV& c2) - { - fill_gradient( &(entries[0]), 16, c1, c2); - } + // Array subscript operator + inline CRGB& operator[](uint8_t x) __attribute__((always_inline)) { + return entries[x]; + } - /// Create palette with three-color gradient - /// @param c1 the starting color for the gradient - /// @param c2 the middle color for the gradient - /// @param c3 the end color for the gradient - CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) - { - fill_gradient( &(entries[0]), 16, c1, c2, c3); - } + // Array subscript operator (const) + inline const CRGB& operator[](uint8_t x) const __attribute__((always_inline)) { + return entries[x]; + } - /// Create palette with four-color gradient - /// @param c1 the starting color for the gradient - /// @param c2 the first middle color for the gradient - /// @param c3 the second middle color for the gradient - /// @param c4 the end color for the gradient - CHSVPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) - { - fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); - } + // 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]; + } -/// 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 - } + // Get the underlying pointer to the CRGB entries making up the palette + operator CRGB*() { + return &(entries[0]); + } - // 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; - }; + // Create palette from a single CRGB color + CRGBPalette16(const CRGB& c1) { + fill_solid_RGB(&(entries[0]), 16, c1); + } - /// 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)); - } - /// @copydoc CRGBPalette16(const CRGBPalette16&) - 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 CHSV palette - CRGBPalette16( const CHSVPalette16& rhs) - { - for( uint8_t i = 0; i < 16; ++i) { - entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion - } - }*/ - -/* - /// Create palette from array of CHSV colors - CRGBPalette16( const CHSV rhs[16]) - { - for( uint8_t i = 0; i < 16; ++i) { - entries[i] = rhs[i]; // implicit HSV-to-RGB conversion - } - }*/ - - /* - /// @copydoc CRGBPalette16(const CHSVPalette16&) - CRGBPalette16& operator=( const CHSVPalette16& rhs) - { - for( uint8_t i = 0; i < 16; ++i) { - entries[i] = rhs.entries[i]; // implicit HSV-to-RGB conversion - } - return *this; - } + // 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 array of CHSV colors - CRGBPalette16& operator=( const CHSV rhs[16]) - { - for( uint8_t i = 0; i < 16; ++i) { - entries[i] = rhs[i]; // implicit HSV-to-RGB conversion - } - return *this; - }*/ + // 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. - /// Create palette from palette stored in PROGMEM - CRGBPalette16( const TProgmemRGBPalette16& rhs) - { - for( uint8_t i = 0; i < 16; ++i) { - entries[i] = *(const uint32_t*)(rhs + i); - } - } - /// @copydoc CRGBPalette16(const TProgmemRGBPalette16&) - CRGBPalette16& operator=( const TProgmemRGBPalette16& rhs) - { - for( uint8_t i = 0; i < 16; ++i) { - entries[i] = *(const uint32_t*)( rhs + i); - } - return *this; - } + CRGBPalette16(TProgmemRGBGradientPalette_bytes progpal) { + *this = progpal; + } - /// @copydoc CHSVPalette16::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( uint8_t i = 0; i < (sizeof( entries)); ++i) { - if( *p != *q) return false; - ++p; - ++q; + CRGBPalette16& operator=(TProgmemRGBGradientPalette_bytes progpal) { + TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint32_t count = 0; + do { + u.dword = *(const uint32_t*)(progent + count); + ++count; + } while (u.index != 255); + + int32_t lastSlotUsed = -1; + + u.dword = *(const uint32_t*)(progent); + CRGB rgbstart(u.r, u.g, u.b); + + uint32_t indexstart = 0; + uint32_t istart8 = 0; + uint32_t iend8 = 0; + while (indexstart < 255) { + ++progent; + u.dword = *(const uint32_t*)(progent); + uint32_t 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; + } } - return true; - } - /// @copydoc CHSVPalette16::operator!= - bool operator!=( const CRGBPalette16 &rhs) const - { - return !( *this == rhs); - } - /// @copydoc CHSVPalette16::operator[] - inline CRGB& operator[] (uint8_t x) __attribute__((always_inline)) - { - return entries[x]; - } - /// @copydoc CHSVPalette16::operator[] - inline const CRGB& operator[] (uint8_t x) const __attribute__((always_inline)) - { - return entries[x]; - } - - /// @copydoc CHSVPalette16::operator[] - inline CRGB& operator[] (int x) __attribute__((always_inline)) - { - return entries[(uint8_t)x]; - } - /// @copydoc CHSVPalette16::operator[] - inline const CRGB& operator[] (int x) const __attribute__((always_inline)) - { - return entries[(uint8_t)x]; - } - - /// Get the underlying pointer to the CHSV entries making up the palette - operator CRGB*() - { - return &(entries[0]); - } - -/* - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) - CRGBPalette16( const CHSV& c1) - { - fill_solid( &(entries[0]), 16, c1); - } - - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) - CRGBPalette16( const CHSV& c1, const CHSV& c2) - { - fill_gradient( &(entries[0]), 16, c1, c2); - } - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) - CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3) - { - fill_gradient( &(entries[0]), 16, c1, c2, c3); - } - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) - CRGBPalette16( const CHSV& c1, const CHSV& c2, const CHSV& c3, const CHSV& c4) - { - fill_gradient( &(entries[0]), 16, c1, c2, c3, c4); - } -*/ - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&) - CRGBPalette16( const CRGB& c1) - { - fill_solid_RGB(&(entries[0]), 16, c1); - } - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&) - CRGBPalette16( const CRGB& c1, const CRGB& c2) - { - fill_gradient_RGB(&(entries[0]), 16, c1, c2); - } - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&) - CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3) - { - fill_gradient_RGB(&(entries[0]), 16, c1, c2, c3); - } - /// @copydoc CHSVPalette16::CHSVPalette16(const CHSV&, const CHSV&, const CHSV&, const CHSV&) - CRGBPalette16( const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) - { - fill_gradient_RGB(&(entries[0]), 16, c1, c2, c3, c4); + 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 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. - /// - /// For example, consider a gradient palette that is all black except - /// for a single, one-element-wide (1/256th!) spike of red in the middle: - /// @code - /// 0, 0,0,0 - /// 124, 0,0,0 - /// 125, 255,0,0 // one 1/256th-palette-wide red stripe - /// 126, 0,0,0 - /// 255, 0,0,0 - /// @endcode - /// A naive conversion of this 256-element palette to a 16-element palette - /// might accidentally completely eliminate the red spike, rendering the - /// palette completely black. - /// - /// However, the conversions provided here would attempt to include a - /// the red stripe in the output, more-or-less as faithfully as possible. - /// So in this case, the resulting CRGBPalette16 palette would have a red - /// stripe in the middle which was 1/16th of a palette wide -- the - /// narrowest possible in a CRGBPalette16. - /// - /// This means that the relative width of stripes in a CRGBPalette16 - /// will be, by definition, different from the widths in the gradient - /// palette. This code attempts to preserve "all the colors", rather than - /// the exact stripe widths at the expense of dropping some colors. - - CRGBPalette16( TProgmemRGBGradientPalette_bytes progpal ) - { - *this = progpal; - } - /// @copydoc CRGBPalette16(TProgmemRGBGradientPalette_bytes) - CRGBPalette16& operator=( TProgmemRGBGradientPalette_bytes progpal ) - { - TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal); - TRGBGradientPaletteEntryUnion u; - - // Count entries - uint16_t count = 0; - do { - u.dword = *(const uint32_t*)(progent + count); - ++count; - } while ( u.index != 255); - - int8_t lastSlotUsed = -1; - - u.dword = *(const uint32_t*)( progent); - CRGB rgbstart( u.r, u.g, u.b); - - int indexstart = 0; - uint8_t istart8 = 0; - uint8_t 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); //!!! todo: implement fill gradient function - indexstart = indexend; - rgbstart = rgbend; + // Creates a palette from a gradient palette in dynamic (heap) memory. + CRGBPalette16& loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes gpal) { + TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); + TRGBGradientPaletteEntryUnion u; + + // Count entries + uint16_t count = 0; + do { + u = *(ent + count); + ++count; + } while (u.index != 255); + + int8_t lastSlotUsed = -1; + + u = *ent; + CRGB rgbstart(u.r, u.g, u.b); + + int indexstart = 0; + uint8_t istart8 = 0; + uint8_t 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; + } } - return *this; + lastSlotUsed = iend8; + } + fill_gradient_RGB(&(entries[0]), istart8, rgbstart, iend8, rgbend); + indexstart = indexend; + rgbstart = rgbend; } - - /// Creates a palette from a gradient palette in dynamic (heap) memory. - /// @copydetails CRGBPalette16::CRGBPalette16(TProgmemRGBGradientPalette_bytes) - CRGBPalette16& loadDynamicGradientPalette( TDynamicRGBGradientPalette_bytes gpal ) - { - TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal); - TRGBGradientPaletteEntryUnion u; - - // Count entries - uint16_t count = 0; - do { - u = *(ent + count); - ++count; - } while ( u.index != 255); - - int8_t lastSlotUsed = -1; - - - u = *ent; - CRGB rgbstart( u.r, u.g, u.b); - - int indexstart = 0; - uint8_t istart8 = 0; - uint8_t 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); //!!! todo: implement fill gradient function - indexstart = indexend; - rgbstart = rgbend; - } - return *this; - } - + return *this; + } }; - -// 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 { + // 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) + 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 @@ -1196,7 +853,7 @@ struct CRGBW { 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]; } + 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; } @@ -1209,35 +866,11 @@ struct CRGBW { return color32; } - //inline bool operator==(const CRGBW& clr) const __attribute__((always_inline)) { - // return color32 == clr.color32; - //} - - //inline bool operator==(const uint32_t clr) const __attribute__((always_inline)) { - // return color32 == clr; - //} - /* - // Conversion operator to CRGB - inline operator CRGB() const __attribute__((always_inline)) { - return CRGB(r, g, b); + // get the average of the R, G, B and W values + uint8_t getAverageLight() const { + return (r + g + b + w) >> 2; } - - 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; - }*/ - - /// Get the average of the R, G, B and W values - uint8_t getAverageLight( ) const { - return (r + g + b + w) >> 2; - } - -}; + }; struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions union { @@ -1266,11 +899,6 @@ struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions 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 764fca4997..4c75a5cc09 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -372,6 +372,8 @@ void userLoop(); #include "soc/wdev_reg.h" #define HW_RND_REGISTER REG_READ(WDEV_RND_REG) #endif +#define inoise8 perlin8 // fastled legacy alias +#define inoise16 perlin16 // fastled legacy alias #define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0) [[gnu::pure]] int getNumVal(const String* req, uint16_t pos); void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255); @@ -408,6 +410,15 @@ void enumerateLedmaps(); [[gnu::hot]] uint8_t get_random_wheel_index(uint8_t pos); [[gnu::hot, gnu::pure]] float mapf(float x, float in_min, float in_max, float out_min, float out_max); uint32_t hashInt(uint32_t s); +int32_t perlin1D_raw(uint32_t x, bool is16bit = false); +int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit = false); +int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit = false); +uint16_t perlin16(uint32_t x); +uint16_t perlin16(uint32_t x, uint32_t y); +uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z); +uint8_t perlin8(uint16_t x); +uint8_t perlin8(uint16_t x, uint16_t y); +uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z); // fast (true) random numbers using hardware RNG, all functions return values in the range lowerlimit to upperlimit-1 // note: for true random numbers with high entropy, do not call faster than every 200ns (5MHz) @@ -483,7 +494,7 @@ __attribute__ ((always_inline)) inline uint8_t qadd8(uint8_t i, uint8_t j) { un __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 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); } - +/* inline uint8_t inoise8(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement inline uint8_t inoise8_raw(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement inline uint8_t inoise8(uint16_t x,uint16_t y) { return 0; } // dummy, needs replacement @@ -491,7 +502,7 @@ inline uint8_t inoise8(uint16_t x) { return 0; } // dummy, needs replacement inline uint8_t inoise16(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement inline uint8_t inoise16(uint16_t x,uint16_t y) { return 0; } // dummy, needs replacement inline uint8_t inoise16(uint16_t x) { return 0; } // dummy, needs replacement - +*/ /* #include // standard math functions. use a lot of flash diff --git a/wled00/util.cpp b/wled00/util.cpp index ade44d5d8e..12cde1aa63 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -653,3 +653,178 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) { uint32_t diff = upperlimit - lowerlimit; return hw_random(diff) + lowerlimit; } + +/* + * Fixed point integer based Perlin noise functions by @dedehai + * Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness + */ +#define PERLIN_SHIFT 1 + +// calculate gradient for corner from hash value +static inline __attribute__((always_inline)) int32_t hashToGradient(uint32_t h) { + // using more steps yields more "detailed" perlin noise but looks less like the original fastled version (adjust PERLIN_SHIFT to compensate, also changes range and needs proper adustment) + // return (h & 0xFF) - 128; // use PERLIN_SHIFT 7 + // return (h & 0x0F) - 8; // use PERLIN_SHIFT 3 + // return (h & 0x07) - 4; // use PERLIN_SHIFT 2 + return (h & 0x03) - 2; // use PERLIN_SHIFT 1 -> closest to original fastled version +} + +// Gradient functions for 1D, 2D and 3D Perlin noise note: forcing inline produces smaller code and makes it 3x faster! +static int32_t gradient1D(uint32_t x0, int32_t dx) { + uint32_t h = x0 * 0x27D4EB2D; + h ^= h >> 15; + h *= 0x92C3412B; + h ^= h >> 13; + h ^= h >> 7; + return (hashToGradient(h) * dx) >> PERLIN_SHIFT; +} + +static inline __attribute__((always_inline)) int32_t gradient2D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy) { + uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D); + h ^= h >> 15; + h *= 0x92C3412B; + h ^= h >> 13; + return (hashToGradient(h) * dx + hashToGradient(h>>PERLIN_SHIFT) * dy) >> (1 + PERLIN_SHIFT); +} + +static inline __attribute__((always_inline)) int32_t gradient3D(uint32_t x0, int32_t dx, uint32_t y0, int32_t dy, uint32_t z0, int32_t dz) { + // fast and good entropy hash from corner coordinates + uint32_t h = (x0 * 0x27D4EB2D) ^ (y0 * 0xB5297A4D) ^ (z0 * 0x1B56C4E9); + h ^= h >> 15; + h *= 0x92C3412B; + h ^= h >> 13; + return ((hashToGradient(h) * dx + hashToGradient(h>>(1+PERLIN_SHIFT)) * dy + hashToGradient(h>>(1 + 2*PERLIN_SHIFT)) * dz) * 85) >> (8 + PERLIN_SHIFT); // scale to 16bit, x*85 >> 8 = x/3 +} + +// fast cubic smoothstep: t*(3 - 2t²), optimized for fixed point, scaled to avoid overflows +static uint32_t smoothstep(const uint32_t t) { + uint32_t t_squared = (t * t) >> 16; + uint32_t factor = (3 << 16) - ((t << 1)); + return (t_squared * factor) >> 18; // scale to avoid overflows and give best resolution +} + +// simple linear interpolation for fixed-point values, scaled for perlin noise use +static inline int32_t lerpPerlin(int32_t a, int32_t b, int32_t t) { + return a + (((b - a) * t) >> 14); // match scaling with smoothstep to yield 16.16bit values +} + +// 1D Perlin noise function that returns a value in range of -24691 to 24689 +int32_t perlin1D_raw(uint32_t x, bool is16bit) { + // integer and fractional part coordinates + int32_t x0 = x >> 16; + int32_t x1 = x0 + 1; + if(is16bit) x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF + + int32_t dx0 = x & 0xFFFF; + int32_t dx1 = dx0 - 0x10000; + // gradient values for the two corners + int32_t g0 = gradient1D(x0, dx0); + int32_t g1 = gradient1D(x1, dx1); + // interpolate and smooth function + int32_t tx = smoothstep(dx0); + int32_t noise = lerpPerlin(g0, g1, tx); + return noise; +} + +// 2D Perlin noise function that returns a value in range of -20633 to 20629 +int32_t perlin2D_raw(uint32_t x, uint32_t y, bool is16bit) { + int32_t x0 = x >> 16; + int32_t y0 = y >> 16; + int32_t x1 = x0 + 1; + int32_t y1 = y0 + 1; + + if(is16bit) { + x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF + y1 = y1 & 0xFF; + } + + int32_t dx0 = x & 0xFFFF; + int32_t dy0 = y & 0xFFFF; + int32_t dx1 = dx0 - 0x10000; + int32_t dy1 = dy0 - 0x10000; + + int32_t g00 = gradient2D(x0, dx0, y0, dy0); + int32_t g10 = gradient2D(x1, dx1, y0, dy0); + int32_t g01 = gradient2D(x0, dx0, y1, dy1); + int32_t g11 = gradient2D(x1, dx1, y1, dy1); + + uint32_t tx = smoothstep(dx0); + uint32_t ty = smoothstep(dy0); + + int32_t nx0 = lerpPerlin(g00, g10, tx); + int32_t nx1 = lerpPerlin(g01, g11, tx); + + int32_t noise = lerpPerlin(nx0, nx1, ty); + return noise; +} + +// 3D Perlin noise function that returns a value in range of -16788 to 16381 +int32_t perlin3D_raw(uint32_t x, uint32_t y, uint32_t z, bool is16bit) { + int32_t x0 = x >> 16; + int32_t y0 = y >> 16; + int32_t z0 = z >> 16; + int32_t x1 = x0 + 1; + int32_t y1 = y0 + 1; + int32_t z1 = z0 + 1; + + if(is16bit) { + x1 = x1 & 0xFF; // wrap back to zero at 0xFF instead of 0xFFFF + y1 = y1 & 0xFF; + z1 = z1 & 0xFF; + } + + int32_t dx0 = x & 0xFFFF; + int32_t dy0 = y & 0xFFFF; + int32_t dz0 = z & 0xFFFF; + int32_t dx1 = dx0 - 0x10000; + int32_t dy1 = dy0 - 0x10000; + int32_t dz1 = dz0 - 0x10000; + + int32_t g000 = gradient3D(x0, dx0, y0, dy0, z0, dz0); + int32_t g001 = gradient3D(x0, dx0, y0, dy0, z1, dz1); + int32_t g010 = gradient3D(x0, dx0, y1, dy1, z0, dz0); + int32_t g011 = gradient3D(x0, dx0, y1, dy1, z1, dz1); + int32_t g100 = gradient3D(x1, dx1, y0, dy0, z0, dz0); + int32_t g101 = gradient3D(x1, dx1, y0, dy0, z1, dz1); + int32_t g110 = gradient3D(x1, dx1, y1, dy1, z0, dz0); + int32_t g111 = gradient3D(x1, dx1, y1, dy1, z1, dz1); + + uint32_t tx = smoothstep(dx0); + uint32_t ty = smoothstep(dy0); + uint32_t tz = smoothstep(dz0); + + int32_t nx0 = lerpPerlin(g000, g100, tx); + int32_t nx1 = lerpPerlin(g010, g110, tx); + int32_t nx2 = lerpPerlin(g001, g101, tx); + int32_t nx3 = lerpPerlin(g011, g111, tx); + int32_t ny0 = lerpPerlin(nx0, nx1, ty); + int32_t ny1 = lerpPerlin(nx2, nx3, ty); + + int32_t noise = lerpPerlin(ny0, ny1, tz); + return noise; +} + +// scaling functions for fastled replacement +uint16_t perlin16(uint32_t x) { + return ((perlin1D_raw(x) * 1159) >> 10) + 32803; //scale to 16bit and offset (fastled range: about 4838 to 60766) +} + +uint16_t perlin16(uint32_t x, uint32_t y) { + return ((perlin2D_raw(x, y) * 1537) >> 10) + 32725; //scale to 16bit and offset (fastled range: about 1748 to 63697) +} + +uint16_t perlin16(uint32_t x, uint32_t y, uint32_t z) { + return ((perlin3D_raw(x, y, z) * 1731) >> 10) + 33147; //scale to 16bit and offset (fastled range: about 4766 to 60840) +} + +uint8_t perlin8(uint16_t x) { + return (((perlin1D_raw((uint32_t)x << 8, true) * 1353) >> 10) + 32769) >> 8; //scale to 16 bit, offset, then scale to 8bit +} + +uint8_t perlin8(uint16_t x, uint16_t y) { + return (((perlin2D_raw((uint32_t)x << 8, (uint32_t)y << 8, true) * 1620) >> 10) + 32771) >> 8; //scale to 16 bit, offset, then scale to 8bit +} + +uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) { + return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit +} \ No newline at end of file From e2ce7adbdf127ef14d31aa6588cf83a85f06c8f2 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 19 Mar 2025 21:31:38 +0100 Subject: [PATCH 03/15] added hsv2rgb16rainbow function, some cleanup - new rainbow function is faster and more accurate than original fastled function --- wled00/colors.cpp | 45 ++++++++++++++------------ wled00/colors.h | 82 +++++++++++++++++++++++------------------------ 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index e904f10ca7..616e7c96b4 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -267,19 +267,19 @@ inline CRGB hsv2rgb(const CHSV& hsv) { // CHSV to CRGB return CRGB(rgb); } + // rainbow spectrum, adapted from fastled //!!! TODO: check and optimized this function -void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { - uint8_t hue = hsv.hue; - uint8_t sat = hsv.sat; - uint8_t val = hsv.val; - uint8_t offset = hue & 0x1F; // 0..31 - uint8_t offset8 = offset << 3; // offset8 = offset * 8 - uint8_t third = scale8(offset8, (256 / 3)); // max = 85 - uint8_t r, g, b; - - if (!(hue & 0x80)) { - if (!(hue & 0x40)) { // section 0-1 - if (!(hue & 0x20)) { +void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { + uint32_t hue = hsv.h; + uint32_t sat = hsv.s; + uint32_t val = hsv.v; + uint32_t offset = hue & 0x1FFF; // offset in current sector 0..8191 (8 sectors) + uint32_t third = (offset * 21846) >> 21 ; // equal to: (offset*8/3))>>8 + uint32_t r, g, b; + + if (!(hue & 0x8000)) { // section 0-3 + if (!(hue & 0x4000)) { // section 0-1 + if (!(hue & 0x2000)) { r = 255 - third; g = third; b = 0; @@ -289,9 +289,8 @@ void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { b = 0; } } else { // section 2-3 - if (!(hue & 0x20)) { - // uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8(offset8, ((256 * 2) / 3)); // max=170 + if (!(hue & 0x2000)) { + uint32_t twothirds = (offset * 21846) >> 20 ; // equal to: (2*offset*8/3)>>8 r = 171 - twothirds; g = 170 + third; b = 0; @@ -302,12 +301,11 @@ void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { } } } else { // section 4-7 - if (!(hue & 0x40)) { - if (!(hue & 0x20)) { + if (!(hue & 0x4000)) { + if (!(hue & 0x2000)) { r = 0; - // uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8(offset8, ((256 * 2) / 3)); // max=170 - g = 171 - twothirds; // K170? + uint32_t twothirds = (offset * 21846) >> 20 ; // equal to: (2*offset*8/3)>>8 + g = 171 - twothirds; b = 85 + twothirds; } else { r = third; @@ -315,7 +313,7 @@ void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { b = 255 - third; } } else { - if (!(hue & 0x20)) { + if (!(hue & 0x2000)) { r = 85 + third; g = 0; b = 171 - third; @@ -368,6 +366,11 @@ void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { rgb.b = b; } +// rainbow spectrum, adapted from fastled //!!! TODO: check and optimized this function +void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { + hsv2rgb_rainbow16(CHSV32(hsv), rgb); +} + 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; diff --git a/wled00/colors.h b/wled00/colors.h index 58d7e5fb4b..e72c3328f9 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -94,55 +94,55 @@ void nblendPaletteTowardPalette(CRGBPalette16& current, CRGBPalette16& target, u // 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] + 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 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]; - } + 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; + // 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 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(const CHSV& rhs) __attribute__((always_inline)) = default; - // allow copy construction - inline CHSV& operator= (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; - } + // 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) From 2618f567a7818722e287eaaa0ae3bd8c5567f157 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 23 Mar 2025 18:50:35 +0100 Subject: [PATCH 04/15] WIP: checking different hsv2RGB_rainbow functions --- wled00/FX.cpp | 46 ++- wled00/FXparticleSystem.cpp | 4 +- wled00/colors.cpp | 583 ++++++++++++++++++++++++++++++++---- wled00/colors.h | 11 +- wled00/fcn_declare.h | 8 +- wled00/ir.cpp | 4 +- wled00/wled.cpp | 73 ++++- 7 files changed, 656 insertions(+), 73 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 1420a863f9..c4db7982c1 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -118,9 +118,51 @@ static um_data_t* getAudioData() { * No blinking. Just plain old static light. */ uint16_t mode_static(void) { - SEGMENT.fill(SEGCOLOR(0)); - return strip.isOffRefreshRequired() ? FRAMETIME : 350; +// fill the segment using hsv2rgb_rainbow and hsv2rgb alternatingly every 0.5 second +static bool useRainbow = true; +static uint32_t lastSwitch = 0; +static uint8_t sat = 255; +static uint8_t val = 100; +static int offset = 0; +if (millis() - lastSwitch > 2000) { + useRainbow = !useRainbow; + lastSwitch = millis(); + // if (useRainbow) sat--; + if (useRainbow) val--; +} + +offset++;//=100; + + +for (unsigned i = 0; i < SEGLEN; i++) { + if (useRainbow) { + + CHSV hsvColor = CHSV(((i+offset) * (0xFF))/(10*255), sat, val); + CRGB rgbColor; + hsv2rgb_rainbow(hsvColor, rgbColor); + //hsv2rgb_rainbow16(hsvColor.h<<8, hsvColor.s, hsvColor.v, rgbColor.raw, false); + SEGMENT.setPixelColor(i, rgbColor.r, rgbColor.g, rgbColor.b); + if(i == 0) SEGMENT.setPixelColor(i, SEGCOLOR(0)); + //SEGMENT.fill(SEGCOLOR(0)); + } else { + CHSV32 hsv32Color; + hsv32Color.h = (((i+offset) * (0xFFFF))/(10*255)); + hsv32Color.s = sat; + hsv32Color.v = val; + CRGBW rgbwColor; + //hsv2rgb_rainbow16(hsv32Color, rgbwColor); + hsv2rgb_rainbow16(hsv32Color.h, hsv32Color.s, hsv32Color.v, rgbwColor.raw, true); + SEGMENT.setPixelColor(i, rgbwColor); + //SEGMENT.fill(SEGCOLOR(0)); + } } + + return FRAMETIME; +} + +// SEGMENT.fill(SEGCOLOR(0)); +// return strip.isOffRefreshRequired() ? FRAMETIME : 350; +//} static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid"; diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index fde07be766..4f40d21c4a 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -657,7 +657,7 @@ void ParticleSystem2D::ParticleSys_render() { CHSV32 baseHSV; rgb2hsv((uint32_t((byte(baseRGB.r) << 16) | (byte(baseRGB.g) << 8) | (byte(baseRGB.b)))), baseHSV); // convert to HSV baseHSV.s = particles[i].sat; // set the saturation - uint32_t tempcolor; + CRGBW tempcolor; hsv2rgb(baseHSV, tempcolor); // convert back to RGB baseRGB = (CRGB)tempcolor; } @@ -1587,7 +1587,7 @@ void ParticleSystem1D::ParticleSys_render() { CHSV32 baseHSV; rgb2hsv((uint32_t((byte(baseRGB.r) << 16) | (byte(baseRGB.g) << 8) | (byte(baseRGB.b)))), baseHSV); // convert to HSV baseHSV.s = advPartProps[i].sat; // set the saturation - uint32_t tempcolor; + CRGBW tempcolor; hsv2rgb(baseHSV, tempcolor); // convert back to RGB baseRGB = (CRGB)tempcolor; } diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 616e7c96b4..5e1a175823 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -229,57 +229,79 @@ 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; - } - 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; +// convert HSV (16bit hue) to RGB (32bit with white = 0), optimized for speed +//__attribute__((optimize("O3"))) +void hsv2rgb(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) + //unsigned region = (unsigned)hsv.h / 10923; // 65536 / 6 = 10923 + //unsigned remainder = ((unsigned)hsv.h - (region * 10923)) * 6; + + // check for zero saturation + if (hsv.s == 0) { + rgb.r = rgb.g = rgb.b = hsv.v; + return; + } + + 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; } } inline CRGB hsv2rgb(const CHSV& hsv) { // CHSV to CRGB CHSV32 hsv32(hsv); - uint32_t rgb; + CRGBW rgb; hsv2rgb(hsv32, rgb); return CRGB(rgb); } -// rainbow spectrum, adapted from fastled //!!! TODO: check and optimized this function -void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { - uint32_t hue = hsv.h; - uint32_t sat = hsv.s; - uint32_t val = hsv.v; - uint32_t offset = hue & 0x1FFF; // offset in current sector 0..8191 (8 sectors) - uint32_t third = (offset * 21846) >> 21 ; // equal to: (offset*8/3))>>8 - uint32_t r, g, b; +void hsv2rgb_rainbow16(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 & 0x8000)) { // section 0-3 - if (!(hue & 0x4000)) { // section 0-1 - if (!(hue & 0x2000)) { + + if (!(hue & 0x80)) { + if (!(hue & 0x40)) { // section 0-1 + if (!(hue & 0x20)) { r = 255 - third; g = third; b = 0; @@ -289,8 +311,8 @@ void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { b = 0; } } else { // section 2-3 - if (!(hue & 0x2000)) { - uint32_t twothirds = (offset * 21846) >> 20 ; // equal to: (2*offset*8/3)>>8 + if (!(hue & 0x20)) { + uint8_t twothirds = third16 >> 20; // max=170 r = 171 - twothirds; g = 170 + third; b = 0; @@ -301,10 +323,10 @@ void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { } } } else { // section 4-7 - if (!(hue & 0x4000)) { - if (!(hue & 0x2000)) { + if (!(hue & 0x40)) { + if (!(hue & 0x20)) { r = 0; - uint32_t twothirds = (offset * 21846) >> 20 ; // equal to: (2*offset*8/3)>>8 + uint8_t twothirds = third16 >> 20; // max=170 g = 171 - twothirds; b = 85 + twothirds; } else { @@ -313,7 +335,7 @@ void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { b = 255 - third; } } else { - if (!(hue & 0x2000)) { + if (!(hue & 0x20)) { r = 85 + third; g = 0; b = 171 - third; @@ -325,39 +347,150 @@ void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { } } - // scale down colors if we're desaturated at all - // and add the brightness_floor to r, g, and b. + // 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; + } + //rgb.r = r; + //rgb.g = g; + //rgb.b = b; +} + +/* +// rainbow spectrum, adapted from fastled +// note: integer types are carefully chosen to maximize performance +void hsv2rgb_rainbow16(const CHSV32& hsv, CRGBW& rgb) { + + uint8_t hue = hsv.h>>8; + uint8_t sat = hsv.s; + uint32_t val = hsv.v; + uint8_t offset = hsv.h & 0x1FFF; // 0..31 + uint32_t third16 = ((int)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 { - uint8_t desat = 255 - sat; - desat = scale8_video(desat, desat); - uint8_t satscale = 255 - desat; - if (r) r = scale8(r, satscale) + 1; - if (g) g = scale8(g, satscale) + 1; - if (b) b = scale8(b, satscale) + 1; - - uint8_t brightness_floor = desat; + //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; } } - // Now scale everything down if we're at value < 255. + // scale everything down if value < 255. if (val != 255) { - val = scale8_video(val, val); if (val == 0) { r = 0; g = 0; b = 0; } else { - if (r) r = scale8(r, val) + 1; - if (g) g = scale8(g, val) + 1; - if (b) b = scale8(b, val) + 1; + 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; } } @@ -365,15 +498,341 @@ void hsv2rgb_rainbow16(const CHSV32& hsv, CRGB& rgb) { rgb.g = g; rgb.b = b; } +*/ -// rainbow spectrum, adapted from fastled //!!! TODO: check and optimized this function -void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { - hsv2rgb_rainbow16(CHSV32(hsv), rgb); +// note: code duplication is for speed, using conversion functions, makes it much slower (about half the speed) +// in order to remove the duplication without much speed impact: add a conversion function, but remove all looped calls to it and raplace those with the 16bit version. +// all attempts using pointers, references or inline functions did not result in a speedup + + + +#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) ) + + +#define K255 255 +#define K171 171 +#define K170 170 +#define K85 85 + +void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) +{ + // Yellow has a higher inherent brightness than + // any other color; 'pure' yellow is perceived to + // be 93% as bright as white. In order to make + // yellow appear the correct relative brightness, + // it has to be rendered brighter than all other + // colors. + // Level Y1 is a moderate boost, the default. + // Level Y2 is a strong boost. + const uint8_t Y1 = 1; + const uint8_t Y2 = 0; + + // G2: Whether to divide all greens by two. + // Depends GREATLY on your particular LEDs + const uint8_t G2 = 0; + + // Gscale: what to scale green down by. + // Depends GREATLY on your particular LEDs + const uint8_t Gscale = 0; + + + uint8_t hue = hsv.hue; + uint8_t sat = hsv.sat; + uint8_t val = hsv.val; + + uint8_t offset = hue & 0x1F; // 0..31 + + // offset8 = offset * 8 + uint8_t offset8 = offset; + { +#if defined(__AVR__) + // Left to its own devices, gcc turns "x <<= 3" into a loop + // It's much faster and smaller to just do three single-bit shifts + // So this business is to force that. + offset8 <<= 1; + asm volatile(""); + offset8 <<= 1; + asm volatile(""); + offset8 <<= 1; +#else + // On ARM and other non-AVR platforms, we just shift 3. + offset8 <<= 3; +#endif + } + + uint8_t third = scale8( offset8, (256 / 3)); // max = 85 + + uint8_t r, g, b; + + if( ! (hue & 0x80) ) { + // 0XX + if( ! (hue & 0x40) ) { + // 00X + //section 0-1 + if( ! (hue & 0x20) ) { + // 000 + //case 0: // R -> O + r = K255 - third; + g = third; + b = 0; + FORCE_REFERENCE(b); + } else { + // 001 + //case 1: // O -> Y + if( Y1 ) { + r = K171; + g = K85 + third ; + b = 0; + FORCE_REFERENCE(b); + } + if( Y2 ) { + r = K170 + third; + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + g = K85 + twothirds; + b = 0; + FORCE_REFERENCE(b); + } + } + } else { + //01X + // section 2-3 + if( ! (hue & 0x20) ) { + // 010 + //case 2: // Y -> G + if( Y1 ) { + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + r = K171 - twothirds; + g = K170 + third; + b = 0; + FORCE_REFERENCE(b); + } + if( Y2 ) { + r = K255 - offset8; + g = K255; + b = 0; + FORCE_REFERENCE(b); + } + } else { + // 011 + // case 3: // G -> A + r = 0; + FORCE_REFERENCE(r); + g = K255 - third; + b = third; + } + } + } else { + // section 4-7 + // 1XX + if( ! (hue & 0x40) ) { + // 10X + if( ! ( hue & 0x20) ) { + // 100 + //case 4: // A -> B + r = 0; + FORCE_REFERENCE(r); + //uint8_t twothirds = (third << 1); + uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 + g = K171 - twothirds; //K170? + b = K85 + twothirds; + + } else { + // 101 + //case 5: // B -> P + r = third; + g = 0; + FORCE_REFERENCE(g); + b = K255 - third; + + } + } else { + if( ! (hue & 0x20) ) { + // 110 + //case 6: // P -- K + r = K85 + third; + g = 0; + FORCE_REFERENCE(g); + b = K171 - third; + + } else { + // 111 + //case 7: // K -> R + r = K170 + third; + g = 0; + FORCE_REFERENCE(g); + b = K85 - third; + + } + } + } + + // This is one of the good places to scale the green down, + // although the client can scale green down as well. + if( G2 ) g = g >> 1; + if( Gscale ) g = scale8_video( g, Gscale); + + // Scale down colors if we're desaturated at all + // and add the brightness_floor to r, g, and b. + if( sat != 255 ) { + if( sat == 0) { + r = 255; b = 255; g = 255; + } else { + uint8_t desat = 255 - sat; + desat = scale8_video( desat, desat); + + uint8_t satscale = 255 - desat; + //satscale = sat; // uncomment to revert to pre-2021 saturation behavior + + + if( r ) r = scale8( r, satscale) + 1; + if( g ) g = scale8( g, satscale) + 1; + if( b ) b = scale8( b, satscale) + 1; + + uint8_t brightness_floor = desat; + r += brightness_floor; + g += brightness_floor; + b += brightness_floor; + } + } + + // Now scale everything down if we're at value < 255. + if( val != 255 ) { + + val = scale8_video( val, val); + if( val == 0 ) { + r=0; g=0; b=0; + } else { + // nscale8x3_video( r, g, b, val); + + if( r ) r = scale8( r, val) + 1; + if( g ) g = scale8( g, val) + 1; + if( b ) b = scale8( b, val) + 1; + } + } + + // Here we have the old AVR "missing std X+n" problem again + // It turns out that fixing it winds up costing more than + // not fixing it. + // To paraphrase Dr Bronner, profile! profile! profile! + //asm volatile( "" : : : "r26", "r27" ); + //asm volatile (" movw r30, r26 \n" : : : "r30", "r31"); + rgb.r = r; + rgb.g = g; + rgb.b = b; } +/* +// rainbow spectrum, adapted from fastled +// note: integer types are carefully chosen to maximize performance +void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { + + // slower version using conversion functions + //CHSV32 hsv32(hsv); + //CRGBW rgbw; + //hsv2rgb_rainbow16(hsv32, rgbw); + //rgb = CRGB(rgbw); + + uint8_t hue = hsv.hue; + uint8_t sat = hsv.sat; + uint32_t val = hsv.val; + uint8_t offset = hue & 0x1F; // 0..31 + uint32_t third16 = ((int)offset * 21846); // offset16 = offset * 1/3<<16 + uint8_t third = third16 >> 13; // max = 85 + uint8_t r, g, b; + + 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 >> 12; // 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 >> 12; // 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; + } + } + + rgb.r = r; + rgb.g = g; + rgb.b = b; +} +*/ 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; + hsv.hsv32 = 0; int32_t r = (rgb>>16)&0xFF; int32_t g = (rgb>>8)&0xFF; int32_t b = rgb&0xFF; @@ -399,11 +858,11 @@ inline CHSV rgb2hsv(const CRGB c) { // CRGB to CHSV } void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb - uint32_t crgb; + CRGBW crgb; hsv2rgb(CHSV32(hue, sat, 255), crgb); - rgb[0] = byte((crgb) >> 16); - rgb[1] = byte((crgb) >> 8); - rgb[2] = byte(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 index e72c3328f9..fee4f4fba8 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -69,8 +69,11 @@ inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return col CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette(); -void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); +//void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); +void hsv2rgb(const CHSV32& hsv, CRGBW& rgb); CRGB hsv2rgb(const CHSV& hsv); +//void hsv2rgb_rainbow16(const CHSV32& hsv, CRGBW& rgb); +void hsv2rgb_rainbow16(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW = false); void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb); void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); void rgb2hsv(const uint32_t rgbw, CHSV32& hsv); @@ -92,6 +95,10 @@ void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c 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); +//test only: remove again once settled +void hsv2rgb_rainbow16_ptr(const CHSV32& hsv, uint8_t* rgb); + + // Representation of an HSV pixel (hue, saturation, value (aka brightness)). struct CHSV { union { @@ -879,7 +886,7 @@ struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions uint8_t s; // saturation uint8_t v; // value }; - uint32_t raw; // 32bit access + uint32_t hsv32; // 32bit access }; inline CHSV32() __attribute__((always_inline)) = default; // default constructor diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 4c75a5cc09..5af35efef9 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -487,8 +487,12 @@ uint16_t ease16InOutCubic(uint16_t i); uint8_t ease8InOutQuad(uint8_t i); // inline math functions -__attribute__ ((always_inline)) inline uint8_t scale8(uint8_t i, uint8_t scale ) { return ((uint32_t)i * (1 + (uint32_t)scale)) >> 8; } -__attribute__ ((always_inline)) inline uint8_t scale8_video(uint8_t i, uint8_t scale ) { return (((uint32_t)i * (uint32_t)scale) >> 8) + ((i&&scale)?1:0); } +__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); } +//original fastled: +//__attribute__ ((always_inline)) inline uint8_t scale8(uint8_t i, uint8_t scale ) { return (((uint16_t)i) * (1 + (uint16_t)(scale))) >> 8; } +//__attribute__ ((always_inline)) inline uint8_t scale8_video(uint8_t i, uint8_t scale ) { uint8_t j = (((int)i * (int)scale) >> 8) + ((i && scale) ? 1 : 0); return j; } + __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; } diff --git a/wled00/ir.cpp b/wled00/ir.cpp index 832c698a32..66327661ba 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -132,7 +132,7 @@ static void changeEffectSpeed(int8_t amount) CHSV32 prim_hsv = sseg.colors[0]; prim_hsv.h += (amount<<8); CRGBW newcolor; - hsv2rgb(prim_hsv, newcolor.color32); + hsv2rgb(prim_hsv, newcolor); newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { @@ -176,7 +176,7 @@ static void changeEffectIntensity(int8_t amount) int32_t new_val = (int32_t)prim_hsv.s + amount; prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255 CRGBW newcolor; - hsv2rgb(prim_hsv, newcolor.color32); + hsv2rgb(prim_hsv, newcolor); newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 8c7e4e21fe..803a5ad836 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -52,7 +52,78 @@ void WLED::loop() static size_t avgStripMillis = 0; unsigned long stripMillis; #endif - +/* +uint32_t start; + uint32_t end; + uint32_t time; + volatile uint32_t temp; + + CHSV32 hsv32; + CRGBW rgbw; + start = micros(); + hsv32.s = 255; + hsv32.v = 255; + for(int i = 0; i < 1000000; i++){ + hsv32.h = i; + //hsv2rgb_rainbow16(hsv32, rgbw); + hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + temp+=rgbw.r; + } + end = micros(); + time = end - start; + Serial.print("s=255,v=255: "); + Serial.print(temp); + Serial.print(" time: "); + Serial.println(time); + + start = micros(); + hsv32.s = 255; + hsv32.v = 250; + for(int i = 0; i < 1000000; i++){ + hsv32.h = i; + //hsv2rgb_rainbow16(hsv32, rgbw); + hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + temp+=rgbw.r; + } + end = micros(); + time = end - start; + Serial.print("s=255,v=250: "); + Serial.print(temp); + Serial.print(" time: "); + Serial.println(time); + + start = micros(); + hsv32.s = 220; + hsv32.v = 255; + for(int i = 0; i < 1000000; i++){ + hsv32.h = i; + //hsv2rgb_rainbow16(hsv32, rgbw); + hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + temp+=rgbw.r; + } + end = micros(); + time = end - start; + Serial.print("s=220,v=255: "); + Serial.print(temp); + Serial.print(" time: "); + Serial.println(time); + + start = micros(); + hsv32.s = 220; + hsv32.v = 210; + for(int i = 0; i < 1000000; i++){ + hsv32.h = i; + //hsv2rgb_rainbow16(hsv32, rgbw); + hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + temp+=rgbw.r; + } + end = micros(); + time = end - start; + Serial.print("s=220,v=210: "); + Serial.print(temp); + Serial.print(" time: "); + Serial.println(time); +*/ handleTime(); #ifndef WLED_DISABLE_INFRARED handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too From 040cf2fb2324fecfca975b17a4e6c1813cffacdb Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Tue, 25 Mar 2025 22:15:09 +0100 Subject: [PATCH 05/15] updated conversion functions (now faster), cleanup, optimizations --- wled00/FX.cpp | 40 ++- wled00/FXparticleSystem.cpp | 22 +- wled00/colors.cpp | 521 ++++-------------------------------- wled00/colors.h | 168 ++++++------ wled00/ir.cpp | 8 +- wled00/wled.cpp | 56 ++-- 6 files changed, 218 insertions(+), 597 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c4db7982c1..530f075251 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -122,36 +122,50 @@ uint16_t mode_static(void) { static bool useRainbow = true; static uint32_t lastSwitch = 0; static uint8_t sat = 255; -static uint8_t val = 100; +static uint8_t val = 255; static int offset = 0; if (millis() - lastSwitch > 2000) { useRainbow = !useRainbow; lastSwitch = millis(); // if (useRainbow) sat--; - if (useRainbow) val--; + if (useRainbow) val--; } offset++;//=100; +delay(50); for (unsigned i = 0; i < SEGLEN; i++) { if (useRainbow) { - - CHSV hsvColor = CHSV(((i+offset) * (0xFF))/(10*255), sat, val); - CRGB rgbColor; - hsv2rgb_rainbow(hsvColor, rgbColor); - //hsv2rgb_rainbow16(hsvColor.h<<8, hsvColor.s, hsvColor.v, rgbColor.raw, false); - SEGMENT.setPixelColor(i, rgbColor.r, rgbColor.g, rgbColor.b); - if(i == 0) SEGMENT.setPixelColor(i, SEGCOLOR(0)); + //CHSV hsvColor = CHSV(((i+offset) * (0xFF))/(2*255), sat, val); + //CRGB rgbColor = hsvColor; + //hsv2rgb_rainbow(hsvColor, rgbColor); + //hsv2rgb_rainbow16(hsvColor.h<<8, hsvColor.s, hsvColor.v, rgbColor.raw, false); + //SEGMENT.setPixelColor(i, rgbColor.r, rgbColor.g, rgbColor.b); + CHSV32 hsv32Color; + hsv32Color.h = (((i+offset) * (0xFFFF))/(2*255)); + hsv32Color.s = sat; + hsv32Color.v = val; + CRGBW rgbwColor = hsv32Color; + //CHSV32 hsv_t = rgbwColor; + SEGMENT.setPixelColor(i, rgbwColor); + if(i == 0) SEGMENT.setPixelColor(i, SEGCOLOR(0)); // paint corner //SEGMENT.fill(SEGCOLOR(0)); } else { CHSV32 hsv32Color; - hsv32Color.h = (((i+offset) * (0xFFFF))/(10*255)); + hsv32Color.h = (((i+offset) * (0xFFFF))/(2*255)); hsv32Color.s = sat; - hsv32Color.v = val; + hsv32Color.v = 255; + CRGBW rgbwColor_t = hsv32Color; + CHSV32 hsv_t = rgbwColor_t; + //hsv_t.h += 1; + //hsv_t.s = 254; + hsv_t.v = val; + //CRGBW rgbwColor = hsv_t; CRGBW rgbwColor; - //hsv2rgb_rainbow16(hsv32Color, rgbwColor); - hsv2rgb_rainbow16(hsv32Color.h, hsv32Color.s, hsv32Color.v, rgbwColor.raw, true); + hsv2rgb_spectrum(hsv_t, rgbwColor); + //hsv2rgb_rainbow(hsv32Color, rgbwColor); + //hsv2rgb_rainbow16(hsv32Color.h, hsv32Color.s, hsv32Color.v, rgbwColor.raw, true); SEGMENT.setPixelColor(i, rgbwColor); //SEGMENT.fill(SEGCOLOR(0)); } diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index 4f40d21c4a..25b9ad1ff2 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; @@ -654,15 +654,12 @@ void ParticleSystem2D::ParticleSys_render() { brightness = min((particles[i].ttl << 1), (int)255); baseRGB = ColorFromPaletteWLED(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 - CRGBW 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) { @@ -1533,7 +1530,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; @@ -1584,15 +1581,12 @@ void ParticleSystem1D::ParticleSys_render() { 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 - CRGBW 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/colors.cpp b/wled00/colors.cpp index 5e1a175823..1fce698294 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -2,7 +2,16 @@ #include "fcn_declare.h" //#include "colors.h" // todo: needed? its already included in fcn declare -> seems to compile fine without this include -> remove? /* - * Color conversion & utility methods + Color conversion & utility methods + + 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) + - fastled replacements are mostly optimized for CHSV32 and CRGBW but using CRGB and HSV are equally fast + - direct conversion (assignment or construction) from CHSV/CHSV32 to CRGB/CRGBW use the "rainbow" method (nicer colors) + - 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, use "hsv2rgb_spectrum" function to convert it back (better color accuracy, rainbow causes slight color shift) + - to manipulate an RGB color in HSV space, use the */ /* @@ -84,6 +93,22 @@ uint32_t color_fade(uint32_t c1, uint8_t amount, bool video) return scaledcolor; } + +/* + 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 +} + // 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) { @@ -128,7 +153,7 @@ CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette) { CHSV palettecolors[4]; // array of colors for the new palette uint8_t keepcolorposition = hw_random8(4); // color position of current random palette to keep - palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette + //!!! update this palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette palettecolors[keepcolorposition].hue += hw_random8(10)-5; // +/- 5 randomness of base color // generate 4 saturation and brightness value numbers // only one saturation is allowed to be below 200 creating mostly vibrant colors @@ -230,13 +255,10 @@ CRGBPalette16 generateRandomPalette() // generate fully random palette } // convert HSV (16bit hue) to RGB (32bit with white = 0), optimized for speed -//__attribute__((optimize("O3"))) -void hsv2rgb(const CHSV32& hsv, CRGBW& rgb) { +__attribute__((optimize("O3"))) 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) - //unsigned region = (unsigned)hsv.h / 10923; // 65536 / 6 = 10923 - //unsigned remainder = ((unsigned)hsv.h - (region * 10923)) * 6; // check for zero saturation if (hsv.s == 0) { @@ -281,15 +303,18 @@ void hsv2rgb(const CHSV32& hsv, CRGBW& rgb) { } } -inline CRGB hsv2rgb(const CHSV& hsv) { // CHSV to CRGB +// 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 rgb; - hsv2rgb(hsv32, rgb); - return CRGB(rgb); + CRGBW rgb32; + hsv2rgb_spectrum(hsv32, rgb32); + rgb = CRGB(rgb32); } - -void hsv2rgb_rainbow16(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW) { +// convert HSV (16bit hue) to RGB (24bit), optimized for speed (integer types and function arguments were very carefully chosen 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("O3"))) 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; @@ -298,7 +323,6 @@ void hsv2rgb_rainbow16(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool 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)) { @@ -392,474 +416,41 @@ void hsv2rgb_rainbow16(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool rgbdata[1] = g; rgbdata[2] = b; } - //rgb.r = r; - //rgb.g = g; - //rgb.b = b; -} - -/* -// rainbow spectrum, adapted from fastled -// note: integer types are carefully chosen to maximize performance -void hsv2rgb_rainbow16(const CHSV32& hsv, CRGBW& rgb) { - - uint8_t hue = hsv.h>>8; - uint8_t sat = hsv.s; - uint32_t val = hsv.v; - uint8_t offset = hsv.h & 0x1FFF; // 0..31 - uint32_t third16 = ((int)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; - } - } - - rgb.r = r; - rgb.g = g; - rgb.b = b; } -*/ - -// note: code duplication is for speed, using conversion functions, makes it much slower (about half the speed) -// in order to remove the duplication without much speed impact: add a conversion function, but remove all looped calls to it and raplace those with the 16bit version. -// all attempts using pointers, references or inline functions did not result in a speedup - - -#define FORCE_REFERENCE(var) asm volatile( "" : : "r" (var) ) - - -#define K255 255 -#define K171 171 -#define K170 170 -#define K85 85 - -void hsv2rgb_rainbow( const CHSV& hsv, CRGB& rgb) -{ - // Yellow has a higher inherent brightness than - // any other color; 'pure' yellow is perceived to - // be 93% as bright as white. In order to make - // yellow appear the correct relative brightness, - // it has to be rendered brighter than all other - // colors. - // Level Y1 is a moderate boost, the default. - // Level Y2 is a strong boost. - const uint8_t Y1 = 1; - const uint8_t Y2 = 0; - - // G2: Whether to divide all greens by two. - // Depends GREATLY on your particular LEDs - const uint8_t G2 = 0; - - // Gscale: what to scale green down by. - // Depends GREATLY on your particular LEDs - const uint8_t Gscale = 0; - - - uint8_t hue = hsv.hue; - uint8_t sat = hsv.sat; - uint8_t val = hsv.val; - - uint8_t offset = hue & 0x1F; // 0..31 - - // offset8 = offset * 8 - uint8_t offset8 = offset; - { -#if defined(__AVR__) - // Left to its own devices, gcc turns "x <<= 3" into a loop - // It's much faster and smaller to just do three single-bit shifts - // So this business is to force that. - offset8 <<= 1; - asm volatile(""); - offset8 <<= 1; - asm volatile(""); - offset8 <<= 1; -#else - // On ARM and other non-AVR platforms, we just shift 3. - offset8 <<= 3; -#endif - } - - uint8_t third = scale8( offset8, (256 / 3)); // max = 85 - - uint8_t r, g, b; - - if( ! (hue & 0x80) ) { - // 0XX - if( ! (hue & 0x40) ) { - // 00X - //section 0-1 - if( ! (hue & 0x20) ) { - // 000 - //case 0: // R -> O - r = K255 - third; - g = third; - b = 0; - FORCE_REFERENCE(b); - } else { - // 001 - //case 1: // O -> Y - if( Y1 ) { - r = K171; - g = K85 + third ; - b = 0; - FORCE_REFERENCE(b); - } - if( Y2 ) { - r = K170 + third; - //uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 - g = K85 + twothirds; - b = 0; - FORCE_REFERENCE(b); - } - } - } else { - //01X - // section 2-3 - if( ! (hue & 0x20) ) { - // 010 - //case 2: // Y -> G - if( Y1 ) { - //uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 - r = K171 - twothirds; - g = K170 + third; - b = 0; - FORCE_REFERENCE(b); - } - if( Y2 ) { - r = K255 - offset8; - g = K255; - b = 0; - FORCE_REFERENCE(b); - } - } else { - // 011 - // case 3: // G -> A - r = 0; - FORCE_REFERENCE(r); - g = K255 - third; - b = third; - } - } - } else { - // section 4-7 - // 1XX - if( ! (hue & 0x40) ) { - // 10X - if( ! ( hue & 0x20) ) { - // 100 - //case 4: // A -> B - r = 0; - FORCE_REFERENCE(r); - //uint8_t twothirds = (third << 1); - uint8_t twothirds = scale8( offset8, ((256 * 2) / 3)); // max=170 - g = K171 - twothirds; //K170? - b = K85 + twothirds; - - } else { - // 101 - //case 5: // B -> P - r = third; - g = 0; - FORCE_REFERENCE(g); - b = K255 - third; - - } - } else { - if( ! (hue & 0x20) ) { - // 110 - //case 6: // P -- K - r = K85 + third; - g = 0; - FORCE_REFERENCE(g); - b = K171 - third; - - } else { - // 111 - //case 7: // K -> R - r = K170 + third; - g = 0; - FORCE_REFERENCE(g); - b = K85 - third; - - } - } - } - - // This is one of the good places to scale the green down, - // although the client can scale green down as well. - if( G2 ) g = g >> 1; - if( Gscale ) g = scale8_video( g, Gscale); - - // Scale down colors if we're desaturated at all - // and add the brightness_floor to r, g, and b. - if( sat != 255 ) { - if( sat == 0) { - r = 255; b = 255; g = 255; - } else { - uint8_t desat = 255 - sat; - desat = scale8_video( desat, desat); - - uint8_t satscale = 255 - desat; - //satscale = sat; // uncomment to revert to pre-2021 saturation behavior - - - if( r ) r = scale8( r, satscale) + 1; - if( g ) g = scale8( g, satscale) + 1; - if( b ) b = scale8( b, satscale) + 1; - - uint8_t brightness_floor = desat; - r += brightness_floor; - g += brightness_floor; - b += brightness_floor; - } - } - - // Now scale everything down if we're at value < 255. - if( val != 255 ) { - - val = scale8_video( val, val); - if( val == 0 ) { - r=0; g=0; b=0; - } else { - // nscale8x3_video( r, g, b, val); - - if( r ) r = scale8( r, val) + 1; - if( g ) g = scale8( g, val) + 1; - if( b ) b = scale8( b, val) + 1; - } - } - - // Here we have the old AVR "missing std X+n" problem again - // It turns out that fixing it winds up costing more than - // not fixing it. - // To paraphrase Dr Bronner, profile! profile! profile! - //asm volatile( "" : : : "r26", "r27" ); - //asm volatile (" movw r30, r26 \n" : : : "r30", "r31"); - rgb.r = r; - rgb.g = g; - rgb.b = b; -} -/* -// rainbow spectrum, adapted from fastled -// note: integer types are carefully chosen to maximize performance -void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb) { - - // slower version using conversion functions - //CHSV32 hsv32(hsv); - //CRGBW rgbw; - //hsv2rgb_rainbow16(hsv32, rgbw); - //rgb = CRGB(rgbw); - - uint8_t hue = hsv.hue; - uint8_t sat = hsv.sat; - uint32_t val = hsv.val; - uint8_t offset = hue & 0x1F; // 0..31 - uint32_t third16 = ((int)offset * 21846); // offset16 = offset * 1/3<<16 - uint8_t third = third16 >> 13; // max = 85 - uint8_t r, g, b; - - 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 >> 12; // 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 >> 12; // 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; - } - } - - rgb.r = r; - rgb.g = g; - rgb.b = b; -} -*/ -void rgb2hsv(const uint32_t rgb, CHSV32& hsv) // convert RGB to HSV (16bit hue), much more accurate and faster than fastled version -{ - hsv.hsv32 = 0; - int32_t r = (rgb>>16)&0xFF; - int32_t g = (rgb>>8)&0xFF; - int32_t b = rgb&0xFF; +// convert RGB to HSV (16bit hue), much more accurate than fastled version. 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 TODO: this is faster on C3, is it slower on ESP32? + //hsv.s = (255 * delta) / maxval; + //if (hsv.s == 0) return; // gray value // assuming gray values are passed rarely, this can be omitted to increase speed 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; } -inline CHSV rgb2hsv(const CRGB c) { // CRGB to CHSV +/* +CHSV rgb2hsv(const CRGB c) { // CRGB to CHSV CHSV32 hsv; rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); return CHSV(hsv); -} +}*/ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb CRGBW crgb; - hsv2rgb(CHSV32(hue, sat, 255), crgb); + hsv2rgb_spectrum(CHSV32(hue, sat, 255), crgb); rgb[0] = crgb.r; rgb[1] = crgb.g; rgb[2] = crgb.b; diff --git a/wled00/colors.h b/wled00/colors.h index fee4f4fba8..810ac5db2e 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -65,19 +65,17 @@ class NeoGammaWLEDMethod { 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 ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette); CRGBPalette16 generateRandomPalette(); -//void hsv2rgb(const CHSV32& hsv, uint32_t& rgb); -void hsv2rgb(const CHSV32& hsv, CRGBW& rgb); -CRGB hsv2rgb(const CHSV& hsv); -//void hsv2rgb_rainbow16(const CHSV32& hsv, CRGBW& rgb); -void hsv2rgb_rainbow16(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW = false); -void hsv2rgb_rainbow(const CHSV& hsv, CRGB& rgb); + +void hsv2rgb_spectrum(const CHSV32& hsv, CRGBW& rgb); +void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb); +void hsv2rgb_rainbow(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW); +void rgb2hsv(const CRGBW& rgb, CHSV32& hsv); void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); -void rgb2hsv(const uint32_t rgbw, CHSV32& hsv); -CHSV rgb2hsv(const CRGB c); void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb CRGB HeatColor(uint8_t temperature); // black body radiation @@ -95,10 +93,6 @@ void fill_gradient_RGB(CRGB* colors, uint32_t num, const CRGB& c1, const CRGB& c 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); -//test only: remove again once settled -void hsv2rgb_rainbow16_ptr(const CHSV32& hsv, uint8_t* rgb); - - // Representation of an HSV pixel (hue, saturation, value (aka brightness)). struct CHSV { union { @@ -193,27 +187,22 @@ struct CRGB { // allow construction from a CHSV color inline CRGB(const CHSV& rhs) __attribute__((always_inline)) { - hsv2rgb_rainbow(rhs, *this); + 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(CHSV(hue, sat, val), *this); - return *this; + 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(CHSV(hue, 255, 255), *this); - return *this; + 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, *this); - return *this; + 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; @@ -832,53 +821,6 @@ class CRGBPalette16 { } }; - // 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) {} - - // 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; - } - - // get the average of the R, G, B and W values - uint8_t getAverageLight() const { - return (r + g + b + w) >> 2; - } - }; - struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions union { struct { @@ -899,13 +841,85 @@ struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions : 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 uint32_t rgb) __attribute__((always_inline)) { - rgb2hsv(rgb, *this); + 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) + + //TODO: allow assignment/construction from 32bit rgb color? or is that ambiguous? -> its not clear inherently, so bettter use a cast (CHSV32 = CRGBW(rgb)) +}; + +// 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; } - inline CHSV32& operator= (const uint32_t& rgb) __attribute__((always_inline)) { // assignment from 32bit rgb color (white channel is ignored) - rgb2hsv(rgb, *this); - return *this; + + // adjust hue: input is 0-255 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/ir.cpp b/wled00/ir.cpp index 66327661ba..cbfcdf1fa7 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -129,10 +129,10 @@ 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(); - CHSV32 prim_hsv = sseg.colors[0]; + CHSV32 prim_hsv = CRGBW(sseg.colors[0]); prim_hsv.h += (amount<<8); CRGBW newcolor; - hsv2rgb(prim_hsv, newcolor); + newcolor = prim_hsv; newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { @@ -172,11 +172,11 @@ 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(); - CHSV32 prim_hsv = sseg.colors[0]; + 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 CRGBW newcolor; - hsv2rgb(prim_hsv, newcolor); + newcolor = prim_hsv; newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 803a5ad836..ba569a28f9 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -52,21 +52,25 @@ void WLED::loop() static size_t avgStripMillis = 0; unsigned long stripMillis; #endif -/* + uint32_t start; uint32_t end; uint32_t time; volatile uint32_t temp; - CHSV32 hsv32; - CRGBW rgbw; + CHSV32 hsv; + CRGBW rgbw = hw_random(); + CRGB rgb; start = micros(); - hsv32.s = 255; - hsv32.v = 255; + hsv.s = 255; + hsv.v = 255; for(int i = 0; i < 1000000; i++){ - hsv32.h = i; - //hsv2rgb_rainbow16(hsv32, rgbw); - hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + rgbw = hw_random(); + //hsv2rgb_rainbow(hsv32, rgbw); // this is slower... 546ms for 1mio calls + //rgbw.color32 = i;//&0xFFFF00; + //rgb2hsv(rgbw, hsv); + //temp+=hsv.h; + rgbw.adjust_hue(-i); temp+=rgbw.r; } end = micros(); @@ -76,13 +80,15 @@ uint32_t start; Serial.print(" time: "); Serial.println(time); +/* start = micros(); - hsv32.s = 255; - hsv32.v = 250; + hsv.s = 255; + hsv.v = 250; for(int i = 0; i < 1000000; i++){ - hsv32.h = i; - //hsv2rgb_rainbow16(hsv32, rgbw); - hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + hsv.h = i; + //hsv2rgb_rainbow(hsv32, rgbw); + //hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + rgbw = hsv; temp+=rgbw.r; } end = micros(); @@ -93,12 +99,13 @@ uint32_t start; Serial.println(time); start = micros(); - hsv32.s = 220; - hsv32.v = 255; + hsv.s = 220; + hsv.v = 255; for(int i = 0; i < 1000000; i++){ - hsv32.h = i; - //hsv2rgb_rainbow16(hsv32, rgbw); - hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + hsv.h = i; + //hsv2rgb_rainbow(hsv32, rgbw); + //hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + rgbw = hsv; temp+=rgbw.r; } end = micros(); @@ -108,13 +115,14 @@ uint32_t start; Serial.print(" time: "); Serial.println(time); - start = micros(); - hsv32.s = 220; - hsv32.v = 210; + start = micros(); + hsv.s = 220; + hsv.v = 210; for(int i = 0; i < 1000000; i++){ - hsv32.h = i; - //hsv2rgb_rainbow16(hsv32, rgbw); - hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + hsv.h = i; + //hsv2rgb_rainbow(hsv32, rgbw); + //hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); + rgbw = hsv; temp+=rgbw.r; } end = micros(); From 8f18ec057d90b45460cfa0c07ff9c84ed9566254 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 26 Mar 2025 07:36:38 +0100 Subject: [PATCH 06/15] more cleanup --- usermods/audioreactive/audio_reactive.h | 4 +- wled00/FX.cpp | 60 +------------------ wled00/FX_2Dfcn.cpp | 2 +- wled00/FX_fcn.cpp | 2 +- wled00/FXparticleSystem.cpp | 6 +- wled00/colors.cpp | 33 ++++------- wled00/colors.h | 7 +-- wled00/wled.cpp | 79 ------------------------- 8 files changed, 23 insertions(+), 170 deletions(-) diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h index d4b4b2d597..3d34488e96 100644 --- a/usermods/audioreactive/audio_reactive.h +++ b/usermods/audioreactive/audio_reactive.h @@ -1995,12 +1995,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/wled00/FX.cpp b/wled00/FX.cpp index 530f075251..1420a863f9 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -118,65 +118,9 @@ static um_data_t* getAudioData() { * No blinking. Just plain old static light. */ uint16_t mode_static(void) { -// fill the segment using hsv2rgb_rainbow and hsv2rgb alternatingly every 0.5 second -static bool useRainbow = true; -static uint32_t lastSwitch = 0; -static uint8_t sat = 255; -static uint8_t val = 255; -static int offset = 0; -if (millis() - lastSwitch > 2000) { - useRainbow = !useRainbow; - lastSwitch = millis(); - // if (useRainbow) sat--; - if (useRainbow) val--; -} - -offset++;//=100; -delay(50); - - -for (unsigned i = 0; i < SEGLEN; i++) { - if (useRainbow) { - //CHSV hsvColor = CHSV(((i+offset) * (0xFF))/(2*255), sat, val); - //CRGB rgbColor = hsvColor; - //hsv2rgb_rainbow(hsvColor, rgbColor); - //hsv2rgb_rainbow16(hsvColor.h<<8, hsvColor.s, hsvColor.v, rgbColor.raw, false); - //SEGMENT.setPixelColor(i, rgbColor.r, rgbColor.g, rgbColor.b); - CHSV32 hsv32Color; - hsv32Color.h = (((i+offset) * (0xFFFF))/(2*255)); - hsv32Color.s = sat; - hsv32Color.v = val; - CRGBW rgbwColor = hsv32Color; - //CHSV32 hsv_t = rgbwColor; - SEGMENT.setPixelColor(i, rgbwColor); - if(i == 0) SEGMENT.setPixelColor(i, SEGCOLOR(0)); // paint corner - //SEGMENT.fill(SEGCOLOR(0)); - } else { - CHSV32 hsv32Color; - hsv32Color.h = (((i+offset) * (0xFFFF))/(2*255)); - hsv32Color.s = sat; - hsv32Color.v = 255; - CRGBW rgbwColor_t = hsv32Color; - CHSV32 hsv_t = rgbwColor_t; - //hsv_t.h += 1; - //hsv_t.s = 254; - hsv_t.v = val; - //CRGBW rgbwColor = hsv_t; - CRGBW rgbwColor; - hsv2rgb_spectrum(hsv_t, rgbwColor); - //hsv2rgb_rainbow(hsv32Color, rgbwColor); - //hsv2rgb_rainbow16(hsv32Color.h, hsv32Color.s, hsv32Color.v, rgbwColor.raw, true); - SEGMENT.setPixelColor(i, rgbwColor); - //SEGMENT.fill(SEGCOLOR(0)); - } -} - - return FRAMETIME; + SEGMENT.fill(SEGCOLOR(0)); + return strip.isOffRefreshRequired() ? FRAMETIME : 350; } - -// SEGMENT.fill(SEGCOLOR(0)); -// return strip.isOffRefreshRequired() ? FRAMETIME : 350; -//} static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid"; diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 893123335e..5014f873ac 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -684,7 +684,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font default: return; } - uint32_t c = ColorFromPaletteWLED(grad, (i+1)*255/h, 255, NOBLEND); + uint32_t c = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND); // pre-scale color for all pixels c = color_fade(c, _segBri); _colorScaled = true; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 595fde823b..9706b11aaf 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1308,7 +1308,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_ if (mapping && vL > 1) paletteIndex = (i*255)/(vL -1); // paletteBlend: 0 - wrap when moving, 1 - always wrap, 2 - never wrap, 3 - none (undefined) if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end" - CRGBW palcol = ColorFromPaletteWLED(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global + CRGBW palcol = ColorFromPalette(_currentPalette, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global palcol.w = W(color); return palcol.color32; diff --git a/wled00/FXparticleSystem.cpp b/wled00/FXparticleSystem.cpp index 25b9ad1ff2..14ef5e7150 100644 --- a/wled00/FXparticleSystem.cpp +++ b/wled00/FXparticleSystem.cpp @@ -648,11 +648,11 @@ 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 = baseRGB; baseHSV.s = particles[i].sat; // set the saturation @@ -1577,7 +1577,7 @@ 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) { diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 1fce698294..69f78654b0 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -1,6 +1,5 @@ #include "wled.h" #include "fcn_declare.h" -//#include "colors.h" // todo: needed? its already included in fcn declare -> seems to compile fine without this include -> remove? /* Color conversion & utility methods @@ -10,9 +9,9 @@ - fastled replacements are mostly optimized for CHSV32 and CRGBW but using CRGB and HSV are equally fast - direct conversion (assignment or construction) from CHSV/CHSV32 to CRGB/CRGBW use the "rainbow" method (nicer colors) - 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, use "hsv2rgb_spectrum" function to convert it back (better color accuracy, rainbow causes slight color shift) - - to manipulate an RGB color in HSV space, use the - */ + - 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 +*/ /* * color blend function, based on FastLED blend function @@ -34,8 +33,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; uint32_t rb = (c1 & 0x00FF00FF) + (c2 & 0x00FF00FF); // mask and add two colors at once @@ -46,13 +44,9 @@ 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 + uint32_t max = std::max(r,g); // check for overflow max = std::max(max,b); max = std::max(max,w); - //unsigned max = r; // check for overflow note - //max = g > max ? g : max; - //max = b > max ? b : max; - //max = w > max ? w : max; if (max > 255) { uint32_t scale = (uint32_t(255)<<8) / max; // division of two 8bit (shifted) values does not work -> use bit shifts and multiplaction instead rb = ((rb * scale) >> 8) & 0x00FF00FF; // @@ -72,9 +66,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 @@ -110,7 +102,7 @@ __attribute__((optimize("O3"))) void adjust_color(CRGBW& rgb, int32_t hueShift, } // 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) +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 @@ -153,7 +145,7 @@ CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette) { CHSV palettecolors[4]; // array of colors for the new palette uint8_t keepcolorposition = hw_random8(4); // color position of current random palette to keep - //!!! update this palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette + palettecolors[keepcolorposition] = rgb2hsv(basepalette.entries[keepcolorposition*5]); // read one of the base colors of the current palette palettecolors[keepcolorposition].hue += hw_random8(10)-5; // +/- 5 randomness of base color // generate 4 saturation and brightness value numbers // only one saturation is allowed to be below 200 creating mostly vibrant colors @@ -311,7 +303,7 @@ void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb) { rgb = CRGB(rgb32); } -// convert HSV (16bit hue) to RGB (24bit), optimized for speed (integer types and function arguments were very carefully chosen for best performance) +// 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("O3"))) void hsv2rgb_rainbow(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW) { @@ -433,7 +425,7 @@ __attribute__((optimize("O3"))) void rgb2hsv(const CRGBW& rgb, CHSV32& hsv) { minval = (r < g) ? ((r < b) ? r : b) : ((g < b) ? g : b); hsv.v = maxval; delta = maxval - minval; - hsv.s = delta == maxval ? 255 : (255 * delta) / maxval; // faster on fully saturated colors, slightly slower otherwise TODO: this is faster on C3, is it slower on ESP32? + hsv.s = delta == maxval ? 255 : (255 * delta) / maxval; // faster on fully saturated colors, slightly slower otherwise //hsv.s = (255 * delta) / maxval; //if (hsv.s == 0) return; // gray value // assuming gray values are passed rarely, this can be omitted to increase speed if (maxval == r) hsv.h = (10923 * (g - b)) / delta; @@ -441,12 +433,11 @@ __attribute__((optimize("O3"))) void rgb2hsv(const CRGBW& rgb, CHSV32& hsv) { else hsv.h = 43690 + (10923 * (r - g)) / delta; } -/* CHSV rgb2hsv(const CRGB c) { // CRGB to CHSV CHSV32 hsv; - rgb2hsv((uint32_t((byte(c.r) << 16) | (byte(c.g) << 8) | (byte(c.b)))), hsv); + rgb2hsv(CRGBW(c), hsv); return CHSV(hsv); -}*/ +} void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) { //hue, sat to rgb CRGBW crgb; diff --git a/wled00/colors.h b/wled00/colors.h index 810ac5db2e..9f7bf4867d 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -3,8 +3,6 @@ // note: some functions/structs have been copied from fastled library, modified and optimized for WLED -#define ColorFromPalette ColorFromPaletteWLED // override fastled version //!!!todo: rename - // 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)) @@ -66,7 +64,7 @@ inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return col [[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 ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND); +[[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(); @@ -75,6 +73,7 @@ void hsv2rgb_spectrum(const CHSV32& hsv, CRGBW& rgb); void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb); void hsv2rgb_rainbow(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW); 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 @@ -843,8 +842,6 @@ struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions // 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) - - //TODO: allow assignment/construction from 32bit rgb color? or is that ambiguous? -> its not clear inherently, so bettter use a cast (CHSV32 = CRGBW(rgb)) }; // 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 diff --git a/wled00/wled.cpp b/wled00/wled.cpp index ba569a28f9..8c7e4e21fe 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -53,85 +53,6 @@ void WLED::loop() unsigned long stripMillis; #endif -uint32_t start; - uint32_t end; - uint32_t time; - volatile uint32_t temp; - - CHSV32 hsv; - CRGBW rgbw = hw_random(); - CRGB rgb; - start = micros(); - hsv.s = 255; - hsv.v = 255; - for(int i = 0; i < 1000000; i++){ - rgbw = hw_random(); - //hsv2rgb_rainbow(hsv32, rgbw); // this is slower... 546ms for 1mio calls - //rgbw.color32 = i;//&0xFFFF00; - //rgb2hsv(rgbw, hsv); - //temp+=hsv.h; - rgbw.adjust_hue(-i); - temp+=rgbw.r; - } - end = micros(); - time = end - start; - Serial.print("s=255,v=255: "); - Serial.print(temp); - Serial.print(" time: "); - Serial.println(time); - -/* - start = micros(); - hsv.s = 255; - hsv.v = 250; - for(int i = 0; i < 1000000; i++){ - hsv.h = i; - //hsv2rgb_rainbow(hsv32, rgbw); - //hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); - rgbw = hsv; - temp+=rgbw.r; - } - end = micros(); - time = end - start; - Serial.print("s=255,v=250: "); - Serial.print(temp); - Serial.print(" time: "); - Serial.println(time); - - start = micros(); - hsv.s = 220; - hsv.v = 255; - for(int i = 0; i < 1000000; i++){ - hsv.h = i; - //hsv2rgb_rainbow(hsv32, rgbw); - //hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); - rgbw = hsv; - temp+=rgbw.r; - } - end = micros(); - time = end - start; - Serial.print("s=220,v=255: "); - Serial.print(temp); - Serial.print(" time: "); - Serial.println(time); - - start = micros(); - hsv.s = 220; - hsv.v = 210; - for(int i = 0; i < 1000000; i++){ - hsv.h = i; - //hsv2rgb_rainbow(hsv32, rgbw); - //hsv2rgb_rainbow16(hsv32.h, hsv32.s, hsv32.v, rgbw.raw, true); - rgbw = hsv; - temp+=rgbw.r; - } - end = micros(); - time = end - start; - Serial.print("s=220,v=210: "); - Serial.print(temp); - Serial.print(" time: "); - Serial.println(time); -*/ handleTime(); #ifndef WLED_DISABLE_INFRARED handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too From 81708d21a5072a481d7b0e85b9014f5a463a58fb Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 26 Mar 2025 08:21:12 +0100 Subject: [PATCH 07/15] final cleanup --- wled00/colors.cpp | 12 ++++++------ wled00/colors.h | 3 --- wled00/palettes.h | 16 ++++++++-------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 4ef5d32db1..675b2b09ef 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -738,12 +738,12 @@ void fill_solid_RGB(CRGB* colors, uint32_t num, const CRGB& 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 - uint32_t t = endpos; - CRGB tc = endcolor; - endcolor = startcolor; - endpos = startpos; - startpos = t; - startcolor = tc; + uint32_t t = endpos; + CRGB tc = endcolor; + endcolor = startcolor; + endpos = startpos; + startpos = t; + startcolor = tc; } int32_t rdistance = endcolor.r - startcolor.r; int32_t gdistance = endcolor.g - startcolor.g; diff --git a/wled00/colors.h b/wled00/colors.h index 9f7bf4867d..7bbec33c52 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -221,9 +221,6 @@ struct CRGB { b = nb; return *this; } -#define R(c) (byte((c) >> 16)) -#define G(c) (byte((c) >> 8)) -#define B(c) (byte(c)) /// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) { diff --git a/wled00/palettes.h b/wled00/palettes.h index 2e9f3a7f89..c8acbf46d5 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -83,7 +83,7 @@ const TProgmemRGBPalette16 OceanColors_p PROGMEM = { CRGB::LightSkyBlue }; -/// Forest colors, greens +// Forest colors, greens const TProgmemRGBPalette16 ForestColors_p PROGMEM = { CRGB::DarkGreen, CRGB::DarkGreen, @@ -106,27 +106,27 @@ const TProgmemRGBPalette16 ForestColors_p PROGMEM = { CRGB::ForestGreen }; -/// HSV Rainbow +// HSV Rainbow const TProgmemRGBPalette16 RainbowColors_p PROGMEM = { 0xFF0000, 0xD52A00, 0xAB5500, 0xAB7F00, 0xABAB00, 0x56D500, 0x00FF00, 0x00D52A, 0x00AB55, 0x0056AA, 0x0000FF, 0x2A00D5, 0x5500AB, 0x7F0081, 0xAB0055, 0xD5002B}; -/// Alias of RainbowStripeColors_p +// Alias of RainbowStripeColors_p #define RainbowStripesColors_p RainbowStripeColors_p -/// HSV Rainbow colors with alternatating stripes of black +// 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. +// 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, From 2c69c5e759219a194e50ba12e9f81346bbdebefa Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 26 Mar 2025 20:55:33 +0100 Subject: [PATCH 08/15] fixed compiler warnings, removed unused stuff --- platformio.ini | 2 - .../usermod_rotary_brightness_color.cpp | 8 +- wled00/FX.cpp | 1 - wled00/colors.cpp | 24 +++--- wled00/colors.h | 80 +++++++++---------- wled00/wled.h | 2 +- 6 files changed, 55 insertions(+), 62 deletions(-) diff --git a/platformio.ini b/platformio.ini index b68353289d..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/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 997d38f5a5..0582c9067a 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -4922,7 +4922,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so byte dy = lerp8by8(x2, y2, rate); //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 - color_fade(color, (255-rate), true); if (grad) SEGMENT.fadePixelColorXY(dx, dy, rate); } diff --git a/wled00/colors.cpp b/wled00/colors.cpp index 675b2b09ef..b3bb98a4df 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -738,29 +738,29 @@ void fill_solid_RGB(CRGB* colors, uint32_t num, const CRGB& 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 - uint32_t t = endpos; + unsigned t = endpos; CRGB tc = endcolor; endcolor = startcolor; endpos = startpos; startpos = t; startcolor = tc; } - int32_t rdistance = endcolor.r - startcolor.r; - int32_t gdistance = endcolor.g - startcolor.g; - int32_t bdistance = endcolor.b - startcolor.b; + int rdistance = endcolor.r - startcolor.r; + int gdistance = endcolor.g - startcolor.g; + int bdistance = endcolor.b - startcolor.b; - int32_t divisor = endpos - startpos; + int divisor = endpos - startpos; divisor = divisor == 0 ? 1 : divisor; // prevent division by zero - int32_t rdelta = (rdistance << 16) / divisor; - int32_t gdelta = (gdistance << 16) / divisor; - int32_t bdelta = (bdistance << 16) / divisor; + int rdelta = (rdistance << 16) / divisor; + int gdelta = (gdistance << 16) / divisor; + int bdelta = (bdistance << 16) / divisor; - int32_t rshifted = startcolor.r << 16; - int32_t gshifted = startcolor.g << 16; - int32_t bshifted = startcolor.b << 16; + int rshifted = startcolor.r << 16; + int gshifted = startcolor.g << 16; + int bshifted = startcolor.b << 16; - for (int32_t i = startpos; i <= endpos; i++) { + for (unsigned i = startpos; i <= endpos; i++) { colors[i] = CRGB(rshifted >> 16, gshifted >> 16, bshifted >> 16); rshifted += rdelta; gshifted += gdelta; diff --git a/wled00/colors.h b/wled00/colors.h index 7bbec33c52..4724e9d5d2 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -22,29 +22,29 @@ uint8_t qsub8(uint8_t i, uint8_t j); int8_t abs8(int8_t i); 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 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 +// 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 + 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; ///< CRGB::red channel value of the color entry - uint8_t g; ///< CRGB::green channel value of the color entry - uint8_t b; ///< CRGB::blue channel value of the color entry + uint8_t index; // index of the color entry in the gradient + uint8_t r; + uint8_t g; + uint8_t b; }; - uint32_t dword; ///< values as a packed 32-bit double word - uint8_t bytes[4]; ///< values as an array + uint32_t dword; // values packed as 32-bit + uint8_t bytes[4]; // values as an array } TRGBGradientPaletteEntryUnion; // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) @@ -122,11 +122,11 @@ struct CHSV { return raw[x]; } - // Default constructor - // @warning Default values are UNITIALIZED! + // default constructor + // @warning default values are UNITIALIZED! inline CHSV() __attribute__((always_inline)) = default; - ///Allow construction from hue, saturation, and value + // 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) { } @@ -194,12 +194,12 @@ struct CRGB { hsv2rgb_rainbow(hue<<8, sat, val, raw, false); return *this; } - // Allow assignment from just a hue, sat and val are set to max + // 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 + // 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; } @@ -214,7 +214,7 @@ struct CRGB { return *this; } - /// allow assignment from red, green, and blue + // 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; @@ -222,7 +222,7 @@ struct CRGB { return *this; } - /// Allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code + // allow assignment from 32-bit (really 24-bit) 0xRRGGBB color code inline CRGB& setColorCode (uint32_t colorcode) __attribute__((always_inline)) { r = R(colorcode); g = G(colorcode); @@ -230,7 +230,6 @@ struct CRGB { return *this; } - // add one CRGB to another, saturating at 0xFF for each channel inline CRGB& operator+= (const CRGB& rhs) { r = qadd8(r, rhs.r); @@ -344,7 +343,7 @@ struct CRGB { return *this; } - /// Return a CRGB object that is a scaled down version of this object + // 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; @@ -354,7 +353,7 @@ struct CRGB { return out; } - /// Return a CRGB object that is a scaled down version of this object + // 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); @@ -363,8 +362,7 @@ struct CRGB { return out; } - /// fadeToBlackBy is a synonym for nscale8(), as a fade instead of a scale - /// @param fadefactor the amount to fade, sent to nscale8() as (255 - fadefactor) + // 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; @@ -373,7 +371,7 @@ struct CRGB { return *this; } - /// "or" operator brings each channel up to the higher of the two values + // "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; @@ -381,7 +379,6 @@ struct CRGB { return *this; } - /// @copydoc operator|= inline CRGB& operator|=(uint8_t d) { if (d > r) r = d; if (d > g) g = d; @@ -389,7 +386,7 @@ struct CRGB { return *this; } - /// "and" operator brings each channel down to the lower of the two values + // "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; @@ -397,7 +394,6 @@ struct CRGB { return *this; } - /// @copydoc operator&= inline CRGB& operator&=(uint8_t d) { if (d < r) r = d; if (d < g) g = d; @@ -405,12 +401,12 @@ struct CRGB { return *this; } - /// This allows testing a CRGB for zero-ness + // 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 + // 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) | @@ -662,7 +658,7 @@ class CRGBPalette16 { 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 (int i = 0; i < (sizeof(entries)); ++i) { + for (unsigned i = 0; i < (sizeof(entries)); ++i) { if (*p != *q) return false; ++p; ++q; @@ -736,24 +732,24 @@ class CRGBPalette16 { TRGBGradientPaletteEntryUnion u; // Count entries - uint32_t count = 0; + int count = 0; do { u.dword = *(const uint32_t*)(progent + count); ++count; } while (u.index != 255); - int32_t lastSlotUsed = -1; + int lastSlotUsed = -1; u.dword = *(const uint32_t*)(progent); CRGB rgbstart(u.r, u.g, u.b); - uint32_t indexstart = 0; - uint32_t istart8 = 0; - uint32_t iend8 = 0; + int indexstart = 0; + int istart8 = 0; + int iend8 = 0; while (indexstart < 255) { ++progent; u.dword = *(const uint32_t*)(progent); - uint32_t indexend = u.index; + int indexend = u.index; CRGB rgbend(u.r, u.g, u.b); istart8 = indexstart / 16; iend8 = indexend / 16; @@ -779,20 +775,20 @@ class CRGBPalette16 { TRGBGradientPaletteEntryUnion u; // Count entries - uint16_t count = 0; + unsigned count = 0; do { u = *(ent + count); ++count; } while (u.index != 255); - int8_t lastSlotUsed = -1; + int lastSlotUsed = -1; u = *ent; CRGB rgbstart(u.r, u.g, u.b); int indexstart = 0; - uint8_t istart8 = 0; - uint8_t iend8 = 0; + int istart8 = 0; + int iend8 = 0; while (indexstart < 255) { ++ent; u = *ent; diff --git a/wled00/wled.h b/wled00/wled.h index 3f5f9cf499..e6c44bace5 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -184,7 +184,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument; #define FASTLED_INTERNAL //remove annoying pragma messages #define USE_GET_MILLISECOND_TIMER -//#include "FastLED.h" //TODO:remove + #include "const.h" #include "colors.h" #include "fcn_declare.h" From 6894f350c738109b03eb395c3ce28150f37964be Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Thu, 27 Mar 2025 07:17:41 +0100 Subject: [PATCH 09/15] bugfixes --- wled00/FX.cpp | 2 +- wled00/colors.h | 13 ++++--------- wled00/fcn_declare.h | 1 + 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 0582c9067a..876b870ebd 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -5695,7 +5695,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 = ((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; } } diff --git a/wled00/colors.h b/wled00/colors.h index 4724e9d5d2..29cd195f2e 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -19,6 +19,7 @@ class CRGBPalette16; uint8_t scale8(uint8_t i, uint8_t scale); uint8_t qadd8(uint8_t i, uint8_t j); uint8_t qsub8(uint8_t i, uint8_t j); +uint8_t qmul8(uint8_t i, uint8_t j); int8_t abs8(int8_t i); typedef uint32_t TProgmemRGBPalette16[16]; @@ -306,15 +307,9 @@ struct CRGB { // multiply each of the channels by a constant, saturating each channel at 0xFF. inline CRGB& operator*= (uint8_t d) { - unsigned red = (unsigned)r * (unsigned)d; - unsigned green = (unsigned)r * (unsigned)d; - unsigned blue = (unsigned)r * (unsigned)d; - if(red > 255) red = 255; - if(green > 255) green = 255; - if(blue > 255) blue = 255; - r = red; - g = green; - b = blue; + r = qmul8(r, d); + g = qmul8(g, d); + b = qmul8(b, d); return *this; } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index dcecd54a68..d04723ec72 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -494,6 +494,7 @@ __attribute__ ((always_inline)) inline uint8_t scale8_video(uint8_t i, uint8_t __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); } /* From b49fdabfbe2cae847867b7f28e0277e535331a1d Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 6 Apr 2025 08:12:15 +0200 Subject: [PATCH 10/15] remove unneeded commented function declares --- wled00/fcn_declare.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index d04723ec72..9bd0d992ce 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -487,9 +487,6 @@ uint8_t ease8InOutQuad(uint8_t i); // 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); } -//original fastled: -//__attribute__ ((always_inline)) inline uint8_t scale8(uint8_t i, uint8_t scale ) { return (((uint16_t)i) * (1 + (uint16_t)(scale))) >> 8; } -//__attribute__ ((always_inline)) inline uint8_t scale8_video(uint8_t i, uint8_t scale ) { uint8_t j = (((int)i * (int)scale) >> 8) + ((i && scale) ? 1 : 0); return j; } __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; } @@ -497,15 +494,6 @@ __attribute__ ((always_inline)) inline uint8_t qsub8(uint8_t i, uint8_t j) { in __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); } -/* -inline uint8_t inoise8(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement -inline uint8_t inoise8_raw(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement -inline uint8_t inoise8(uint16_t x,uint16_t y) { return 0; } // dummy, needs replacement -inline uint8_t inoise8(uint16_t x) { return 0; } // dummy, needs replacement -inline uint8_t inoise16(uint16_t x,uint16_t y,uint16_t z) { return 0; } // dummy, needs replacement -inline uint8_t inoise16(uint16_t x,uint16_t y) { return 0; } // dummy, needs replacement -inline uint8_t inoise16(uint16_t x) { return 0; } // dummy, needs replacement -*/ /* #include // standard math functions. use a lot of flash From 47773b49226fe9ac6bdddc1dd381cd68a34ed0b4 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sun, 13 Apr 2025 16:11:35 +0200 Subject: [PATCH 11/15] code cleanup, moved (most) fastled functions into fastled_fcn.cpp - resolves licensing issue - moved PRNG into its own space - prettified some code --- wled00/FX.cpp | 13 +- wled00/FX.h | 7 - wled00/FX_fcn.cpp | 1 + wled00/PRNG.h | 24 + wled00/colors.cpp | 254 +----- wled00/colors.h | 781 +---------------- wled00/fcn_declare.h | 41 - wled00/ir.cpp | 9 +- wled00/palettes.h | 203 ++--- wled00/src/dependencies/fastled/LICENSE.txt | 20 + .../src/dependencies/fastled/fastled_fcn.cpp | 259 ++++++ wled00/src/dependencies/fastled/fastled_fcn.h | 784 ++++++++++++++++++ wled00/util.cpp | 27 +- wled00/wled_math.cpp | 22 - 14 files changed, 1249 insertions(+), 1196 deletions(-) create mode 100644 wled00/PRNG.h create mode 100644 wled00/src/dependencies/fastled/LICENSE.txt create mode 100644 wled00/src/dependencies/fastled/fastled_fcn.cpp create mode 100644 wled00/src/dependencies/fastled/fastled_fcn.h diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 25898cd2bc..410e2f78fc 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -14,6 +14,7 @@ #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" @@ -69,7 +70,7 @@ //#define MAX_FREQUENCY 5120 //#define MAX_FREQ_LOG10 3.71f -PRNG prng; // pseudo-random number generator class +static PRNG prng(hw_random()); // pseudo-random number generator class, seed = hardware random number // effect utility functions uint8_t sin_gap(uint16_t in) { @@ -4144,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 = 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. + 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 = prng.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 (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)); } - prng.setSeed(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"; @@ -6011,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] = 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 diff --git a/wled00/FX.h b/wled00/FX.h index d43bcb4a5b..fddebbb369 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -38,13 +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() diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 69acff8fa1..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! diff --git a/wled00/PRNG.h b/wled00/PRNG.h new file mode 100644 index 0000000000..c1fa69fcd7 --- /dev/null +++ b/wled00/PRNG.h @@ -0,0 +1,24 @@ +#include "wled.h" + +// Pseudo-Random-Number-Generator for 16bit and 8bit random numbers used by some effects (fastled replacement) +// allows the same sequence of random numbers to be generated by setting the seed +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() { + uint32_t s = seed; + s *= 0x9E37; + s ^= s >> 11; + seed = (s & 0xFFFF) ^ (s >> 16); + 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() >> 8; } + 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; } +}; \ No newline at end of file diff --git a/wled00/colors.cpp b/wled00/colors.cpp index b3bb98a4df..589696ca67 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -1,21 +1,21 @@ #include "wled.h" #include "fcn_declare.h" +#include "colors.h" + /* - Color conversion & utility methods - - 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) - - fastled replacements are mostly optimized for CHSV32 and CRGBW but using CRGB and HSV are equally fast - - direct conversion (assignment or construction) from CHSV/CHSV32 to CRGB/CRGBW use the "rainbow" method (nicer colors) - - 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 -*/ + * Color conversion & utility methods + */ + +/* + * 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, based on FastLED blend function - * the calculation for each color is: result = (A*(amountOfA) + A + B*(amountOfB) + B) / 256 with amountOfA = 255 - amountOfB + * 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 @@ -46,11 +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 - max = std::max(max,b); - max = std::max(max,w); - 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 @@ -104,7 +103,7 @@ __attribute__((optimize("O3"))) void adjust_color(CRGBW& rgb, int32_t hueShift, hsv2rgb_spectrum(hsv, rgb); // convert back to RGB } -// 1:1 replacement of fastled function optimized for ESP, slightly faster, more accurate and uses less flash (~ -200bytes) +// 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) { @@ -251,7 +250,7 @@ CRGBPalette16 generateRandomPalette() // generate fully random palette } // convert HSV (16bit hue) to RGB (32bit with white = 0), optimized for speed -__attribute__((optimize("O3"))) void hsv2rgb_spectrum(const CHSV32& hsv, CRGBW& rgb) { +__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) @@ -307,114 +306,7 @@ void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb) { rgb = CRGB(rgb32); } -// 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("O3"))) 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; - } -} - -// convert RGB to HSV (16bit hue), much more accurate than fastled version. note: using "O3" makes it ~5% faster at minimal flash cost (~20 bytes) +// 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; @@ -430,8 +322,7 @@ __attribute__((optimize("O3"))) void rgb2hsv(const CRGBW& rgb, CHSV32& hsv) { hsv.v = maxval; delta = maxval - minval; hsv.s = delta == maxval ? 255 : (255 * delta) / maxval; // faster on fully saturated colors, slightly slower otherwise - //hsv.s = (255 * delta) / maxval; - //if (hsv.s == 0) return; // gray value // assuming gray values are passed rarely, this can be omitted to increase speed + // 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; @@ -498,27 +389,6 @@ void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins } } -// black body radiation to RGB (from fastled) -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; -} - #ifndef WLED_DISABLE_HUESYNC void colorXYtoRGB(float x, float y, byte* rgb) //coordinates to rgb (https://www.developers.meethue.com/documentation/color-conversions-rgb-xy) { @@ -727,85 +597,3 @@ uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct32(uint32_t color) b = gammaT[b]; return RGBW32(r, g, b, w); } - -// 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; - } -} \ No newline at end of file diff --git a/wled00/colors.h b/wled00/colors.h index 29cd195f2e..e7afe720f3 100644 --- a/wled00/colors.h +++ b/wled00/colors.h @@ -1,7 +1,18 @@ #ifndef WLED_COLORS_H #define WLED_COLORS_H - -// note: some functions/structs have been copied from fastled library, modified and optimized for WLED +#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)))) @@ -10,43 +21,8 @@ #define B(c) (byte(c)) #define W(c) (byte((c) >> 24)) -//forward declarations -struct CRGB; -struct CHSV; struct CRGBW; struct CHSV32; -class CRGBPalette16; -uint8_t scale8(uint8_t i, uint8_t scale); -uint8_t qadd8(uint8_t i, uint8_t j); -uint8_t qsub8(uint8_t i, uint8_t j); -uint8_t qmul8(uint8_t i, uint8_t j); -int8_t abs8(int8_t i); - -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; // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) class NeoGammaWLEDMethod { @@ -72,13 +48,11 @@ CRGBPalette16 generateRandomPalette(); void hsv2rgb_spectrum(const CHSV32& hsv, CRGBW& rgb); void hsv2rgb_spectrum(const CHSV& hsv, CRGB& rgb); -void hsv2rgb_rainbow(uint16_t h, uint8_t s, uint8_t v, uint8_t* rgbdata, bool isRGBW); 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 -CRGB HeatColor(uint8_t temperature); // black body radiation 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); @@ -86,727 +60,6 @@ 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); -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); - -// 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(R(colorcode)), g(G(colorcode)), b(B(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 = R(colorcode); - g = G(colorcode); - b = B(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 = R(colorcode); - g = G(colorcode); - b = B(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; - } -}; struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions union { @@ -822,13 +75,18 @@ struct CHSV32 { // 32bit HSV color with 16bit hue for more accurate conversions // 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) }; @@ -885,7 +143,7 @@ struct CRGBW { return color32; } - // adjust hue: input is 0-255 for full color cycle, input can be negative + // 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; @@ -901,6 +159,7 @@ struct CRGBW { 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; diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 9bd0d992ce..049f5398df 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -398,10 +398,6 @@ 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); -uint8_t triwave8(uint8_t in); -uint16_t triwave16(uint16_t in); -uint8_t quadwave8(uint8_t in); -uint8_t cubicwave8(uint8_t in); um_data_t* simulateSound(uint8_t simulationId); void enumerateLedmaps(); @@ -480,20 +476,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); -uint8_t ease8InOutCubic(uint8_t i); -uint16_t ease16InOutCubic(uint16_t i); -uint8_t ease8InOutQuad(uint8_t i); - -// 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); } /* #include // standard math functions. use a lot of flash @@ -507,29 +489,6 @@ __attribute__ ((always_inline)) inline int8_t lerp8by8(uint8_t a, uint8_t b, u #define floor_t floorf */ -// PRNG for 16bit and 8bit random numbers used by some effects (fastled replacement) -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() { - uint32_t s = seed; - s *= 0x9E37; - s ^= s >> 11; - seed = (s & 0xFFFF) ^ (s >> 16); - 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() >> 8; } - 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; } -}; -extern PRNG prng; - //wled_serial.cpp void handleSerial(); void updateBaudRate(uint32_t rate); diff --git a/wled00/ir.cpp b/wled00/ir.cpp index cbfcdf1fa7..4ea42ba454 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -129,10 +129,8 @@ 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(); - CHSV32 prim_hsv = CRGBW(sseg.colors[0]); - prim_hsv.h += (amount<<8); - CRGBW newcolor; - newcolor = prim_hsv; + 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++) { @@ -175,8 +173,7 @@ static void changeEffectIntensity(int8_t 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 - CRGBW newcolor; - newcolor = prim_hsv; + CRGBW newcolor = prim_hsv; newcolor.w = W(sseg.colors[0]); if (irApplyToAllSelected) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) { diff --git a/wled00/palettes.h b/wled00/palettes.h index c8acbf46d5..c6b558c8a3 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -3,135 +3,140 @@ * 4 bytes per color: index, red, green, blue */ -// 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 +#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 + 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 + 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 + 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 + 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}; - -// Alias of RainbowStripeColors_p -#define RainbowStripesColors_p RainbowStripeColors_p + 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}; + 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}; + 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 + +// 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. const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, 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 af855ce32d..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,7 +376,10 @@ uint16_t crc16(const unsigned char* data_p, size_t length) { return crc; } -// fastled beat per minute and beatsin replacements +// 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(). @@ -425,25 +428,7 @@ uint8_t beatsin8_t(uint16_t beats_per_minute, uint8_t lowest, uint8_t highest, u uint8_t result = lowest + scaledbeat; return result; } - -// 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)); -} +// end of FastLED functions /////////////////////////////////////////////////////////////////////////////// // Begin simulateSound (to enable audio enhanced effects to display something) diff --git a/wled00/wled_math.cpp b/wled00/wled_math.cpp index cb9f0b1b3b..8b2d927b22 100644 --- a/wled00/wled_math.cpp +++ b/wled00/wled_math.cpp @@ -245,25 +245,3 @@ uint32_t sqrt32_bw(uint32_t x) { return res; } -// 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; -} From cb9ba989968f85e6f2e27583188f7001acf9ecec Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Tue, 15 Apr 2025 19:15:27 +0200 Subject: [PATCH 12/15] Rename PRNG.h to prng.h --- wled00/{PRNG.h => prng.h} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename wled00/{PRNG.h => prng.h} (99%) diff --git a/wled00/PRNG.h b/wled00/prng.h similarity index 99% rename from wled00/PRNG.h rename to wled00/prng.h index c1fa69fcd7..738df0d2c1 100644 --- a/wled00/PRNG.h +++ b/wled00/prng.h @@ -21,4 +21,4 @@ class PRNG { uint8_t random8() { return random16() >> 8; } 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; } -}; \ No newline at end of file +}; From f1cb1ee44e4ccf9d7d180c5b5c97f4aa75cc1173 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Tue, 15 Apr 2025 20:04:27 +0200 Subject: [PATCH 13/15] remove duplicate line Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 5a7bd1250e..e32b2b6999 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7887,7 +7887,7 @@ uint16_t mode_particlefireworks(void) { counter = 0; speed += 3 + ((SEGMENT.intensity >> 6)); // increase speed to form a second wave PartSys->sources[j].source.hue += hueincrement; // new color for next circle - PartSys->sources[j].source.sat = min((uint16_t)150, hw_random16()); + PartSys->sources[j].source.sat = 100 + hw_random16(156); PartSys->sources[j].source.sat = 100 + hw_random16(156); } angle += angleincrement; // set angle for next particle From 398fb2124d151a2c9694f8b38bb99015ed07188e Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 16 Apr 2025 19:09:58 +0200 Subject: [PATCH 14/15] fix coderabbit messup. --- wled00/FX.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e32b2b6999..2bb984521c 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7888,7 +7888,6 @@ uint16_t mode_particlefireworks(void) { speed += 3 + ((SEGMENT.intensity >> 6)); // increase speed to form a second wave PartSys->sources[j].source.hue += hueincrement; // new color for next circle PartSys->sources[j].source.sat = 100 + hw_random16(156); - PartSys->sources[j].source.sat = 100 + hw_random16(156); } angle += angleincrement; // set angle for next particle } From a2e343e3df6ac846645a44dd8fe016ecccd35d99 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 18 Apr 2025 12:20:15 +0200 Subject: [PATCH 15/15] fixed prng: it now generates a full sequence of random numbers thx @TripleWhy for pointing out the flaw --- wled00/prng.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/wled00/prng.h b/wled00/prng.h index 738df0d2c1..da0a84f124 100644 --- a/wled00/prng.h +++ b/wled00/prng.h @@ -1,7 +1,8 @@ #include "wled.h" -// Pseudo-Random-Number-Generator for 16bit and 8bit random numbers used by some effects (fastled replacement) -// allows the same sequence of random numbers to be generated by setting the seed +// 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; @@ -10,15 +11,13 @@ class PRNG { void setSeed(uint16_t s) { seed = s; } uint16_t getSeed() const { return seed; } uint16_t random16() { - uint32_t s = seed; - s *= 0x9E37; - s ^= s >> 11; - seed = (s & 0xFFFF) ^ (s >> 16); - return seed; + 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() >> 8; } + 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; } };