colortool: add support for fg/bg color slot designation

This commit is contained in:
Yatao Li 2018-02-14 22:40:28 +08:00
parent e0248749eb
commit 22dd8a8e01
7 changed files with 126 additions and 49 deletions

View file

@ -0,0 +1,83 @@
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Linq;
namespace ColorTool
{
public class ColorScheme
{
public uint[] colorTable = null;
public uint? foreground = null;
public uint? background = null;
public int CalculateIndex(uint value) =>
colorTable.Select((color, idx) => Tuple.Create(color, idx))
.OrderBy(Difference(value))
.First().Item2;
private static Func<Tuple<uint, int>, double> Difference(uint c1) =>
// heuristic 1: nearest neighbor in RGB space
// tup => Distance(RGB(c1), RGB(tup.Item1));
// heuristic 2: nearest neighbor in RGB space
tup => Distance(HSV(c1), HSV(tup.Item1));
private static double Distance(uint[] c1c, uint[] c2c)
=> Math.Sqrt(c1c.Zip(c2c, (a, b) => Math.Pow((int)a - (int)b, 2)).Sum());
internal static uint[] RGB(uint c) => new[] { c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF };
internal static uint[] HSV(uint c)
{
var rgb = RGB(c).Select(_ => (int)_).ToArray();
int max = rgb.Max();
int min = rgb.Min();
int d = max - min;
int h = 0;
int s = (int)(255 * ((max == 0) ? 0 : d / (double)max));
int v = max;
if (d != 0)
{
double dh;
if (rgb[0] == max) dh = ((rgb[1] - rgb[2]) / (double)d);
else if (rgb[1] == max) dh = 2.0 + ((rgb[2] - rgb[0]) / (double)d);
else /* if (rgb[2] == max) */ dh = 4.0 + ((rgb[0] - rgb[1]) / (double)d);
dh *= 60;
if (dh < 0) dh += 360.0;
h = (int)(dh * 255.0 / 360.0);
}
return new[] { (uint)h, (uint)s, (uint)v };
}
internal void Dump()
{
Action<string, uint> _dump = (str, c) =>
{
var rgb = RGB(c);
var hsv = HSV(c);
Console.WriteLine($"{str} =\tRGB({rgb[0]}, {rgb[1]}, {rgb[2]}),\tHSV({hsv[0]}, {hsv[1]}, {hsv[2]})");
};
for (int i = 0; i < 16; ++i)
{
_dump($"Color[{i}]", colorTable[i]);
}
if (foreground != null)
{
_dump("FG ", foreground.Value);
}
if (background != null)
{
_dump("BG ", background.Value);
}
}
}
}

View file

@ -44,6 +44,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ColorScheme.cs" />
<Compile Include="ConsoleAPI.cs" />
<Compile Include="IniSchemeParser.cs" />
<Compile Include="ISchemeParser.cs" />

View file

@ -32,7 +32,7 @@ namespace ColorTool
public uint cbSize;
public COORD dwSize;
public COORD dwCursorPosition;
public short wAttributes;
public ushort wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
@ -56,6 +56,7 @@ namespace ColorTool
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleScreenBufferInfoEx(IntPtr ConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO_EX ConsoleScreenBufferInfoEx);
////////////////////////////////////////////////////////////////////////
public static uint RGB(int r, int g, int b)

View file

@ -7,6 +7,6 @@ namespace ColorTool
{
interface ISchemeParser
{
uint[] ParseScheme(string schemeName);
ColorScheme ParseScheme(string schemeName);
}
}

View file

@ -104,7 +104,7 @@ namespace ColorTool
return null;
}
public uint[] ParseScheme(string schemeName)
public ColorScheme ParseScheme(string schemeName)
{
bool success = true;
@ -147,7 +147,7 @@ namespace ColorTool
}
}
return colorTable;
return new ColorScheme { colorTable = colorTable };
}
}
}

View file

@ -176,7 +176,7 @@ namespace ColorTool
Console.BackgroundColor = currentBackground;
}
static bool SetProperties(uint[] colorTable)
static bool SetProperties(ColorScheme colorScheme)
{
CONSOLE_SCREEN_BUFFER_INFO_EX csbiex = CONSOLE_SCREEN_BUFFER_INFO_EX.Create();
int STD_OUTPUT_HANDLE = -11;
@ -187,7 +187,13 @@ namespace ColorTool
csbiex.srWindow.Bottom++;
for (int i = 0; i < 16; i++)
{
csbiex.ColorTable[i] = colorTable[i];
csbiex.ColorTable[i] = colorScheme.colorTable[i];
}
if(colorScheme.background != null && colorScheme.foreground != null)
{
int fgidx = colorScheme.CalculateIndex(colorScheme.foreground.Value);
int bgidx = colorScheme.CalculateIndex(colorScheme.background.Value);
csbiex.wAttributes = (ushort)(fgidx | (bgidx << 4));
}
SetConsoleScreenBufferInfoEx(hOut, ref csbiex);
}
@ -200,19 +206,20 @@ namespace ColorTool
}
return success;
}
static bool SetDefaults(uint[] colorTable)
static bool SetDefaults(ColorScheme colorScheme)
{
//TODO
RegistryKey consoleKey = Registry.CurrentUser.OpenSubKey("Console", true);
for (int i = 0; i < colorTable.Length; i++)
for (int i = 0; i < colorScheme.colorTable.Length; i++)
{
string valueName = "ColorTable" + (i < 10 ? "0" : "") + i;
consoleKey.SetValue(valueName, colorTable[i], RegistryValueKind.DWord);
consoleKey.SetValue(valueName, colorScheme.colorTable[i], RegistryValueKind.DWord);
}
Console.WriteLine(Resources.WroteToDefaults);
return true;
}
static void Main(string[] args)
{
if (args.Length < 1)
@ -258,19 +265,19 @@ namespace ColorTool
string schemeName = args[args.Length - 1];
uint[] colorTable = null;
ColorScheme colorScheme = null;
ISchemeParser[] parsers = { new XmlSchemeParser(), new IniSchemeParser() };
foreach (var parser in parsers)
{
uint[] table = parser.ParseScheme(schemeName);
if (table != null)
var scheme = parser.ParseScheme(schemeName);
if (scheme != null)
{
colorTable = table;
colorScheme = scheme;
break;
}
}
if (colorTable == null)
if (colorScheme == null)
{
Console.WriteLine(string.Format(Resources.SchemeNotFound, schemeName));
return;
@ -278,11 +285,11 @@ namespace ColorTool
if (setDefaults)
{
SetDefaults(colorTable);
SetDefaults(colorScheme);
}
if (setProperties)
{
SetProperties(colorTable);
SetProperties(colorScheme);
}
}

View file

@ -4,6 +4,8 @@
//
using System;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Xml;
using static ColorTool.ConsoleAPI;
@ -30,6 +32,8 @@ namespace ColorTool
"Ansi 11 Color", // BRIGHT_YELLOW
"Ansi 15 Color" // BRIGHT_WHITE
};
static string FG_KEY = "Foreground Color";
static string BG_KEY = "Background Color";
static string RED_KEY = "Red Component";
static string GREEN_KEY = "Green Component";
static string BLUE_KEY = "Blue Component";
@ -121,7 +125,7 @@ namespace ColorTool
}
public uint[] ParseScheme(string schemeName)
public ColorScheme ParseScheme(string schemeName)
{
XmlDocument xmlDoc = loadXmlScheme(schemeName); // Create an XML document object
if (xmlDoc == null) return null;
@ -129,39 +133,20 @@ namespace ColorTool
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[COLOR_TABLE_SIZE];
uint? fgColor = null, bgColor = null;
int colorsFound = 0;
bool success = false;
foreach (XmlNode tableEntry in children)
foreach (var tableEntry in children.OfType<XmlNode>().Where(_ => _.Name == "key"))
{
if (tableEntry.Name == "key")
{
int index = -1;
for (int i = 0; i < COLOR_TABLE_SIZE; i++)
{
if (PLIST_COLOR_NAMES[i] == tableEntry.InnerText)
{
index = i;
break;
}
}
if (index == -1)
{
continue;
}
uint rgb = 0; ;
XmlNode components = tableEntry.NextSibling;
success = parseRgbFromXml(components, ref rgb);
if (!success)
{
break;
}
else
{
colorTable[index] = rgb;
colorsFound++;
}
}
uint rgb = 0;
int index = -1;
XmlNode components = tableEntry.NextSibling;
success = parseRgbFromXml(components, ref rgb);
if (!success) { break; }
else if (tableEntry.InnerText == FG_KEY) { fgColor = rgb; }
else if (tableEntry.InnerText == BG_KEY) { bgColor = rgb; }
else if (-1 != (index = Array.IndexOf(PLIST_COLOR_NAMES, tableEntry.InnerText)))
{ colorTable[index] = rgb; colorsFound++; }
}
if (colorsFound < COLOR_TABLE_SIZE)
{
@ -172,8 +157,8 @@ namespace ColorTool
{
return null;
}
return colorTable;
return new ColorScheme { colorTable = colorTable, foreground = fgColor, background = bgColor };
}
}
}