Search code examples
c#colors24-bit

Convert 4-bit color (=ConsoleColor) to RGB and back


I want to write a console-like gui for a game, and I start with a real console. So I have a class that specifies the colors. Now I want to convert consolecolor to rgb (24-bit) and the other way round. I have tried this:

int d = (((int)col & 8) != 0) ? 255 : 128;
r = (((int)col & 4) != 0) ? d : 0;
g = (((int)col & 2) != 0) ? d : 0;
b = (((int)col & 1) != 0) ? d : 0;

4-Bit colors have this bit-scheme: drgb. When d is 0, the color will be dark; if it's 1, the color will be bright and the rgb values will be 255. My problem is: Color 1000 (bright black) and Color 0111 (dark white) are black and gray in my program. They should be darkgray and lightgray. And how to convert the colors back with rounding and not just converting specific colors back?


Solution

  • Colors are defined in a 3-dimensional parameter space with the three coordinate axes R, G and B. You can calculate the distance between two colors a and b like this:

    double dist = Math.Sqrt(Sqr(a.R - b.R) + Sqr(a.G - b.G) + Sqr(a.B - b.B));
    

    This formula uses this method

    private static int Sqr(int x)
    {
        return x * x;
    }
    

    The best matching color is the one with the smallest distance. You can also use the square of the distance for the comparison of distances (for efficiency):

    int sqrDist = Sqr(a.R - b.R) + Sqr(a.G - b.G) + Sqr(a.B - b.B);
    

    Now define an array with the 16 Console colors:

    Color[] consoleColors = new[] {
        Colors.Black,
        Colors.Red,
        ...
    };
    

    and get the best match with

    Color bestMatch;
    int bestSqrDist = Int32.MaxValue;
    foreach (Color consoleColor in consoleColors) {
        int sqrDist = Sqr(theColor.R - consoleColor.R) +
                      Sqr(theColor.G - consoleColor.G) +
                      Sqr(theColor.B - consoleColor.B);
        if (sqrDist < bestSqrDist) {
            bestMatch = consoleColor;
            bestSqrDist = sqrDist;
        }
    }
    

    A more sophisticated approach is to use an algorithm like Floyd–Steinberg dithering to not only reduce the color depth pixel by pixel but to spread the conversion error among several pixels to give the illusion of having subtle color nuances.