Comments (10)
There is a fundamental difference between the ANSI color red, and the RGB color red.
Most terminal emulators allow you to specify which RGB color each ANSI color represents. Looking at iTerm on mac, one can specify for the red color code to mean any RGB color.
In this case, red is set to a washed out red color, but the RGB color code still shows up as the "true" red.
from scone.
I'm looking into RGB colors. It turns out it is a little bit more complicated than I thought.
There seem to be three levels of ANSI colors:
- ANSI 16 (16 different colors, the colors currently supported by scone)
- ANSI 256 (256 different colors, looking at https://jonasjacek.github.io/colors/)
- Truecolor ANSI (the usual ~16.7 million colors used in ordinary images, looking at https://stackoverflow.com/a/26665998)
I really don't know how to handle this. Maybe each level of color depth should have it's own type that can be used interchangeably in the application. Not sure how ANSI colors (which usually can be customised in the terminal settings) should be treated when combined with actual colors (ANSI 256/Truecolor).
from scone.
I would like to have as much backwards compatibility, but I have a hard time seeing how I could implement colors internally and still have them work seamlessly.
Should scone just try to print out RGB colors for systems which do not specify it handles RGB colors?
Maybe just drop the compatibility thing completely since most terminals today handle at least 256 colors.
from scone.
Sorry for the incoherent text, it's 2AM. None of the code was tested and probably does not compile.
I think best-effort compatibility can be handled by approximation as long as you assume the user has a mostly sane 16-color colorscheme:
Define each color as an RGB vector:
struct Color
{
ubyte r;
ubyte g;
ubyte b;
uint squaredDistanceTo( Color b )
{
// needs int casts and may be incorrect, wrote it from memory
return abs(r - b.r) ^^ 2 + abs(g - b.g) ^^ 2 + abs(b - b.b) ^^ 2;
}
}
define the ANSI colors as constants (I'm not thinking about style here, just how it'd work):
enum Red = Color(255,0,0);
enum DarkRed = Color(128,0,0);
// same for the rest of the colors
// some kind of function so we can also get ANSI colors by index
Color getANSI16Color( in ubyte index )
{
switch(index)
{
case 0: return Red;
// etc.
}
}
equivalent code also be written for the 256-color palette (an array may be better than a switch)
Programmer could choose to use the base set of 16 constants, extended 256 constants or just plain RGB with the Color()
constructor.
Then for compatibility: if running on a 16-color terminal, you'd approximate to the nearest color:
uint approximateTo16( in Color base )
{
auto bestSquaredDistance = uint.max;
uint bestColor;
foreach(c; 0 .. 16)
{
const candidate = getANSI16Color( c );
const distance = base.squaredDistanceTo( candidate );
if( distance < bestSquaredDistance )
{
bestSquaredDistance = distance;
bestColor = c;
}
}
return bestColor;
}
With that you will know which color's sequence to emit.
This should be fast enough to do it for every cell on a 4k screen-sized terminal 100 times per second, and if not fast enough, can be optimized to execute in a few cycles (ideas: unroll the loop, get rid of branches if possible, use D array expressions)
For 256 colors the approximation code above may be too slow, but can be optimized by using a better algorithm (generate an octree with the 256 colors - can probably be done at compile-time, and run a loop that cuts off a half of the color space on each iteration - should get most colors approximated in 8 iterations. Octree is probably overkill, a simple 3D grid should work)
This is probably a solved problem with a better solution than what I wrote (search color approximation? color quantization? etc)
TLDR:
- Programmer can always use RGB colors
- But they can have the '16-color programmer experience' with RGB constants, and same for 256
- Detect whether the terminal supports RGB, 256 or 16 colors
- When drawing colored output:
- If we support RGB, output that
- If 256, approximate to the closest of the 256 colors and output that
- If 16, approximate to the closest of the 16 colors and output that
from scone.
from scone.
Detecting if a terminal supports 256 colors or more is not that easy (currently I've only looked at POSIX (mac specifically)).
The only way I've figured out to detect color support is tput colors
, which returns how many colors the terminal says it support. Emphasis on says, as, from what I understand, it only parses the TERM
variable (echo $TERM
). Although old, this article outlines why this could be misleading.
I currently see no proper way of actually detecting color support of a terminal (only assuming), but maybe tput colors
is good enough though? Although, I have not checked of how to detect this on Windows.
Maybe a middle way would be to still support ANSI 16 colors (currently Color
), and add a new type ColorRGB
. This new type would handle RGB in the way you described, converting to ANSI 256 if Truecolor is not supported (again, I have no idea if it is possible to detect Truecolor support).
from scone.
From my experience, tput colors
probably is not enough (I've almost never seen it correspond to reality, and never seen it give me more than 256. )
I think this can be done on case-by-case basis to cover most cases on most platforms, but there is probably no 'perfect' way.
Source: https://gist.github.com/XVilka/8346728
- Some terminals set env var
$COLORTERM
which can have valuetruecolor
or24bit
for truecolor support. I can confirm that this works on my machine (Linux Mint 19.2 based on Ubuntu 18.04) on at leastmate-terminal
,konsole
,kitty
and that it matches their 24-bit capability, while onxterm
only 8 colors work by default and$COLORTERM
is not set. - There is the issue that a child terminal may inherit
$COLORTERM
from a parent terminal. This happens to me when launchingxterm
frommate-terminal
same fortmux
andscreen
Interestinglytmux
works with true color out of the box, I don't think that was true in the past.screen
is broken. - According to above article,
ncurses
/terminfo
does expose RGB support now, but naively tryingtput RGB
does not do anything. Maybe it'd be worth it looking at their source code. - There is also the Querying The Terminal approach, not sure how viable it is to do that in a library.
- I'd like a way to programatically force RGB or 256 colors. As a user of vim/tmux it can be (or used to in the past, it's not so bad nowadays) excruciatingly hard to get them to output the correct color sequences even if I knew (and had tested) that my terminal can handle 24bit color. If I could explicitly set this, I could then expose it with a "--force-truecolor" flag with a disclaimer that the user has to know what they're doing (I write dev tools, so my kind of user should know)
- If you could determine the name of the terminal emulator, you could have a whitelist based on which terminals support 24bit. (see the list in the source above). See the first answer here:
https://unix.stackexchange.com/questions/264329/get-the-terminal-emulator-name-inside-the-shell-script . This is a pretty nasty thing to do in a library, though (and may need different handling even across unix systems, not sure how portable it is). On my system, this correctly returnsmate_terminal
even from a nestedzsh
->bash
->zsh
shell in the terminal, and also correctly returnsscreen
fromscreen
->zsh
->bash
->zsh
in the same terminal.
TLDR
I think querying $COLORTERM
may be 'good enough' for most cases, but given that it can be broken (e.g. screen
) there is even more of a need for a way to override it. Still, anyone 'advanced' enough to use screen
should be able to disable the $COLORTERM
variable.
Note: I did not find a reliable way to detect 256 color support. But it does seem to work everywhere where 24bit colors are supported.
Note 2: xterm
and screen
actually do support more than 8 colors, but not by default. But it seems that everything is moving in the direction of 24bit by default, so that may change.
from scone.
Thank you for your input @kiith-sa! 🙏
I'll read it more thoroughly later tonight.
from scone.
I believe that just to detect (guess) how many colors the terminal supports we could go with checking in the following order: (I would prefer to not check the emulators name, but I might do that still)
$COLORTERM
, if set check if it supports 256bit.tput colors
, as a fallback and just assume this is the maximum color amount. I've never seen this return more than what the terminal supports.
The info you provided is very useful, and once I get to implement this I'll revisit this thread.
Once again, thank you very much @kiith-sa for the help you've provided ❤️
from scone.
6 months later, and I would like to add that struct Color
has been given a state, where it can be either ansi
, rgb
, or same
.
from scone.
Related Issues (20)
- "Unresolved external symbols" when compiling on Windows HOT 3
- Window resize detection HOT 4
- Settings HOT 2
- Unittests HOT 2
- Resizing error with Windows Terminal HOT 3
- Release v3 HOT 3
- Mouse HOT 1
- Rename general Input to Keyboard HOT 1
- Hyper.js resizing not working
- Resizing mega-issue HOT 2
- Rework README HOT 2
- TextStyle HOT 6
- Styled text wrapper
- Improve POSIX output HOT 1
- RGB output on Windows HOT 2
- Enable POSIX style rendering on Windows HOT 2
- CLI-like output HOT 4
- Replace sconeSetup
- Release v3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scone.