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