osb/source/core/StarColor.cpp
Kai Blaschke 431a9c00a5
Fixed a huge amount of Clang warnings
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably.

99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified.

Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects.

Most remaining warnings are now unused parameters.
2024-02-19 16:55:19 +01:00

632 lines
14 KiB
C++

#include "StarColor.hpp"
#include "StarMap.hpp"
#include "StarEncode.hpp"
#include "StarFormat.hpp"
#include "StarInterpolation.hpp"
namespace Star {
Color const Color::Red = Color::rgba(255, 73, 66, 255);
Color const Color::Orange = Color::rgba(255, 180, 47, 255);
Color const Color::Yellow = Color::rgba(255, 239, 30, 255);
Color const Color::Green = Color::rgba(79, 230, 70, 255);
Color const Color::Blue = Color::rgba(38, 96, 255, 255);
Color const Color::Indigo = Color::rgba(75, 0, 130, 255);
Color const Color::Violet = Color::rgba(160, 119, 255, 255);
Color const Color::Black = Color::rgba(0, 0, 0, 255);
Color const Color::White = Color::rgba(255, 255, 255, 255);
Color const Color::Magenta = Color::rgba(221, 92, 249, 255);
Color const Color::DarkMagenta = Color::rgba(142, 33, 144, 255);
Color const Color::Cyan = Color::rgba(0, 220, 233, 255);
Color const Color::DarkCyan = Color::rgba(0, 137, 165, 255);
Color const Color::CornFlowerBlue = Color::rgba(100, 149, 237, 255);
Color const Color::Gray = Color::rgba(160, 160, 160, 255);
Color const Color::LightGray = Color::rgba(192, 192, 192, 255);
Color const Color::DarkGray = Color::rgba(128, 128, 128, 255);
Color const Color::DarkGreen = Color::rgba(0, 128, 0, 255);
Color const Color::Pink = Color::rgba(255, 162, 187, 255);
Color const Color::Clear = Color::rgba(0, 0, 0, 0);
CaseInsensitiveStringMap<Color> const Color::NamedColors{
{"red", Color::Red},
{"orange", Color::Orange},
{"yellow", Color::Yellow},
{"green", Color::Green},
{"blue", Color::Blue},
{"indigo", Color::Indigo},
{"violet", Color::Violet},
{"black", Color::Black},
{"white", Color::White},
{"magenta", Color::Magenta},
{"darkmagenta", Color::DarkMagenta},
{"cyan", Color::Cyan},
{"darkcyan", Color::DarkCyan},
{"cornflowerblue", Color::CornFlowerBlue},
{"gray", Color::Gray},
{"lightgray", Color::LightGray},
{"darkgray", Color::DarkGray},
{"darkgreen", Color::DarkGreen},
{"pink", Color::Pink},
{"clear", Color::Clear}
};
Color Color::rgbf(const Vec3F& c) {
return rgbaf(c[0], c[1], c[2], 1.0f);
}
Color Color::rgbaf(const Vec4F& c) {
return rgbaf(c[0], c[1], c[2], c[3]);
}
Color Color::rgbf(float r, float g, float b) {
return rgbaf(r, g, b, 1.0f);
}
Color Color::rgbaf(float r, float g, float b, float a) {
Color c;
c.m_data[0] = clamp(r, 0.0f, 1.0f);
c.m_data[1] = clamp(g, 0.0f, 1.0f);
c.m_data[2] = clamp(b, 0.0f, 1.0f);
c.m_data[3] = clamp(a, 0.0f, 1.0f);
return c;
}
Color Color::rgb(uint8_t r, uint8_t g, uint8_t b) {
return rgba(r, g, b, 255);
}
Color Color::rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
Color c;
c.m_data[0] = r / 255.0f;
c.m_data[1] = g / 255.0f;
c.m_data[2] = b / 255.0f;
c.m_data[3] = a / 255.0f;
return c;
}
Color Color::fromUint32(uint32_t v) {
Color c;
c.setAlpha(((uint8_t*)(&v))[3]);
c.setRed(((uint8_t*)(&v))[2]);
c.setGreen(((uint8_t*)(&v))[1]);
c.setBlue(((uint8_t*)(&v))[0]);
return c;
}
Color Color::temperature(float temp) {
// Magic numbers ahoy!
Color c;
c.setAlpha(255);
temp = clamp<float>(temp, 1000, 40000);
temp /= 100;
double r, g, b;
if (temp <= 66) {
r = 255;
g = clamp<double>(99.4708025861 * log(temp) - 161.1195681661, 0, 255);
if (temp <= 19) {
b = 0;
} else {
b = clamp<double>(138.5177312231 * log(temp - 10) - 305.0447927307, 0, 255);
}
} else {
r = clamp<double>(329.698727446 * pow(temp - 60, -0.1332047592), 0, 255);
g = clamp<double>(288.1221695283 * pow(temp - 60, -0.0755148492), 0, 255);
b = 255;
}
c.setRedF((float)r / 255.0f);
c.setGreenF((float)g / 255.0f);
c.setBlueF((float)b / 255.0f);
return c;
}
Color Color::rgb(Vec3B const& c) {
return rgb(c[0], c[1], c[2]);
}
Color Color::rgba(Vec4B const& c) {
return rgba(c[0], c[1], c[2], c[3]);
}
Color Color::hsv(float h, float s, float v) {
return hsva(h, s, v, 1.0f);
}
Color Color::hsva(float h, float s, float v, float a) {
h = clamp(h, 0.0f, 1.0f);
s = clamp(s, 0.0f, 1.0f);
v = clamp(v, 0.0f, 1.0f);
a = clamp(a, 0.0f, 1.0f);
Color retColor;
if (s == 0.0f) {
retColor.setRedF(v);
retColor.setGreenF(v);
retColor.setBlueF(v);
retColor.setAlphaF(a);
} else {
float var_h, var_i, var_1, var_2, var_3, var_r, var_g, var_b;
var_h = h * 6.0f;
if (var_h == 6.0f)
var_h = 0.0f; // H must be < 1
var_i = floor(var_h);
var_1 = v * (1.0f - s);
var_2 = v * (1.0f - s * (var_h - var_i));
var_3 = v * (1.0f - s * (1.0f - (var_h - var_i)));
if (var_i == 0) {
var_r = v;
var_g = var_3;
var_b = var_1;
} else if (var_i == 1) {
var_r = var_2;
var_g = v;
var_b = var_1;
} else if (var_i == 2) {
var_r = var_1;
var_g = v;
var_b = var_3;
} else if (var_i == 3) {
var_r = var_1;
var_g = var_2;
var_b = v;
} else if (var_i == 4) {
var_r = var_3;
var_g = var_1;
var_b = v;
} else {
var_r = v;
var_g = var_1;
var_b = var_2;
}
retColor.setRedF(var_r);
retColor.setGreenF(var_g);
retColor.setBlueF(var_b);
retColor.setAlphaF(a);
}
return retColor;
}
Color Color::hsv(Vec3F const& c) {
return Color::hsv(c[0], c[1], c[2]);
}
Color Color::hsva(Vec4F const& c) {
return Color::hsva(c[0], c[1], c[2], c[3]);
}
Color Color::grayf(float g) {
return Color::rgbf(g, g, g);
}
Color Color::gray(uint8_t g) {
return Color::rgb(g, g, g);
}
Color::Color() {}
Color::Color(StringView name) {
if (name.beginsWith('#'))
*this = fromHex(name.substr(1));
else {
auto i = NamedColors.find(String(name).toLower());
if (i != NamedColors.end())
*this = i->second;
else
throw ColorException(strf("Named color {} not found", name), false);
}
}
float Color::redF() const {
return m_data[0];
}
float Color::greenF() const {
return m_data[1];
}
float Color::blueF() const {
return m_data[2];
}
float Color::alphaF() const {
return m_data[3];
}
bool Color::isClear() const {
return m_data[3] == 0;
}
uint8_t Color::red() const {
return uint8_t(round(m_data[0] * 255));
}
uint8_t Color::green() const {
return uint8_t(round(m_data[1] * 255));
}
uint8_t Color::blue() const {
return uint8_t(round(m_data[2] * 255));
}
uint8_t Color::alpha() const {
return uint8_t(m_data[3] * 255);
}
void Color::setRedF(float r) {
m_data[0] = clamp(r, 0.0f, 1.0f);
}
void Color::setGreenF(float g) {
m_data[1] = clamp(g, 0.0f, 1.0f);
}
void Color::setBlueF(float b) {
m_data[2] = clamp(b, 0.0f, 1.0f);
}
void Color::setAlphaF(float a) {
m_data[3] = clamp(a, 0.0f, 1.0f);
}
void Color::setRed(uint8_t r) {
m_data[0] = r / 255.0f;
}
void Color::setGreen(uint8_t g) {
m_data[1] = g / 255.0f;
}
void Color::setBlue(uint8_t b) {
m_data[2] = b / 255.0f;
}
void Color::setAlpha(uint8_t a) {
m_data[3] = a / 255.0f;
}
uint32_t Color::toUint32() const {
uint32_t val;
((uint8_t*)(&val))[3] = alpha();
((uint8_t*)(&val))[2] = red();
((uint8_t*)(&val))[1] = green();
((uint8_t*)(&val))[0] = blue();
return val;
}
Color Color::fromHex(StringView s) {
return Color::rgba(hexToVec4B(s));
}
Vec4B Color::toRgba() const {
return Vec4B(red(), green(), blue(), alpha());
}
Vec3B Color::toRgb() const {
return Vec3B(red(), green(), blue());
}
Vec4F Color::toRgbaF() const {
return Vec4F(redF(), greenF(), blueF(), alphaF());
}
Vec3F Color::toRgbF() const {
return Vec3F(redF(), greenF(), blueF());
}
Vec4F Color::toHsva() const {
float h, s, v;
float var_r = redF();
float var_g = greenF();
float var_b = blueF();
// Min. value of RGB
float var_min = min(min(var_r, var_g), var_b);
// Max. value of RGB
float var_max = max(max(var_r, var_g), var_b);
// Delta RGB value
float del_max = var_max - var_min;
v = var_max;
if (del_max == 0.0f) { // This is a gray, no chroma...
h = 0.0f;
s = 0.0f;
} else { // Chromatic data
s = del_max / var_max;
float del_r = (((var_max - var_r) / 6.0f) + (del_max / 2.0f)) / del_max;
float del_g = (((var_max - var_g) / 6.0f) + (del_max / 2.0f)) / del_max;
float del_b = (((var_max - var_b) / 6.0f) + (del_max / 2.0f)) / del_max;
if (var_r == var_max)
h = del_b - del_g;
else if (var_g == var_max)
h = (1.0f / 3.0f) + del_r - del_b;
else
/*if (var_b == var_max)*/ h = (2.0f / 3.0f) + del_g - del_r;
if (h < 0.0f)
h += 1.0f;
if (h >= 1.0f)
h -= 1.0f;
}
return Vec4F(h, s, v, alphaF());
}
String Color::toHex() const {
auto rgba = toRgba();
return hexEncode((char*)rgba.ptr(), rgba[3] == 255 ? 3 : 4);
}
float Color::hue() const {
return toHsva()[0];
}
float Color::saturation() const {
// Min. value of RGB
float var_min = min(min(m_data[0], m_data[1]), m_data[2]);
// Max. value of RGB
float var_max = max(max(m_data[0], m_data[1]), m_data[2]);
// Delta RGB value
float del_max = var_max - var_min;
if (del_max == 0.0f) { // This is a gray, no chroma...
return 0.0f;
} else
return del_max / var_max;
}
float Color::value() const {
return max(max(m_data[0], m_data[1]), m_data[2]);
}
void Color::setHue(float h) {
auto hsva = toHsva();
*this = Color::hsva(clamp(h, 0.0f, 1.0f), hsva[1], hsva[2], alphaF());
}
void Color::setSaturation(float s) {
auto hsva = toHsva();
*this = Color::hsva(hsva[0], clamp(s, 0.0f, 1.0f), hsva[2], alphaF());
}
void Color::setValue(float v) {
auto hsva = toHsva();
*this = Color::hsva(hsva[0], hsva[1], clamp(v, 0.0f, 1.0f), alphaF());
}
void Color::hueShift(float h) {
setHue(pfmod(hue() + h, 1.0f));
}
void Color::fade(float value) {
m_data *= (1.0f - value);
m_data.clamp(0.0f, 1.0f);
}
bool Color::operator==(const Color& c) const {
return m_data == c.m_data;
}
bool Color::operator!=(const Color& c) const {
return m_data != c.m_data;
}
std::ostream& operator<<(std::ostream& os, const Color& c) {
os << c.toRgbaF();
return os;
}
float Color::toLinear(float in) {
const float a = 0.055f;
if (in <= 0.04045f)
return in / 12.92f;
return powf((in + a) / (1.0f + a), 2.4f);
}
float Color::fromLinear(float in) {
const float a = 0.055f;
if (in <= 0.0031308f)
return 12.92f * in;
return (1.0f + a) * powf(in, 1.0f / 2.4f) - a;
}
void Color::convertToLinear() {
setRedF(toLinear(redF()));
setGreenF(toLinear(greenF()));
setBlueF(toLinear(blueF()));
}
void Color::convertToSRGB() {
setRedF(fromLinear(redF()));
setGreenF(fromLinear(greenF()));
setBlueF(fromLinear(blueF()));
}
Color Color::toLinear() {
Color c = *this;
c.convertToLinear();
return c;
}
Color Color::toSRGB() {
Color c = *this;
c.convertToSRGB();
return c;
}
Color Color::contrasting() {
Color c = *this;
c.setHue(c.hue() + 120);
return c;
}
Color Color::complementary() {
Color c = *this;
c.setHue(c.hue() + 180);
return c;
}
Color Color::mix(Color const& c, float amount) const {
return Color::rgbaf(lerp(clamp(amount, 0.0f, 1.0f), toRgbaF(), c.toRgbaF()));
}
Color Color::multiply(float amount) const {
return Color::rgbaf(m_data * amount);
}
Color Color::operator+(Color const& c) const {
return Color::rgbaf(m_data + c.toRgbaF());
}
Color Color::operator*(Color const& c) const {
return Color::rgbaf(m_data.piecewiseMultiply(c.toRgbaF()));
}
Color& Color::operator+=(Color const& c) {
return * this = *this + c;
}
Color& Color::operator*=(Color const& c) {
return * this = *this * c;
}
Vec4B Color::hueShiftVec4B(Vec4B color, float hue) {
float h, s, v;
float var_r = color[0] / 255.0f;
float var_g = color[1] / 255.0f;
float var_b = color[2] / 255.0f;
// Min. value of RGB
float var_min = min(min(var_r, var_g), var_b);
// Max. value of RGB
float var_max = max(max(var_r, var_g), var_b);
// Delta RGB value
float del_max = var_max - var_min;
v = var_max;
if (del_max == 0.0f) { // This is a gray, no chroma...
h = 0.0f;
s = 0.0f;
} else { // Chromatic data
s = del_max / var_max;
float vd = 1.0f / 6.0f;
float dmh = del_max * 0.5f;
float dmi = 1.0f / del_max;
float del_r = (((var_max - var_r) * vd) + dmh) * dmi;
float del_g = (((var_max - var_g) * vd) + dmh) * dmi;
float del_b = (((var_max - var_b) * vd) + dmh) * dmi;
if (var_r == var_max)
h = del_b - del_g;
else if (var_g == var_max)
h = (1.0f / 3.0f) + del_r - del_b;
else
h = (2.0f / 3.0f) + del_g - del_r;
if (h < 0.0f)
h += 1.0f;
if (h >= 1.0f)
h -= 1.0f;
}
h += hue;
if (h >= 1.0f)
h -= 1.0f;
if (s == 0.0f) {
auto c = uint8_t(round(v * 255));
return Vec4B(c, c, c, color[3]);
} else {
float var_h, var_i, var_1, var_2, var_3, var_r, var_g, var_b;
var_h = h * 6.0f;
if (var_h == 6.0f)
var_h = 0.0f; // H must be < 1
var_i = floor(var_h);
var_1 = v * (1.0f - s);
var_2 = v * (1.0f - s * (var_h - var_i));
var_3 = v * (1.0f - s * (1.0f - (var_h - var_i)));
if (var_i == 0) {
var_r = v;
var_g = var_3;
var_b = var_1;
} else if (var_i == 1) {
var_r = var_2;
var_g = v;
var_b = var_1;
} else if (var_i == 2) {
var_r = var_1;
var_g = v;
var_b = var_3;
} else if (var_i == 3) {
var_r = var_1;
var_g = var_2;
var_b = v;
} else if (var_i == 4) {
var_r = var_3;
var_g = var_1;
var_b = v;
} else {
var_r = v;
var_g = var_1;
var_b = var_2;
}
return Vec4B(uint8_t(round(var_r * 255)), uint8_t(round(var_g * 255)), uint8_t(round(var_b * 255)), color[3]);
}
}
Vec4B Color::hexToVec4B(StringView s) {
Array<uint8_t, 4> cbytes;
if (s.utf8Size() == 3) {
nibbleDecode(s.utf8Ptr(), 3, (char*)cbytes.data(), 4);
cbytes[0] = (cbytes[0] << 4) | cbytes[0];
cbytes[1] = (cbytes[1] << 4) | cbytes[1];
cbytes[2] = (cbytes[2] << 4) | cbytes[2];
cbytes[3] = 255;
} else if (s.utf8Size() == 4) {
nibbleDecode(s.utf8Ptr(), 4, (char*)cbytes.data(), 4);
cbytes[0] = (cbytes[0] << 4) | cbytes[0];
cbytes[1] = (cbytes[1] << 4) | cbytes[1];
cbytes[2] = (cbytes[2] << 4) | cbytes[2];
cbytes[3] = (cbytes[3] << 4) | cbytes[3];
} else if (s.utf8Size() == 6) {
hexDecode(s.utf8Ptr(), 6, (char*)cbytes.data(), 4);
cbytes[3] = 255;
} else if (s.utf8Size() == 8) {
hexDecode(s.utf8Ptr(), 8, (char*)cbytes.data(), 4);
} else {
throw ColorException(strf("Improper size {} for hex string '{}' in Color::hexToVec4B", s.utf8Size(), s), false);
}
return Vec4B(std::move(cbytes));
}
}