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