Introduction of classes ColorRGB and ColorRGBA to unify color data definition and manipulation

(cherry picked from commit prusa3d/PrusaSlicer@d0bff2d996 )
This commit is contained in:
enricoturri1966 2023-10-20 15:41:26 +08:00 committed by Noisyfox
parent 36ffb18059
commit 28d0147d09
60 changed files with 1290 additions and 1174 deletions

View file

@ -56,6 +56,8 @@ set(lisbslic3r_sources
Clipper2Utils.cpp
Clipper2Utils.hpp
ClipperZUtils.hpp
Color.cpp
Color.hpp
Config.cpp
Config.hpp
CurveAnalyzer.cpp

412
src/libslic3r/Color.cpp Normal file
View file

@ -0,0 +1,412 @@
#include "libslic3r.h"
#include "Color.hpp"
#include <random>
static const float INV_255 = 1.0f / 255.0f;
namespace Slic3r {
// Conversion from RGB to HSV color space
// The input RGB values are in the range [0, 1]
// The output HSV values are in the ranges h = [0, 360], and s, v = [0, 1]
static void RGBtoHSV(float r, float g, float b, float& h, float& s, float& v)
{
assert(0.0f <= r && r <= 1.0f);
assert(0.0f <= g && g <= 1.0f);
assert(0.0f <= b && b <= 1.0f);
const float max_comp = std::max(std::max(r, g), b);
const float min_comp = std::min(std::min(r, g), b);
const float delta = max_comp - min_comp;
if (delta > 0.0f) {
if (max_comp == r)
h = 60.0f * (std::fmod(((g - b) / delta), 6.0f));
else if (max_comp == g)
h = 60.0f * (((b - r) / delta) + 2.0f);
else if (max_comp == b)
h = 60.0f * (((r - g) / delta) + 4.0f);
s = (max_comp > 0.0f) ? delta / max_comp : 0.0f;
}
else {
h = 0.0f;
s = 0.0f;
}
v = max_comp;
while (h < 0.0f) { h += 360.0f; }
while (h > 360.0f) { h -= 360.0f; }
assert(0.0f <= s && s <= 1.0f);
assert(0.0f <= v && v <= 1.0f);
assert(0.0f <= h && h <= 360.0f);
}
// Conversion from HSV to RGB color space
// The input HSV values are in the ranges h = [0, 360], and s, v = [0, 1]
// The output RGB values are in the range [0, 1]
static void HSVtoRGB(float h, float s, float v, float& r, float& g, float& b)
{
assert(0.0f <= s && s <= 1.0f);
assert(0.0f <= v && v <= 1.0f);
assert(0.0f <= h && h <= 360.0f);
const float chroma = v * s;
const float h_prime = std::fmod(h / 60.0f, 6.0f);
const float x = chroma * (1.0f - std::abs(std::fmod(h_prime, 2.0f) - 1.0f));
const float m = v - chroma;
if (0.0f <= h_prime && h_prime < 1.0f) {
r = chroma;
g = x;
b = 0.0f;
}
else if (1.0f <= h_prime && h_prime < 2.0f) {
r = x;
g = chroma;
b = 0.0f;
}
else if (2.0f <= h_prime && h_prime < 3.0f) {
r = 0.0f;
g = chroma;
b = x;
}
else if (3.0f <= h_prime && h_prime < 4.0f) {
r = 0.0f;
g = x;
b = chroma;
}
else if (4.0f <= h_prime && h_prime < 5.0f) {
r = x;
g = 0.0f;
b = chroma;
}
else if (5.0f <= h_prime && h_prime < 6.0f) {
r = chroma;
g = 0.0f;
b = x;
}
else {
r = 0.0f;
g = 0.0f;
b = 0.0f;
}
r += m;
g += m;
b += m;
assert(0.0f <= r && r <= 1.0f);
assert(0.0f <= g && g <= 1.0f);
assert(0.0f <= b && b <= 1.0f);
}
class Randomizer
{
std::random_device m_rd;
public:
float random_float(float min, float max) {
std::mt19937 rand_generator(m_rd());
std::uniform_real_distribution<float> distrib(min, max);
return distrib(rand_generator);
}
};
ColorRGB::ColorRGB(float r, float g, float b)
: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f) })
{
}
ColorRGB::ColorRGB(unsigned char r, unsigned char g, unsigned char b)
: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f) })
{
}
bool ColorRGB::operator < (const ColorRGB& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] < other.m_data[i])
return true;
}
return false;
}
bool ColorRGB::operator > (const ColorRGB& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] > other.m_data[i])
return true;
}
return false;
}
ColorRGB ColorRGB::operator + (const ColorRGB& other) const
{
ColorRGB ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGB ColorRGB::operator * (float value) const
{
assert(value >= 0.0f);
ColorRGB ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGBA::ColorRGBA(float r, float g, float b, float a)
: m_data({ std::clamp(r, 0.0f, 1.0f), std::clamp(g, 0.0f, 1.0f), std::clamp(b, 0.0f, 1.0f), std::clamp(a, 0.0f, 1.0f) })
{
}
ColorRGBA::ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
: m_data({ std::clamp(r * INV_255, 0.0f, 1.0f), std::clamp(g * INV_255, 0.0f, 1.0f), std::clamp(b * INV_255, 0.0f, 1.0f), std::clamp(a * INV_255, 0.0f, 1.0f) })
{
}
bool ColorRGBA::operator < (const ColorRGBA& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] < other.m_data[i])
return true;
}
return false;
}
bool ColorRGBA::operator > (const ColorRGBA& other) const
{
for (size_t i = 0; i < 3; ++i) {
if (m_data[i] > other.m_data[i])
return true;
}
return false;
}
ColorRGBA ColorRGBA::operator + (const ColorRGBA& other) const
{
ColorRGBA ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(m_data[i] + other.m_data[i], 0.0f, 1.0f);
}
return ret;
}
ColorRGBA ColorRGBA::operator * (float value) const
{
assert(value >= 0.0f);
ColorRGBA ret;
for (size_t i = 0; i < 3; ++i) {
ret.m_data[i] = std::clamp(value * m_data[i], 0.0f, 1.0f);
}
ret.m_data[3] = this->m_data[3];
return ret;
}
ColorRGB operator * (float value, const ColorRGB& other) { return other * value; }
ColorRGBA operator * (float value, const ColorRGBA& other) { return other * value; }
ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t)
{
assert(0.0f <= t && t <= 1.0f);
return (1.0f - t) * a + t * b;
}
ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t)
{
assert(0.0f <= t && t <= 1.0f);
return (1.0f - t) * a + t * b;
}
ColorRGB complementary(const ColorRGB& color)
{
return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b() };
}
ColorRGBA complementary(const ColorRGBA& color)
{
return { 1.0f - color.r(), 1.0f - color.g(), 1.0f - color.b(), color.a() };
}
ColorRGB saturate(const ColorRGB& color, float factor)
{
float h, s, v;
RGBtoHSV(color.r(), color.g(), color.b(), h, s, v);
s = std::clamp(s * factor, 0.0f, 1.0f);
float r, g, b;
HSVtoRGB(h, s, v, r, g, b);
return { r, g, b };
}
ColorRGBA saturate(const ColorRGBA& color, float factor)
{
return to_rgba(saturate(to_rgb(color), factor), color.a());
}
ColorRGB opposite(const ColorRGB& color)
{
float h, s, v;
RGBtoHSV(color.r(), color.g(), color.b(), h, s, v);
h += 65.0f; // 65 instead 60 to avoid circle values
if (h > 360.0f)
h -= 360.0f;
Randomizer rnd;
s = rnd.random_float(0.65f, 1.0f);
v = rnd.random_float(0.65f, 1.0f);
float r, g, b;
HSVtoRGB(h, s, v, r, g, b);
return { r, g, b };
}
ColorRGB opposite(const ColorRGB& a, const ColorRGB& b)
{
float ha, sa, va;
RGBtoHSV(a.r(), a.g(), a.b(), ha, sa, va);
float hb, sb, vb;
RGBtoHSV(b.r(), b.g(), b.b(), hb, sb, vb);
float delta_h = std::abs(ha - hb);
float start_h = (delta_h > 180.0f) ? std::min(ha, hb) : std::max(ha, hb);
start_h += 5.0f; // to avoid circle change of colors for 120 deg
if (delta_h < 180.0f)
delta_h = 360.0f - delta_h;
Randomizer rnd;
float out_h = start_h + 0.5f * delta_h;
if (out_h > 360.0f)
out_h -= 360.0f;
float out_s = rnd.random_float(0.65f, 1.0f);
float out_v = rnd.random_float(0.65f, 1.0f);
float out_r, out_g, out_b;
HSVtoRGB(out_h, out_s, out_v, out_r, out_g, out_b);
return { out_r, out_g, out_b };
}
bool can_decode_color(const std::string &color)
{
return (color.size() == 7 && color.front() == '#') || (color.size() == 9 && color.front() == '#');
}
bool decode_color(const std::string& color_in, ColorRGB& color_out)
{
ColorRGBA rgba;
if (!decode_color(color_in, rgba))
return false;
color_out = to_rgb(rgba);
return true;
}
bool decode_color(const std::string& color_in, ColorRGBA& color_out)
{
auto hex_digit_to_int = [](const char c) {
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
};
color_out = ColorRGBA::BLACK();
if (can_decode_color(color_in)) {
const char *c = color_in.data() + 1;
if (color_in.size() == 7) {
for (unsigned int i = 0; i < 3; ++i) {
const int digit1 = hex_digit_to_int(*c++);
const int digit2 = hex_digit_to_int(*c++);
if (digit1 != -1 && digit2 != -1)
color_out.set(i, float(digit1 * 16 + digit2) * INV_255);
}
} else {
for (unsigned int i = 0; i < 4; ++i) {
const int digit1 = hex_digit_to_int(*c++);
const int digit2 = hex_digit_to_int(*c++);
if (digit1 != -1 && digit2 != -1)
color_out.set(i, float(digit1 * 16 + digit2) * INV_255);
}
}
} else
return false;
assert(0.0f <= color_out.r() && color_out.r() <= 1.0f);
assert(0.0f <= color_out.g() && color_out.g() <= 1.0f);
assert(0.0f <= color_out.b() && color_out.b() <= 1.0f);
assert(0.0f <= color_out.a() && color_out.a() <= 1.0f);
return true;
}
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGB>& colors_out)
{
colors_out = std::vector<ColorRGB>(colors_in.size(), ColorRGB::BLACK());
for (size_t i = 0; i < colors_in.size(); ++i) {
if (!decode_color(colors_in[i], colors_out[i]))
return false;
}
return true;
}
bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGBA>& colors_out)
{
colors_out = std::vector<ColorRGBA>(colors_in.size(), ColorRGBA::BLACK());
for (size_t i = 0; i < colors_in.size(); ++i) {
if (!decode_color(colors_in[i], colors_out[i]))
return false;
}
return true;
}
std::string encode_color(const ColorRGB& color)
{
char buffer[64];
::sprintf(buffer, "#%02X%02X%02X", color.r_uchar(), color.g_uchar(), color.b_uchar());
return std::string(buffer);
}
std::string encode_color(const ColorRGBA& color) { return encode_color(to_rgb(color)); }
ColorRGB to_rgb(const ColorRGBA& other_rgba) { return { other_rgba.r(), other_rgba.g(), other_rgba.b() }; }
ColorRGBA to_rgba(const ColorRGB& other_rgb) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), 1.0f }; }
ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha) { return { other_rgb.r(), other_rgb.g(), other_rgb.b(), alpha }; }
ColorRGBA picking_decode(unsigned int id)
{
return {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff)) * INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
}
unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b) { return r + (g << 8) + (b << 16); }
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
} // namespace Slic3r

170
src/libslic3r/Color.hpp Normal file
View file

@ -0,0 +1,170 @@
#ifndef slic3r_Color_hpp_
#define slic3r_Color_hpp_
#include <array>
#include <algorithm>
namespace Slic3r {
class ColorRGB
{
std::array<float, 3> m_data{1.0f, 1.0f, 1.0f};
public:
ColorRGB() = default;
ColorRGB(float r, float g, float b);
ColorRGB(unsigned char r, unsigned char g, unsigned char b);
ColorRGB(const ColorRGB& other) = default;
ColorRGB& operator = (const ColorRGB& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGB& other) const { return m_data == other.m_data; }
bool operator != (const ColorRGB& other) const { return !operator==(other); }
bool operator < (const ColorRGB& other) const;
bool operator > (const ColorRGB& other) const;
ColorRGB operator + (const ColorRGB& other) const;
ColorRGB operator * (float value) const;
const float* const data() const { return m_data.data(); }
float r() const { return m_data[0]; }
float g() const { return m_data[1]; }
float b() const { return m_data[2]; }
void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); }
void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); }
void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); }
void set(unsigned int comp, float value) {
assert(0 <= comp && comp <= 2);
m_data[comp] = std::clamp(value, 0.0f, 1.0f);
}
unsigned char r_uchar() const { return static_cast<unsigned char>(m_data[0] * 255.0f); }
unsigned char g_uchar() const { return static_cast<unsigned char>(m_data[1] * 255.0f); }
unsigned char b_uchar() const { return static_cast<unsigned char>(m_data[2] * 255.0f); }
static const ColorRGB BLACK() { return { 0.0f, 0.0f, 0.0f }; }
static const ColorRGB BLUE() { return { 0.0f, 0.0f, 1.0f }; }
static const ColorRGB BLUEISH() { return { 0.5f, 0.5f, 1.0f }; }
static const ColorRGB DARK_GRAY() { return { 0.25f, 0.25f, 0.25f }; }
static const ColorRGB DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f }; }
static const ColorRGB GRAY() { return { 0.5f, 0.5f, 0.5f }; }
static const ColorRGB GREEN() { return { 0.0f, 1.0f, 0.0f }; }
static const ColorRGB GREENISH() { return { 0.5f, 1.0f, 0.5f }; }
static const ColorRGB LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f }; }
static const ColorRGB ORANGE() { return { 0.92f, 0.50f, 0.26f }; }
static const ColorRGB RED() { return { 1.0f, 0.0f, 0.0f }; }
static const ColorRGB REDISH() { return { 1.0f, 0.5f, 0.5f }; }
static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; }
static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; }
static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; }
static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; }
static const ColorRGB Z() { return { 0.0f, 0.0f, 0.75f }; }
};
class ColorRGBA
{
std::array<float, 4> m_data{ 1.0f, 1.0f, 1.0f, 1.0f };
public:
ColorRGBA() = default;
ColorRGBA(float r, float g, float b, float a);
ColorRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
ColorRGBA(const ColorRGBA& other) = default;
ColorRGBA& operator = (const ColorRGBA& other) { m_data = other.m_data; return *this; }
bool operator == (const ColorRGBA& other) const { return m_data == other.m_data; }
bool operator != (const ColorRGBA& other) const { return !operator==(other); }
bool operator < (const ColorRGBA& other) const;
bool operator > (const ColorRGBA& other) const;
ColorRGBA operator + (const ColorRGBA& other) const;
ColorRGBA operator * (float value) const;
const float* const data() const { return m_data.data(); }
float r() const { return m_data[0]; }
float g() const { return m_data[1]; }
float b() const { return m_data[2]; }
float a() const { return m_data[3]; }
void r(float r) { m_data[0] = std::clamp(r, 0.0f, 1.0f); }
void g(float g) { m_data[1] = std::clamp(g, 0.0f, 1.0f); }
void b(float b) { m_data[2] = std::clamp(b, 0.0f, 1.0f); }
void a(float a) { m_data[3] = std::clamp(a, 0.0f, 1.0f); }
void set(unsigned int comp, float value) {
assert(0 <= comp && comp <= 3);
m_data[comp] = std::clamp(value, 0.0f, 1.0f);
}
unsigned char r_uchar() const { return static_cast<unsigned char>(m_data[0] * 255.0f); }
unsigned char g_uchar() const { return static_cast<unsigned char>(m_data[1] * 255.0f); }
unsigned char b_uchar() const { return static_cast<unsigned char>(m_data[2] * 255.0f); }
unsigned char a_uchar() const { return static_cast<unsigned char>(m_data[3] * 255.0f); }
bool is_transparent() const { return m_data[3] < 1.0f; }
static const ColorRGBA BLACK() { return { 0.0f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA BLUE() { return { 0.0f, 0.0f, 1.0f, 1.0f }; }
static const ColorRGBA BLUEISH() { return { 0.5f, 0.5f, 1.0f, 1.0f }; }
static const ColorRGBA DARK_GRAY() { return { 0.25f, 0.25f, 0.25f, 1.0f }; }
static const ColorRGBA DARK_YELLOW() { return { 0.5f, 0.5f, 0.0f, 1.0f }; }
static const ColorRGBA GRAY() { return { 0.5f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA GREEN() { return { 0.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA GREENISH() { return { 0.5f, 1.0f, 0.5f, 1.0f }; }
static const ColorRGBA LIGHT_GRAY() { return { 0.75f, 0.75f, 0.75f, 1.0f }; }
static const ColorRGBA ORANGE() { return { 0.923f, 0.504f, 0.264f, 1.0f }; }
static const ColorRGBA RED() { return { 1.0f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA REDISH() { return { 1.0f, 0.5f, 0.5f, 1.0f }; }
static const ColorRGBA YELLOW() { return { 1.0f, 1.0f, 0.0f, 1.0f }; }
static const ColorRGBA WHITE() { return { 1.0f, 1.0f, 1.0f, 1.0f }; }
static const ColorRGBA X() { return { 0.75f, 0.0f, 0.0f, 1.0f }; }
static const ColorRGBA Y() { return { 0.0f, 0.75f, 0.0f, 1.0f }; }
static const ColorRGBA Z() { return { 0.0f, 0.0f, 0.75f, 1.0f }; }
};
extern ColorRGB operator * (float value, const ColorRGB& other);
extern ColorRGBA operator * (float value, const ColorRGBA& other);
extern ColorRGB lerp(const ColorRGB& a, const ColorRGB& b, float t);
extern ColorRGBA lerp(const ColorRGBA& a, const ColorRGBA& b, float t);
extern ColorRGB complementary(const ColorRGB& color);
extern ColorRGBA complementary(const ColorRGBA& color);
extern ColorRGB saturate(const ColorRGB& color, float factor);
extern ColorRGBA saturate(const ColorRGBA& color, float factor);
extern ColorRGB opposite(const ColorRGB& color);
extern ColorRGB opposite(const ColorRGB& a, const ColorRGB& b);
extern bool can_decode_color(const std::string& color);
extern bool decode_color(const std::string& color_in, ColorRGB& color_out);
extern bool decode_color(const std::string& color_in, ColorRGBA& color_out);
extern bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGB>& colors_out);
extern bool decode_colors(const std::vector<std::string>& colors_in, std::vector<ColorRGBA>& colors_out);
extern std::string encode_color(const ColorRGB& color);
extern std::string encode_color(const ColorRGBA& color);
extern ColorRGB to_rgb(const ColorRGBA& other_rgba);
extern ColorRGBA to_rgba(const ColorRGB& other_rgb);
extern ColorRGBA to_rgba(const ColorRGB& other_rgb, float alpha);
extern ColorRGBA picking_decode(unsigned int id);
extern unsigned int picking_encode(unsigned char r, unsigned char g, unsigned char b);
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue);
} // namespace Slic3r
#endif /* slic3r_Color_hpp_ */