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