RgbColor, RgbwColor, HsbColor and HslColor now support constexpr

This commit is contained in:
2021-03-22 13:20:54 +01:00
parent 47c3bd30c0
commit ee531d7240
8 changed files with 801 additions and 802 deletions

View File

@ -1,68 +0,0 @@
/*-------------------------------------------------------------------------
HsbColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HsbColor.h"
HsbColor::HsbColor(const RgbColor& color)
{
// convert colors to float between (0.0 - 1.0)
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b;
float d = max - min;
float h = 0.0;
float v = max;
float s = (v == 0.0f) ? 0 : (d / v);
if (d != 0.0f)
{
if (r == max)
{
h = (g - b) / d + (g < b ? 6.0f : 0.0f);
}
else if (g == max)
{
h = (b - r) / d + 2.0f;
}
else
{
h = (r - g) / d + 4.0f;
}
h /= 6.0f;
}
H = h;
S = s;
B = v;
}

View File

@ -1,4 +1,3 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
HsbColor provides a color object that can be directly consumed by NeoPixelBus HsbColor provides a color object that can be directly consumed by NeoPixelBus
@ -26,35 +25,34 @@ License along with NeoPixel. If not, see
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
#pragma once #pragma once
#include <Arduino.h> struct RgbColor;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// HsbColor represents a color object that is represented by Hue, Saturation, Brightness // HsbColor represents a color object that is represented by Hue, Saturation, Brightness
// component values. It contains helpful color routines to manipulate the // component values. It contains helpful color routines to manipulate the
// color. // color.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
struct HsbColor struct HsbColor
{ {
// ------------------------------------------------------------------------
// Construct a HsbColor that will have its values set in latter operations
// ------------------------------------------------------------------------
constexpr HsbColor();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a HsbColor using H, S, B values (0.0 - 1.0) // Construct a HsbColor using H, S, B values (0.0 - 1.0)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
HsbColor(float h, float s, float b) : constexpr HsbColor(float h, float s, float b);
H(h), S(s), B(b)
{ // ------------------------------------------------------------------------
}; // Construct a HsbColor using HsbColor
// ------------------------------------------------------------------------
constexpr HsbColor(const HsbColor& color);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a HsbColor using RgbColor // Construct a HsbColor using RgbColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
HsbColor(const RgbColor& color); constexpr HsbColor(const RgbColor& color);
// ------------------------------------------------------------------------
// Construct a HsbColor that will have its values set in latter operations
// CAUTION: The H,S,B members are not initialized and may not be consistent
// ------------------------------------------------------------------------
HsbColor()
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable // LinearBlend between two colors by the amount defined by progress variable
@ -63,14 +61,10 @@ struct HsbColor
// progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right // progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them // and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
template <typename T_NEOHUEBLEND> static HsbColor LinearBlend(const HsbColor& left, template <typename T_NEOHUEBLEND>
static constexpr HsbColor LinearBlend(const HsbColor& left,
const HsbColor& right, const HsbColor& right,
float progress) float progress);
{
return HsbColor(T_NEOHUEBLEND::HueBlend(left.H, right.H, progress),
left.S + ((right.S - left.S) * progress),
left.B + ((right.B - left.B) * progress));
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// BilinearBlend between four colors by the amount defined by 2d variable // BilinearBlend between four colors by the amount defined by 2d variable
@ -81,33 +75,107 @@ struct HsbColor
// x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space
// y - unit value (0.0 - 1.0) that defines the blend progress in vertical space // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
template <typename T_NEOHUEBLEND> static HsbColor BilinearBlend(const HsbColor& c00, template <typename T_NEOHUEBLEND>
static constexpr HsbColor BilinearBlend(const HsbColor& c00,
const HsbColor& c01, const HsbColor& c01,
const HsbColor& c10, const HsbColor& c10,
const HsbColor& c11, const HsbColor& c11,
float x, float x,
float y) float y);
{
float v00 = (1.0f - x) * (1.0f - y);
float v10 = x * (1.0f - y);
float v01 = (1.0f - x) * y;
float v11 = x * y;
return HsbColor(
T_NEOHUEBLEND::HueBlend(
T_NEOHUEBLEND::HueBlend(c00.H, c10.H, x),
T_NEOHUEBLEND::HueBlend(c01.H, c11.H, x),
y),
c00.S * v00 + c10.S * v10 + c01.S * v01 + c11.S * v11,
c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11);
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Hue, Saturation, Brightness color members // Hue, Saturation, Brightness color members
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
float H; float H{};
float S; float S{};
float B; float B{};
private:
static constexpr HsbColor convertToHsbColor(const RgbColor& color);
}; };
#include "RgbColor.h"
constexpr HsbColor::HsbColor() = default;
constexpr HsbColor::HsbColor(float h, float s, float b) :
H{h}, S{s}, B{b}
{
}
constexpr HsbColor::HsbColor(const HsbColor& color) = default;
constexpr HsbColor::HsbColor(const RgbColor& color) :
HsbColor{convertToHsbColor(color)}
{
}
//template <typename T_NEOHUEBLEND>
//static constexpr HsbColor HsbColor::LinearBlend<T_NEOHUEBLEND>(const HsbColor& left,
// const HsbColor& right,
// float progress)
//{
// return HsbColor(T_NEOHUEBLEND::HueBlend(left.H, right.H, progress),
// left.S + ((right.S - left.S) * progress),
// left.B + ((right.B - left.B) * progress));
//}
//template <typename T_NEOHUEBLEND>
//static constexpr HsbColor HsbColor::BilinearBlend<T_NEOHUEBLEND>(const HsbColor& c00,
// const HsbColor& c01,
// const HsbColor& c10,
// const HsbColor& c11,
// float x,
// float y)
//{
// float v00 = (1.0f - x) * (1.0f - y);
// float v10 = x * (1.0f - y);
// float v01 = (1.0f - x) * y;
// float v11 = x * y;
// return HsbColor(
// T_NEOHUEBLEND::HueBlend(
// T_NEOHUEBLEND::HueBlend(c00.H, c10.H, x),
// T_NEOHUEBLEND::HueBlend(c01.H, c11.H, x),
// y),
// c00.S * v00 + c10.S * v10 + c01.S * v01 + c11.S * v11,
// c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11);
//}
constexpr HsbColor HsbColor::convertToHsbColor(const RgbColor& color)
{
// convert colors to float between (0.0 - 1.0)
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b;
float d = max - min;
HsbColor hsb{};
hsb.H = 0.f;
hsb.B = max;
hsb.S = (hsb.B == 0.0f) ? 0 : (d / hsb.B);
if (d != 0.0f)
{
if (r == max)
{
hsb.H = (g - b) / d + (g < b ? 6.0f : 0.0f);
}
else if (g == max)
{
hsb.H = (b - r) / d + 2.0f;
}
else
{
hsb.H = (r - g) / d + 4.0f;
}
hsb.H /= 6.0f;
}
return hsb;
}

View File

@ -1,72 +0,0 @@
/*-------------------------------------------------------------------------
HslColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HslColor.h"
HslColor::HslColor(const RgbColor& color)
{
// convert colors to float between (0.0 - 1.0)
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b;
float h, s, l;
l = (max + min) / 2.0f;
if (max == min)
{
h = s = 0.0f;
}
else
{
float d = max - min;
s = (l > 0.5f) ? d / (2.0f - (max + min)) : d / (max + min);
if (r > g && r > b)
{
h = (g - b) / d + (g < b ? 6.0f : 0.0f);
}
else if (g > b)
{
h = (b - r) / d + 2.0f;
}
else
{
h = (r - g) / d + 4.0f;
}
h /= 6.0f;
}
H = h;
S = s;
L = l;
}

View File

@ -25,37 +25,35 @@ License along with NeoPixel. If not, see
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
#pragma once #pragma once
#include <Arduino.h> struct RgbColor;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// HslColor represents a color object that is represented by Hue, Saturation, Lightness // HslColor represents a color object that is represented by Hue, Saturation, Lightness
// component values. It contains helpful color routines to manipulate the // component values. It contains helpful color routines to manipulate the
// color. // color.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
struct HslColor struct HslColor
{ {
// ------------------------------------------------------------------------
// Construct a HslColor that will have its values set in latter operations
// ------------------------------------------------------------------------
constexpr HslColor();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a HslColor using H, S, L values (0.0 - 1.0) // Construct a HslColor using H, S, L values (0.0 - 1.0)
// L should be limited to between (0.0 - 0.5) // L should be limited to between (0.0 - 0.5)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
HslColor(float h, float s, float l) : constexpr HslColor(float h, float s, float l);
H(h), S(s), L(l)
{ // ------------------------------------------------------------------------
}; // Construct a HslColor using HslColor
// ------------------------------------------------------------------------
constexpr HslColor(const HslColor& color);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a HslColor using RgbColor // Construct a HslColor using RgbColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
HslColor(const RgbColor& color); constexpr HslColor(const RgbColor& color);
// ------------------------------------------------------------------------
// Construct a HslColor that will have its values set in latter operations
// CAUTION: The H,S,L members are not initialized and may not be consistent
// ------------------------------------------------------------------------
HslColor()
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable // LinearBlend between two colors by the amount defined by progress variable
@ -64,14 +62,10 @@ struct HslColor
// progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right // progress - (0.0 - 1.0) value where 0.0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them // and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
template <typename T_NEOHUEBLEND> static HslColor LinearBlend(const HslColor& left, template <typename T_NEOHUEBLEND>
static constexpr HslColor LinearBlend(const HslColor& left,
const HslColor& right, const HslColor& right,
float progress) float progress);
{
return HslColor(T_NEOHUEBLEND::HueBlend(left.H, right.H, progress),
left.S + ((right.S - left.S) * progress),
left.L + ((right.L - left.L) * progress));
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// BilinearBlend between four colors by the amount defined by 2d variable // BilinearBlend between four colors by the amount defined by 2d variable
@ -82,32 +76,110 @@ struct HslColor
// x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space
// y - unit value (0.0 - 1.0) that defines the blend progress in vertical space // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
template <typename T_NEOHUEBLEND> static HslColor BilinearBlend(const HslColor& c00, template <typename T_NEOHUEBLEND>
static constexpr HslColor BilinearBlend(const HslColor& c00,
const HslColor& c01, const HslColor& c01,
const HslColor& c10, const HslColor& c10,
const HslColor& c11, const HslColor& c11,
float x, float x,
float y) float y);
{
float v00 = (1.0f - x) * (1.0f - y);
float v10 = x * (1.0f - y);
float v01 = (1.0f - x) * y;
float v11 = x * y;
return HslColor(
T_NEOHUEBLEND::HueBlend(
T_NEOHUEBLEND::HueBlend(c00.H, c10.H, x),
T_NEOHUEBLEND::HueBlend(c01.H, c11.H, x),
y),
c00.S * v00 + c10.S * v10 + c01.S * v01 + c11.S * v11,
c00.L * v00 + c10.L * v10 + c01.L * v01 + c11.L * v11);
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Hue, Saturation, Lightness color members // Hue, Saturation, Lightness color members
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
float H; float H{};
float S; float S{};
float L; float L{};
private:
static constexpr HslColor convertToHslColor(const RgbColor& color);
}; };
#include "RgbColor.h"
constexpr HslColor::HslColor() = default;
constexpr HslColor::HslColor(float h, float s, float l) :
H{h}, S{s}, L{l}
{
}
constexpr HslColor::HslColor(const HslColor& color) = default;
constexpr HslColor::HslColor(const RgbColor& color) :
HslColor{convertToHslColor(color)}
{
}
//template <typename T_NEOHUEBLEND>
//static constexpr HslColor HslColor::LinearBlend<T_NEOHUEBLEND>(const HslColor& left,
// const HslColor& right,
// float progress)
//{
// return HslColor(T_NEOHUEBLEND::HueBlend(left.H, right.H, progress),
// left.S + ((right.S - left.S) * progress),
// left.L + ((right.L - left.L) * progress));
//};
//template <typename T_NEOHUEBLEND>
//static constexpr HslColor HslColor::BilinearBlend<T_NEOHUEBLEND>(const HslColor& c00,
// const HslColor& c01,
// const HslColor& c10,
// const HslColor& c11,
// float x,
// float y)
//{
// float v00 = (1.0f - x) * (1.0f - y);
// float v10 = x * (1.0f - y);
// float v01 = (1.0f - x) * y;
// float v11 = x * y;
// return HslColor(
// T_NEOHUEBLEND::HueBlend(
// T_NEOHUEBLEND::HueBlend(c00.H, c10.H, x),
// T_NEOHUEBLEND::HueBlend(c01.H, c11.H, x),
// y),
// c00.S * v00 + c10.S * v10 + c01.S * v01 + c11.S * v11,
// c00.L * v00 + c10.L * v10 + c01.L * v01 + c11.L * v11);
//}
constexpr HslColor HslColor::convertToHslColor(const RgbColor& color)
{
// convert colors to float between (0.0 - 1.0)
float r = color.R / 255.0f;
float g = color.G / 255.0f;
float b = color.B / 255.0f;
float max = (r > g && r > b) ? r : (g > b) ? g : b;
float min = (r < g && r < b) ? r : (g < b) ? g : b;
HslColor hsl;
hsl.L = (max + min) / 2.0f;
if (max == min)
{
hsl.H = hsl.S = 0.0f;
}
else
{
float d = max - min;
hsl.S = (hsl.L > 0.5f) ? d / (2.0f - (max + min)) : d / (max + min);
if (r > g && r > b)
{
hsl.H = (g - b) / d + (g < b ? 6.0f : 0.0f);
}
else if (g > b)
{
hsl.H = (b - r) / d + 2.0f;
}
else
{
hsl.H = (r - g) / d + 4.0f;
}
hsl.H /= 6.0f;
}
return hsl;
}

View File

@ -1,249 +0,0 @@
/*-------------------------------------------------------------------------
RgbColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HslColor.h"
#include "HsbColor.h"
static float _CalcColor(float p, float q, float t)
{
if (t < 0.0f)
t += 1.0f;
if (t > 1.0f)
t -= 1.0f;
if (t < 1.0f / 6.0f)
return p + (q - p) * 6.0f * t;
if (t < 0.5f)
return q;
if (t < 2.0f / 3.0f)
return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f);
return p;
}
RgbColor::RgbColor(const HslColor& color)
{
float r;
float g;
float b;
float h = color.H;
float s = color.S;
float l = color.L;
if (color.S == 0.0f || color.L == 0.0f)
{
r = g = b = l; // achromatic or black
}
else
{
float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s);
float p = 2.0f * l - q;
r = _CalcColor(p, q, h + 1.0f / 3.0f);
g = _CalcColor(p, q, h);
b = _CalcColor(p, q, h - 1.0f / 3.0f);
}
R = (uint8_t)(r * 255.0f);
G = (uint8_t)(g * 255.0f);
B = (uint8_t)(b * 255.0f);
}
RgbColor::RgbColor(const HsbColor& color)
{
float r;
float g;
float b;
float h = color.H;
float s = color.S;
float v = color.B;
if (color.S == 0.0f)
{
r = g = b = v; // achromatic or black
}
else
{
if (h < 0.0f)
{
h += 1.0f;
}
else if (h >= 1.0f)
{
h -= 1.0f;
}
h *= 6.0f;
int i = (int)h;
float f = h - i;
float q = v * (1.0f - s * f);
float p = v * (1.0f - s);
float t = v * (1.0f - s * (1.0f - f));
switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default:
r = v;
g = p;
b = q;
break;
}
}
R = (uint8_t)(r * 255.0f);
G = (uint8_t)(g * 255.0f);
B = (uint8_t)(b * 255.0f);
}
uint8_t RgbColor::CalculateBrightness() const
{
return (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
}
RgbColor RgbColor::Dim(uint8_t ratio) const
{
// specifically avoids float math
return RgbColor(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio));
}
RgbColor RgbColor::Brighten(uint8_t ratio) const
{
// specifically avoids float math
return RgbColor(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio));
}
void RgbColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
}
if (G > delta)
{
G -= delta;
}
else
{
G = 0;
}
if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
}
void RgbColor::Lighten(uint8_t delta)
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
RgbColor RgbColor::LinearBlend(const RgbColor& left, const RgbColor& right, float progress)
{
return RgbColor( left.R + ((right.R - left.R) * progress),
left.G + ((right.G - left.G) * progress),
left.B + ((right.B - left.B) * progress));
}
RgbColor RgbColor::BilinearBlend(const RgbColor& c00,
const RgbColor& c01,
const RgbColor& c10,
const RgbColor& c11,
float x,
float y)
{
float v00 = (1.0f - x) * (1.0f - y);
float v10 = x * (1.0f - y);
float v01 = (1.0f - x) * y;
float v11 = x * y;
return RgbColor(
c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11,
c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11,
c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11);
}

View File

@ -25,7 +25,8 @@ License along with NeoPixel. If not, see
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
#pragma once #pragma once
#include <Arduino.h> #include <cstdint>
#include "NeoSettings.h" #include "NeoSettings.h"
struct HslColor; struct HslColor;
@ -33,97 +34,87 @@ struct HsbColor;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// RgbColor represents a color object that is represented by Red, Green, Blue // RgbColor represents a color object that is represented by Red, Green, Blue
// component values. It contains helpful color routines to manipulate the // component values. It contains helpful color routines to manipulate the
// color. // color.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
struct RgbColor struct RgbColor
{ {
typedef NeoRgbCurrentSettings SettingsObject; using SettingsObject = NeoRgbCurrentSettings;
// ------------------------------------------------------------------------
// Construct a RgbColor that will have its values set in latter operations
// ------------------------------------------------------------------------
constexpr RgbColor();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbColor using R, G, B values (0-255) // Construct a RgbColor using R, G, B values (0-255)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbColor(uint8_t r, uint8_t g, uint8_t b) : constexpr RgbColor(uint8_t r, uint8_t g, uint8_t b);
R(r), G(g), B(b)
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbColor using a single brightness value (0-255) // Construct a RgbColor using a single brightness value (0-255)
// This works well for creating gray tone colors // This works well for creating gray tone colors
// (0) = black, (255) = white, (128) = gray // (0) = black, (255) = white, (128) = gray
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbColor(uint8_t brightness) : constexpr RgbColor(uint8_t brightness);
R(brightness), G(brightness), B(brightness)
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbColor using HslColor // Construct a RgbColor using HslColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbColor(const HslColor& color); constexpr RgbColor(const RgbColor& color);
// ------------------------------------------------------------------------
// Construct a RgbColor using HslColor
// ------------------------------------------------------------------------
constexpr RgbColor(const HslColor& color);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbColor using HsbColor // Construct a RgbColor using HsbColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbColor(const HsbColor& color); constexpr RgbColor(const HsbColor& color);
// ------------------------------------------------------------------------
// Construct a RgbColor that will have its values set in latter operations
// CAUTION: The R,G,B members are not initialized and may not be consistent
// ------------------------------------------------------------------------
RgbColor()
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Comparison operators // Comparison operators
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool operator==(const RgbColor& other) const constexpr bool operator==(const RgbColor& other) const;
{
return (R == other.R && G == other.G && B == other.B);
};
bool operator!=(const RgbColor& other) const constexpr bool operator!=(const RgbColor& other) const;
{
return !(*this == other);
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// CalculateBrightness will calculate the overall brightness // CalculateBrightness will calculate the overall brightness
// NOTE: This is a simple linear brightness // NOTE: This is a simple linear brightness
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
uint8_t CalculateBrightness() const; constexpr uint8_t CalculateBrightness() const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Dim will return a new color that is blended to black with the given ratio // Dim will return a new color that is blended to black with the given ratio
// ratio - (0-255) where 255 will return the original color and 0 will return black // ratio - (0-255) where 255 will return the original color and 0 will return black
// //
// NOTE: This is a simple linear blend // NOTE: This is a simple linear blend
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbColor Dim(uint8_t ratio) const; constexpr RgbColor Dim(uint8_t ratio) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Brighten will return a new color that is blended to white with the given ratio // Brighten will return a new color that is blended to white with the given ratio
// ratio - (0-255) where 255 will return the original color and 0 will return white // ratio - (0-255) where 255 will return the original color and 0 will return white
// //
// NOTE: This is a simple linear blend // NOTE: This is a simple linear blend
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbColor Brighten(uint8_t ratio) const; constexpr RgbColor Brighten(uint8_t ratio) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Darken will adjust the color by the given delta toward black // Darken will adjust the color by the given delta toward black
// NOTE: This is a simple linear change // NOTE: This is a simple linear change
// delta - (0-255) the amount to dim the color // delta - (0-255) the amount to dim the color
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void Darken(uint8_t delta); constexpr void Darken(uint8_t delta);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Lighten will adjust the color by the given delta toward white // Lighten will adjust the color by the given delta toward white
// NOTE: This is a simple linear change // NOTE: This is a simple linear change
// delta - (0-255) the amount to lighten the color // delta - (0-255) the amount to lighten the color
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void Lighten(uint8_t delta); constexpr void Lighten(uint8_t delta);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable // LinearBlend between two colors by the amount defined by progress variable
@ -132,8 +123,8 @@ struct RgbColor
// progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them // and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static RgbColor LinearBlend(const RgbColor& left, const RgbColor& right, float progress); static constexpr RgbColor LinearBlend(const RgbColor& left, const RgbColor& right, float progress);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// BilinearBlend between four colors by the amount defined by 2d variable // BilinearBlend between four colors by the amount defined by 2d variable
// c00 - upper left quadrant color // c00 - upper left quadrant color
@ -143,51 +134,310 @@ struct RgbColor
// x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space
// y - unit value (0.0 - 1.0) that defines the blend progress in vertical space // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static RgbColor BilinearBlend(const RgbColor& c00, static constexpr RgbColor BilinearBlend(const RgbColor& c00,
const RgbColor& c01, const RgbColor& c01,
const RgbColor& c10, const RgbColor& c10,
const RgbColor& c11, const RgbColor& c11,
float x, float x,
float y); float y);
uint32_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) constexpr uint32_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) const;
{
auto total = 0;
total += R * settings.RedTenthMilliAmpere / 255;
total += G * settings.GreenTenthMilliAmpere / 255;
total += B * settings.BlueTenthMilliAmpere / 255;
return total;
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Red, Green, Blue color members (0-255) where // Red, Green, Blue color members (0-255) where
// (0,0,0) is black and (255,255,255) is white // (0,0,0) is black and (255,255,255) is white
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
uint8_t R; uint8_t R{};
uint8_t G; uint8_t G{};
uint8_t B; uint8_t B{};
private: private:
inline static uint8_t _elementDim(uint8_t value, uint8_t ratio) inline static constexpr uint8_t _elementDim(uint8_t value, uint8_t ratio);
{ inline static constexpr uint8_t _elementBrighten(uint8_t value, uint8_t ratio);
return (static_cast<uint16_t>(value) * (static_cast<uint16_t>(ratio) + 1)) >> 8; inline static constexpr float _CalcColor(float p, float q, float t);
} inline static constexpr RgbColor convertToRgbColor(const HslColor& color);
inline static constexpr RgbColor convertToRgbColor(const HsbColor& color);
inline static uint8_t _elementBrighten(uint8_t value, uint8_t ratio)
{
uint16_t element = ((static_cast<uint16_t>(value) + 1) << 8) / (static_cast<uint16_t>(ratio) + 1);
if (element > 255)
{
element = 255;
}
else
{
element -= 1;
}
return element;
}
}; };
#include "HslColor.h"
#include "HsbColor.h"
constexpr RgbColor::RgbColor() = default;
constexpr RgbColor::RgbColor(uint8_t r, uint8_t g, uint8_t b) :
R{r}, G{g}, B{b}
{
}
constexpr RgbColor::RgbColor(uint8_t brightness) :
R{brightness}, G{brightness}, B{brightness}
{
}
constexpr RgbColor::RgbColor(const RgbColor& color) = default;
constexpr RgbColor::RgbColor(const HslColor& color) :
RgbColor{convertToRgbColor(color)}
{
}
constexpr RgbColor::RgbColor(const HsbColor& color) :
RgbColor{convertToRgbColor(color)}
{
}
constexpr bool RgbColor::operator==(const RgbColor& other) const
{
return (R == other.R && G == other.G && B == other.B);
};
constexpr bool RgbColor::operator!=(const RgbColor& other) const
{
return !(*this == other);
};
constexpr uint8_t RgbColor::CalculateBrightness() const
{
return (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
}
constexpr RgbColor RgbColor::Dim(uint8_t ratio) const
{
// specifically avoids float math
return RgbColor(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio));
}
constexpr RgbColor RgbColor::Brighten(uint8_t ratio) const
{
// specifically avoids float math
return RgbColor(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio));
}
constexpr void RgbColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
}
if (G > delta)
{
G -= delta;
}
else
{
G = 0;
}
if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
}
constexpr void RgbColor::Lighten(uint8_t delta)
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
constexpr RgbColor RgbColor::LinearBlend(const RgbColor& left, const RgbColor& right, float progress)
{
return RgbColor( left.R + ((right.R - left.R) * progress),
left.G + ((right.G - left.G) * progress),
left.B + ((right.B - left.B) * progress));
}
constexpr RgbColor RgbColor::BilinearBlend(const RgbColor& c00,
const RgbColor& c01,
const RgbColor& c10,
const RgbColor& c11,
float x,
float y)
{
float v00 = (1.0f - x) * (1.0f - y);
float v10 = x * (1.0f - y);
float v01 = (1.0f - x) * y;
float v11 = x * y;
return RgbColor(
c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11,
c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11,
c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11);
}
constexpr uint32_t RgbColor::CalcTotalTenthMilliAmpere(const SettingsObject& settings) const
{
auto total = 0;
total += R * settings.RedTenthMilliAmpere / 255;
total += G * settings.GreenTenthMilliAmpere / 255;
total += B * settings.BlueTenthMilliAmpere / 255;
return total;
}
constexpr uint8_t RgbColor::_elementDim(uint8_t value, uint8_t ratio)
{
return (static_cast<uint16_t>(value) * (static_cast<uint16_t>(ratio) + 1)) >> 8;
}
constexpr uint8_t RgbColor::_elementBrighten(uint8_t value, uint8_t ratio)
{
uint16_t element = ((static_cast<uint16_t>(value) + 1) << 8) / (static_cast<uint16_t>(ratio) + 1);
if (element > 255)
{
element = 255;
}
else
{
element -= 1;
}
return element;
}
constexpr float RgbColor::_CalcColor(float p, float q, float t)
{
if (t < 0.0f)
t += 1.0f;
if (t > 1.0f)
t -= 1.0f;
if (t < 1.0f / 6.0f)
return p + (q - p) * 6.0f * t;
if (t < 0.5f)
return q;
if (t < 2.0f / 3.0f)
return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f);
return p;
}
constexpr RgbColor RgbColor::convertToRgbColor(const HslColor& color)
{
float r{};
float g{};
float b{};
float h = color.H;
float s = color.S;
float l = color.L;
if (color.S == 0.0f || color.L == 0.0f)
{
r = g = b = l; // achromatic or black
}
else
{
float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s);
float p = 2.0f * l - q;
r = _CalcColor(p, q, h + 1.0f / 3.0f);
g = _CalcColor(p, q, h);
b = _CalcColor(p, q, h - 1.0f / 3.0f);
}
return RgbColor{(uint8_t)(r * 255.0f), (uint8_t)(g * 255.0f), (uint8_t)(b * 255.0f)};
}
constexpr RgbColor RgbColor::convertToRgbColor(const HsbColor& color)
{
float r{};
float g{};
float b{};
float h = color.H;
float s = color.S;
float v = color.B;
if (color.S == 0.0f)
{
r = g = b = v; // achromatic or black
}
else
{
if (h < 0.0f)
{
h += 1.0f;
}
else if (h >= 1.0f)
{
h -= 1.0f;
}
h *= 6.0f;
int i = (int)h;
float f = h - i;
float q = v * (1.0f - s * f);
float p = v * (1.0f - s);
float t = v * (1.0f - s * (1.0f - f));
switch (i)
{
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
default:
r = v;
g = p;
b = q;
break;
}
}
return RgbColor{(uint8_t)(r * 255.0f), (uint8_t)(g * 255.0f), (uint8_t)(b * 255.0f)};
}

View File

@ -1,177 +0,0 @@
/*-------------------------------------------------------------------------
RgbwColor provides a color object that can be directly consumed by NeoPixelBus
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include "RgbColor.h"
#include "HslColor.h"
#include "HsbColor.h"
#include "RgbwColor.h"
RgbwColor::RgbwColor(const HslColor& color)
{
RgbColor rgbColor(color);
*this = rgbColor;
}
RgbwColor::RgbwColor(const HsbColor& color)
{
RgbColor rgbColor(color);
*this = rgbColor;
}
uint8_t RgbwColor::CalculateBrightness() const
{
uint8_t colorB = (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
if (W > colorB)
{
return W;
}
else
{
return colorB;
}
}
RgbwColor RgbwColor::Dim(uint8_t ratio) const
{
// specifically avoids float math
return RgbwColor(_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio), _elementDim(W, ratio));
}
RgbwColor RgbwColor::Brighten(uint8_t ratio) const
{
// specifically avoids float math
return RgbwColor(_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio), _elementBrighten(W, ratio));
}
void RgbwColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
}
if (G > delta)
{
G -= delta;
}
else
{
G = 0;
}
if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
if (W > delta)
{
W -= delta;
}
else
{
W = 0;
}
}
void RgbwColor::Lighten(uint8_t delta)
{
if (IsColorLess())
{
if (W < 255 - delta)
{
W += delta;
}
else
{
W = 255;
}
}
else
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
}
RgbwColor RgbwColor::LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress)
{
return RgbwColor( left.R + ((right.R - left.R) * progress),
left.G + ((right.G - left.G) * progress),
left.B + ((right.B - left.B) * progress),
left.W + ((right.W - left.W) * progress) );
}
RgbwColor RgbwColor::BilinearBlend(const RgbwColor& c00,
const RgbwColor& c01,
const RgbwColor& c10,
const RgbwColor& c11,
float x,
float y)
{
float v00 = (1.0f - x) * (1.0f - y);
float v10 = x * (1.0f - y);
float v01 = (1.0f - x) * y;
float v11 = x * y;
return RgbwColor(
c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11,
c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11,
c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11,
c00.W * v00 + c10.W * v10 + c01.W * v01 + c11.W * v11 );
}

View File

@ -25,7 +25,9 @@ License along with NeoPixel. If not, see
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
#pragma once #pragma once
#include <Arduino.h> #include <cstdint>
#include "NeoSettings.h"
struct RgbColor; struct RgbColor;
struct HslColor; struct HslColor;
@ -38,90 +40,68 @@ struct HsbColor;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
struct RgbwColor struct RgbwColor
{ {
typedef NeoRgbwCurrentSettings SettingsObject; using SettingsObject = NeoRgbwCurrentSettings;
// ------------------------------------------------------------------------
// Construct a RgbwColor that will have its values set in latter operations
// ------------------------------------------------------------------------
constexpr RgbwColor();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbwColor using R, G, B, W values (0-255) // Construct a RgbwColor using R, G, B, W values (0-255)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) : constexpr RgbwColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0);
R(r), G(g), B(b), W(w)
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbColor using a single brightness value (0-255) // Construct a RgbColor using a single brightness value (0-255)
// This works well for creating gray tone colors // This works well for creating gray tone colors
// (0) = black, (255) = white, (128) = gray // (0) = black, (255) = white, (128) = gray
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor(uint8_t brightness) : constexpr RgbwColor(uint8_t brightness);
R(0), G(0), B(0), W(brightness)
{ // ------------------------------------------------------------------------
}; // Construct a RgbwColor using RgbwColor
// ------------------------------------------------------------------------
constexpr RgbwColor(const RgbwColor& color);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbwColor using RgbColor // Construct a RgbwColor using RgbColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor(const RgbColor& color) : constexpr RgbwColor(const RgbColor& color);
R(color.R),
G(color.G),
B(color.B),
W(0)
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbwColor using HslColor // Construct a RgbwColor using HslColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor(const HslColor& color); constexpr RgbwColor(const HslColor& color);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Construct a RgbwColor using HsbColor // Construct a RgbwColor using HsbColor
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor(const HsbColor& color); constexpr RgbwColor(const HsbColor& color);
// ------------------------------------------------------------------------
// Construct a RgbwColor that will have its values set in latter operations
// CAUTION: The R,G,B, W members are not initialized and may not be consistent
// ------------------------------------------------------------------------
RgbwColor()
{
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Comparison operators // Comparison operators
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool operator==(const RgbwColor& other) const constexpr bool operator==(const RgbwColor& other) const;
{
return (R == other.R && G == other.G && B == other.B && W == other.W);
};
bool operator!=(const RgbwColor& other) const constexpr bool operator!=(const RgbwColor& other) const;
{
return !(*this == other);
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Returns if the color is grey, all values are equal other than white // Returns if the color is grey, all values are equal other than white
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool IsMonotone() const constexpr bool IsMonotone() const;
{
return (R == B && R == G);
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Returns if the color components are all zero, the white component maybe // Returns if the color components are all zero, the white component maybe
// anything // anything
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool IsColorLess() const constexpr bool IsColorLess() const;
{
return (R == 0 && B == 0 && G == 0);
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// CalculateBrightness will calculate the overall brightness // CalculateBrightness will calculate the overall brightness
// NOTE: This is a simple linear brightness // NOTE: This is a simple linear brightness
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
uint8_t CalculateBrightness() const; constexpr uint8_t CalculateBrightness() const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Dim will return a new color that is blended to black with the given ratio // Dim will return a new color that is blended to black with the given ratio
@ -129,7 +109,7 @@ struct RgbwColor
// //
// NOTE: This is a simple linear blend // NOTE: This is a simple linear blend
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor Dim(uint8_t ratio) const; constexpr RgbwColor Dim(uint8_t ratio) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Brighten will return a new color that is blended to white with the given ratio // Brighten will return a new color that is blended to white with the given ratio
@ -137,21 +117,21 @@ struct RgbwColor
// //
// NOTE: This is a simple linear blend // NOTE: This is a simple linear blend
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
RgbwColor Brighten(uint8_t ratio) const; constexpr RgbwColor Brighten(uint8_t ratio) const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Darken will adjust the color by the given delta toward black // Darken will adjust the color by the given delta toward black
// NOTE: This is a simple linear change // NOTE: This is a simple linear change
// delta - (0-255) the amount to dim the color // delta - (0-255) the amount to dim the color
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void Darken(uint8_t delta); constexpr void Darken(uint8_t delta);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Lighten will adjust the color by the given delta toward white // Lighten will adjust the color by the given delta toward white
// NOTE: This is a simple linear change // NOTE: This is a simple linear change
// delta - (0-255) the amount to lighten the color // delta - (0-255) the amount to lighten the color
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void Lighten(uint8_t delta); constexpr void Lighten(uint8_t delta);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// LinearBlend between two colors by the amount defined by progress variable // LinearBlend between two colors by the amount defined by progress variable
@ -160,8 +140,8 @@ struct RgbwColor
// progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right // progress - (0.0 - 1.0) value where 0 will return left and 1.0 will return right
// and a value between will blend the color weighted linearly between them // and a value between will blend the color weighted linearly between them
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static RgbwColor LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress); static constexpr RgbwColor LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// BilinearBlend between four colors by the amount defined by 2d variable // BilinearBlend between four colors by the amount defined by 2d variable
// c00 - upper left quadrant color // c00 - upper left quadrant color
@ -171,54 +151,249 @@ struct RgbwColor
// x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space // x - unit value (0.0 - 1.0) that defines the blend progress in horizontal space
// y - unit value (0.0 - 1.0) that defines the blend progress in vertical space // y - unit value (0.0 - 1.0) that defines the blend progress in vertical space
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static RgbwColor BilinearBlend(const RgbwColor& c00, static constexpr RgbwColor BilinearBlend(const RgbwColor& c00,
const RgbwColor& c01, const RgbwColor& c01,
const RgbwColor& c10, const RgbwColor& c10,
const RgbwColor& c11, const RgbwColor& c11,
float x, float x,
float y); float y);
uint16_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) constexpr uint16_t CalcTotalTenthMilliAmpere(const SettingsObject& settings) const;
{
auto total = 0;
total += R * settings.RedTenthMilliAmpere / 255;
total += G * settings.GreenTenthMilliAmpere / 255;
total += B * settings.BlueTenthMilliAmpere / 255;
total += W * settings.WhiteCurrent / 255;
return total;
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Red, Green, Blue, White color members (0-255) where // Red, Green, Blue, White color members (0-255) where
// (0,0,0,0) is black and (255,255,255, 0) and (0,0,0,255) is white // (0,0,0,0) is black and (255,255,255, 0) and (0,0,0,255) is white
// Note (255,255,255,255) is extreme bright white // Note (255,255,255,255) is extreme bright white
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
uint8_t R; uint8_t R{};
uint8_t G; uint8_t G{};
uint8_t B; uint8_t B{};
uint8_t W; uint8_t W{};
private: private:
inline static uint8_t _elementDim(uint8_t value, uint8_t ratio) inline static constexpr uint8_t _elementDim(uint8_t value, uint8_t ratio);
inline static constexpr uint8_t _elementBrighten(uint8_t value, uint8_t ratio);
};
#include "RgbColor.h"
#include "HslColor.h"
#include "HsbColor.h"
constexpr RgbwColor::RgbwColor() = default;
constexpr RgbwColor::RgbwColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) :
R{r}, G{g}, B{b}, W{w}
{
}
constexpr RgbwColor::RgbwColor(uint8_t brightness) :
R{}, G{}, B{}, W{brightness}
{
}
constexpr RgbwColor::RgbwColor(const RgbwColor& color) = default;
constexpr RgbwColor::RgbwColor(const RgbColor& color) :
R{color.R},
G{color.G},
B{color.B},
W{}
{
}
constexpr RgbwColor::RgbwColor(const HslColor& color) :
RgbwColor{RgbColor{color}}
{
}
constexpr RgbwColor::RgbwColor(const HsbColor& color) :
RgbwColor{RgbColor{color}}
{
}
constexpr bool RgbwColor::operator==(const RgbwColor& other) const
{
return (R == other.R && G == other.G && B == other.B && W == other.W);
}
constexpr bool RgbwColor::operator!=(const RgbwColor& other) const
{
return !(*this == other);
}
constexpr bool RgbwColor::IsMonotone() const
{
return (R == B && R == G);
}
constexpr bool RgbwColor::IsColorLess() const
{
return (R == 0 && B == 0 && G == 0);
}
constexpr uint8_t RgbwColor::CalculateBrightness() const
{
uint8_t colorB = (uint8_t)(((uint16_t)R + (uint16_t)G + (uint16_t)B) / 3);
if (W > colorB)
{ {
return (static_cast<uint16_t>(value) * (static_cast<uint16_t>(ratio) + 1)) >> 8; return W;
}
else
{
return colorB;
}
}
constexpr RgbwColor RgbwColor::Dim(uint8_t ratio) const
{
// specifically avoids float math
return RgbwColor{_elementDim(R, ratio), _elementDim(G, ratio), _elementDim(B, ratio), _elementDim(W, ratio)};
}
constexpr RgbwColor RgbwColor::Brighten(uint8_t ratio) const
{
// specifically avoids float math
return RgbwColor{_elementBrighten(R, ratio), _elementBrighten(G, ratio), _elementBrighten(B, ratio), _elementBrighten(W, ratio)};
}
constexpr void RgbwColor::Darken(uint8_t delta)
{
if (R > delta)
{
R -= delta;
}
else
{
R = 0;
} }
inline static uint8_t _elementBrighten(uint8_t value, uint8_t ratio) if (G > delta)
{ {
uint16_t element = ((static_cast<uint16_t>(value) + 1) << 8) / (static_cast<uint16_t>(ratio) + 1); G -= delta;
}
else
{
G = 0;
}
if (element > 255) if (B > delta)
{
B -= delta;
}
else
{
B = 0;
}
if (W > delta)
{
W -= delta;
}
else
{
W = 0;
}
}
constexpr void RgbwColor::Lighten(uint8_t delta)
{
if (IsColorLess())
{
if (W < 255 - delta)
{ {
element = 255; W += delta;
} }
else else
{ {
element -= 1; W = 255;
} }
return element;
} }
}; else
{
if (R < 255 - delta)
{
R += delta;
}
else
{
R = 255;
}
if (G < 255 - delta)
{
G += delta;
}
else
{
G = 255;
}
if (B < 255 - delta)
{
B += delta;
}
else
{
B = 255;
}
}
}
constexpr RgbwColor RgbwColor::LinearBlend(const RgbwColor& left, const RgbwColor& right, float progress)
{
return RgbwColor( left.R + ((right.R - left.R) * progress),
left.G + ((right.G - left.G) * progress),
left.B + ((right.B - left.B) * progress),
left.W + ((right.W - left.W) * progress) );
}
constexpr RgbwColor RgbwColor::BilinearBlend(const RgbwColor& c00,
const RgbwColor& c01,
const RgbwColor& c10,
const RgbwColor& c11,
float x,
float y)
{
float v00 = (1.0f - x) * (1.0f - y);
float v10 = x * (1.0f - y);
float v01 = (1.0f - x) * y;
float v11 = x * y;
return RgbwColor(
c00.R * v00 + c10.R * v10 + c01.R * v01 + c11.R * v11,
c00.G * v00 + c10.G * v10 + c01.G * v01 + c11.G * v11,
c00.B * v00 + c10.B * v10 + c01.B * v01 + c11.B * v11,
c00.W * v00 + c10.W * v10 + c01.W * v01 + c11.W * v11 );
}
constexpr uint16_t RgbwColor::CalcTotalTenthMilliAmpere(const SettingsObject& settings) const
{
auto total = 0;
total += R * settings.RedTenthMilliAmpere / 255;
total += G * settings.GreenTenthMilliAmpere / 255;
total += B * settings.BlueTenthMilliAmpere / 255;
total += W * settings.WhiteCurrent / 255;
return total;
}
constexpr uint8_t RgbwColor::_elementDim(uint8_t value, uint8_t ratio)
{
return (static_cast<uint16_t>(value) * (static_cast<uint16_t>(ratio) + 1)) >> 8;
}
constexpr uint8_t RgbwColor::_elementBrighten(uint8_t value, uint8_t ratio)
{
uint16_t element = ((static_cast<uint16_t>(value) + 1) << 8) / (static_cast<uint16_t>(ratio) + 1);
if (element > 255)
{
element = 255;
}
else
{
element -= 1;
}
return element;
}