Windows
C#
新人プログラマ応援

プリントできる画像クラスを書いてみた

まえおき

この時期は新入さんが入ってきてプログラミングを基礎から教える機会も多いですね.プログラミングの入口といえばやっぱりHello, World!でしょうか.C#だとこんな感じですね.

System.Console.WriteLine("Hello, World!");

さて,プログラミングに不慣れな新人さんは怖いもの知らずにこんなコードも書いちゃいます.

System.Console.WriteLine(42);

いやいや文字列を渡さないと...と思いきや,しっかりと動いてくれますね.
C#のConsole.WriteLineはいろいろなオーバーロードが用意されています.上の例だとWriteLine(int value)が呼ばれます.

さて他のオーバーロードも見てみると,Write(object value)というのも用意されています.object型が渡せるということは何でもWriteLineできるということですね.ToStringメソッドの結果を標準出力に表示してくれます.

ここで,新人さんはこんなコードを書いちゃうかもしれません.

var bmp = new Bitmap("pronamachan.png");
System.Console.WriteLine(bmp);

実行結果はこんな感じです.

> foo.exe
System.Drawing.Bitmap

これは,,,,:scream:
新人さんはこんな地味な結果を期待してはいなかったはず.何とかしないとプログラミングに興味をなくしてしまうかもしれません!

ほんだい

前置きが長くなってしまいましたが,新人さんのモチベを下げないためにも,プリントできる画像クラスを書いてみました.

using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Linq;

namespace PrintableBitmap
{
    class PrintableBitmap
    {
        public Bitmap Bitmap { private set; get; }
        public PrintableBitmap(string filename) => Bitmap = new Bitmap(filename);
        public override string ToString()
        {
            int w = Bitmap.Width;
            int h = (int)(Bitmap.Height * 0.42);
            int height = 64;
            int width = (int)(w / (float)h * height);
            var resizebmp = new Bitmap(Bitmap, width, height);
            var text = new StringBuilder((width + 1) * height);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    var pxl = resizebmp.GetPixel(x, y);
                    text.Append(ToChar((byte)((pxl.R + pxl.B + pxl.G) / 3)));
                }
                text.Append('\n');
            }
            return text.ToString().TrimEnd('\n');
        }
        private char ToChar(byte pixel)
        {
            var table = new List<(byte p, char c)>
            {
                (249, ' '), (239, '`'), (233 , '.'), (217 , ','), (198 , '-'), (185, '~'), (173, '+'),
                (160, '<'), (147 , '>'), (134, 'o'), (109, '='), (83, '*'), (52, '%'), (13 , 'X'), (0, '@')
            };
            return table.SkipWhile(x => x.p > pixel).First().c;
        }
    }
}

クラスを作ってToStringメソッドをオーバーライドすればいいですね.いい感じに表示されるように高さを適当に縮めてサイズを決めて,RGBの平均値をとってグレースケールにしたあとASCII文字に変換してます.ちなみにBitmapクラスはsealedだったので継承せずにこんな感じで実装しています.あとC#は久しぶりに書いたのですが,C#7で導入されたタプルを使ってみました.便利ですね.

使ってみるとこんな感じです.

var bmp = new PrintableBitmap("pronamachan.png");
System.Console.WriteLine(bmp);

image2.png

まずまずじゃないでしょうか.これなら新人さんも楽しくプログラミングを続けられるかもですね:muscle: