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