Search code examples
c#colorsconsoleconsole-application

Convert RGB values to a ConsoleColor (C#)


So I wish to build an ASCII Renderer, that takes an image and converts each pixel to a character. However, I thought it would be cool if I could also get Color values as well, not just white characters. However I ran into a problem: the image format I'm using (PPM, for convenience) uses RGB values, and the C# console only has 16 colors, so I'd need to estimate the color. The problem came, however, that nothing i've tried works.

Here's the snippet I tried:

    static ConsoleColor FindClosestConsoleColor(int r, int g, int b)
    {
        ConsoleColor closest = ConsoleColor.Black;
        double closestDist = double.MaxValue;

        foreach (ConsoleColor consoleColor in Enum.GetValues(typeof(ConsoleColor)))
        {
            if (consoleColor == ConsoleColor.Black || consoleColor == ConsoleColor.DarkBlue || consoleColor == ConsoleColor.DarkCyan ||
                consoleColor == ConsoleColor.DarkGreen || consoleColor == ConsoleColor.DarkMagenta || consoleColor == ConsoleColor.DarkRed ||
                consoleColor == ConsoleColor.DarkYellow || consoleColor == ConsoleColor.Gray || consoleColor == ConsoleColor.DarkGray ||
                consoleColor == ConsoleColor.Blue || consoleColor == ConsoleColor.Cyan || consoleColor == ConsoleColor.Green ||
                consoleColor == ConsoleColor.Magenta || consoleColor == ConsoleColor.Red || consoleColor == ConsoleColor.Yellow ||
                consoleColor == ConsoleColor.White)
            {
                int consoleColorValue = (int)consoleColor;
                int consoleRed = (consoleColorValue & 0xFF0000) >> 16;
                int consoleGreen = (consoleColorValue & 0x00FF00) >> 8;
                int consoleBlue = consoleColorValue & 0x0000FF;

                // Calculate the Euclidean distance between the two colors
                double distance = Math.Sqrt(Math.Pow(consoleRed - r, 2) + Math.Pow(consoleGreen - g, 2) + Math.Pow(consoleBlue - b, 2));

                // Check if this ConsoleColor is closer to the given RGB values
                if (distance < closestDist)
                {
                    closestDist = distance;
                    closest = consoleColor;
                }
            }
        }

        return closest;
    }

However this snippet only returned the color black, even when I fed it just straight red. (255, 0, 0)

I also tried reading this StackOverflow article, but that was for the System.Drawing.Color namespace. I'm looking to just convert raw RGB values to a ConsoleColor.

Any help would be appreciated! Thanks, TheGame12


Solution

  • I figured it out! For those wondering, here's what I did.

    // First, I defined a list of the ConsoleColor's RGB values as an array of int tuples
        private static readonly (int, int, int)[] colorValuesInt = new (int, int, int)[] {
            (0, 0, 255), (0, 255, 255), (0, 0, 128), (0, 139, 139), (169, 169, 169), (0, 100, 0), (139, 0, 139), (139, 0, 0), (139, 139, 0), (128, 128, 128), (0, 128, 0), (255, 0, 255), (255, 0, 0), (255, 255, 255), (255, 255, 0)
        };
    
    // Then used this function
        public static Color RGBToConsoleColor(int r, int g, int b) {
     
            // Make a lamda square function
            Func<int, int> square = (int x) => x * x;
    
            Color[] consoleColors = new[] {
                Color.Blue, Color.Cyan, Color.DarkBlue, Color.DarkCyan, Color.DarkGray, Color.DarkGreen, Color.DarkMagenta, Color.DarkRed, Color.DarkYellow, Color.Gray, Color.Green, Color.Magenta, Color.Red, Color.White, Color.Yellow
            };
    
            Color best = Color.Black;
    
            int bestSquareDistance = Int32.MaxValue;
            for (int i = 0; i < consoleColors.Length; i++) {
    
                Color c = consoleColors[i];
                (int, int, int) colorValues = colorValuesInt[i];
    
                int squareDistance = square(r - colorValues.Item1) + square(g - colorValues.Item2) + square(b - colorValues.Item3);
    
                if (squareDistance < bestSquareDistance) {
                    best = c;
                    bestSquareDistance = squareDistance;
                }
    
            }
    
            return best;
    
        }
    
    Thanks so much to @Serg, they provided articles that helped me figure it out :D