1 /** 2 Color in RGBA format with some support for ARGB format. 3 4 Copyright: 5 Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. 6 Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) 7 Copyright (c) 2017-2018 Godot-D contributors 8 9 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License) 10 11 12 */ 13 module godot.core.color; 14 15 import godot.core.string; 16 17 import std.math; 18 import std.algorithm.comparison; 19 import core.stdc.stddef : wchar_t; 20 21 /** 22 A color is represented as red, green and blue (r,g,b) components. Additionally, “a” represents the alpha component, often used for transparency. Values are in floating point and usually range from 0 to 1. Some methods (such as set_modulate(color)) may accept values > 1. 23 */ 24 struct Color 25 { 26 @nogc nothrow: 27 28 union 29 { 30 struct 31 { 32 float r = 0f; 33 float g = 0f; 34 float b = 0f; 35 float a = 1f; 36 } 37 float[4] components; 38 } 39 40 this(float r, float g, float b, float a = 1f) 41 { 42 this.r = r; 43 this.g = g; 44 this.b = b; 45 this.a = a; 46 } 47 48 static float _parseCol(in string p_str, int p_ofs) 49 { 50 int ig=0; 51 52 for(int i=0;i<2;i++) 53 { 54 int c= cast(int) cast(wchar_t) p_str[i+p_ofs]; 55 int v=0; 56 57 if (c>='0' && c<='9') { 58 v=c-'0'; 59 } else if (c>='a' && c<='f') { 60 v=c-'a'; 61 v+=10; 62 } else if (c>='A' && c<='F') { 63 v=c-'A'; 64 v+=10; 65 } else { 66 return -1; 67 } 68 69 if (i==0) 70 ig+=v*16; 71 else 72 ig+=v; 73 } 74 75 return ig; 76 77 } 78 79 uint to32() const 80 { 81 82 uint c=cast(ubyte)(r*255); 83 c<<=8; 84 c|=cast(ubyte)(g*255); 85 c<<=8; 86 c|=cast(ubyte)(b*255); 87 c<<=8; 88 c|=cast(ubyte)(a*255); 89 90 return c; 91 } 92 93 uint toARGB32() const 94 { 95 uint c=cast(ubyte)(a*255); 96 c<<=8; 97 c|=cast(ubyte)(r*255); 98 c<<=8; 99 c|=cast(ubyte)(g*255); 100 c<<=8; 101 c|=cast(ubyte)(b*255); 102 103 return c; 104 } 105 106 float gray() const 107 { 108 return (r+g+b)/3.0; 109 } 110 111 float hue() const 112 { 113 114 float minv = min( r, g ); 115 minv = min( minv, b ); 116 float maxv = max( r, g ); 117 maxv = max( maxv, b ); 118 119 float delta = maxv - minv; 120 121 if( delta == 0 ) 122 return 0; 123 124 float h; 125 if( r == maxv ) 126 h = ( g - b ) / delta; // between yellow & magenta 127 else if( g == maxv ) 128 h = 2 + ( b - r ) / delta; // between cyan & yellow 129 else 130 h = 4 + ( r - g ) / delta; // between magenta & cyan 131 132 h/=6.0; 133 if (h<0) 134 h+=1.0; 135 136 return h; 137 } 138 139 float saturation() const 140 { 141 float minv = min( r, g ); 142 minv = min( minv, b ); 143 float maxv = max( r, g ); 144 maxv = max( maxv, b ); 145 float delta = maxv - minv; 146 return (maxv!=0) ? (delta / maxv) : 0; 147 148 } 149 150 float value() const 151 { 152 float maxv = max( r, g ); 153 maxv = max( maxv, b ); 154 return maxv; 155 } 156 157 void setHsv(float p_h, float p_s, float p_v, float p_alpha) 158 { 159 int i; 160 float f, p, q, t; 161 a=p_alpha; 162 163 if( p_s == 0 ) 164 { 165 // acp_hromatic (grey) 166 r = g = b = p_v; 167 return; 168 } 169 170 p_h *=6.0; 171 p_h = fmod(p_h,6); 172 i = cast(int)floor( p_h ); 173 174 f = p_h - i; 175 p = p_v * ( 1 - p_s ); 176 q = p_v * ( 1 - p_s * f ); 177 t = p_v * ( 1 - p_s * ( 1 - f ) ); 178 179 switch( i ) { 180 case 0: // Red is the dominant color 181 r = p_v; 182 g = t; 183 b = p; 184 break; 185 case 1: // Green is the dominant color 186 r = q; 187 g = p_v; 188 b = p; 189 break; 190 case 2: 191 r = p; 192 g = p_v; 193 b = t; 194 break; 195 case 3: // Blue is the dominant color 196 r = p; 197 g = q; 198 b = p_v; 199 break; 200 case 4: 201 r = t; 202 g = p; 203 b = p_v; 204 break; 205 default: // (5) Red is the dominant color 206 r = p_v; 207 g = p; 208 b = q; 209 break; 210 } 211 } 212 213 void invert() 214 { 215 r=1.0-r; 216 g=1.0-g; 217 b=1.0-b; 218 } 219 220 void contrast() 221 { 222 r=fmod(r+0.5,1.0); 223 g=fmod(g+0.5,1.0); 224 b=fmod(b+0.5,1.0); 225 } 226 Color inverted() const 227 { 228 Color c=this; 229 c.invert(); 230 return c; 231 } 232 Color contrasted() const 233 { 234 Color c=this; 235 c.contrast(); 236 return c; 237 } 238 239 Color linearInterpolate(in Color p_b, float p_t) const { 240 241 Color res=this; 242 243 res.r+= (p_t * (p_b.r-r)); 244 res.g+= (p_t * (p_b.g-g)); 245 res.b+= (p_t * (p_b.b-b)); 246 res.a+= (p_t * (p_b.a-a)); 247 248 return res; 249 } 250 alias lerp = linearInterpolate; 251 252 Color blend(in Color p_over) const { 253 254 255 Color res; 256 float sa = 1.0 - p_over.a; 257 res.a = a*sa+p_over.a; 258 if (res.a==0) 259 { 260 return Color(0,0,0,0); 261 } 262 else 263 { 264 res.r = (r*a*sa + p_over.r * p_over.a)/res.a; 265 res.g = (g*a*sa + p_over.g * p_over.a)/res.a; 266 res.b = (b*a*sa + p_over.b * p_over.a)/res.a; 267 } 268 return res; 269 } 270 271 Color toLinear() const 272 { 273 return Color( 274 r<0.04045 ? r * (1.0 / 12.92) : pow((r + 0.055) * (1.0 / (1 + 0.055)), 2.4), 275 g<0.04045 ? g * (1.0 / 12.92) : pow((g + 0.055) * (1.0 / (1 + 0.055)), 2.4), 276 b<0.04045 ? b * (1.0 / 12.92) : pow((b + 0.055) * (1.0 / (1 + 0.055)), 2.4), 277 a 278 ); 279 } 280 281 Color hex(uint p_hex) 282 { 283 float a = (p_hex&0xFF)/255.0; 284 p_hex>>=8; 285 float b = (p_hex&0xFF)/255.0; 286 p_hex>>=8; 287 float g = (p_hex&0xFF)/255.0; 288 p_hex>>=8; 289 float r = (p_hex&0xFF)/255.0; 290 291 return Color(r,g,b,a); 292 } 293 294 /+Color html(in ref String color) 295 { 296 if (color.length()==0) 297 return Color(); 298 if (color[0]=='#') 299 color=color.substr(1,color.length()-1); 300 301 bool alpha=false; 302 303 if (color.length()==8) { 304 alpha=true; 305 } else if (color.length()==6) { 306 alpha=false; 307 } else { 308 ERR_PRINT(String("Invalid Color Code: ") + p_color); 309 ERR_FAIL_V(Color()); 310 } 311 312 int a=255; 313 if (alpha) { 314 a=_parse_col(color,0); 315 if (a<0) { 316 ERR_PRINT("Invalid Color Code: "+p_color); 317 ERR_FAIL_V(Color()); 318 } 319 } 320 321 int from=alpha?2:0; 322 323 int r=_parse_col(color,from+0); 324 if (r<0) { 325 ERR_PRINT("Invalid Color Code: "+p_color); 326 ERR_FAIL_V(Color()); 327 } 328 int g=_parse_col(color,from+2); 329 if (g<0) { 330 ERR_PRINT("Invalid Color Code: "+p_color); 331 ERR_FAIL_V(Color()); 332 } 333 int b=_parse_col(color,from+4); 334 if (b<0) { 335 ERR_PRINT("Invalid Color Code: "+p_color); 336 ERR_FAIL_V(Color()); 337 } 338 339 return Color(r/255.0,g/255.0,b/255.0,a/255.0); 340 } 341 342 bool html_is_valid(const String& p_color) 343 { 344 String color = p_color; 345 346 if (color.length()==0) 347 return false; 348 if (color[0]=='#') 349 color=color.substr(1,color.length()-1); 350 351 bool alpha=false; 352 353 if (color.length()==8) { 354 alpha=true; 355 } else if (color.length()==6) { 356 alpha=false; 357 } else { 358 return false; 359 } 360 361 int a=255; 362 if (alpha) { 363 a=_parse_col(color,0); 364 if (a<0) { 365 return false; 366 } 367 } 368 369 int from=alpha?2:0; 370 371 int r=_parse_col(color,from+0); 372 if (r<0) { 373 return false; 374 } 375 int g=_parse_col(color,from+2); 376 if (g<0) { 377 return false; 378 } 379 int b=_parse_col(color,from+4); 380 if (b<0) { 381 return false; 382 } 383 384 return true; 385 }+/ 386 387 private static char[2] _toHex(float p_val) 388 { 389 char[2] ret; 390 391 int v = cast(int)(p_val * 255); 392 v = clamp(v,0,255); 393 394 foreach(int i; 0..2) 395 { 396 int lv = cast(char)(v&0xF); 397 if (lv<10) 398 ret[0]=cast(char)('0'+lv); 399 else 400 ret[0]=cast(char)('a'+lv-10); 401 402 v>>=4; 403 } 404 405 return ret; 406 } 407 408 char[8] toHtml() const 409 { 410 char[8] ret; 411 ret[0..2] = _toHex(r); 412 ret[2..4] = _toHex(g); 413 ret[4..6] = _toHex(b); 414 ret[6..8] = _toHex(a); 415 return ret; 416 } 417 418 419 /+bool operator<(const Color& p_color) const { 420 421 if (r==p_color.r) { 422 if (g==p_color.g) { 423 if(b==p_color.b) { 424 return (a<p_color.a); 425 } else 426 return (b<p_color.b); 427 } else 428 return g<p_color.g; 429 } else 430 return r<p_color.r; 431 432 }+/ 433 } 434