将颜色转换为控制台颜色?

22

如何将System.Drawing.Color转换为类似的System.ConsoleColor


1
我建议看一下http://stackoverflow.com/questions/28211009,而不是跟随这里提供的(全部错误的)答案 >_> - mafu
1
这与 OP 所询问的相反。你提供的答案涉及将控制台颜色映射到系统绘图颜色,这比将任何系统绘图颜色映射到最接近的控制台颜色更简单。 - Plunder Bunny
8个回答

40

这里是控制台颜色十六进制值,由.NET 4.5转换而来。首先是程序:

using System;
using System.Drawing;

class Program
{
    static void Main(string[] args)
    {
        foreach (var n in Enum.GetNames(typeof(ConsoleColor)))
            Console.WriteLine("{0,-12} #{1:X6}", n, Color.FromName(n).ToArgb() & 0xFFFFFF);
    }
}

这里是输出结果。正如您所看到的,DarkYellow 的报告存在问题。它的全部32位显示为零。其它颜色的alpha通道都是0xFF。

Black        #000000
DarkBlue     #00008B
DarkGreen    #006400
DarkCyan     #008B8B
DarkRed      #8B0000
DarkMagenta  #8B008B
DarkYellow   #000000   <-- see comments
Gray         #808080
DarkGray     #A9A9A9
Blue         #0000FF
Green        #008000
Cyan         #00FFFF
Red          #FF0000
Magenta      #FF00FF
Yellow       #FFFF00
White        #FFFFFF

编辑:我有点过度兴奋,现在提供了一种从RGB转换到最近的ConsoleColor值的转换器。请注意,对于演示工具的依赖关系仅限于System.Windows.Media; 实际函数本身只引用System.Drawing

using System;
using System.Windows.Media;

class NearestConsoleColor
{
    static ConsoleColor ClosestConsoleColor(byte r, byte g, byte b)
    {
        ConsoleColor ret = 0;
        double rr = r, gg = g, bb = b, delta = double.MaxValue;

        foreach (ConsoleColor cc in Enum.GetValues(typeof(ConsoleColor)))
        {
            var n = Enum.GetName(typeof(ConsoleColor), cc);
            var c = System.Drawing.Color.FromName(n == "DarkYellow" ? "Orange" : n); // bug fix
            var t = Math.Pow(c.R - rr, 2.0) + Math.Pow(c.G - gg, 2.0) + Math.Pow(c.B - bb, 2.0);
            if (t == 0.0)
                return cc;
            if (t < delta)
            {
                delta = t;
                ret = cc;
            }
        }
        return ret;
    }

    static void Main()
    {
        foreach (var pi in typeof(Colors).GetProperties())
        {
            var c = (Color)ColorConverter.ConvertFromString(pi.Name);
            var cc = ClosestConsoleColor(c.R, c.G, c.B);

            Console.ForegroundColor = cc;
            Console.WriteLine("{0,-20} {1} {2}", pi.Name, c, Enum.GetName(typeof(ConsoleColor), cc));
        }
    }
}

输出结果(部分)...

测试输出


1
上面的DarkYellow十六进制代码(帖子开头)是错误的。它显示为#000000,但应该是#d7c32a(如此处所述=>http://rgb.to/color/6990/dark-yellow)。 - SuperJMN
1
@SuperJMN 请注意我在原始帖子中的评论:“正如您所看到的,DarkYellow 的报告存在问题。” - Glenn Slayden
@SuperJMN 没问题,我更新了答案以更加强调 System.Drawing 产生了误导性的结果,以防止未来的误解。 - Glenn Slayden
@JohnSmith "Tomāto," "tomăto?" - Glenn Slayden
@GlennSlayden? - John Smith
显示剩余2条评论

25
public static System.ConsoleColor FromColor(System.Drawing.Color c) {
    int index = (c.R > 128 | c.G > 128 | c.B > 128) ? 8 : 0; // Bright bit
    index |= (c.R > 64) ? 4 : 0; // Red bit
    index |= (c.G > 64) ? 2 : 0; // Green bit
    index |= (c.B > 64) ? 1 : 0; // Blue bit
    return (System.ConsoleColor)index;
}

ConsoleColors 枚举似乎使用 EGA 风格的调色板顺序,该顺序为:

index Brgb
  0   0000  dark black
  1   0001  dark blue
  2   0010  dark green
  3   0011  dark cyan
  4   0100  dark red
  5   0101  dark purple
  6   0110  dark yellow (brown)
  7   0111  dark white (light grey)
  8   1000  bright black (dark grey)
  9   1001  bright blue
 10   1010  bright green
 11   1011  bright cyan    
 12   1100  bright red
 13   1101  bright purple
 14   1110  bright yellow
 15   1111  bright white

您可以将24位颜色(或通过忽略alpha通道使其为32位颜色)大致映射到具有亮度分量的3位颜色。在这种情况下,如果任何System.Drawing.Color的红色,绿色或蓝色字节大于128,则设置“亮度”位,并且如果等效源字节高于64,则设置红色,绿色,蓝色位。


17

遗憾的是,即使Windows控制台支持RGB颜色,但Console类仅公开枚举ConsoleColor,这大大限制了您可以使用的可能颜色。如果您想要将一个Color结构映射到“最接近”的ConsoleColor,那将会很棘手。

但是,如果您想让命名的Color与相应的ConsoleColor匹配,您可以创建这样一个映射:

var map = new Dictionary<Color, ConsoleColor>();
map[Color.Red] = ConsoleColor.Red;
map[Color.Blue] = ConsoleColor.Blue;
etc...

如果性能并不那么重要,你可以通过字符串往返来实现。 (仅适用于命名颜色)

var color = Enum.Parse(typeof(ConsoleColor), color.Name);

编辑:这里是一个关于寻找颜色“接近度”的问题的链接


没有使用Console类,这是否可能? - Alon Gubkin
看起来不是这样。SetConsoleTextAttribute Win32 API 仅为 R、G、B 定义了 4 个 1 位标志,再加上一个强度位。这只支持 ConsoleColor 枚举支持的 16 种颜色。 - Josh
3
在Vista及以上版本中可以实现。您需要P/Invoke SetConsoleScreenBufferInfoEx(),http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/f8fb4005-475d-4edc-99b3-b74519ce5a4e - Hans Passant
你必须意识到,命名的颜色并不对应;特别是灰色、深灰色、绿色和深黄色会产生非常不同的颜色。 - Lucero

2
您可以使用反射。
public static class ColorHelpers
{
    public static bool TryGetConsoleColor(Color color, out ConsoleColor consoleColor)
    {
        foreach (PropertyInfo property in typeof (Color).GetProperties())
        {
            Color c = (Color) property.GetValue(null);

            if (color == c)
            {
                int index = Array.IndexOf(Enum.GetNames(typeof (ConsoleColor)), property.Name);
                if (index != -1)
                {
                    consoleColor = (ConsoleColor) Enum.GetValues(typeof (ConsoleColor)).GetValue(index);
                    return true;
                }
            }
        }
        consoleColor = default (ConsoleColor);
        return false;
    }
}

使用方法:

private static void Main()
{
    ConsoleColor c;
    if (ColorHelpers.TryGetConsoleColor(Color.Red, out c))
    {
        Console.ForegroundColor = c;
    }
}

2
在Vista及更高版本中,请参见SetConsoleScreenBufferInfoEx API函数。
有关用法示例,请参考另一个非常相似的StackOverflow问题的我的回答。(感谢Hans Passant提供原始答案)。

1
在6版本之前的PowerShell.exe中,以及任何ConsoleWindowClass窗口中,独特的默认“白底蓝字”颜色实际上是在$Host.UI.RawUI中检查时的DarkYellowDarkMagenta。这是因为ConsoleColor枚举值只是控制台颜色表中的索引,这个表是可配置的(参见this answer about DarkYellow)。
以下是默认控制台颜色表的十六进制RGB值:
Value Hex RGB Name
    0 #000000 Black
    1 #000080 DarkBlue
    2 #008000 DarkGreen
    3 #008080 DarkCyan
    4 #800000 DarkRed
    5 #012456 DarkMagenta
    6 #EEEDF0 DarkYellow
    7 #C0C0C0 Gray
    8 #808080 DarkGray
    9 #0000FF Blue
   10 #00FF00 Green
   11 #00FFFF Cyan
   12 #FF0000 Red
   13 #FF00FF Magenta
   14 #FFFF00 Yellow
   15 #FFFFFF White

1

通过使用Color类的GetHueGetBrightnessGetSaturation方法,可以实现简单而有效的方法。

public static ConsoleColor GetConsoleColor(this Color color) {
    if (color.GetSaturation() < 0.5) {
        // we have a grayish color
        switch ((int)(color.GetBrightness()*3.5)) {
        case 0:  return ConsoleColor.Black;
        case 1:  return ConsoleColor.DarkGray;
        case 2:  return ConsoleColor.Gray;
        default: return ConsoleColor.White;
        }
    }
    int hue = (int)Math.Round(color.GetHue()/60, MidpointRounding.AwayFromZero);
    if (color.GetBrightness() < 0.4) {
        // dark color
        switch (hue) {
        case 1:  return ConsoleColor.DarkYellow;
        case 2:  return ConsoleColor.DarkGreen;
        case 3:  return ConsoleColor.DarkCyan;
        case 4:  return ConsoleColor.DarkBlue;
        case 5:  return ConsoleColor.DarkMagenta;
        default: return ConsoleColor.DarkRed;
        }
    }
    // bright color
    switch (hue) {
    case 1:  return ConsoleColor.Yellow;
    case 2:  return ConsoleColor.Green;
    case 3:  return ConsoleColor.Cyan;
    case 4:  return ConsoleColor.Blue;
    case 5:  return ConsoleColor.Magenta;
    default: return ConsoleColor.Red;
    }
}

请注意,控制台的颜色名称与众所周知的颜色不匹配。因此,如果您测试一个颜色映射方案,您必须记住,例如,在众所周知的颜色中,灰色是深灰色,浅灰色是灰色,绿色是深绿色,酸橙色是纯绿色,橄榄色是深黄色。

1

简单的一个...

Public Shared Function ColorToConsoleColor(cColor As Color) As ConsoleColor
        Dim cc As ConsoleColor
        If Not System.Enum.TryParse(Of ConsoleColor)(cColor.Name, cc) Then
            Dim intensity = If(Color.Gray.GetBrightness() < cColor.GetBrightness(), 8, 0)
            Dim r = If(cColor.R >= &H80, 4, 0)
            Dim g = If(cColor.G >= &H80, 2, 0)
            Dim b = If(cColor.B >= &H80, 1, 0)

            cc = CType(intensity + r + g + b, ConsoleColor)
        End If
        Return cc
    End Function

也许现在有点晚了,但那不是C#。在我看来,它看起来像VB.NET。 - Yellowsink

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接