Giter Site home page Giter Site logo

RGB color about scone HOT 10 CLOSED

vladdeSV avatar vladdeSV commented on June 27, 2024
RGB color

from scone.

Comments (10)

vladdeSV avatar vladdeSV commented on June 27, 2024

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.

iterm color comparison

from scone.

vladdeSV avatar vladdeSV commented on June 27, 2024

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:

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.

vladdeSV avatar vladdeSV commented on June 27, 2024

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.

kiith-sa avatar kiith-sa commented on June 27, 2024

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.

vladdeSV avatar vladdeSV commented on June 27, 2024

from scone.

vladdeSV avatar vladdeSV commented on June 27, 2024

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.

kiith-sa avatar kiith-sa commented on June 27, 2024

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 value truecolor or 24bit for truecolor support. I can confirm that this works on my machine (Linux Mint 19.2 based on Ubuntu 18.04) on at least mate-terminal, konsole, kitty and that it matches their 24-bit capability, while on xterm 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 launching xterm from mate-terminal same for tmux and screen Interestingly tmux 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 trying tput 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 returns mate_terminal even from a nested zsh->bash->zsh shell in the terminal, and also correctly returns screen from screen->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.

vladdeSV avatar vladdeSV commented on June 27, 2024

Thank you for your input @kiith-sa! 🙏

I'll read it more thoroughly later tonight.

from scone.

vladdeSV avatar vladdeSV commented on June 27, 2024

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)

  1. $COLORTERM, if set check if it supports 256bit.
  2. 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.

vladdeSV avatar vladdeSV commented on June 27, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.