1 /** 
2 Module containing various color related utilities
3  */
4 module sily.color;
5 
6 import std.algorithm : canFind;
7 import std.algorithm.comparison;
8 import std.algorithm.comparison: compclamp = clamp;
9 import std.array : replace;
10 import std.conv;
11 import std.math;
12 import std.regex;
13 import std.stdio;
14 import std.string;
15 import std.uni : toLower;
16 import std.traits: isNumeric;
17 
18 import sily.vector;
19 import sily.meta.swizzle;
20 import sily.array;
21 
22 /// GLSL style alias to Color
23 alias col = Color;
24 
25 /// Constructs color from 8 bit (0-255) components
26 alias Color8 = (float R, float G, float B, float A = 255) => Color(R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f);
27 
28 /// Color structure with data accesible with `[N]` or swizzling
29 struct Color {
30     /// Color data
31     public float[4] data = [ 1.0f ];
32 
33     /// Alias to allow easy `data` access
34     alias data this;
35     /// Alias to data
36     alias arrayof = data;
37 
38     /** 
39     Constructs Color from float components. If no components present
40     color will default to white ([1, 1, 1, 1])
41     Example:
42     ---
43     // You can explicitly define alpha or omit it
44     Color red = Color(1, 0, 0);
45     Color reda = Color(1, 0, 0, 1); 
46     // If presented with one or two components
47     // color will fill rgb portion of itself
48     // with only this component
49     // You can also omit/define alpha with it
50     Color gray = Color(0.5); 
51     Color graya = Color(0.5, 0.8);
52     // Also theres two aliases to help you
53     // GLSL style color as type
54     col c = col(0.2, 0.4, 1)
55     // And custom constructor that allows you
56     // to use 8 bit values (0-255)
57     Color webCol = Color8(255, 0, 255);
58     // Colors can be accessed with array slicing,
59     // by using color symbols or swizzling (rgba)
60     float rcomp = c.r;
61     float gcomp = c[1];
62     float bcomp = c.b;
63     float[] redblue = c.rb;
64     float[] redbluebluegreen = c.rbbg;
65     ---
66     */
67     this(in float val) {
68         foreach (i; 0 .. 3) { data[i] = val; }
69         data[3] = 1.0f;
70     }
71     /// Ditto
72     this(in float val, in float a) {
73         foreach (i; 0 .. 3) { data[i] = val; }
74         data[3] = a;
75     }
76     /// Ditto
77     this(in float[3] vals...) {
78         data = vals ~ [1.0f];
79     }
80     /// Ditto
81     this(in float[4] vals...) {
82         data = vals;
83     }
84 
85     /// Returns color component in 8 bit form
86     ubyte r8() { return cast(ubyte) (data[0] * 255u); }
87     /// Ditto
88     ubyte g8() { return cast(ubyte) (data[1] * 255u); }
89     /// Ditto
90     ubyte b8() { return cast(ubyte) (data[2] * 255u); }
91     /// Ditto
92     ubyte a8() { return cast(ubyte) (data[3] * 255u); }
93 
94     /* -------------------------------------------------------------------------- */
95     /*                         UNARY OPERATIONS OVERRIDES                         */
96     /* -------------------------------------------------------------------------- */
97     
98     /// opBinary x [+, -, *, /, %] y
99     auto opBinary(string op, R)(in Color!(R, N) b) const if ( isNumeric!R ) {
100         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
101         VecType ret = VecType();
102         foreach (i; 0 .. 4) { mixin( "ret.data[i] = data[i] " ~ op ~ " b.data[i];" ); }
103         return ret;
104     }
105 
106     /// Ditto
107     auto opBinaryRight(string op, R)(in Color!(R, N) b) const if ( isNumeric!R ) {
108         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
109         VecType ret = VecType();
110         foreach (i; 0 .. 4) { mixin( "ret[i] = b.data[i] " ~ op ~ " data[i];" ); }
111         return ret;
112     }
113 
114     /// Ditto
115     auto opBinary(string op, R)(in R b) const if ( isNumeric!R ) {
116         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
117         VecType ret = VecType();
118         foreach (i; 0 .. 4) { mixin( "ret.data[i] = data[i] " ~ op ~ " b;" ); }
119         return ret;
120     }
121 
122     /// Ditto
123     auto opBinaryRight(string op, R)(in R b) const if ( isNumeric!R ) {
124         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
125         VecType ret = VecType();
126         foreach (i; 0 .. 4) { mixin( "ret[i] = b " ~ op ~ " data[i];" ); }
127         return ret;
128     }
129 
130     /// opEquals x == y
131     bool opEquals(R)(in Color!(R, 4) b) const if ( isNumeric!R ) {
132         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
133         bool eq = true;
134         foreach (i; 0 .. 4) { eq = eq && data[i] == b.data[i]; }
135         return eq;
136     }
137 
138     /// opCmp x [< > <= >=] y
139     int opCmp(R)(in Color!(R, N) b) const if ( isNumeric!R ) {
140         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
141         float al = length;
142         float bl = b.length;
143         if (al == bl) return 0;
144         if (al < bl) return -1;
145         return 1;
146     }
147 
148     /// opUnary [-, +, --, ++] x
149     auto opUnary(string op)() if(op == "-"){
150         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
151         VecType ret = VecType();
152         if (op == "-")
153             foreach (i; 0 .. 4) { ret.data[i] = -data[i]; }
154         return ret;
155     }
156     
157     /// opOpAssign x [+, -, *, /, %]= y
158     auto opOpAssign(string op, R)( in Color!(R, N) b ) if ( isNumeric!R ) { 
159         // assert(/* this !is null && */ b !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
160         foreach (i; 0 .. 4) { mixin( "data[i] = data[i] " ~ op ~ " b.data[i];" ); }
161         return this;
162     }
163     
164     /// Ditto
165     auto opOpAssign(string op, R)( in R b ) if ( isNumeric!R ) { 
166         // assert(this !is null, "\nOP::ERROR nullptr Color!" ~ 4.to!string ~ ".");
167         foreach (i; 0 .. 4) { mixin( "data[i] = data[i] " ~ op ~ " b;" ); }
168         return this;
169     }
170 
171     /// Returns hash 
172     size_t toHash() const @safe nothrow {
173         return typeid(data).getHash(&data);
174     }
175     
176     // incredible magic from sily.meta
177     // idk how it works but it works awesome
178     // and im not going to touch it at all
179     enum AccessString = "r g b a"; // exclude from docs
180     mixin accessByString!(float, 4, "data", AccessString); // exclude from docs
181     
182     /** 
183     Returns color transformed to float vector.
184     Also direct assign syntax is allowed:
185     ---
186     // Assigns rgba values
187     Vector!(float, 4) v4 = Color(0.4, 1.0);
188     // Only rgb values
189     Vector!(float, 3) v3 = Color(0.7);
190     ---
191     */
192     public Vector4f asVector4f() {
193         return Vector4f(data);
194     }
195     /// Ditto
196     public Vector3f asVector3f() {
197         return Vector3f(data[0..3]);
198     }
199 
200     /// Returns copy of color
201     public Color copyof() {
202         return Color(data);
203     }
204 
205     /// Returns string representation of color: `[1.00, 1.00, 1.00, 1.00]`
206     public string toString() const {
207         import std.conv : to;
208         string s;
209         s ~= "[";
210         foreach (i; 0 .. 4) {
211             s ~= format("%.2f", data[i]);
212             if (i != 4 - 1) s ~= ", ";
213         }
214         s ~= "]";
215         return s;
216     }
217 
218     /// Returns pointer to data
219     float* ptr() return {
220         return data.ptr;
221     }
222 
223     /* -------------------------------------------------------------------------- */
224     /*                                 PROPERTIES                                 */
225     /* -------------------------------------------------------------------------- */
226 
227     /// Inverts color
228     public void invert() {
229         data[0] = 1.0f - data[0];
230         data[1] = 1.0f - data[1];
231         data[2] = 1.0f - data[2];
232         data[3] = 1.0f - data[3];
233     }
234 
235     /// Returns the luminance of the color in the [0.0, 1.0] range
236     public float luminance() {
237         return compclamp(0.2126f * data[0] + 0.7152f * data[1] + 0.0722f * data[2], 0.0f, 1.0f);
238     } 
239 
240     /** 
241     Returns the linear interpolation with another color
242     Params:
243       p_to = Color to interpolate with
244       p_weight = Interpolation factor in [0.0, 1.0] range
245     */
246 	public void lerp(const Color p_to, float p_weight) {
247 		data[0] += (p_weight * (p_to.data[0] - data[0]));
248 		data[1] += (p_weight * (p_to.data[1] - data[1]));
249 		data[2] += (p_weight * (p_to.data[2] - data[2]));
250 		data[3] += (p_weight * (p_to.data[3] - data[3]));
251 	} 
252 
253     /** 
254     Darkens color by `p_amount`
255     Params: 
256       p_amount = Amount to darken
257     */
258 	public void darken(float p_amount) {
259 		data[0] = data[0] * (1.0f - p_amount);
260 		data[1] = data[1] * (1.0f - p_amount);
261 		data[2] = data[2] * (1.0f - p_amount);
262 	}
263 
264     /** 
265     Lightens color by `p_amount`
266     Params: 
267       p_amount = Amount to lighten
268     */
269 	public void lighten(float p_amount) {
270 		data[0] = data[0] + (1.0f - data[0]) * p_amount;
271 		data[1] = data[1] + (1.0f - data[1]) * p_amount;
272 		data[2] = data[2] + (1.0f - data[2]) * p_amount;
273 	}
274 
275     /** 
276     Clamps color values between `min` and `max`
277     Params: 
278       min = Minimal allowed value
279       max = Maximal allowed value
280     */
281     void clamp(float min = 0.0f, float max = 1.0f) {
282         data[0] = compclamp(data[0], min, max);
283         data[1] = compclamp(data[1], min, max);
284         data[2] = compclamp(data[2], min, max);
285         data[3] = compclamp(data[3], min, max);
286     }
287 
288     /* -------------------------------------------------------------------------- */
289     /*                                    HTML                                    */
290     /* -------------------------------------------------------------------------- */
291 
292     private uint tocolbit(uint c_bits, int[4] c_order ...) {
293         uint c_size = 2.pow(c_bits) - 1;
294         uint c_shift = c_bits;
295         uint c = (data[c_order[0]] * c_size).round.to!uint;
296         c <<= c_shift;
297         c |= (data[c_order[1]] * c_size).round.to!uint;
298         c <<= c_shift;
299         c |= (data[c_order[2]] * c_size).round.to!uint;
300         c <<= c_shift;
301         c |= (data[c_order[3]] * c_size).round.to!uint;
302         return c;
303     }
304 
305     // public uint toargb32() { return tocolbit(8, 3, 0, 1, 2); }
306     // public uint toabgr32() { return tocolbit(8, 3, 2, 1, 0); }
307     // public uint torgba32() { return tocolbit(8, 0, 1, 2, 3); }
308     // public uint toargb64() { return tocolbit(16, 3, 0, 1, 2); }
309     // public uint toabgr64() { return tocolbit(16, 3, 2, 1, 0); }
310     // public uint torgba64() { return tocolbit(16, 0, 1, 2, 3); }
311 
312     private string _to_hex(float p_val) const {
313         int v = (p_val * 255).round.to!int;
314         v = v.clamp(0, 255);
315         string ret;
316 
317         for (int i = 0; i < 2; i++) {
318             char[2] c = [ 0, 0 ];
319             int lv = v & 0xF;
320             if (lv < 10) {
321                 c[0] = ('0' + lv).to!char;
322             } else {
323                 c[0] = ('a' + lv - 10).to!char;
324             }
325 
326             v >>= 4;
327             string cs = c.to!string;
328             ret = cs ~ ret;
329         }
330 
331         return ret;
332     }
333 
334     /** 
335     Returns html representation of color in format `#RRGGBB`
336     If `p_alpha` is true returns color in format `#RRGGBBAA`
337     Params:
338       p_alpha = Include alpha?
339     Returns: Html string
340     */
341     public string toHtml(bool p_alpha = false) const {
342         string txt;
343         txt ~= _to_hex(data[0]);
344         txt ~= _to_hex(data[1]);
345         txt ~= _to_hex(data[2]);
346         if (p_alpha) {
347             txt ~= _to_hex(data[3]);
348         }
349         return txt;
350     }
351 
352     /** 
353     Returns hex representation of color in format `0xrrggbb`
354     Returns: uint hex
355     */
356     public uint toHex() {
357         int r = rint(data[0] * 255);
358         int g = rint(data[1] * 255);
359         int b = rint(data[2] * 255);
360         return ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff);
361     }
362 
363     /* -------------------------------------------------------------------------- */
364     /*                                   SETTERS                                  */
365     /* -------------------------------------------------------------------------- */
366 
367     private void setHex(uint p_hex, uint c_mask, uint c_bits, bool c_hasAlpha) {
368         float c_size = (2.pow(c_bits) - 1).to!float;
369         uint c_shift = c_bits;
370         float a = 0;
371         if (c_hasAlpha) {
372             a = (p_hex & c_mask) / c_size;
373             p_hex >>= c_shift;
374         }
375         float b = (p_hex & c_mask) / c_size;
376         p_hex >>= c_shift;
377         float g = (p_hex & c_mask) / c_size;
378         p_hex >>= c_shift;
379         float r = (p_hex & c_mask) / c_size;
380 
381         data = [r, g, b, a];
382     }
383 
384     /** 
385     Sets color to hexadecimal value
386     Params:
387       p_hex = uint hex value to set color to
388       p_hasAlpha = Does p_hex include alpha
389     */
390     public void setHex(uint p_hex, bool p_hasAlpha = false) { setHex(p_hex, 0xFF, 8, p_hasAlpha); }
391     // public void setHex64(uint p_hex, bool p_hasAlpha = false) { setHex(p_hex, 0xFFFF, 16, p_hasAlpha); }
392 
393     /** 
394     Sets color from hsv
395     Params:
396       p_h = hue
397       p_s = saturation
398       p_v = value
399       p_alpha = alpha
400     */
401     public void setHsv(float p_h, float p_s, float p_v, float p_alpha = 1.0f) {
402         int i;
403         float f, p, q, t;
404         data[3] = p_alpha;
405 
406         if (p_s == 0) {
407             // Achromatic (grey)
408             data[0] = data[1] = data[2] = p_v;
409             return;
410         }
411 
412         p_h *= 6.0f;
413         p_h = p_h.fmod(6);
414         i = p_h.floor().to!int;
415 
416         f = p_h - i;
417         p = p_v * (1 - p_s);
418         q = p_v * (1 - p_s * f);
419         t = p_v * (1 - p_s * (1 - f));
420 
421         switch (i) {
422             case 0: // Red is the dominant color
423                 data[0] = p_v;
424                 data[1] = t;
425                 data[2] = p;
426                 break;
427             case 1: // Green is the dominant color
428                 data[0] = q;
429                 data[1] = p_v;
430                 data[2] = p;
431                 break;
432             case 2:
433                 data[0] = p;
434                 data[1] = p_v;
435                 data[2] = t;
436                 break;
437             case 3: // Blue is the dominant color
438                 data[0] = p;
439                 data[1] = q;
440                 data[2] = p_v;
441                 break;
442             case 4:
443                 data[0] = t;
444                 data[1] = p;
445                 data[2] = p_v;
446                 break;
447             default: // (5) Red is the dominant color
448                 data[0] = p_v;
449                 data[1] = p;
450                 data[2] = q;
451                 break;
452         }
453     }
454 
455     /** 
456     Sets color from html string in format `#RRGGBB`
457     Params:
458       html = Color string
459     */
460     public void setHtml(string html) {
461         auto rg = regex(r"/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i");
462         auto m = matchAll(html, rg);
463         m.popFront();
464         float _r = to!int(m.front.hit, 16) / 255; m.popFront();
465         float _g = to!int(m.front.hit, 16) / 255; m.popFront();
466         float _b = to!int(m.front.hit, 16) / 255; m.popFront();
467         data = [to!float(_r), to!float(_g), to!float(_b), 1.0f];
468     }
469 
470     /* -------------------------------------------------------------------------- */
471     /*                               HSV PROPERTIES                               */
472     /* -------------------------------------------------------------------------- */
473 
474     /// Returns `h` component of hsv
475     public float hue() const {
476         float cmin = data[0].min(data[1]);
477         cmin = cmin.min(data[2]);
478         float cmax = data[0].max(data[1]);
479         cmax = cmax.min(data[2]);
480 
481         float delta = cmax - cmin;
482         if (delta == 0) { return 0; }
483 
484         float h;
485         if (data[0] == cmax) {
486             h = (data[1] - data[2]) / delta;
487         } else
488         if (data[1] == cmax) {
489             h = 2 + (data[2] - data[0]) / delta;
490         } else {
491             h = 4 + (data[2] - data[0]) / delta;
492         }
493 
494         h /= 6.0f;
495         if (h < 0) {
496             h += 1.0f;
497         }
498         return h;
499     }
500 
501     /// Returns `s` component of hsv
502     public float saturation() const {
503         float cmin = data[0].min(data[1]);
504         cmin = cmin.min(data[2]);
505         float cmax = data[0].max(data[1]);
506         cmax = cmax.min(data[2]);
507 
508         float delta = cmax - cmin;
509 
510         return (cmax != 0) ? (delta / cmax) : 0;
511     }
512 
513     /// Returns `v` component of hsv
514     public float value() const {
515         float cmax = data[0].max(data[1]);
516         cmax = cmax.max(data[2]);
517         return cmax;
518     }
519 
520     /* -------------------------------------------------------------------------- */
521     /*                                BASH GETTERS                                */
522     /* -------------------------------------------------------------------------- */
523 
524     /** 
525     Sets color from ANSI index
526     Params:
527       ansi = Color index
528     */
529     void setAnsi8(int ansi) {
530         if (ansi > 100) ansi -= 92;
531         if (ansi > 90) ansi -= 82;
532         if (ansi > 40) ansi -= 40;
533         if (ansi > 30) ansi -= 30;
534         if (ansi >= lowHEX.length) ansi = lowHEX.length.to!int - 1;
535         if (ansi < 0) ansi = 0;
536         // write(ansi);
537         setHex(lowHEX[ansi]);
538     }
539 
540     /** 
541     Sets color from ANSI256 index
542     Params:
543       ansi = Color index
544     */
545     void setAnsi(int ansi) {
546         if (ansi < 0 || ansi > 255) {
547             data = [0, 0, 0, 0];
548             return;
549         }
550         if (ansi < 16) {
551             setHex(Color.lowHEX[ansi]);
552             return;
553         }
554 
555         if (ansi > 231) {
556             const int s = (ansi - 232) * 10 + 8;
557             data = [s / 255.0f, s / 255.0f, s / 255.0f, 1];
558             return;
559         }
560 
561         const int n = ansi - 16;
562         int _b = n % 6;
563         int _g = (n - _b) / 6 % 6;
564         int _r = (n - _b - _g * 6) / 36 % 6;
565         _b = _b ? _b * 40 + 55 : 0;
566         _r = _r ? _r * 40 + 55 : 0;
567         _g = _g ? _g * 40 + 55 : 0;
568 
569         data = [_r / 255.0, _g / 255.0, _b / 255.0, 1];
570     }
571 
572     /* -------------------------------------------------------------------------- */
573     /*                                BASH SETTERS                                */
574     /* -------------------------------------------------------------------------- */
575     
576     private alias rint = (a) => to!int(round(a));
577 
578 
579     private float colorDistance(Color e1, Color e2) {
580         int rmean = ( cast(int) e1.r + cast(int) e2.r ) / 2;
581         int r = cast(int) e1.r - cast(int) e2.r;
582         int g = cast(int) e1.g - cast(int) e2.g;
583         int b = cast(int) e1.b - cast(int) e2.b;
584         // return sqrt(
585         //     (((512 + rmean) * r * r) >> 8) + 
586         //     4.0f * g * g + 
587         //     (((767 - rmean) * b * b) >> 8)
588         //     );
589         return sqrt((2 + rmean / 255) * r * r + 4 * g * g + (2 + (255 - rmean) / 255) * b * b + 0.0f);
590     }
591     
592     /** 
593     Returns closest ANSI8 color index 
594     Params:
595       isBackground = Is color a background
596     Returns: ANSI8 color
597     */
598     int toAnsi8(bool isBackground = false) {
599         /*
600         39 - default foreground, 49 - default backgound
601         Main colors are from 30 - 39, light variant is 90 - 97.
602         97 is white, 30 is black. to get background - add 10 to color code
603         goes like:
604         black, red, green, yellow, blue, magenta, cyan, lgray
605         then repeat with lighter variation
606         */
607 
608         int ri = rint(data[0] * 255);
609         int gi = rint(data[1] * 255);
610         int bi = rint(data[2] * 255);
611 
612         if (ri / 64 == gi / 64 && gi / 64 == bi / 64) {
613             // 0 128 192 255
614             if (ri <= 64) return 30 + (isBackground ? 10 : 0);
615             if (ri <= 160) return 90 + (isBackground ? 10 : 0);
616             if (ri <= 224) return 37 + (isBackground ? 10 : 0);
617             if (ri <= 255) return 97 + (isBackground ? 10 : 0);
618         }
619         
620         // int diff = 255 * 3;
621         float diff = 255 * 3;
622         int pos = 0;
623         // writeln(lowHEX[15] - hex);
624         for (int i = 0; i < 16; i ++) {
625             if (i == 0 || i == 7 || i == 8 || i == 15) continue;
626             int rh = lowRGB[i * 3];
627             int gh = lowRGB[i * 3 + 1];
628             int bh = lowRGB[i * 3 + 2];
629             // int rd = abs(rh - ri);
630             // int gd = abs(gh - gi);
631             // int bd = abs(bh - bi);
632             // int udiff = rd + gd + bd;
633             float udiff = colorDistance(col(ri, gi, bi), col(rh, gh, bh));
634             if (udiff < diff) {
635                 diff = udiff;
636                 pos = i;
637             }
638         }
639         // write(pos);
640 
641         int ansi = pos + 30;
642         if (pos >= 8) ansi += 60 - 8;
643         return ansi + (isBackground ? 10 : 0);
644     }
645 
646     /** 
647     Returns closest ANSI256 color index
648     */
649     int toAnsi() {
650         /*
651         256 ANSI color coding is:
652         0 - 14 Special colors, probably check by hand
653         goes like:
654         black, red, green, yellow, blue, magenta, cyan, lgray
655         then repeat with lighter variation
656         16 - 231 RGB colors with color coding like this:
657         Pure R component is on 16, 52, 88, 124, 160, 196. Aka map(r, comp)
658         B component is r +0..5
659         G component is rb +0,6,12,18,24,30 (but not 36 coz it's next red)
660         in end rgb coding, considering mcol = floor(col*5)
661         rgbansi = 16 + (16 * r) + (6 * g) + b;
662         232 - 255 Grayscale from dark to light
663         refer to https://misc.flogisoft.com/_media/bash/colors_format/256-colors.sh-v2.png
664         */
665 
666         float r = data[0];
667         float g = data[1];
668         float b = data[2];
669 
670         int ri = rint(data[0] * 255);
671         int gi = rint(data[1] * 255);
672         int bi = rint(data[2] * 255);
673 
674         if (ri / 16 == gi / 16 && gi / 16 == bi / 16) {
675             // handles grayscale
676             if (ri < 8) {
677                 return 16;
678             }
679 
680             if (ri > 248) {
681                 return 231;
682             }
683 
684             return rint(((ri - 8.0) / 247.0) * 24.0) + 232;
685         }
686 
687         int ansi = 16
688             + ( 36 * rint(r * 5)) 
689             + ( 6 *  rint(g * 5)) 
690             +        rint(b * 5);
691 
692         return ansi;
693     }
694 
695     /** 
696     Returns closest bash ANSI8 color string 
697     Params:
698       isBackground = Is color a background
699     Returns: Bash ANSI8 color string
700     */
701     string toAnsi8String(bool isBackground = false) {
702         return "\033[" ~ toAnsi8(isBackground).to!string ~ "m";
703     }
704 
705     /** 
706     Returns closest bash ANSI256 color string 
707     Params:
708       isBackground = Is color a background
709     Returns: Bash ANSI256 color string
710     */
711     string toAnsiString(bool isBackground = false) {
712         return (isBackground ? "\033[48;5;" : "\033[38;5;") ~ 
713             toAnsi().to!string ~ "m";
714     }
715 
716     /** 
717     Returns bash truecolor string
718     Params:
719       isBackground = Is color a background
720     Returns: Bash truecolor string
721     */
722     string toTrueColorString(bool isBackground = false) {
723         return (isBackground ? "\033[48;2;" : "\033[38;2;") ~ 
724             rint(data[0] * 255).to!string ~ ";" ~ 
725             rint(data[1] * 255).to!string ~ ";"  ~ 
726             rint(data[2] * 255).to!string  ~ "m";
727         
728     }
729 
730 
731     // TODO add colours from godot
732     // LINK https://github.com/godotengine/godot/blob/master/core/math/color.cpp
733     
734     private static const uint[] lowHEX = [
735     0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0,
736     0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff
737     ];
738 
739     private static const ushort[] lowRGB = [
740         0, 0, 0,  128, 0, 0,  0, 128, 0,  128, 128, 0,  0, 0, 128,  128, 0, 128,  0, 128, 128,  192, 192, 192,
741         128, 128, 128,  255, 0, 0,  0, 255, 0,  255, 255, 0,  0, 0, 255,  255, 0, 255,  0, 255, 255,  255, 255, 255
742     ];
743 }
744 
745 /// Enum containing most common web colors
746 enum Colors: Color {
747     aliceBlue            = Color8(240,248,255), /// <font color=aliceBlue>&#x25FC;</font>
748     antiqueWhite         = Color8(250,235,215), /// <font color=antiqueWhite>&#x25FC;</font>
749     aqua                 = Color8(0,255,255),   /// <font color=aqua>&#x25FC;</font>
750     aquamarine           = Color8(127,255,212), /// <font color=aquamarine>&#x25FC;</font>
751     azure                = Color8(240,255,255), /// <font color=azure>&#x25FC;</font>
752     beige                = Color8(245,245,220), /// <font color=beige>&#x25FC;</font>
753     bisque               = Color8(255,228,196), /// <font color=bisque>&#x25FC;</font>
754     black                = Color8(0,0,0),       /// <font color=black>&#x25FC;</font>
755     blanchedAlmond       = Color8(255,235,205), /// <font color=blanchedAlmond>&#x25FC;</font>
756     blue                 = Color8(0,0,255),     /// <font color=blue>&#x25FC;</font>
757     blueViolet           = Color8(138,43,226),  /// <font color=blueViolet>&#x25FC;</font>
758     brown                = Color8(165,42,42),   /// <font color=brown>&#x25FC;</font>
759     burlyWood            = Color8(222,184,135), /// <font color=burlyWood>&#x25FC;</font>
760     cadetBlue            = Color8(95,158,160),  /// <font color=cadetBlue>&#x25FC;</font>
761     chartreuse           = Color8(127,255,0),   /// <font color=chartreuse>&#x25FC;</font>
762     chocolate            = Color8(210,105,30),  /// <font color=chocolate>&#x25FC;</font>
763     coral                = Color8(255,127,80),  /// <font color=coral>&#x25FC;</font>
764     cornflowerBlue       = Color8(100,149,237), /// <font color=cornflowerBlue>&#x25FC;</font>
765     cornsilk             = Color8(255,248,220), /// <font color=cornsilk>&#x25FC;</font>
766     crimson              = Color8(220,20,60),   /// <font color=crimson>&#x25FC;</font>
767     cyan                 = Color8(0,255,255),   /// <font color=cyan>&#x25FC;</font>
768     darkBlue             = Color8(0,0,139),     /// <font color=darkBlue>&#x25FC;</font>
769     darkCyan             = Color8(0,139,139),   /// <font color=darkCyan>&#x25FC;</font>
770     darkGoldenrod        = Color8(184,134,11),  /// <font color=darkGoldenrod>&#x25FC;</font>
771     darkGray             = Color8(169,169,169), /// <font color=darkGray>&#x25FC;</font>
772     darkGrey             = Color8(169,169,169), /// <font color=darkGrey>&#x25FC;</font>
773     darkGreen            = Color8(0,100,0),     /// <font color=darkGreen>&#x25FC;</font>
774     darkKhaki            = Color8(189,183,107), /// <font color=darkKhaki>&#x25FC;</font>
775     darkMagenta          = Color8(139,0,139),   /// <font color=darkMagenta>&#x25FC;</font>
776     darkOliveGreen       = Color8(85,107,47),   /// <font color=darkOliveGreen>&#x25FC;</font>
777     darkOrange           = Color8(255,140,0),   /// <font color=darkOrange>&#x25FC;</font>
778     darkOrchid           = Color8(153,50,204),  /// <font color=darkOrchid>&#x25FC;</font>
779     darkRed              = Color8(139,0,0),     /// <font color=darkRed>&#x25FC;</font>
780     darkSalmon           = Color8(233,150,122), /// <font color=darkSalmon>&#x25FC;</font>
781     darkSeaGreen         = Color8(143,188,143), /// <font color=darkSeaGreen>&#x25FC;</font>
782     darkSlateBlue        = Color8(72,61,139),   /// <font color=darkSlateBlue>&#x25FC;</font>
783     darkSlateGray        = Color8(47,79,79),    /// <font color=darkSlateGray>&#x25FC;</font>
784     darkSlateGrey        = Color8(47,79,79),    /// <font color=darkSlateGrey>&#x25FC;</font>
785     darkTurquoise        = Color8(0,206,209),   /// <font color=darkTurquoise>&#x25FC;</font>
786     darkViolet           = Color8(148,0,211),   /// <font color=darkViolet>&#x25FC;</font>
787     deepPink             = Color8(255,20,147),  /// <font color=deepPink>&#x25FC;</font>
788     deepSkyBlue          = Color8(0,191,255),   /// <font color=deepSkyBlue>&#x25FC;</font>
789     dimGray              = Color8(105,105,105), /// <font color=dimGray>&#x25FC;</font>
790     dimGrey              = Color8(105,105,105), /// <font color=dimGrey>&#x25FC;</font>
791     dodgerBlue           = Color8(30,144,255),  /// <font color=dodgerBlue>&#x25FC;</font>
792     fireBrick            = Color8(178,34,34),   /// <font color=fireBrick>&#x25FC;</font>
793     floralWhite          = Color8(255,250,240), /// <font color=floralWhite>&#x25FC;</font>
794     forestGreen          = Color8(34,139,34),   /// <font color=forestGreen>&#x25FC;</font>
795     fuchsia              = Color8(255,0,255),   /// <font color=fuchsia>&#x25FC;</font>
796     gainsboro            = Color8(220,220,220), /// <font color=gainsboro>&#x25FC;</font>
797     ghostWhite           = Color8(248,248,255), /// <font color=ghostWhite>&#x25FC;</font>
798     gold                 = Color8(255,215,0),   /// <font color=gold>&#x25FC;</font>
799     goldenrod            = Color8(218,165,32),  /// <font color=goldenrod>&#x25FC;</font>
800     gray                 = Color8(128,128,128), /// <font color=gray>&#x25FC;</font>
801     grey                 = Color8(128,128,128), /// <font color=grey>&#x25FC;</font>
802     green                = Color8(0,128,0),     /// <font color=green>&#x25FC;</font>
803     greenYellow          = Color8(173,255,47),  /// <font color=greenYellow>&#x25FC;</font>
804     honeydew             = Color8(240,255,240), /// <font color=honeydew>&#x25FC;</font>
805     hotPink              = Color8(255,105,180), /// <font color=hotPink>&#x25FC;</font>
806     indianRed            = Color8(205,92,92),   /// <font color=indianRed>&#x25FC;</font>
807     indigo               = Color8(75,0,130),    /// <font color=indigo>&#x25FC;</font>
808     ivory                = Color8(255,255,240), /// <font color=ivory>&#x25FC;</font>
809     khaki                = Color8(240,230,140), /// <font color=khaki>&#x25FC;</font>
810     lavender             = Color8(230,230,250), /// <font color=lavender>&#x25FC;</font>
811     lavenderBlush        = Color8(255,240,245), /// <font color=lavenderBlush>&#x25FC;</font>
812     lawnGreen            = Color8(124,252,0),   /// <font color=lawnGreen>&#x25FC;</font>
813     lemonChiffon         = Color8(255,250,205), /// <font color=lemonChiffon>&#x25FC;</font>
814     lightBlue            = Color8(173,216,230), /// <font color=lightBlue>&#x25FC;</font>
815     lightCoral           = Color8(240,128,128), /// <font color=lightCoral>&#x25FC;</font>
816     lightCyan            = Color8(224,255,255), /// <font color=lightCyan>&#x25FC;</font>
817     lightGoldenrodYellow = Color8(250,250,210), /// <font color=lightGoldenrodYellow>&#x25FC;</font>
818     lightGray            = Color8(211,211,211), /// <font color=lightGray>&#x25FC;</font>
819     lightGrey            = Color8(211,211,211), /// <font color=lightGrey>&#x25FC;</font>
820     lightGreen           = Color8(144,238,144), /// <font color=lightGreen>&#x25FC;</font>
821     lightPink            = Color8(255,182,193), /// <font color=lightPink>&#x25FC;</font>
822     lightSalmon          = Color8(255,160,122), /// <font color=lightSalmon>&#x25FC;</font>
823     lightSeaGreen        = Color8(32,178,170),  /// <font color=lightSeaGreen>&#x25FC;</font>
824     lightSkyBlue         = Color8(135,206,250), /// <font color=lightSkyBlue>&#x25FC;</font>
825     lightSlateGray       = Color8(119,136,153), /// <font color=lightSlateGray>&#x25FC;</font>
826     lightSlateGrey       = Color8(119,136,153), /// <font color=lightSlateGrey>&#x25FC;</font>
827     lightSteelBlue       = Color8(176,196,222), /// <font color=lightSteelBlue>&#x25FC;</font>
828     lightYellow          = Color8(255,255,224), /// <font color=lightYellow>&#x25FC;</font>
829     lime                 = Color8(0,255,0),     /// <font color=lime>&#x25FC;</font>
830     limeGreen            = Color8(50,205,50),   /// <font color=limeGreen>&#x25FC;</font>
831     linen                = Color8(250,240,230), /// <font color=linen>&#x25FC;</font>
832     magenta              = Color8(255,0,255),   /// <font color=magenta>&#x25FC;</font>
833     maroon               = Color8(128,0,0),     /// <font color=maroon>&#x25FC;</font>
834     mediumAquamarine     = Color8(102,205,170), /// <font color=mediumAquamarine>&#x25FC;</font>
835     mediumBlue           = Color8(0,0,205),     /// <font color=mediumBlue>&#x25FC;</font>
836     mediumOrchid         = Color8(186,85,211),  /// <font color=mediumOrchid>&#x25FC;</font>
837     mediumPurple         = Color8(147,112,219), /// <font color=mediumPurple>&#x25FC;</font>
838     mediumSeaGreen       = Color8(60,179,113),  /// <font color=mediumSeaGreen>&#x25FC;</font>
839     mediumSlateBlue      = Color8(123,104,238), /// <font color=mediumSlateBlue>&#x25FC;</font>
840     mediumSpringGreen    = Color8(0,250,154),   /// <font color=mediumSpringGreen>&#x25FC;</font>
841     mediumTurquoise      = Color8(72,209,204),  /// <font color=mediumTurquoise>&#x25FC;</font>
842     mediumVioletRed      = Color8(199,21,133),  /// <font color=mediumVioletRed>&#x25FC;</font>
843     midnightBlue         = Color8(25,25,112),   /// <font color=midnightBlue>&#x25FC;</font>
844     mintCream            = Color8(245,255,250), /// <font color=mintCream>&#x25FC;</font>
845     mistyRose            = Color8(255,228,225), /// <font color=mistyRose>&#x25FC;</font>
846     moccasin             = Color8(255,228,181), /// <font color=moccasin>&#x25FC;</font>
847     navajoWhite          = Color8(255,222,173), /// <font color=navajoWhite>&#x25FC;</font>
848     navy                 = Color8(0,0,128),     /// <font color=navy>&#x25FC;</font>
849     oldLace              = Color8(253,245,230), /// <font color=oldLace>&#x25FC;</font>
850     olive                = Color8(128,128,0),   /// <font color=olive>&#x25FC;</font>
851     oliveDrab            = Color8(107,142,35),  /// <font color=oliveDrab>&#x25FC;</font>
852     orange               = Color8(255,165,0),   /// <font color=orange>&#x25FC;</font>
853     orangeRed            = Color8(255,69,0),    /// <font color=orangeRed>&#x25FC;</font>
854     orchid               = Color8(218,112,214), /// <font color=orchid>&#x25FC;</font>
855     paleGoldenrod        = Color8(238,232,170), /// <font color=paleGoldenrod>&#x25FC;</font>
856     paleGreen            = Color8(152,251,152), /// <font color=paleGreen>&#x25FC;</font>
857     paleTurquoise        = Color8(175,238,238), /// <font color=paleTurquoise>&#x25FC;</font>
858     paleVioletRed        = Color8(219,112,147), /// <font color=paleVioletRed>&#x25FC;</font>
859     papayaWhip           = Color8(255,239,213), /// <font color=papayaWhip>&#x25FC;</font>
860     peachPuff            = Color8(255,218,185), /// <font color=peachPuff>&#x25FC;</font>
861     peru                 = Color8(205,133,63),  /// <font color=peru>&#x25FC;</font>
862     pink                 = Color8(255,192,203), /// <font color=pink>&#x25FC;</font>
863     plum                 = Color8(221,160,221), /// <font color=plum>&#x25FC;</font>
864     powderBlue           = Color8(176,224,230), /// <font color=powderBlue>&#x25FC;</font>
865     purple               = Color8(128,0,128),   /// <font color=purple>&#x25FC;</font>
866     red                  = Color8(255,0,0),     /// <font color=red>&#x25FC;</font>
867     rosyBrown            = Color8(188,143,143), /// <font color=rosyBrown>&#x25FC;</font>
868     royalBlue            = Color8(65,105,225),  /// <font color=royalBlue>&#x25FC;</font>
869     saddleBrown          = Color8(139,69,19),   /// <font color=saddleBrown>&#x25FC;</font>
870     salmon               = Color8(250,128,114), /// <font color=salmon>&#x25FC;</font>
871     sandyBrown           = Color8(244,164,96),  /// <font color=sandyBrown>&#x25FC;</font>
872     seaGreen             = Color8(46,139,87),   /// <font color=seaGreen>&#x25FC;</font>
873     seashell             = Color8(255,245,238), /// <font color=seashell>&#x25FC;</font>
874     sienna               = Color8(160,82,45),   /// <font color=sienna>&#x25FC;</font>
875     silver               = Color8(192,192,192), /// <font color=silver>&#x25FC;</font>
876     skyBlue              = Color8(135,206,235), /// <font color=skyBlue>&#x25FC;</font>
877     slateBlue            = Color8(106,90,205),  /// <font color=slateBlue>&#x25FC;</font>
878     slateGray            = Color8(112,128,144), /// <font color=slateGray>&#x25FC;</font>
879     slateGrey            = Color8(112,128,144), /// <font color=slateGrey>&#x25FC;</font>
880     snow                 = Color8(255,250,250), /// <font color=snow>&#x25FC;</font>
881     springGreen          = Color8(0,255,127),   /// <font color=springGreen>&#x25FC;</font>
882     steelBlue            = Color8(70,130,180),  /// <font color=steelBlue>&#x25FC;</font>
883     tan                  = Color8(210,180,140), /// <font color=tan>&#x25FC;</font>
884     teal                 = Color8(0,128,128),   /// <font color=teal>&#x25FC;</font>
885     thistle              = Color8(216,191,216), /// <font color=thistle>&#x25FC;</font>
886     tomato               = Color8(255,99,71),   /// <font color=tomato>&#x25FC;</font>
887     turquoise            = Color8(64,224,208),  /// <font color=turquoise>&#x25FC;</font>
888     violet               = Color8(238,130,238), /// <font color=violet>&#x25FC;</font>
889     wheat                = Color8(245,222,179), /// <font color=wheat>&#x25FC;</font>
890     white                = Color8(255,255,255), /// <font color=white>&#x25FC;</font>
891     whiteSmoke           = Color8(245,245,245), /// <font color=whiteSmoke>&#x25FC;</font>
892     yellow               = Color8(255,255,0),   /// <font color=yellow>&#x25FC;</font>
893     yellowGreen          = Color8(154,205,50)   /// <font color=yellowGreen>&#x25FC;</font>
894 }