diff --git a/xLights/Color.cpp b/xLights/Color.cpp index 069b9851d..28943a506 100644 --- a/xLights/Color.cpp +++ b/xLights/Color.cpp @@ -171,7 +171,6 @@ void xlColor::SetFromString(const std::string &str) { blue = c->second.blue; alpha = c->second.alpha; } else { - //need to do the slower lookups wxColor c(str); red = c.Red(); @@ -182,53 +181,55 @@ void xlColor::SetFromString(const std::string &str) { } static void fromHSV(xlColor & rgb, const HSVValue &hsv) { double red, green, blue; - - if (0.0f == hsv.saturation) { + double value = std::clamp(hsv.value, 0.0, 1.0); + double saturation = std::clamp(hsv.saturation, 0.0, 1.0); + if (0.0f == saturation) { // Grey - red = hsv.value; - green = hsv.value; - blue = hsv.value; + red = value; + green = value; + blue = value; } else { // not grey - double hue = hsv.hue * 6.0; // sector 0 to 5 + double hue = std::clamp(hsv.hue, 0.0, 1.0) * 6.0; // sector 0 to 5 int i = (int)std::floor(hue); double f = hue - i; // fractional part of h - double p = hsv.value * (1.0 - hsv.saturation); + double p = value * (1.0 - saturation); switch (i) { + case 6: case 0: - red = hsv.value; - green = hsv.value * (1.0 - hsv.saturation * (1.0 - f)); + red = value; + green = value * (1.0 - saturation * (1.0 - f)); blue = p; break; case 1: - red = hsv.value * (1.0 - hsv.saturation * f); - green = hsv.value; + red = value * (1.0 - saturation * f); + green = value; blue = p; break; case 2: red = p; - green = hsv.value; - blue = hsv.value * (1.0 - hsv.saturation * (1.0 - f)); + green = value; + blue = value * (1.0 - saturation * (1.0 - f)); break; case 3: red = p; - green = hsv.value * (1.0 - hsv.saturation * f); - blue = hsv.value; + green = value * (1.0 - saturation * f); + blue = value; break; case 4: - red = hsv.value * (1.0 - hsv.saturation * (1.0 - f)); + red = value * (1.0 - saturation * (1.0 - f)); green = p; blue = hsv.value; break; default: // case 5: - red = hsv.value; + red = value; green = p; - blue = hsv.value * (1.0 - hsv.saturation * f); + blue = value * (1.0 - saturation * f); break; } } diff --git a/xLights/effects/ButterflyEffect.cpp b/xLights/effects/ButterflyEffect.cpp index f7a1afd12..42e1ce7e8 100644 --- a/xLights/effects/ButterflyEffect.cpp +++ b/xLights/effects/ButterflyEffect.cpp @@ -104,53 +104,62 @@ void ButterflyEffect::Render(Effect *effect, const SettingsMap &SettingsMap, Ren const double offset = (ButterflyDirection==1 ? -1 : 1) * double(curState)/200.0; #ifdef HASISPC - if (Style < 6) { - ispc::ButterflyData data; - data.offset = offset; - data.chunks = Chunks; - data.skip = Skip; - data.curState = curState; - data.colorScheme = ColorScheme; - data.width = buffer.BufferWi; - data.height = buffer.BufferHt; - data.numColors = colorcnt; - for (int x = 0; x < colorcnt; x++) { - const xlColor &c = buffer.palette.GetColor(x); - data.colors[x].v[0] = c.red; - data.colors[x].v[1] = c.green; - data.colors[x].v[2] = c.blue; - data.colors[x].v[3] = c.alpha; - } - int max = buffer.BufferHt * buffer.BufferWi; - constexpr int bfBlockSize = 4096; - int blocks = max / bfBlockSize + 1; - parallel_for(0, blocks, [&data, &buffer, max, Style](int y) { - int start = y * bfBlockSize; - int end = start + bfBlockSize; - if (end > max) { - end = max; - } - switch (Style) { - case 1: - ButterflyEffectStyle1(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); - break; - case 2: - ButterflyEffectStyle2(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); - break; - case 3: - ButterflyEffectStyle3(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); - break; - case 4: - ButterflyEffectStyle4(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); - break; - case 5: - ButterflyEffectStyle5(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); - break; - } - }); - return; + ispc::ButterflyData data; + data.offset = offset; + data.chunks = Chunks; + data.skip = Skip; + data.curState = curState; + data.colorScheme = ColorScheme; + data.width = buffer.BufferWi; + data.height = buffer.BufferHt; + data.numColors = colorcnt; + for (int x = 0; x < colorcnt; x++) { + const xlColor &c = buffer.palette.GetColor(x); + data.colors[x].v[0] = c.red; + data.colors[x].v[1] = c.green; + data.colors[x].v[2] = c.blue; + data.colors[x].v[3] = c.alpha; } -#endif + if (Style > 5) { + // slightly different setup for "plasmas" + int state = (buffer.curPeriod - buffer.curEffStartPer); // frames 0 to N + double Speed_plasma = (Style == 10) ? (101-butterFlySpeed)*3 : (101-butterFlySpeed)*5; + double time = (state+1.0)/Speed_plasma; + data.plasmaTime = time; + data.plasmaStyle = Style; + } + + int max = buffer.BufferHt * buffer.BufferWi; + constexpr int bfBlockSize = 4096; + int blocks = max / bfBlockSize + 1; + parallel_for(0, blocks, [&data, &buffer, max, Style](int y) { + int start = y * bfBlockSize; + int end = start + bfBlockSize; + if (end > max) { + end = max; + } + switch (Style) { + case 1: + ButterflyEffectStyle1(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); + break; + case 2: + ButterflyEffectStyle2(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); + break; + case 3: + ButterflyEffectStyle3(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); + break; + case 4: + ButterflyEffectStyle4(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); + break; + case 5: + ButterflyEffectStyle5(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); + break; + default: + ButterflyEffectPlasmaStyles(data, start, end, (ispc::uint8_t4 *)buffer.GetPixels()); + break; + } + }); +#else const int xc=buffer.BufferWi/2; const int yc=buffer.BufferHt/2; int block = buffer.BufferHt * buffer.BufferWi > 100 ? 1 : -1; @@ -363,5 +372,6 @@ void ButterflyEffect::Render(Effect *effect, const SettingsMap &SettingsMap, Ren } } }, block); +#endif } diff --git a/xLights/effects/ispc/ButterflyFunctions.ispc b/xLights/effects/ispc/ButterflyFunctions.ispc index b8e586e62..76fbb6e35 100644 --- a/xLights/effects/ispc/ButterflyFunctions.ispc +++ b/xLights/effects/ispc/ButterflyFunctions.ispc @@ -13,13 +13,19 @@ struct ButterflyData uint16 height; uint16 numColors; + float plasmaTime; + int plasmaStyle; }; +const uniform float pi = 3.14159; const uniform float pi2 = 3.14159*2.0; inline uint8<4> hsv2rgb(float16 h, float16 s, float16 v) { float16<3> rgb; + h = clamp(h, 0.0f16, 1.0f16); + s = clamp(s, 0.0f16, 1.0f16); + v = clamp(v, 0.0f16, 1.0f16); if (0.0f == s) { rgb.r = v; rgb.g = v; @@ -31,6 +37,7 @@ inline uint8<4> hsv2rgb(float16 h, float16 s, float16 v) { float16 p = v * (1.0f16 - s); switch (i) { + case 6: case 0: rgb.r = v; rgb.g = v * (1.0f16 - s * (1.0f16 - f)); @@ -228,3 +235,78 @@ export void ButterflyEffectStyle5(const uniform ButterflyData &data, } } } + + +export void ButterflyEffectPlasmaStyles(const uniform ButterflyData &data, + uniform int startIdx, uniform int endIdx, + uniform uint8<4> result[]) { + uniform float16 invh = rcp((uniform float16)data.height); + uniform float16 invw = rcp((uniform float16)data.width); + uniform float16 time = data.plasmaTime; + uniform const float16 onehalf = 1.0 / 2.0; + uniform const float16 onethird = 1.0 / 3.0; + uniform float16 halfTime = time / 2.0; + uniform float16 thirdTime = time / 3.0; + uniform float16 fifthTime = time / 5.0; + uniform float16 chunks = data.chunks; + + foreach (index = startIdx...endIdx) { + int32 y = index / data.width; + int32 x = index - (y * data.width); + float16 v = 0; + + float16 rx = ((float16)x) * invw - 0.5f16; + float16 ry = ((float16)y) * invh - 0.5f16; + + // 1st equation + v = sin(rx * 10.0f16 + time); + + // second equation + v += sin(10.0f16 * (rx * sin(halfTime) + ry * cos(thirdTime)) + time); + + // third equation + float cx = rx + 0.5f16 * sin(fifthTime); + float cy = ry + 0.5f16 * cos(thirdTime); + v += sin(sqrt(100*((cx*cx)+(cy*cy))+1+time)); + + v += sin(rx + time); + v += sin((ry + time) * onehalf); + v += sin((rx + ry + time) * onehalf); + + v += sin(sqrt(rx * rx + ry * ry + 1.0) + time); + v = v * onehalf; + + uint8<4> color; + color.a = 255; + switch (data.plasmaStyle) { + case 6: + color.r = (sin(v * chunks * pi) + 1.0f16) * 128.0f16; + color.g= (cos(v * chunks * pi) + 1.0f16) * 128.0f16; + color.b = 0; + break; + case 7: + color.r = 1; + color.g = (cos(v * chunks * pi) + 1.0f16) * 128.0f16; + color.b = (sin(v * chunks * pi) + 1.0f16) * 128.0f16; + break; + case 8: + color.r = (sin(v * chunks * pi) + 1.0f16) * 128.0f16; + color.g= (sin(v * chunks * pi + 2.0f16 * pi * onethird) + 1.0f16) * 128.0f16; + color.b =(sin(v * chunks * pi + 4.0f16 * pi * onethird) + 1.0f16) * 128.0f16; + break; + case 9: + color.r = color.g = color.b = (sin(v * chunks * pi) + 1.0f16) * 128.0f16; + break; + case 10: + if (data.numColors >= 2) { + float16 h = sin(v * chunks * pi + 2.0f16 * pi * onethird) + 0.5f16; + color = getMultiColorBlend(data, h, false); + } else { + color.r = 0; color.g = 0; color.b = 0; + } + break; + } + result[index] = color; + + } +} diff --git a/xLights/effects/ispc/ButterflyFunctions.ispc.h b/xLights/effects/ispc/ButterflyFunctions.ispc.h index a7698d297..aff827663 100644 --- a/xLights/effects/ispc/ButterflyFunctions.ispc.h +++ b/xLights/effects/ispc/ButterflyFunctions.ispc.h @@ -59,6 +59,8 @@ struct ButterflyData { uint16_t width; uint16_t height; uint16_t numColors; + float plasmaTime; + int32_t plasmaStyle; }; #endif @@ -69,6 +71,11 @@ struct ButterflyData { #if defined(__cplusplus) && (! defined(__ISPC_NO_EXTERN_C) || !__ISPC_NO_EXTERN_C ) extern "C" { #endif // __cplusplus +#if defined(__cplusplus) + extern void ButterflyEffectPlasmaStyles(const struct ButterflyData &data, int32_t startIdx, int32_t endIdx, uint8_t4 * result); +#else + extern void ButterflyEffectPlasmaStyles(const struct ButterflyData *data, int32_t startIdx, int32_t endIdx, uint8_t4 * result); +#endif // ButterflyEffectPlasmaStyles function declaraion #if defined(__cplusplus) extern void ButterflyEffectStyle1(const struct ButterflyData &data, int32_t startIdx, int32_t endIdx, uint8_t4 * result); #else diff --git a/xLights/effects/metal/ButterflyFunctions.metal b/xLights/effects/metal/ButterflyFunctions.metal index b34d7b0ef..ef88b0002 100644 --- a/xLights/effects/metal/ButterflyFunctions.metal +++ b/xLights/effects/metal/ButterflyFunctions.metal @@ -5,58 +5,14 @@ using namespace metal; #include "MetalEffectDataTypes.h" -constant float pi2 = 3.14159*2.0; - +constant half pi2 = 3.14159h * 2.0h; +constant half pi = 3.14159h; +constant half4 K = half4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); uchar4 hsv2rgb(half3 c) { - float3 rgb; - half h = c.r; - half s = c.g; - half v = c.b; - if (0.0f == s) { - rgb.r = v; - rgb.g = v; - rgb.b = v; - } else { // not grey - half hue = h * 6.0h; // sector 0 to 5 - int i = (int)floor(hue); - half f = hue - (half)i; // fractional part of h - half p = v * (1.0h - s); - - switch (i) { - case 0: - rgb.r = v; - rgb.g = v * (1.0h - s * (1.0h - f)); - rgb.b = p; - break; - case 1: - rgb.r = v * (1.0h - s * f); - rgb.g = v; - rgb.b = p; - break; - case 2: - rgb.r = p; - rgb.g = v; - rgb.b = v * (1.0h - s * (1.0h - f)); - break; - case 3: - rgb.r = p; - rgb.g = v * (1.0h - s * f); - rgb.b = v; - break; - case 4: - rgb.r = v * (1.0h - s * (1.0h - f)); - rgb.g = p; - rgb.b = v; - break; - default: // case 5: - rgb.r = v; - rgb.g = p; - rgb.b = v * (1.0h - s * f); - break; - } - } - rgb *= 255.0; - return uchar4(rgb.x, rgb.y, rgb.z, 255); + c = clamp(c, 0.0h, 1.0h); + half3 p = abs(fract(c.xxx + K.xyz) * 6.0h - K.www); + c = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0h, 1.0h), c.y); + return uchar4(c.r * 255.0h, c.g * 255.0h, c.b * 255.0h, 255); } uint8_t channelBlend(uint8_t c1, uint8_t c2, half ratio) { @@ -201,3 +157,78 @@ kernel void ButterflyEffectStyle5(constant ButterflyData &data, } } } + + + +kernel void ButterflyEffectPlasmaStyles(constant ButterflyData &data, + device uchar4* result, + uint index [[thread_position_in_grid]]) { + const half invh = 1.0h / ((half)(data.height)); + const half invw = 1.0h / ((half)(data.width)); + const half time = data.plasmaTime; + const half onehalf = 1.0 / 2.0; + const half onethird = 1.0 / 3.0; + const half halfTime = time / 2.0; + const half thirdTime = time / 3.0; + const half fifthTime = time / 5.0; + const half chunks = data.chunks; + + if (index > (data.width * data.height)) return; + int x = index % data.width; + int y = index / data.width; + + half v = 0; + + half rx = ((half)x) * invw - 0.5h; + half ry = ((half)y) * invh - 0.5h; + + // 1st equation + v = sin(rx * 10.0h + time); + + // second equation + v += sin(10.0h * (rx * sin(halfTime) + ry * cos(thirdTime)) + time); + + // third equation + float cx = rx + 0.5h * sin(fifthTime); + float cy = ry + 0.5h * cos(thirdTime); + v += sin(sqrt(100*((cx*cx)+(cy*cy))+1+time)); + + v += sin(rx + time); + v += sin((ry + time) * onehalf); + v += sin((rx + ry + time) * onehalf); + + v += sin(sqrt(rx * rx + ry * ry + 1.0) + time); + v = v * onehalf; + + uchar4 color; + color.a = 255; + switch (data.plasmaStyle) { + case 6: + color.r = (sin(v * chunks * pi) + 1.0h) * 128.0h; + color.g= (cos(v * chunks * pi) + 1.0h) * 128.0h; + color.b = 0; + break; + case 7: + color.r = 1; + color.g = (cos(v * chunks * pi) + 1.0h) * 128.0h; + color.b = (sin(v * chunks * pi) + 1.0h) * 128.0h; + break; + case 8: + color.r = (sin(v * chunks * pi) + 1.0h) * 128.0h; + color.g= (sin(v * chunks * pi + 2.0h * pi * onethird) + 1.0h) * 128.0h; + color.b =(sin(v * chunks * pi + 4.0h * pi * onethird) + 1.0h) * 128.0h; + break; + case 9: + color.r = color.g = color.b = (sin(v * chunks * pi) + 1.0h) * 128.0h; + break; + case 10: + if (data.numColors >= 2) { + half h = sin(v * chunks * pi + 2.0h * pi * onethird) + 0.5h; + color = getMultiColorBlend(data, h, false); + } else { + color.r = 0; color.g = 0; color.b = 0; + } + break; + } + result[index] = color; +} diff --git a/xLights/effects/metal/MetalButterflyEffect.mm b/xLights/effects/metal/MetalButterflyEffect.mm index 1248da45f..4c88f8724 100644 --- a/xLights/effects/metal/MetalButterflyEffect.mm +++ b/xLights/effects/metal/MetalButterflyEffect.mm @@ -18,6 +18,11 @@ functions[3] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectStyle3"); functions[4] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectStyle4"); functions[5] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectStyle5"); + functions[6] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectPlasmaStyles"); + functions[7] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectPlasmaStyles"); + functions[8] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectPlasmaStyles"); + functions[9] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectPlasmaStyles"); + functions[10] = MetalComputeUtilities::INSTANCE.FindComputeFunction("ButterflyEffectPlasmaStyles"); } ~MetalButterflyEffectData() { for (auto &f : functions) { @@ -119,6 +124,17 @@ bool Render(int style, ButterflyData &data, RenderBuffer &buffer) { rdata.chunks = Chunks; rdata.skip = Skip; rdata.colorScheme = ColorScheme; + rdata.plasmaStyle = Style; + if (Style > 5) { + // slightly different setup for "plasmas" + int state = (buffer.curPeriod - buffer.curEffStartPer); // frames 0 to N + double Speed_plasma = (Style == 10) ? (101-butterFlySpeed)*3 : (101-butterFlySpeed)*5; + double time = (state+1.0)/Speed_plasma; + rdata.plasmaTime = time; + } else { + rdata.plasmaTime = 0.0; + } + for (int x = 0; x < rdata.numColors; x++) { rdata.colors[x] = buffer.palette.GetColor(x).asChar4(); } diff --git a/xLights/effects/metal/MetalEffectDataTypes.h b/xLights/effects/metal/MetalEffectDataTypes.h index 4aa2539cd..ec4fca339 100644 --- a/xLights/effects/metal/MetalEffectDataTypes.h +++ b/xLights/effects/metal/MetalEffectDataTypes.h @@ -17,6 +17,8 @@ struct ButterflyData uint16_t height; uint16_t numColors; + float plasmaTime; + int plasmaStyle; }; diff --git a/xLights/effects/metal/PinwheelFunctions.metal b/xLights/effects/metal/PinwheelFunctions.metal index 2836ce8d1..3545b0bb3 100644 --- a/xLights/effects/metal/PinwheelFunctions.metal +++ b/xLights/effects/metal/PinwheelFunctions.metal @@ -8,6 +8,7 @@ using namespace metal; constant simd::float4 K = simd::float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); uchar4 hsv2rgb(simd::float3 c) { + c = clamp(c, 0.0, 1.0); simd::float3 p = abs(fract(c.xxx + K.xyz) * 6.0h - K.www); c = c.z * mix(K.xxx, clamp(p - K.xxx, 0.0h, 1.0h), c.y); return uchar4(c.r * 255.0h, c.g * 255.0h, c.b * 255.0h, 255);