引言
看到酷安上有這樣一個活動,萌生了用 C# 生成字符畫的想法档痪,先放出原圖字柠。
§1 黑白
將圖像轉(zhuǎn)換成字符畫在 C# 中很簡單探越,思路大致如下:
- 加載圖像,逐像素提取明度窑业。
- 根據(jù)明度映射到字符列表中對應的字符钦幔。
- 輸出字符。
GetChars
函數(shù)負責將傳入的圖像按一定比例導出字符畫的字符串常柄。hScale
為橫向比例鲤氢,即每次跳過的橫向像素數(shù);vScale
為縱向比例西潘,在控制臺中輸出推薦為hScale
的 2 倍卷玉。
private static string GetChars(Bitmap bmp, int hScale, int vScale)
{
StringBuilder sb = new StringBuilder();
for (int h = 0; h < bmp.Height; h += vScale)
{
for (int w = 0; w < bmp.Width; w += hScale)
{
Color color = bmp.GetPixel(w, h);
float brightness = color.GetBrightness(); // 這里的明度也可以使用 RGB 分量合成
char ch = GetChar(brightness);
sb.Append(ch);
}
sb.AppendLine();
}
return sb.ToString();
}
GetChar
負責做明度到字符的映射工作,由于brightness
取值范圍為 [0, 1]喷市,所以需要乘 0.99 防止index
越界相种。listChar
是可用的字符列表,自定義只需遵循一條規(guī)則品姓,從左往右字符應該越來越復雜寝并。
private static readonly List<char> listChar =
new List<char>() { ' ', '^', '+', '!', '$', '#', '*', '%', '@' };
private static char GetChar(float brightness)
{
int index = (int)(brightness * 0.99 * listChar.Count);
return listChar[index];
}
調(diào)用函數(shù),輸出結(jié)果腹备。初具雛形衬潦,黑白樣式減少了不少神韻。
§2 有限彩色
2.1 Console
一開始希望通過改變Console.ForegroundColor
屬性來改變色彩植酥,但是殘酷的事實是這個屬性只接受ConsoleColor
枚舉中的 16 個顏色镀岛。將全彩圖片映射成 16 色輸出,費力不討好友驮,遂求其他方法漂羊。
2.2 Colorful.Console
找到了一個彩色控制臺的庫 Colorful Console⌒读簦看網(wǎng)頁介紹挺厲害的拨与,RGB、漸變色艾猜、多色輸出……妥了买喧,這肯定符合我們的需要捻悯,通過 nuget 可以直接添加到項目中。
在引用區(qū)域加一行淤毛,就可以把代碼中的Console
用ColorfulConsole
替代今缚。
using Console = Colorful.Console;
GetChars
函數(shù)需要改變一下,因為每個字符的顏色不同低淡,所以要在函數(shù)里面增加輸出姓言。好簡單,輸出內(nèi)容后面加個顏色的參數(shù)就可以了蔗蹋。
private static string GetChars(Bitmap bmp, int hScale, int vScale, bool shouldDraw)
{
StringBuilder sb = new StringBuilder();
for (int h = 0; h < bmp.Height; h += vScale)
{
for (int w = 0; w < bmp.Width; w += hScale)
{
Color color = bmp.GetPixel(w, h);
float brightness = color.GetBrightness();
char ch = GetChar(brightness);
if (shouldDraw)
{
Console.Write(ch, color);
}
sb.Append(ch);
}
if (shouldDraw) { Console.WriteLine(); }
sb.AppendLine();
}
return sb.ToString();
}
然而現(xiàn)實再一次殘酷起來何荚,輸出結(jié)果一片黑,使用白色背景看一看猪杭。
可能看不清餐塘,不過牛角的位置確實有幾個字符不是黑色,那我們換張圖片來看皂吮〗渖担可以看到確實有彩色輸出,不過效果尚可的僅限最前面的一些字符蜂筹,之后白色完全不見了需纳。
在測試官網(wǎng)上的操作都沒有問題后,我陷入了深深的思考艺挪,NMD不翩,為什么?直到我看到了官網(wǎng)上最下面的一段話麻裳。
Colorful.Console can only write to the console in 16 different colors (including the black that's used as the console's background, by default!) in a single console session. This is a limitation of the Windows console itself (ref: MSDN), and it's one that I wasn't able to work my way around. If you know of a workaround, let me know!
Colorful.Console
只能同時輸出 16 種顏色慌盯,果然原版Console
能接受的ConsoleColor
枚舉也是 16 種顏色是算計好的〉嗥鳎可惡,難道只能到此為止了嗎俱箱?
我不甘心国瓮。
§3 全彩
終于,我找到了這個 visual studio - Custom text color in C# console application? - Stack Overflow狞谱。在下面 Alexei Shcherbakov 和 Olivier Jacot-Descombes 的回答中乃摹,我看到了希望。
Since Windows 10 Anniversary Update, console can use ANSI/VT100 color codes
You need set flagENABLE_VIRTUAL_TERMINAL_PROCESSING(0x4)
by SetConsoleMode
Use sequences:
"\x1b[48;5;" + s + "m"
- set background color by index in table (0-255)
"\x1b[38;5;" + s + "m"
- set foreground color by index in table (0-255)
"\x1b[48;2;" + r + ";" + g + ";" + b + "m"
- set background by r,g,b values
"\x1b[38;2;" + r + ";" + g + ";" + b + "m"
- set foreground by r,g,b values
Important notice: Internally Windows have only 256 (or 88) colors in table and Windows will used nearest to (r,g,b) value from table.
有了這個神奇的ENABLE_VIRTUAL_TERMINAL_PROCESSING(0x4)
跟衅,就可以隨意修改前后景顏色了孵睬。說干就干,首先需要增加一個NativeMethods
類伶跷,用來 Call kernel32.dll
里的 3 個函數(shù)掰读。
using System;
using System.Runtime.InteropServices;
namespace Img2ColorfulChars
{
internal class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleMode(IntPtr handle, out int mode);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int handle);
}
}
然后在主程序Main
函數(shù)里一開始增加以下三行秘狞,-11
代表STD_OUTPUT_HANDLE
(GetStdHandle function - Windows Console | Microsoft Docs), 0x4
就是上面所說的ENABLE_VIRTUAL_TERMINAL_PROCESSING
蹈集。
var handle = NativeMethods.GetStdHandle(-11);
NativeMethods.GetConsoleMode(handle, out int mode);
NativeMethods.SetConsoleMode(handle, mode | 0x4);
因為我們要修改的是字符的前景色烁试,所以把上一節(jié)中GetChars
函數(shù)里的
Console.Write(ch, color);
替換為
Console.Write($"\x1b[38;2;{color.R};{color.G};{color.B}m{ch}");
輸出結(jié)果如下,完美拢肆。
尾聲
多彩的細節(jié)减响,巧妙的象征寒跳,這就是青春啊(不是)莲蜘。
而這個項目真正的用法:
項目鏈接
-
github.com/Roy0309/Img2ColorfulChars
僅全彩版本,適用于1.0版