|
@@ -0,0 +1,467 @@
|
|
|
|
+#ifndef COLOR_H
|
|
|
|
+#define COLOR_H
|
|
|
|
+
|
|
|
|
+#include <godot/godot_color.h>
|
|
|
|
+
|
|
|
|
+#include <cmath>
|
|
|
|
+
|
|
|
|
+#include <String.h>
|
|
|
|
+
|
|
|
|
+namespace godot {
|
|
|
|
+
|
|
|
|
+// @Todo move these to a more global file.
|
|
|
|
+
|
|
|
|
+// or should I? 🤔
|
|
|
|
+#define MIN(a, b) (a < b ? a : b)
|
|
|
|
+#define MAX(a, b) (a > b ? a : b)
|
|
|
|
+
|
|
|
|
+struct Color {
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+private:
|
|
|
|
+ static float _parse_col(const String& p_str, int p_ofs) {
|
|
|
|
+
|
|
|
|
+ int ig=0;
|
|
|
|
+
|
|
|
|
+ for(int i=0;i<2;i++) {
|
|
|
|
+
|
|
|
|
+ int c= (int) (wchar_t) p_str[i+p_ofs];
|
|
|
|
+ int v=0;
|
|
|
|
+
|
|
|
|
+ if (c>='0' && c<='9') {
|
|
|
|
+ v=c-'0';
|
|
|
|
+ } else if (c>='a' && c<='f') {
|
|
|
|
+ v=c-'a';
|
|
|
|
+ v+=10;
|
|
|
|
+ } else if (c>='A' && c<='F') {
|
|
|
|
+ v=c-'A';
|
|
|
|
+ v+=10;
|
|
|
|
+ } else {
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (i==0)
|
|
|
|
+ ig+=v*16;
|
|
|
|
+ else
|
|
|
|
+ ig+=v;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ig;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+public:
|
|
|
|
+
|
|
|
|
+ union {
|
|
|
|
+
|
|
|
|
+ struct {
|
|
|
|
+ float r;
|
|
|
|
+ float g;
|
|
|
|
+ float b;
|
|
|
|
+ float a;
|
|
|
|
+ };
|
|
|
|
+ float components[4];
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ bool operator==(const Color &p_color) const { return (r==p_color.r && g==p_color.g && b==p_color.b && a==p_color.a ); }
|
|
|
|
+ bool operator!=(const Color &p_color) const { return (r!=p_color.r || g!=p_color.g || b!=p_color.b || a!=p_color.a ); }
|
|
|
|
+
|
|
|
|
+ uint32_t to_32() const
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ uint32_t c=(uint8_t)(a*255);
|
|
|
|
+ c<<=8;
|
|
|
|
+ c|=(uint8_t)(r*255);
|
|
|
|
+ c<<=8;
|
|
|
|
+ c|=(uint8_t)(g*255);
|
|
|
|
+ c<<=8;
|
|
|
|
+ c|=(uint8_t)(b*255);
|
|
|
|
+
|
|
|
|
+ return c;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ uint32_t to_ARGB32() const
|
|
|
|
+ {
|
|
|
|
+ uint32_t c=(uint8_t)(a*255);
|
|
|
|
+ c<<=8;
|
|
|
|
+ c|=(uint8_t)(r*255);
|
|
|
|
+ c<<=8;
|
|
|
|
+ c|=(uint8_t)(g*255);
|
|
|
|
+ c<<=8;
|
|
|
|
+ c|=(uint8_t)(b*255);
|
|
|
|
+
|
|
|
|
+ return c;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ float gray() const
|
|
|
|
+ {
|
|
|
|
+ return (r+g+b)/3.0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ float get_h() const
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ float min = MIN( r, g );
|
|
|
|
+ min = MIN( min, b );
|
|
|
|
+ float max = MAX( r, g );
|
|
|
|
+ max = MAX( max, b );
|
|
|
|
+
|
|
|
|
+ float delta = max - min;
|
|
|
|
+
|
|
|
|
+ if( delta == 0 )
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ float h;
|
|
|
|
+ if( r == max )
|
|
|
|
+ h = ( g - b ) / delta; // between yellow & magenta
|
|
|
|
+ else if( g == max )
|
|
|
|
+ h = 2 + ( b - r ) / delta; // between cyan & yellow
|
|
|
|
+ else
|
|
|
|
+ h = 4 + ( r - g ) / delta; // between magenta & cyan
|
|
|
|
+
|
|
|
|
+ h/=6.0;
|
|
|
|
+ if (h<0)
|
|
|
|
+ h+=1.0;
|
|
|
|
+
|
|
|
|
+ return h;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ float get_s() const
|
|
|
|
+ {
|
|
|
|
+ float min = MIN( r, g );
|
|
|
|
+ min = MIN( min, b );
|
|
|
|
+ float max = MAX( r, g );
|
|
|
|
+ max = MAX( max, b );
|
|
|
|
+ float delta = max - min;
|
|
|
|
+ return (max!=0) ? (delta / max) : 0;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ float get_v() const
|
|
|
|
+ {
|
|
|
|
+ float max = MAX( r, g );
|
|
|
|
+ max = MAX( max, b );
|
|
|
|
+ return max;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void set_hsv(float p_h, float p_s, float p_v, float p_alpha=1.0)
|
|
|
|
+ {
|
|
|
|
+ int i;
|
|
|
|
+ float f, p, q, t;
|
|
|
|
+ a=p_alpha;
|
|
|
|
+
|
|
|
|
+ if( p_s == 0 ) {
|
|
|
|
+ // acp_hromatic (grey)
|
|
|
|
+ r = g = b = p_v;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ p_h *=6.0;
|
|
|
|
+ p_h = ::fmod(p_h,6);
|
|
|
|
+ i = ::floor( p_h );
|
|
|
|
+
|
|
|
|
+ f = p_h - i;
|
|
|
|
+ p = p_v * ( 1 - p_s );
|
|
|
|
+ q = p_v * ( 1 - p_s * f );
|
|
|
|
+ t = p_v * ( 1 - p_s * ( 1 - f ) );
|
|
|
|
+
|
|
|
|
+ switch( i ) {
|
|
|
|
+ case 0: // Red is the dominant color
|
|
|
|
+ r = p_v;
|
|
|
|
+ g = t;
|
|
|
|
+ b = p;
|
|
|
|
+ break;
|
|
|
|
+ case 1: // Green is the dominant color
|
|
|
|
+ r = q;
|
|
|
|
+ g = p_v;
|
|
|
|
+ b = p;
|
|
|
|
+ break;
|
|
|
|
+ case 2:
|
|
|
|
+ r = p;
|
|
|
|
+ g = p_v;
|
|
|
|
+ b = t;
|
|
|
|
+ break;
|
|
|
|
+ case 3: // Blue is the dominant color
|
|
|
|
+ r = p;
|
|
|
|
+ g = q;
|
|
|
|
+ b = p_v;
|
|
|
|
+ break;
|
|
|
|
+ case 4:
|
|
|
|
+ r = t;
|
|
|
|
+ g = p;
|
|
|
|
+ b = p_v;
|
|
|
|
+ break;
|
|
|
|
+ default: // (5) Red is the dominant color
|
|
|
|
+ r = p_v;
|
|
|
|
+ g = p;
|
|
|
|
+ b = q;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ float& operator[](int idx) {
|
|
|
|
+ return components[idx];
|
|
|
|
+ }
|
|
|
|
+ const float& operator[](int idx) const {
|
|
|
|
+ return components[idx];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void invert()
|
|
|
|
+ {
|
|
|
|
+ r=1.0-r;
|
|
|
|
+ g=1.0-g;
|
|
|
|
+ b=1.0-b;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void contrast()
|
|
|
|
+ {
|
|
|
|
+ r=::fmod(r+0.5,1.0);
|
|
|
|
+ g=::fmod(g+0.5,1.0);
|
|
|
|
+ b=::fmod(b+0.5,1.0);
|
|
|
|
+ }
|
|
|
|
+ Color inverted() const
|
|
|
|
+ {
|
|
|
|
+ Color c=*this;
|
|
|
|
+ c.invert();
|
|
|
|
+ return c;
|
|
|
|
+ }
|
|
|
|
+ Color contrasted() const
|
|
|
|
+ {
|
|
|
|
+ Color c=*this;
|
|
|
|
+ c.contrast();
|
|
|
|
+ return c;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Color linear_interpolate(const Color& p_b, float p_t) const {
|
|
|
|
+
|
|
|
|
+ Color res=*this;
|
|
|
|
+
|
|
|
|
+ res.r+= (p_t * (p_b.r-r));
|
|
|
|
+ res.g+= (p_t * (p_b.g-g));
|
|
|
|
+ res.b+= (p_t * (p_b.b-b));
|
|
|
|
+ res.a+= (p_t * (p_b.a-a));
|
|
|
|
+
|
|
|
|
+ return res;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Color blend(const Color& p_over) const {
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ Color res;
|
|
|
|
+ float sa = 1.0 - p_over.a;
|
|
|
|
+ res.a = a*sa+p_over.a;
|
|
|
|
+ if (res.a==0) {
|
|
|
|
+ return Color(0,0,0,0);
|
|
|
|
+ } else {
|
|
|
|
+ res.r = (r*a*sa + p_over.r * p_over.a)/res.a;
|
|
|
|
+ res.g = (g*a*sa + p_over.g * p_over.a)/res.a;
|
|
|
|
+ res.b = (b*a*sa + p_over.b * p_over.a)/res.a;
|
|
|
|
+ }
|
|
|
|
+ return res;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Color to_linear() const {
|
|
|
|
+
|
|
|
|
+ return Color(
|
|
|
|
+ r<0.04045 ? r * (1.0 / 12.92) : ::pow((r + 0.055) * (1.0 / (1 + 0.055)), 2.4),
|
|
|
|
+ g<0.04045 ? g * (1.0 / 12.92) : ::pow((g + 0.055) * (1.0 / (1 + 0.055)), 2.4),
|
|
|
|
+ b<0.04045 ? b * (1.0 / 12.92) : ::pow((b + 0.055) * (1.0 / (1 + 0.055)), 2.4),
|
|
|
|
+ a
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static Color hex(uint32_t p_hex)
|
|
|
|
+ {
|
|
|
|
+ float a = (p_hex&0xFF)/255.0;
|
|
|
|
+ p_hex>>=8;
|
|
|
|
+ float b = (p_hex&0xFF)/255.0;
|
|
|
|
+ p_hex>>=8;
|
|
|
|
+ float g = (p_hex&0xFF)/255.0;
|
|
|
|
+ p_hex>>=8;
|
|
|
|
+ float r = (p_hex&0xFF)/255.0;
|
|
|
|
+
|
|
|
|
+ return Color(r,g,b,a);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static Color html(const String& p_color)
|
|
|
|
+ {
|
|
|
|
+ String color = p_color;
|
|
|
|
+ if (color.length()==0)
|
|
|
|
+ return Color();
|
|
|
|
+ if (color[0]=='#')
|
|
|
|
+ color=color.substr(1,color.length()-1);
|
|
|
|
+
|
|
|
|
+ bool alpha=false;
|
|
|
|
+
|
|
|
|
+ if (color.length()==8) {
|
|
|
|
+ alpha=true;
|
|
|
|
+ } else if (color.length()==6) {
|
|
|
|
+ alpha=false;
|
|
|
|
+ } else {
|
|
|
|
+ // @Todo error reporting
|
|
|
|
+ // ERR_EXPLAIN("Invalid Color Code: "+p_color);
|
|
|
|
+ // ERR_FAIL_V(Color());
|
|
|
|
+ return Color();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int a=255;
|
|
|
|
+ if (alpha) {
|
|
|
|
+ a=_parse_col(color,0);
|
|
|
|
+ if (a<0) {
|
|
|
|
+ // @Todo error reporting
|
|
|
|
+ // ERR_EXPLAIN("Invalid Color Code: "+p_color);
|
|
|
|
+ // ERR_FAIL_V(Color());
|
|
|
|
+ return Color();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int from=alpha?2:0;
|
|
|
|
+
|
|
|
|
+ int r=_parse_col(color,from+0);
|
|
|
|
+ if (r<0) {
|
|
|
|
+ // @Todo error reporting
|
|
|
|
+ // ERR_EXPLAIN("Invalid Color Code: "+p_color);
|
|
|
|
+ // ERR_FAIL_V(Color());
|
|
|
|
+ return Color();
|
|
|
|
+ }
|
|
|
|
+ int g=_parse_col(color,from+2);
|
|
|
|
+ if (g<0) {
|
|
|
|
+ // @Todo error reporting
|
|
|
|
+ // ERR_EXPLAIN("Invalid Color Code: "+p_color);
|
|
|
|
+ // ERR_FAIL_V(Color());
|
|
|
|
+ return Color();
|
|
|
|
+ }
|
|
|
|
+ int b=_parse_col(color,from+4);
|
|
|
|
+ if (b<0) {
|
|
|
|
+ // @Todo error reporting
|
|
|
|
+ // ERR_EXPLAIN("Invalid Color Code: "+p_color);
|
|
|
|
+ // ERR_FAIL_V(Color());
|
|
|
|
+ return Color();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return Color(r/255.0,g/255.0,b/255.0,a/255.0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static bool html_is_valid(const String& p_color)
|
|
|
|
+ {
|
|
|
|
+ String color = p_color;
|
|
|
|
+
|
|
|
|
+ if (color.length()==0)
|
|
|
|
+ return false;
|
|
|
|
+ if (color[0]=='#')
|
|
|
|
+ color=color.substr(1,color.length()-1);
|
|
|
|
+
|
|
|
|
+ bool alpha=false;
|
|
|
|
+
|
|
|
|
+ if (color.length()==8) {
|
|
|
|
+ alpha=true;
|
|
|
|
+ } else if (color.length()==6) {
|
|
|
|
+ alpha=false;
|
|
|
|
+ } else {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int a=255;
|
|
|
|
+ if (alpha) {
|
|
|
|
+ a=_parse_col(color,0);
|
|
|
|
+ if (a<0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int from=alpha?2:0;
|
|
|
|
+
|
|
|
|
+ int r=_parse_col(color,from+0);
|
|
|
|
+ if (r<0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ int g=_parse_col(color,from+2);
|
|
|
|
+ if (g<0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ int b=_parse_col(color,from+4);
|
|
|
|
+ if (b<0) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+private:
|
|
|
|
+#ifndef CLAMP
|
|
|
|
+#define CLAMP(m_a,m_min,m_max) (((m_a)<(m_min))?(m_min):(((m_a)>(m_max))?m_max:m_a))
|
|
|
|
+#endif
|
|
|
|
+ static String _to_hex(float p_val) {
|
|
|
|
+
|
|
|
|
+ int v = p_val * 255;
|
|
|
|
+ v = CLAMP(v,0,255);
|
|
|
|
+ String ret;
|
|
|
|
+
|
|
|
|
+ for(int i=0;i<2;i++) {
|
|
|
|
+
|
|
|
|
+ wchar_t c[2]={0,0};
|
|
|
|
+ int lv = v&0xF;
|
|
|
|
+ if (lv<10)
|
|
|
|
+ c[0]='0'+lv;
|
|
|
|
+ else
|
|
|
|
+ c[0]='a'+lv-10;
|
|
|
|
+
|
|
|
|
+ v>>=4;
|
|
|
|
+ String cs=(const wchar_t*)c;
|
|
|
|
+ ret = cs + ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+public:
|
|
|
|
+
|
|
|
|
+ String to_html(bool p_alpha=true) const
|
|
|
|
+ {
|
|
|
|
+ String txt;
|
|
|
|
+ txt+=_to_hex(r);
|
|
|
|
+ txt+=_to_hex(g);
|
|
|
|
+ txt+=_to_hex(b);
|
|
|
|
+ if (p_alpha)
|
|
|
|
+ txt=_to_hex(a)+txt;
|
|
|
|
+ return txt;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bool operator<(const Color& p_color) const; //used in set keys
|
|
|
|
+ operator String() const
|
|
|
|
+ {
|
|
|
|
+ return String(); // @Todo
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * No construct parameters, r=0, g=0, b=0. a=255
|
|
|
|
+ */
|
|
|
|
+ Color() {
|
|
|
|
+ r=0; g=0; b=0; a=1.0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * RGB / RGBA construct parameters. Alpha is optional, but defaults to 1.0
|
|
|
|
+ */
|
|
|
|
+ Color(float p_r,float p_g,float p_b,float p_a=1.0) { r=p_r; g=p_g; b=p_b; a=p_a; }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+bool Color::operator<(const Color& p_color) const {
|
|
|
|
+
|
|
|
|
+ if (r==p_color.r) {
|
|
|
|
+ if (g==p_color.g) {
|
|
|
|
+ if(b==p_color.b) {
|
|
|
|
+ return (a<p_color.a);
|
|
|
|
+ } else
|
|
|
|
+ return (b<p_color.b);
|
|
|
|
+ } else
|
|
|
|
+ return g<p_color.g;
|
|
|
|
+ } else
|
|
|
|
+ return r<p_color.r;
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#endif // COLOR_H
|