Optimization: color_blend() (#4245)

Removing the bool saves on code size and makes the function a tiny bit faster. Also this is a cleaner solution IMHO.

-updated blend function to optimized 8bit calculation
- efficient color blend calculation in fews operations possible
- omitting min / max checks makes it faster on average
- using 8bit for "blend" variable does not significantly influence the resulting color, just transition points are slightly shifted but yield very good results (and better than the original 16bit version using the old fastled math with improper rounding)
- updated drawCircle and drawLine to use 8bit directly instead of 16bit with a shift
This commit is contained in:
Damian Schneider 2024-12-19 18:20:56 +01:00 committed by GitHub
parent 217d2aeb7f
commit 26397ee8ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 88 additions and 100 deletions

View File

@ -200,7 +200,7 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
} else
{
SEGMENT.setPixelColor(index, back? col0 : col1);
if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, rem));
if (i == ledIndex) SEGMENT.setPixelColor(index, color_blend(back? col0 : col1, back? col1 : col0, uint8_t(rem)));
}
}
return FRAMETIME;
@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
SEGENV.step = it;
}
SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), fade));
SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux1), SEGMENT.color_wheel(SEGENV.aux0), uint8_t(fade)));
return FRAMETIME;
}
static const char _data_FX_MODE_RANDOM_COLOR[] PROGMEM = "Random Colors@!,Fade time;;!;01";
@ -338,7 +338,7 @@ uint16_t mode_breath(void) {
var = sin16_t(counter) / 103; //close to parabolic in range 0-8192, max val. 23170
}
unsigned lum = 30 + var;
uint8_t lum = 30 + var;
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum));
}
@ -353,7 +353,7 @@ static const char _data_FX_MODE_BREATH[] PROGMEM = "Breathe@!;!,!;!;01";
*/
uint16_t mode_fade(void) {
unsigned counter = (strip.now * ((SEGMENT.speed >> 3) +10));
unsigned lum = triwave16(counter) >> 8;
uint8_t lum = triwave16(counter) >> 8;
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), lum));
@ -421,7 +421,7 @@ uint16_t mode_rainbow(void) {
counter = counter >> 8;
if (SEGMENT.intensity < 128){
SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,128-SEGMENT.intensity));
SEGMENT.fill(color_blend(SEGMENT.color_wheel(counter),WHITE,uint8_t(128-SEGMENT.intensity)));
} else {
SEGMENT.fill(SEGMENT.color_wheel(counter));
}
@ -523,7 +523,7 @@ static uint16_t running_base(bool saw, bool dual=false) {
unsigned b = (SEGLEN-1-i)*x_scale - counter;
uint8_t t = sin_gap(b);
uint32_t cb = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 2), t);
ca = color_blend(ca, cb, 127);
ca = color_blend(ca, cb, uint8_t(127));
}
SEGMENT.setPixelColor(i, ca);
}
@ -1336,7 +1336,7 @@ uint16_t gradient_base(bool loading) {
val = min(abs(pp-i),min(abs(p1-i),abs(p2-i)));
}
val = (brd > val) ? (val * 255) / brd : 255;
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), val));
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(0), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 1), uint8_t(val)));
}
return FRAMETIME;
@ -1470,7 +1470,7 @@ uint16_t mode_fairy() {
unsigned globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers
for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
unsigned bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255;
uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255;
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
unsigned flasherPos = f*flasherDistance;
SEGMENT.setPixelColor(flasherPos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(PRNG16 >> 8, false, false, 0), bri));
@ -1521,7 +1521,7 @@ uint16_t mode_fairytwinkle() {
if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change
if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state
unsigned fadeprog = 255 - ((stateTime * 255) / riseFallTime);
unsigned flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog);
uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog);
unsigned lastR = PRNG16;
unsigned diff = 0;
while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough
@ -1815,7 +1815,7 @@ uint16_t mode_oscillate(void) {
uint32_t color = BLACK;
for (unsigned j = 0; j < numOscillators; j++) {
if(i >= (unsigned)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) {
color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128);
color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), uint8_t(128));
}
}
SEGMENT.setPixelColor(i, color);
@ -2508,7 +2508,7 @@ static uint16_t ripple_base() {
int left = rippleorigin - propI -1;
int right = rippleorigin + propI +2;
for (int v = 0; v < 4; v++) {
unsigned mag = scale8(cubicwave8((propF>>2) + v * 64), amp);
uint8_t mag = scale8(cubicwave8((propF>>2) + v * 64), amp);
SEGMENT.setPixelColor(left + v, color_blend(SEGMENT.getPixelColor(left + v), col, mag)); // TODO
SEGMENT.setPixelColor(right - v, color_blend(SEGMENT.getPixelColor(right - v), col, mag)); // TODO
}
@ -2551,7 +2551,7 @@ uint16_t mode_ripple_rainbow(void) {
} else {
SEGENV.aux0--;
}
SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,235));
SEGMENT.fill(color_blend(SEGMENT.color_wheel(SEGENV.aux0),BLACK,uint8_t(235)));
return ripple_base();
}
static const char _data_FX_MODE_RIPPLE_RAINBOW[] PROGMEM = "Ripple Rainbow@!,Wave #;;!;12";
@ -2670,7 +2670,7 @@ static uint16_t twinklefox_base(bool cat)
} 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), deltabright * 8));
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)));
} else {
// if the new pixel is not at all brighter than the background color,
// just use the background color.
@ -2766,7 +2766,7 @@ uint16_t mode_halloween_eyes()
const uint32_t eyeColor = SEGMENT.color_from_palette(data.color, false, false, 0);
uint32_t c = eyeColor;
if (fadeInAnimationState < 256u) {
c = color_blend(backgroundColor, eyeColor, fadeInAnimationState);
c = color_blend(backgroundColor, eyeColor, uint8_t(fadeInAnimationState));
} else if (elapsedTime > minimumOnTimeBegin) {
const uint32_t remainingTime = (elapsedTime >= duration) ? 0u : (duration - elapsedTime);
if (remainingTime > minimumOnTimeEnd) {
@ -2919,7 +2919,7 @@ static uint16_t spots_base(uint16_t threshold)
if (wave > threshold) {
unsigned index = 0 + pos + i;
unsigned s = (wave - threshold)*255 / (0xFFFF - threshold);
SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s));
SEGMENT.setPixelColor(index, color_blend(SEGMENT.color_from_palette(index, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255-s)));
}
}
}
@ -3365,12 +3365,12 @@ uint16_t candle(bool multi)
}
if (i > 0) {
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s));
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), uint8_t(s)));
SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep;
} else {
for (int j = 0; j < SEGLEN; j++) {
SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s));
SEGMENT.setPixelColor(j, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), uint8_t(s)));
}
SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep;
@ -3488,7 +3488,7 @@ uint16_t mode_starburst(void) {
float age = it-stars[j].birth;
if (age < particleIgnition) {
c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), 254.5f*((age / particleIgnition))));
c = CRGB(color_blend(WHITE, RGBW32(c.r,c.g,c.b,0), uint8_t(254.5f*((age / particleIgnition)))));
} else {
// Figure out how much to fade and shrink the star based on
// its age relative to its lifetime
@ -3499,8 +3499,7 @@ uint16_t mode_starburst(void) {
} else {
age -= particleIgnition;
fade = (age / particleFadeTime); // Fading star
byte f = 254.5f*fade;
c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), f));
c = CRGB(color_blend(RGBW32(c.r,c.g,c.b,0), SEGCOLOR(1), uint8_t(254.5f*fade)));
}
}
@ -3639,9 +3638,9 @@ uint16_t mode_exploding_fireworks(void)
uint32_t spColor = (SEGMENT.palette) ? SEGMENT.color_wheel(sparks[i].colIndex) : SEGCOLOR(0);
CRGB c = CRGB::Black; //HeatColor(sparks[i].col);
if (prog > 300) { //fade from white to spark color
c = CRGB(color_blend(spColor, WHITE, (prog - 300)*5));
c = CRGB(color_blend(spColor, WHITE, uint8_t((prog - 300)*5)));
} else if (prog > 45) { //fade from spark color to black
c = CRGB(color_blend(BLACK, spColor, prog - 45));
c = CRGB(color_blend(BLACK, spColor, uint8_t(prog - 45)));
unsigned cooling = (300 - prog) >> 5;
c.g = qsub8(c.g, cooling);
c.b = qsub8(c.b, cooling * 2);
@ -3701,10 +3700,10 @@ uint16_t mode_drip(void)
drops[j].colIndex = 1; // drop state (0 init, 1 forming, 2 falling, 5 bouncing)
}
SEGMENT.setPixelColor(indexToVStrip(SEGLEN-1, stripNr), color_blend(BLACK,SEGCOLOR(0), sourcedrop));// water source
SEGMENT.setPixelColor(indexToVStrip(SEGLEN-1, stripNr), color_blend(BLACK,SEGCOLOR(0), uint8_t(sourcedrop)));// water source
if (drops[j].colIndex==1) {
if (drops[j].col>255) drops[j].col=255;
SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col));
SEGMENT.setPixelColor(indexToVStrip(uint16_t(drops[j].pos), stripNr), color_blend(BLACK,SEGCOLOR(0),uint8_t(drops[j].col)));
drops[j].col += map(SEGMENT.speed, 0, 255, 1, 6); // swelling
@ -3721,11 +3720,11 @@ uint16_t mode_drip(void)
for (int i=1;i<7-drops[j].colIndex;i++) { // some minor math so we don't expand bouncing droplets
unsigned pos = constrain(uint16_t(drops[j].pos) +i, 0, SEGLEN-1); //this is BAD, returns a pos >= SEGLEN occasionally
SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),drops[j].col/i)); //spread pixel with fade while falling
SEGMENT.setPixelColor(indexToVStrip(pos, stripNr), color_blend(BLACK,SEGCOLOR(0),uint8_t(drops[j].col/i))); //spread pixel with fade while falling
}
if (drops[j].colIndex > 2) { // during bounce, some water is on the floor
SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(SEGCOLOR(0),BLACK,drops[j].col));
SEGMENT.setPixelColor(indexToVStrip(0, stripNr), color_blend(SEGCOLOR(0),BLACK,uint8_t(drops[j].col)));
}
} else { // we hit bottom
if (drops[j].colIndex > 2) { // already hit once, so back to forming
@ -3948,7 +3947,7 @@ uint16_t mode_heartbeat(void) {
}
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255 - (SEGENV.aux1 >> 8)));
SEGMENT.setPixelColor(i, color_blend(SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), uint8_t(255 - (SEGENV.aux1 >> 8))));
}
return FRAMETIME;
@ -4147,7 +4146,7 @@ static uint16_t phased_base(uint8_t moder) { // We're making si
val += *phase * (i % modVal +1) /2; // This sets the varying phase change of the waves. By Andrew Tuline.
unsigned b = cubicwave8(val); // Now we make an 8 bit sinewave.
b = (b > cutOff) ? (b - cutOff) : 0; // A ternary operator to cutoff the light.
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), b));
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, false, 0), uint8_t(b)));
index += 256 / SEGLEN;
if (SEGLEN > 256) index ++; // Correction for segments longer than 256 LEDs
}
@ -4236,7 +4235,7 @@ uint16_t mode_sinewave(void) { // Adjustable sinewave. By Andrew Tul
unsigned freq = SEGMENT.intensity/4;//SEGMENT.fft2/8; // Frequency of the signal.
for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows:
int pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub)..
uint8_t pixBri = cubicwave8((i*freq)+SEGENV.step);//qsuba(cubicwave8((i*freq)+SEGENV.step), (255-SEGMENT.intensity)); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub)..
//setPixCol(i, i*colorIndex/255, pixBri);
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*colorIndex/255, false, PALETTE_SOLID_WRAP, 0), pixBri));
}
@ -4486,7 +4485,7 @@ uint16_t mode_blends(void) {
unsigned dataSize = sizeof(uint32_t) * (pixelLen + 1); // max segment length of 56 pixels on 16 segment ESP8266
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
unsigned blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
uint8_t blendSpeed = map(SEGMENT.intensity, 0, UINT8_MAX, 10, 128);
unsigned shift = (strip.now * ((SEGMENT.speed >> 3) +1)) >> 8;
for (unsigned i = 0; i < pixelLen; i++) {
@ -6325,7 +6324,7 @@ static const char _data_FX_MODE_2DPLASMAROTOZOOM[] PROGMEM = "Rotozoomer@!,Scale
/////////////////////////////////
uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuline.
// This currently has no controls.
#define maxsteps 16 // Case statement wouldn't allow a variable.
#define MAXSTEPS 16 // Case statement wouldn't allow a variable.
unsigned maxRipples = 16;
unsigned dataSize = sizeof(Ripple) * maxRipples;
@ -6343,7 +6342,6 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli
// printUmData();
if (SEGENV.call == 0) {
SEGENV.aux0 = 255;
SEGMENT.custom1 = *binNum;
SEGMENT.custom2 = *maxVol * 2;
}
@ -6374,17 +6372,17 @@ uint16_t mode_ripplepeak(void) { // * Ripple peak. By Andrew Tuli
break;
case 0:
SEGMENT.setPixelColor(ripples[i].pos, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0));
SEGMENT.setPixelColor(ripples[i].pos, SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0));
ripples[i].state++;
break;
case maxsteps: // At the end of the ripples. 254 is an inactive mode.
case MAXSTEPS: // At the end of the ripples. 254 is an inactive mode.
ripples[i].state = 254;
break;
default: // Middle of the ripples.
SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2));
SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), SEGENV.aux0/ripples[i].state*2));
SEGMENT.setPixelColor((ripples[i].pos + ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), uint8_t(2*255/ripples[i].state)));
SEGMENT.setPixelColor((ripples[i].pos - ripples[i].state + SEGLEN) % SEGLEN, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(ripples[i].color, false, PALETTE_SOLID_WRAP, 0), uint8_t(2*255/ripples[i].state)));
ripples[i].state++; // Next step.
break;
} // switch step
@ -6504,8 +6502,8 @@ uint16_t mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
for (int i=0; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+strip.now, 5000+i*segmentSampleAvg);
SEGMENT.setPixelColor(i+SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*8));
SEGMENT.setPixelColor(SEGLEN/2-i-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*8));
SEGMENT.setPixelColor(i+SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
SEGMENT.setPixelColor(SEGLEN/2-i-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
}
if (tempsamp >= gravcen->topLED)
@ -6597,7 +6595,7 @@ uint16_t mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
for (int i=0; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+strip.now, 5000+i*segmentSampleAvg);
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*8));
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), uint8_t(segmentSampleAvg*8)));
}
if (tempsamp >= gravcen->topLED)
@ -6623,7 +6621,7 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
float volumeSmth = *(float*) um_data->u_data[0];
SEGMENT.fade_out(224); // 6.25%
unsigned my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0);
uint8_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0);
for (size_t i=0; i<SEGMENT.intensity/32+1U; i++) {
// if SEGLEN equals 1, we will always set color to the first and only pixel, but the effect is still good looking
@ -6653,7 +6651,7 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
if(SEGENV.aux0 != secondHand) {
SEGENV.aux0 = secondHand;
int pixBri = volumeRaw * SEGMENT.intensity / 64;
uint8_t pixBri = volumeRaw * SEGMENT.intensity / 64;
for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri));
}
@ -6773,7 +6771,7 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
if (SEGENV.aux0 != secondHand) {
SEGENV.aux0 = secondHand;
int pixBri = volumeRaw * SEGMENT.intensity / 64;
uint8_t pixBri = volumeRaw * SEGMENT.intensity / 64;
SEGMENT.setPixelColor(SEGLEN/2, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri));
for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
@ -6912,7 +6910,7 @@ uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
for (int i=0; i <SEGMENT.intensity/8; i++) {
unsigned segLoc = random16(SEGLEN); // 16 bit for larger strands of LED's.
SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(myVals[i%32]+i*4, false, PALETTE_SOLID_WRAP, 0), volumeSmth));
SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(myVals[i%32]+i*4, false, PALETTE_SOLID_WRAP, 0), uint8_t(volumeSmth)));
}
return FRAMETIME;
@ -6946,11 +6944,11 @@ uint16_t mode_blurz(void) { // Blurz. By Andrew Tuline.
SEGENV.step += FRAMETIME;
if (SEGENV.step > SPEED_FORMULA_L) {
unsigned segLoc = random16(SEGLEN);
SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), 2*fftResult[SEGENV.aux0%16]));
SEGMENT.setPixelColor(segLoc, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(2*fftResult[SEGENV.aux0%16]*240/max(1, SEGLEN-1), false, PALETTE_SOLID_WRAP, 0), uint8_t(2*fftResult[SEGENV.aux0%16])));
++(SEGENV.aux0) %= 16; // make sure it doesn't cross 16
SEGENV.step = 1;
SEGMENT.blur(SEGMENT.intensity);
SEGMENT.blur(SEGMENT.intensity); // note: blur > 210 results in a alternating pattern, this could be fixed by mapping but some may like it (very old bug)
}
return FRAMETIME;
@ -7013,7 +7011,7 @@ uint16_t mode_freqmap(void) { // Map FFT_MajorPeak to SEGLEN.
unsigned pixCol = (log10f(FFT_MajorPeak) - 1.78f) * 255.0f/(MAX_FREQ_LOG10 - 1.78f); // Scale log10 of frequency values to the 255 colour index.
if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
unsigned bright = (int)my_magnitude;
uint8_t bright = (uint8_t)my_magnitude;
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), bright));
@ -7100,7 +7098,7 @@ uint16_t mode_freqpixels(void) { // Freqpixel. By Andrew Tuline.
if (FFT_MajorPeak < 61.0f) pixCol = 0; // handle underflow
for (int i=0; i < SEGMENT.intensity/32+1; i++) {
unsigned locn = random16(0,SEGLEN);
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(SEGMENT.intensity+pixCol, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude));
}
return FRAMETIME;
@ -7234,12 +7232,12 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
unsigned locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise.
// if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only
locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4));
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), uint8_t(fftResult[i % 16]*4)));
}
return FRAMETIME;
} // mode_noisemove()
static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin
static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Move speed,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin
//////////////////////
@ -7314,7 +7312,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
if (samplePeak) {
SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92));
} else {
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude));
}
// loop will not execute if SEGLEN equals 1
for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left

View File

@ -182,7 +182,7 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col)
#ifndef WLED_DISABLE_MODE_BLEND
// if blending modes, blend with underlying pixel
if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true);
if (_modeBlend) tmpCol = color_blend16(strip.getPixelColorXY(start + xX, startY + yY), col, uint16_t(0xFFFFU - progress()));
#endif
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
@ -513,25 +513,25 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col,
unsigned oldFade = 0;
while (x < y) {
float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
uint16_t fade = float(0xFF) * (ceilf(yf) - yf); // how much color to keep
if (oldFade > fade) y--;
oldFade = fade;
setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true));
setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true));
setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true));
setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true));
setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true));
setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true));
setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true));
setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true));
setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true));
setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true));
setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true));
setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true));
setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade));
setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade));
setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade));
setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade));
setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade));
setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade));
setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade));
setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade));
setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade));
setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade));
setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade));
setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade));
setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade));
setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade));
setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade));
setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade));
x++;
}
} else {
@ -608,13 +608,13 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
float gradient = x1-x0 == 0 ? 1.0f : float(y1-y0) / float(x1-x0);
float intersectY = y0;
for (int x = x0; x <= x1; x++) {
unsigned keep = float(0xFFFF) * (intersectY-int(intersectY)); // how much color to keep
unsigned seep = 0xFFFF - keep; // how much background to keep
uint8_t keep = float(0xFF) * (intersectY-int(intersectY)); // how much color to keep
uint8_t seep = 0xFF - keep; // how much background to keep
int y = int(intersectY);
if (steep) std::swap(x,y); // temporaryly swap if steep
// pixel coverage is determined by fractional part of y co-ordinate
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep, true));
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep, true));
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep));
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep));
intersectY += gradient;
if (steep) std::swap(x,y); // restore if steep
}

View File

@ -406,9 +406,9 @@ uint8_t Segment::currentMode() const {
uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const {
if (slot >= NUM_COLORS) slot = 0;
#ifndef WLED_DISABLE_MODE_BLEND
return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
return isInTransition() ? color_blend16(_t->_segT._colorT[slot], colors[slot], progress()) : colors[slot];
#else
return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
return isInTransition() ? color_blend16(_t->_colorT[slot], colors[slot], progress()) : colors[slot];
#endif
}
@ -819,14 +819,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
indexMir += offset; // offset/phase
if (indexMir >= stop) indexMir -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexMir), col, uint16_t(0xFFFFU - progress()));
#endif
strip.setPixelColor(indexMir, tmpCol);
}
indexSet += offset; // offset/phase
if (indexSet >= stop) indexSet -= len; // wrap
#ifndef WLED_DISABLE_MODE_BLEND
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
if (_modeBlend) tmpCol = color_blend16(strip.getPixelColor(indexSet), col, uint16_t(0xFFFFU - progress()));
#endif
strip.setPixelColor(indexSet, tmpCol);
}
@ -1083,6 +1083,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
/*
* blurs segment content, source: FastLED colorutils.cpp
* Note: for blur_amount > 215 this function does not work properly (creates alternating pattern)
*/
void Segment::blur(uint8_t blur_amount, bool smear) {
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"

View File

@ -5,30 +5,18 @@
*/
/*
* color blend function
* 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
*/
uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if (blend == 0) return color1;
unsigned blendmax = b16 ? 0xFFFF : 0xFF;
if (blend == blendmax) return color2;
unsigned shift = b16 ? 16 : 8;
uint32_t w1 = W(color1);
uint32_t r1 = R(color1);
uint32_t g1 = G(color1);
uint32_t b1 = B(color1);
uint32_t w2 = W(color2);
uint32_t r2 = R(color2);
uint32_t g2 = G(color2);
uint32_t b2 = B(color2);
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return RGBW32(r3, g3, b3, w3);
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
uint32_t rb1 = color1 & 0x00FF00FF;
uint32_t wg1 = (color1>>8) & 0x00FF00FF;
uint32_t rb2 = color2 & 0x00FF00FF;
uint32_t wg2 = (color2>>8) & 0x00FF00FF;
uint32_t rb3 = ((((rb1 << 8) | rb2) + (rb2 * blend) - (rb1 * blend)) >> 8) & 0x00FF00FF;
uint32_t wg3 = ((((wg1 << 8) | wg2) + (wg2 * blend) - (wg1 * blend))) & 0xFF00FF00;
return rb3 | wg3;
}
/*

View File

@ -78,7 +78,8 @@ class NeoGammaWLEDMethod {
};
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
[[gnu::hot]] uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
[[gnu::hot]] 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]] uint32_t color_add(uint32_t,uint32_t, bool fast=false);
[[gnu::hot]] uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false);
CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette);