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>◼</font> 748 antiqueWhite = Color8(250,235,215), /// <font color=antiqueWhite>◼</font> 749 aqua = Color8(0,255,255), /// <font color=aqua>◼</font> 750 aquamarine = Color8(127,255,212), /// <font color=aquamarine>◼</font> 751 azure = Color8(240,255,255), /// <font color=azure>◼</font> 752 beige = Color8(245,245,220), /// <font color=beige>◼</font> 753 bisque = Color8(255,228,196), /// <font color=bisque>◼</font> 754 black = Color8(0,0,0), /// <font color=black>◼</font> 755 blanchedAlmond = Color8(255,235,205), /// <font color=blanchedAlmond>◼</font> 756 blue = Color8(0,0,255), /// <font color=blue>◼</font> 757 blueViolet = Color8(138,43,226), /// <font color=blueViolet>◼</font> 758 brown = Color8(165,42,42), /// <font color=brown>◼</font> 759 burlyWood = Color8(222,184,135), /// <font color=burlyWood>◼</font> 760 cadetBlue = Color8(95,158,160), /// <font color=cadetBlue>◼</font> 761 chartreuse = Color8(127,255,0), /// <font color=chartreuse>◼</font> 762 chocolate = Color8(210,105,30), /// <font color=chocolate>◼</font> 763 coral = Color8(255,127,80), /// <font color=coral>◼</font> 764 cornflowerBlue = Color8(100,149,237), /// <font color=cornflowerBlue>◼</font> 765 cornsilk = Color8(255,248,220), /// <font color=cornsilk>◼</font> 766 crimson = Color8(220,20,60), /// <font color=crimson>◼</font> 767 cyan = Color8(0,255,255), /// <font color=cyan>◼</font> 768 darkBlue = Color8(0,0,139), /// <font color=darkBlue>◼</font> 769 darkCyan = Color8(0,139,139), /// <font color=darkCyan>◼</font> 770 darkGoldenrod = Color8(184,134,11), /// <font color=darkGoldenrod>◼</font> 771 darkGray = Color8(169,169,169), /// <font color=darkGray>◼</font> 772 darkGrey = Color8(169,169,169), /// <font color=darkGrey>◼</font> 773 darkGreen = Color8(0,100,0), /// <font color=darkGreen>◼</font> 774 darkKhaki = Color8(189,183,107), /// <font color=darkKhaki>◼</font> 775 darkMagenta = Color8(139,0,139), /// <font color=darkMagenta>◼</font> 776 darkOliveGreen = Color8(85,107,47), /// <font color=darkOliveGreen>◼</font> 777 darkOrange = Color8(255,140,0), /// <font color=darkOrange>◼</font> 778 darkOrchid = Color8(153,50,204), /// <font color=darkOrchid>◼</font> 779 darkRed = Color8(139,0,0), /// <font color=darkRed>◼</font> 780 darkSalmon = Color8(233,150,122), /// <font color=darkSalmon>◼</font> 781 darkSeaGreen = Color8(143,188,143), /// <font color=darkSeaGreen>◼</font> 782 darkSlateBlue = Color8(72,61,139), /// <font color=darkSlateBlue>◼</font> 783 darkSlateGray = Color8(47,79,79), /// <font color=darkSlateGray>◼</font> 784 darkSlateGrey = Color8(47,79,79), /// <font color=darkSlateGrey>◼</font> 785 darkTurquoise = Color8(0,206,209), /// <font color=darkTurquoise>◼</font> 786 darkViolet = Color8(148,0,211), /// <font color=darkViolet>◼</font> 787 deepPink = Color8(255,20,147), /// <font color=deepPink>◼</font> 788 deepSkyBlue = Color8(0,191,255), /// <font color=deepSkyBlue>◼</font> 789 dimGray = Color8(105,105,105), /// <font color=dimGray>◼</font> 790 dimGrey = Color8(105,105,105), /// <font color=dimGrey>◼</font> 791 dodgerBlue = Color8(30,144,255), /// <font color=dodgerBlue>◼</font> 792 fireBrick = Color8(178,34,34), /// <font color=fireBrick>◼</font> 793 floralWhite = Color8(255,250,240), /// <font color=floralWhite>◼</font> 794 forestGreen = Color8(34,139,34), /// <font color=forestGreen>◼</font> 795 fuchsia = Color8(255,0,255), /// <font color=fuchsia>◼</font> 796 gainsboro = Color8(220,220,220), /// <font color=gainsboro>◼</font> 797 ghostWhite = Color8(248,248,255), /// <font color=ghostWhite>◼</font> 798 gold = Color8(255,215,0), /// <font color=gold>◼</font> 799 goldenrod = Color8(218,165,32), /// <font color=goldenrod>◼</font> 800 gray = Color8(128,128,128), /// <font color=gray>◼</font> 801 grey = Color8(128,128,128), /// <font color=grey>◼</font> 802 green = Color8(0,128,0), /// <font color=green>◼</font> 803 greenYellow = Color8(173,255,47), /// <font color=greenYellow>◼</font> 804 honeydew = Color8(240,255,240), /// <font color=honeydew>◼</font> 805 hotPink = Color8(255,105,180), /// <font color=hotPink>◼</font> 806 indianRed = Color8(205,92,92), /// <font color=indianRed>◼</font> 807 indigo = Color8(75,0,130), /// <font color=indigo>◼</font> 808 ivory = Color8(255,255,240), /// <font color=ivory>◼</font> 809 khaki = Color8(240,230,140), /// <font color=khaki>◼</font> 810 lavender = Color8(230,230,250), /// <font color=lavender>◼</font> 811 lavenderBlush = Color8(255,240,245), /// <font color=lavenderBlush>◼</font> 812 lawnGreen = Color8(124,252,0), /// <font color=lawnGreen>◼</font> 813 lemonChiffon = Color8(255,250,205), /// <font color=lemonChiffon>◼</font> 814 lightBlue = Color8(173,216,230), /// <font color=lightBlue>◼</font> 815 lightCoral = Color8(240,128,128), /// <font color=lightCoral>◼</font> 816 lightCyan = Color8(224,255,255), /// <font color=lightCyan>◼</font> 817 lightGoldenrodYellow = Color8(250,250,210), /// <font color=lightGoldenrodYellow>◼</font> 818 lightGray = Color8(211,211,211), /// <font color=lightGray>◼</font> 819 lightGrey = Color8(211,211,211), /// <font color=lightGrey>◼</font> 820 lightGreen = Color8(144,238,144), /// <font color=lightGreen>◼</font> 821 lightPink = Color8(255,182,193), /// <font color=lightPink>◼</font> 822 lightSalmon = Color8(255,160,122), /// <font color=lightSalmon>◼</font> 823 lightSeaGreen = Color8(32,178,170), /// <font color=lightSeaGreen>◼</font> 824 lightSkyBlue = Color8(135,206,250), /// <font color=lightSkyBlue>◼</font> 825 lightSlateGray = Color8(119,136,153), /// <font color=lightSlateGray>◼</font> 826 lightSlateGrey = Color8(119,136,153), /// <font color=lightSlateGrey>◼</font> 827 lightSteelBlue = Color8(176,196,222), /// <font color=lightSteelBlue>◼</font> 828 lightYellow = Color8(255,255,224), /// <font color=lightYellow>◼</font> 829 lime = Color8(0,255,0), /// <font color=lime>◼</font> 830 limeGreen = Color8(50,205,50), /// <font color=limeGreen>◼</font> 831 linen = Color8(250,240,230), /// <font color=linen>◼</font> 832 magenta = Color8(255,0,255), /// <font color=magenta>◼</font> 833 maroon = Color8(128,0,0), /// <font color=maroon>◼</font> 834 mediumAquamarine = Color8(102,205,170), /// <font color=mediumAquamarine>◼</font> 835 mediumBlue = Color8(0,0,205), /// <font color=mediumBlue>◼</font> 836 mediumOrchid = Color8(186,85,211), /// <font color=mediumOrchid>◼</font> 837 mediumPurple = Color8(147,112,219), /// <font color=mediumPurple>◼</font> 838 mediumSeaGreen = Color8(60,179,113), /// <font color=mediumSeaGreen>◼</font> 839 mediumSlateBlue = Color8(123,104,238), /// <font color=mediumSlateBlue>◼</font> 840 mediumSpringGreen = Color8(0,250,154), /// <font color=mediumSpringGreen>◼</font> 841 mediumTurquoise = Color8(72,209,204), /// <font color=mediumTurquoise>◼</font> 842 mediumVioletRed = Color8(199,21,133), /// <font color=mediumVioletRed>◼</font> 843 midnightBlue = Color8(25,25,112), /// <font color=midnightBlue>◼</font> 844 mintCream = Color8(245,255,250), /// <font color=mintCream>◼</font> 845 mistyRose = Color8(255,228,225), /// <font color=mistyRose>◼</font> 846 moccasin = Color8(255,228,181), /// <font color=moccasin>◼</font> 847 navajoWhite = Color8(255,222,173), /// <font color=navajoWhite>◼</font> 848 navy = Color8(0,0,128), /// <font color=navy>◼</font> 849 oldLace = Color8(253,245,230), /// <font color=oldLace>◼</font> 850 olive = Color8(128,128,0), /// <font color=olive>◼</font> 851 oliveDrab = Color8(107,142,35), /// <font color=oliveDrab>◼</font> 852 orange = Color8(255,165,0), /// <font color=orange>◼</font> 853 orangeRed = Color8(255,69,0), /// <font color=orangeRed>◼</font> 854 orchid = Color8(218,112,214), /// <font color=orchid>◼</font> 855 paleGoldenrod = Color8(238,232,170), /// <font color=paleGoldenrod>◼</font> 856 paleGreen = Color8(152,251,152), /// <font color=paleGreen>◼</font> 857 paleTurquoise = Color8(175,238,238), /// <font color=paleTurquoise>◼</font> 858 paleVioletRed = Color8(219,112,147), /// <font color=paleVioletRed>◼</font> 859 papayaWhip = Color8(255,239,213), /// <font color=papayaWhip>◼</font> 860 peachPuff = Color8(255,218,185), /// <font color=peachPuff>◼</font> 861 peru = Color8(205,133,63), /// <font color=peru>◼</font> 862 pink = Color8(255,192,203), /// <font color=pink>◼</font> 863 plum = Color8(221,160,221), /// <font color=plum>◼</font> 864 powderBlue = Color8(176,224,230), /// <font color=powderBlue>◼</font> 865 purple = Color8(128,0,128), /// <font color=purple>◼</font> 866 red = Color8(255,0,0), /// <font color=red>◼</font> 867 rosyBrown = Color8(188,143,143), /// <font color=rosyBrown>◼</font> 868 royalBlue = Color8(65,105,225), /// <font color=royalBlue>◼</font> 869 saddleBrown = Color8(139,69,19), /// <font color=saddleBrown>◼</font> 870 salmon = Color8(250,128,114), /// <font color=salmon>◼</font> 871 sandyBrown = Color8(244,164,96), /// <font color=sandyBrown>◼</font> 872 seaGreen = Color8(46,139,87), /// <font color=seaGreen>◼</font> 873 seashell = Color8(255,245,238), /// <font color=seashell>◼</font> 874 sienna = Color8(160,82,45), /// <font color=sienna>◼</font> 875 silver = Color8(192,192,192), /// <font color=silver>◼</font> 876 skyBlue = Color8(135,206,235), /// <font color=skyBlue>◼</font> 877 slateBlue = Color8(106,90,205), /// <font color=slateBlue>◼</font> 878 slateGray = Color8(112,128,144), /// <font color=slateGray>◼</font> 879 slateGrey = Color8(112,128,144), /// <font color=slateGrey>◼</font> 880 snow = Color8(255,250,250), /// <font color=snow>◼</font> 881 springGreen = Color8(0,255,127), /// <font color=springGreen>◼</font> 882 steelBlue = Color8(70,130,180), /// <font color=steelBlue>◼</font> 883 tan = Color8(210,180,140), /// <font color=tan>◼</font> 884 teal = Color8(0,128,128), /// <font color=teal>◼</font> 885 thistle = Color8(216,191,216), /// <font color=thistle>◼</font> 886 tomato = Color8(255,99,71), /// <font color=tomato>◼</font> 887 turquoise = Color8(64,224,208), /// <font color=turquoise>◼</font> 888 violet = Color8(238,130,238), /// <font color=violet>◼</font> 889 wheat = Color8(245,222,179), /// <font color=wheat>◼</font> 890 white = Color8(255,255,255), /// <font color=white>◼</font> 891 whiteSmoke = Color8(245,245,245), /// <font color=whiteSmoke>◼</font> 892 yellow = Color8(255,255,0), /// <font color=yellow>◼</font> 893 yellowGreen = Color8(154,205,50) /// <font color=yellowGreen>◼</font> 894 }