1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // 256-color terminal utilities
18 module iv
.termrgb
/*is aliced*/;
21 //version = termrgb_weighted_colors;
22 //version = termrgb_disable_256_colors;
23 //version = termrgb_gamma_correct;
26 // ////////////////////////////////////////////////////////////////////////// //
27 /// Terminal type (yeah, i know alot of 'em)
28 public enum TermType
{
32 linux
, // linux console
36 public __gshared TermType termType
= TermType
.other
; ///
37 __gshared
bool isTermRedirected
= true; ///
40 /// is TTY stdin or stdout redirected? note that stderr *can* be redirected.
41 public @property bool ttyIsRedirected () nothrow @trusted @nogc { pragma(inline
, true); return isTermRedirected
; }
44 shared static this () nothrow @trusted @nogc {
46 import core
.stdc
.stdlib
: getenv
;
47 import core
.stdc
.string
: strcmp
;
48 auto tt
= getenv("TERM");
51 while (len
< 5 && tt
[len
]) ++len
;
52 if (len
>= 4 && tt
[0..4] == "rxvt") termType
= TermType
.rxvt
;
53 else if (len
>= 5 && tt
[0..5] == "xterm") termType
= TermType
.xterm
;
54 else if (len
>= 5 && tt
[0..5] == "linux") termType
= TermType
.linux
;
58 import core
.sys
.posix
.unistd
: isatty
, STDIN_FILENO
, STDOUT_FILENO
;
59 import core
.sys
.posix
.termios
: tcgetattr
;
60 import core
.sys
.posix
.termios
: termios
;
61 if (isatty(STDIN_FILENO
) && isatty(STDOUT_FILENO
)) {
62 termios origMode
= void;
63 if (tcgetattr(STDIN_FILENO
, &origMode
) == 0) isTermRedirected
= false;
69 // ////////////////////////////////////////////////////////////////////////// //
70 /// k8sterm color table, lol
71 static immutable uint[256] ttyRGBTable
= {
73 // standard terminal colors
90 // rgb colors [16..231]
92 foreach (ubyte r
; 0..6) {
93 foreach (ubyte g
; 0..6) {
94 foreach (ubyte b
; 0..6) {
95 uint cr
= (r
== 0 ?
0 : 0x37+0x28*r
); assert(cr
<= 255);
96 uint cg
= (g
== 0 ?
0 : 0x37+0x28*g
); assert(cg
<= 255);
97 uint cb
= (b
== 0 ?
0 : 0x37+0x28*b
); assert(cb
<= 255);
98 res
[f
++] = (cr
<<16)|
(cg
<<8)|cb
;
103 // b/w shades [232..255]
104 foreach (ubyte n
; 0..24) {
105 uint c
= 0x08+0x0a*n
; assert(c
<= 255);
106 res
[f
++] = (c
<<16)|
(c
<<8)|c
;
113 static immutable uint[16] ttyRGB16
= {
115 // standard terminal colors
116 version(tty_linux_dumb
) {
133 } else version(tty_linux_hi
) {
172 version(termrgb_gamma_correct
) {
173 // color in sRGB space
175 float r
=0, g
=0, b
=0; // [0..1]
176 //alias x = r, y = g, z = b;
177 this (float ar
, float ag
, float ab
) pure nothrow @safe @nogc { r
= ar
; g
= ag
; b
= ab
; }
178 this() (in auto ref FXYZ c
) pure nothrow @safe @nogc {
180 immutable float xs
= c
.x
* 3.2406+c
.y
*-1.5372+c
.z
*-0.4986;
181 immutable float ys
= c
.x
*-0.9689+c
.y
* 1.8758+c
.z
* 0.0415;
182 immutable float zs
= c
.x
* 0.0557+c
.y
*-0.2040+c
.z
* 1.0570;
183 r
= valueFromLinear(xs
);
184 g
= valueFromLinear(ys
);
185 b
= valueFromLinear(zs
);
187 r
= valueFromLinear(c
.x
);
188 g
= valueFromLinear(c
.y
);
189 b
= valueFromLinear(c
.z
);
193 // linear to gamma conversion
194 // value should be in [0..1] range
195 static T
valueFromLinear(T
: real) (T v
) pure nothrow @safe @nogc {
196 import std
.math
: pow
;
197 return (v
> 0.0031308 ?
1.055*pow(v
, (1.0/2.4))-0.055 : 12.92*v
);
201 // color in linear space
203 float x
=0, y
=0, z
=0; // [0..1]
204 this (float ax
, float ay
, float az
) pure nothrow @safe @nogc { x
= ax
; y
= ay
; z
= az
; }
205 this() (in auto ref SRGB c
) pure nothrow @safe @nogc {
207 immutable float rl
= valueFromGamma(c
.r
);
208 immutable float gl
= valueFromGamma(c
.g
);
209 immutable float bl
= valueFromGamma(c
.b
);
210 // observer. = 2degs, Illuminant = D65
211 x
= rl
*0.4124+gl
*0.3576+bl
*0.1805;
212 y
= rl
*0.2126+gl
*0.7152+bl
*0.0722;
213 z
= rl
*0.0193+gl
*0.1192+bl
*0.9505;
215 x
= valueFromGamma(c
.r
);
216 y
= valueFromGamma(c
.g
);
217 z
= valueFromGamma(c
.b
);
221 // gamma to linear conversion
222 // value should be in [0..1] range
223 static T
valueFromGamma(T
: real) (T v
) pure nothrow @safe @nogc {
224 import std
.math
: pow
;
225 return (v
> 0.04045 ?
pow((v
+0.055)/1.055, 2.4) : v
/12.92);
231 /// Convert 256-color terminal color number to approximate rgb values
232 public void ttyColor2rgb (ubyte cnum
, out ubyte r
, out ubyte g
, out ubyte b
) pure nothrow @trusted @nogc {
233 pragma(inline
, true);
234 r
= cast(ubyte)(ttyRGBTable
.ptr
[cnum
]>>16);
235 g
= cast(ubyte)(ttyRGBTable
.ptr
[cnum
]>>8);
236 b
= cast(ubyte)(ttyRGBTable
.ptr
[cnum
]);
240 } else if (cnum == 8) {
242 } else if (cnum >= 0 && cnum < 16) {
243 r = (cnum&(1<<0) ? (cnum&(1<<3) ? 0xff : 0x80) : 0x00);
244 g = (cnum&(1<<1) ? (cnum&(1<<3) ? 0xff : 0x80) : 0x00);
245 b = (cnum&(1<<2) ? (cnum&(1<<3) ? 0xff : 0x80) : 0x00);
246 } else if (cnum >= 16 && cnum < 232) {
247 // [0..5] -> [0..255]
248 b = cast(ubyte)(((cnum-16)%6)*51);
249 g = cast(ubyte)((((cnum-16)/6)%6)*51);
250 r = cast(ubyte)((((cnum-16)/6/6)%6)*51);
251 } else if (cnum >= 232 && cnum <= 255) {
252 // [0..23] (0 is very dark gray; 23 is *almost* white)
253 b = g = r = cast(ubyte)(8+(cnum-232)*10);
259 public alias ttyColor2RGB
= ttyColor2rgb
;
262 public alias ttyColor2Rgb
= ttyColor2rgb
;
265 immutable static ubyte[256] tty256to16tbl
= () {
267 foreach (ubyte idx
; 0..256) {
268 immutable cc
= ttyRGBTable
[idx
];
269 immutable r
= (cc
>>16)&0xff;
270 immutable g
= (cc
>>8)&0xff;
271 immutable b
= cc
&0xff;
272 res
[idx
] = ttyRGB
!false(r
, g
, b
);
274 foreach (ubyte idx
; 0..16) res
[idx
] = idx
;
279 immutable static ubyte[256] tty256to8tbl
= () {
281 foreach (ubyte idx
; 0..256) {
282 immutable cc
= ttyRGBTable
[idx
];
283 immutable r
= (cc
>>16)&0xff;
284 immutable g
= (cc
>>8)&0xff;
285 immutable b
= cc
&0xff;
286 res
[idx
] = ttyRGB
!(false, true)(r
, g
, b
);
288 foreach (ubyte idx
; 0..8) { res
[idx
] = idx
; res
[idx
+8] = idx
; }
293 /// convert 256-color code to 16-color Linux console code
294 public ubyte tty2linux (ubyte ttyc
) nothrow @trusted @nogc {
295 pragma(inline
, true);
296 return (termType
!= TermType
.linux ? ttyc
: tty256to16tbl
[ttyc
]);
300 /// convert 256-color code to 8-color Linux console code
301 public ubyte tty2linux8 (ubyte ttyc
) nothrow @trusted @nogc {
302 pragma(inline
, true);
303 return (termType
!= TermType
.linux ? ttyc
: tty256to8tbl
[ttyc
]);
308 public enum TtyRGB(ubyte r
, ubyte g
, ubyte b
, bool allow256
=true) = ttyRGB
!allow256(r
, g
, b
);
311 public enum TtyRGB(string rgb
, bool allow256
=true) = ttyRGB
!allow256(rgb
);
313 public alias TtyRgb
= TtyRGB
; /// Ditto.
316 /// Convert rgb values to approximate 256-color (or 16-color) teminal color number
317 public ubyte ttyRGB(bool allow256
=true, bool only8
=false) (const(char)[] rgb
) pure nothrow @trusted @nogc {
318 static int c2h (immutable char ch
) pure nothrow @trusted @nogc {
319 if (ch
>= '0' && ch
<= '9') return ch
-'0';
320 else if (ch
>= 'A' && ch
<= 'F') return ch
-'A'+10;
321 else if (ch
>= 'a' && ch
<= 'f') return ch
-'a'+10;
326 while (rgb
.length
&& (rgb
[0] <= ' ' || rgb
[0] == '#')) rgb
= rgb
[1..$];
327 while (rgb
.length
&& rgb
[$-1] <= ' ') rgb
= rgb
[0..$-1];
328 if (rgb
.length
== 3) {
329 foreach (immutable char ch
; rgb
) if (c2h(ch
) < 0) return 7; // normal gray
331 cast(ubyte)(255*c2h(rgb
[0])/15),
332 cast(ubyte)(255*c2h(rgb
[1])/15),
333 cast(ubyte)(255*c2h(rgb
[2])/15),
335 } else if (rgb
.length
== 6) {
336 foreach (immutable char ch
; rgb
) if (c2h(ch
) < 0) return 7; // normal gray
338 cast(ubyte)(16*c2h(rgb
[0])+c2h(rgb
[1])),
339 cast(ubyte)(16*c2h(rgb
[2])+c2h(rgb
[3])),
340 cast(ubyte)(16*c2h(rgb
[4])+c2h(rgb
[5])),
343 return 7; // normal gray
348 /// Convert rgb values to approximate 256-color (or 16-color) teminal color number
349 public ubyte ttyRGB(bool allow256
=true, bool only8
=false) (ubyte r
, ubyte g
, ubyte b
) pure nothrow @trusted @nogc {
350 // use standard (weighted) color distance function to find the closest match
351 // d = ((r2-r1)*0.30)^^2+((g2-g1)*0.59)^^2+((b2-b1)*0.11)^^2
352 version(termrgb_gamma_correct
) {
353 static if (only8
) { enum lastc
= 8; alias rgbtbl
= ttyRGB16
; }
355 version(termrgb_disable_256_colors
) { enum lastc
= 16; alias rgbtbl
= ttyRGB16
; }
356 else { static if (allow256
) { enum lastc
= 256; alias rgbtbl
= ttyRGBTable
;} else { enum lastc
= 16; alias rgbtbl
= ttyRGB16
; } }
358 double dist
= double.max
;
360 immutable l0
= FXYZ(SRGB(r
/255.0f, g
/255.0f, b
/255.0f));
361 foreach (immutable idx
, uint cc
; rgbtbl
[0..lastc
]) {
362 auto linear
= FXYZ(SRGB(((cc
>>16)&0xff)/255.0f, ((cc
>>8)&0xff)/255.0f, (cc
&0xff)/255.0f));
366 //double dd = linear.x*linear.x+linear.y*linear.y+linear.z*linear.z;
367 double dd = (linear
.x
*linear
.x
)*0.30+(linear
.y
*linear
.y
)*0.59+(linear
.z
*linear
.z
)*0.11;
369 resclr
= cast(ubyte)idx
;
375 enum n
= 16384; // scale
376 enum m0
= 4916; // 0.30*16384
377 enum m1
= 9666; // 0.59*16384
378 enum m2
= 1802; // 0.11*16384
379 long dist
= long.max
;
381 static if (only8
) { enum lastc
= 8; alias rgbtbl
= ttyRGB16
; }
383 version(termrgb_disable_256_colors
) { enum lastc
= 16; alias rgbtbl
= ttyRGB16
; }
384 else { static if (allow256
) { enum lastc
= 256; alias rgbtbl
= ttyRGBTable
;} else { enum lastc
= 16; alias rgbtbl
= ttyRGB16
; } }
386 foreach (immutable idx
, uint cc
; rgbtbl
[0..lastc
]) {
387 version(termrgb_weighted_colors
) {
388 long dr
= cast(int)((cc
>>16)&0xff)-cast(int)r
;
389 dr
= ((dr
*m0
)*(dr
*m0
))/n
;
391 long dg
= cast(int)((cc
>>8)&0xff)-cast(int)g
;
392 dg
= ((dg
*m1
)*(dg
*m1
))/n
;
394 long db = cast(int)(cc
&0xff)-cast(int)b
;
395 db = ((db*m2
)*(db*m2
))/n
;
400 long dr
= cast(int)((cc
>>16)&0xff)-cast(int)r
;
403 long dg
= cast(int)((cc
>>8)&0xff)-cast(int)g
;
406 long db = cast(int)(cc
&0xff)-cast(int)b
;
413 resclr
= cast(ubyte)idx
;
415 if (d
== 0) break; // no better match is possible
422 public alias ttyRgb
= ttyRGB
; /// Ditto.