Skip to content

Commit

Permalink
Clamp the HSV -> RGB conversions, add Plasma versions of Butterfly to…
Browse files Browse the repository at this point in the history
… ispc and Metal
  • Loading branch information
dkulp committed Jan 17, 2025
1 parent f871a45 commit 26e0c89
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 116 deletions.
39 changes: 20 additions & 19 deletions xLights/Color.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
}
}
Expand Down
102 changes: 56 additions & 46 deletions xLights/effects/ButterflyEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -363,5 +372,6 @@ void ButterflyEffect::Render(Effect *effect, const SettingsMap &SettingsMap, Ren
}
}
}, block);
#endif
}

82 changes: 82 additions & 0 deletions xLights/effects/ispc/ButterflyFunctions.ispc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -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;

}
}
7 changes: 7 additions & 0 deletions xLights/effects/ispc/ButterflyFunctions.ispc.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ struct ButterflyData {
uint16_t width;
uint16_t height;
uint16_t numColors;
float plasmaTime;
int32_t plasmaStyle;
};
#endif

Expand All @@ -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
Expand Down
Loading

0 comments on commit 26e0c89

Please sign in to comment.