Skip to content

Commit

Permalink
[led] Implement support for RGBW in Lightness Computer, and lightness…
Browse files Browse the repository at this point in the history
… display in LEDView
  • Loading branch information
jcelerier committed Nov 19, 2024
1 parent 7e511e4 commit 1ac0e1e
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 16 deletions.
99 changes: 88 additions & 11 deletions examples/Advanced/UI/LEDView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ struct led_item
};
#endif

enum LEDInputMode
{
RGB,
RGBW,
Lightness01,
Lightness8bit,
};
struct LEDView
{
halp_meta(name, "LED View")
Expand All @@ -66,13 +73,15 @@ struct LEDView

struct ins
{
struct : halp::val_port<"RGB", std::vector<int>>
struct : halp::val_port<"RGB", std::vector<float>>
{
enum widget
{
control
};
} values;

halp::enum_t<LEDInputMode, "Mode"> mode;
} inputs;

#if 0
Expand Down Expand Up @@ -105,6 +114,8 @@ struct LEDView
{
public:
std::vector<QColor> m_pixels;
std::string m_mode = "RGB";
LEDInputMode m_display_mode{};

Layer(
const Process::ProcessModel& process, const Process::Context& doc,
Expand All @@ -116,31 +127,97 @@ struct LEDView
const Process::PortFactoryList& portFactory
= doc.app.interfaces<Process::PortFactoryList>();

auto inl = static_cast<Process::ControlInlet*>(process.inlets().front());
auto led_inl = static_cast<Process::ControlInlet*>(process.inlets()[0]);
auto mode_inl = static_cast<Process::ControlInlet*>(process.inlets()[1]);
auto mode = ossia::value_to_pretty_string(mode_inl->value());

auto fact = portFactory.get(inl->concreteKey());
auto port = fact->makePortItem(*inl, doc, this, this);
connect(
mode_inl, &Process::ControlInlet::executionValueChanged, this,
[this, mode_inl](const ossia::value& v) {
auto str = ossia::convert<std::string>(mode_inl->value());
if(str != m_mode)
{
m_pixels.clear();
m_mode = str;
update();
}
});

auto fact = portFactory.get(led_inl->concreteKey());
auto port = fact->makePortItem(*led_inl, doc, this, this);
port->setPos(0, 5);

connect(
inl, &Process::ControlInlet::executionValueChanged, this,
led_inl, &Process::ControlInlet::executionValueChanged, this,
[this](const ossia::value& v) {
if(auto list = v.target<std::vector<ossia::value>>())
{
update_mode();
m_pixels.clear();
auto& vec = *list;
for(int i = 0, N = vec.size() - 2; i < N; i += 3)

switch(m_display_mode)
{
const auto r = std::clamp(ossia::convert<int>(vec[i]), 0, 255);
const auto g = std::clamp(ossia::convert<int>(vec[i + 1]), 0, 255);
const auto b = std::clamp(ossia::convert<int>(vec[i + 2]), 0, 255);
m_pixels.push_back(QColor::fromRgb(r, g, b));
case LEDInputMode::RGB: {
auto& vec = *list;
for(int i = 0, N = vec.size() - 2; i < N; i += 3)
{
const auto r = std::clamp(ossia::convert<float>(vec[i]), 0.f, 255.f);
const auto g = std::clamp(ossia::convert<float>(vec[i + 1]), 0.f, 255.f);
const auto b = std::clamp(ossia::convert<float>(vec[i + 2]), 0.f, 255.f);
m_pixels.push_back(QColor::fromRgb(r, g, b));
}
break;
}
case LEDInputMode::RGBW: {
auto& vec = *list;
for(int i = 0, N = vec.size() - 3; i < N; i += 4)
{
const auto r = std::clamp(ossia::convert<float>(vec[i]), 0.f, 255.f);
const auto g = std::clamp(ossia::convert<float>(vec[i + 1]), 0.f, 255.f);
const auto b = std::clamp(ossia::convert<float>(vec[i + 2]), 0.f, 255.f);
// fixme
const auto w = std::clamp(ossia::convert<float>(vec[i + 3]), 0.f, 255.f);
m_pixels.push_back(QColor::fromRgb(r, g, b));
}
break;
}
case LEDInputMode::Lightness01: {
auto& vec = *list;
for(int i = 0, N = vec.size(); i < N; i++)
{
const auto l = std::clamp(ossia::convert<float>(vec[i]), 0.f, 1.f);
m_pixels.push_back(QColor::fromRgbF(l, l, l));
}
break;
}
case LEDInputMode::Lightness8bit: {
auto& vec = *list;
for(int i = 0, N = vec.size(); i < N; i++)
{
const auto l = std::clamp(ossia::convert<float>(vec[i]), 0.f, 255.f);
m_pixels.push_back(QColor::fromRgb(l, l, l));
}
break;
}
}

update();
}
});
}

void update_mode()
{
if(m_mode == "RGB")
m_display_mode = LEDInputMode::RGB;
else if(m_mode == "RGBW")
m_display_mode = LEDInputMode::RGBW;
else if(m_mode == "Lightness01")
m_display_mode = LEDInputMode::Lightness01;
else if(m_mode == "Lightness8bit")
m_display_mode = LEDInputMode::Lightness8bit;
}

void reset()
{
m_pixels.clear();
Expand Down
117 changes: 112 additions & 5 deletions examples/Advanced/Utilities/LightnessComputer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct LightnessComputer
{
halp__enum_combobox(
"Mode", Lightness, Lightness, Value, Saturation, Hue, HSV, HSL, Red, Green,
Blue, Alpha, RGB)
Blue, Alpha, RGB, RGBW)
} mode;
} inputs;

Expand Down Expand Up @@ -126,6 +126,101 @@ struct LightnessComputer
}
}

// https://gist.github.com/ciembor/1494530
static std::array<double, 3> hsl(float r, float g, float b)
{
struct hsl
{
double h, s, l;
} result;

r /= 255;
g /= 255;
b /= 255;

float max = std::max(std::max(r, g), b);
float min = std::min(std::min(r, g), b);

result.h = result.s = result.l = (max + min) / 2;

if(max == min)
{
result.h = result.s = 0; // achromatic
}
else
{
float d = max - min;
result.s = (result.l > 0.5) ? d / (2 - max - min) : d / (max + min);

if(max == r)
{
result.h = (g - b) / d + (g < b ? 6 : 0);
}
else if(max == g)
{
result.h = (b - r) / d + 2;
}
else if(max == b)
{
result.h = (r - g) / d + 4;
}

result.h /= 6;
}

return {result.h, result.s, result.l};
}

//http://blog.saikoled.com/post/44677718712/how-to-convert-from-hsi-to-rgb-white
static void hsi2rgbw(double h, double s, double i, int* rgbw)
{
using namespace std;
int r, g, b, w;
double cos_h, cos_1047_h;
//h = fmod(h,360); // cycle h around to 0-360 degrees
h = h * 2. * 3.14159; // * h = 3.14159 * h / 180.; // Convert to radians.
// s /= 100.;
// i /= 100.; //from percentage to ratio
s = s > 0. ? (s < 1. ? s : 1.) : 0.; // clamp s and i to interval [0,1]
i = i > 0. ? (i < 1. ? i : 1.) : 0.;
i = i * sqrt(i); //shape intensity to have finer granularity near 0

if(h < 2.09439)
{
cos_h = cos(h);
cos_1047_h = cos(1.047196667 - h);
r = s * 4095. * i / 3. * (1. + cos_h / cos_1047_h);
g = s * 4095. * i / 3. * (1. + (1. - cos_h / cos_1047_h));
b = 0.;
w = 4095. * (1. - s) * i;
}
else if(h < 4.188787)
{
h = h - 2.09439;
cos_h = cos(h);
cos_1047_h = cos(1.047196667 - h);
g = s * 4095. * i / 3. * (1. + cos_h / cos_1047_h);
b = s * 4095. * i / 3. * (1. + (1. - cos_h / cos_1047_h));
r = 0.;
w = 4095. * (1. - s) * i;
}
else
{
h = h - 4.188787;
cos_h = cos(h);
cos_1047_h = cos(1.047196667 - h);
b = s * 4095. * i / 3. * (1. + cos_h / cos_1047_h);
r = s * 4095. * i / 3. * (1. + (1. - cos_h / cos_1047_h));
g = 0.;
w = 4095. * (1. - s) * i;
}

rgbw[0] = r;
rgbw[1] = g;
rgbw[2] = b;
rgbw[3] = w;
}

// Communication with UI
struct processor_to_ui
{
Expand Down Expand Up @@ -217,12 +312,10 @@ struct LightnessComputer
case ins::mode::HSL: {
samples.resize(in_tex.height * in_tex.width * 3);
apply([&](uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
auto [h, s, v] = hsv(r, g, b);
auto [h, s, l] = hsl(r, g, b);
samples[i++] = h;
samples[i++] = s;
const auto lightness = linear_to_y(r / 255.f, g / 255.f, b / 255.f);
const auto perceptual_lightness = y_to_lstar(lightness);
samples[i++] = perceptual_lightness;
samples[i++] = l;
});
break;
}
Expand Down Expand Up @@ -267,6 +360,20 @@ struct LightnessComputer
});
break;
}
case ins::mode::RGBW: {
samples.resize(in_tex.height * in_tex.width * 4);
apply([&](uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
const auto [h, s, l] = hsl(r, g, b);

int rgbw[4];
hsi2rgbw(h, s, l, rgbw);
samples[i++] = rgbw[0];
samples[i++] = rgbw[1];
samples[i++] = rgbw[2];
samples[i++] = rgbw[3];
});
break;
}
}

// Notify the UI with the new texture at a reduced rate
Expand Down

0 comments on commit 1ac0e1e

Please sign in to comment.