【C#】ネコのイラストを描く（Bitmap）

# はじめに

これまでにC#を使った画像処理の記事を3つほど書きましたが、画像の描画については「1ピクセルずつ描画する」「同じ図形を一定のパターンで描画し続ける」というレベルに留まっていました。

そこで今回はお絵かきソフトでイラストを作る時のように、「複数の図形を組み合わせて猫のイラストを描画する」という処理を作ってみました。

* [【C#】画像生成の基本（Bitmap）](https://qiita.com/nkojima/items/ab776e03ae80a2b2e3bb)
* [【C#】2つの画像の差分を得る（Bitmap）](https://qiita.com/nkojima/items/1fb901c13f9707808bc0)
* [【C#】市松模様を描画する（Bitmap）](https://qiita.com/nkojima/items/b61599c02f4d91ab6269)

# コード

* [長方形、多角形、楕円、円弧、扇形を描く](https://dobon.net/vb/dotnet/graphics/drawrectangle.html)と[塗りつぶした図形を描く](https://dobon.net/vb/dotnet/graphics/fillrectangel.html)という記事を参考にして、猫の顔のパーツを1つずつ作って重ね合わせました。
 * 顔と目は楕円、耳は三角形（＝ポリゴン）、口は円弧、髭は直線とそれぞれ異なる図形を使いました。
 * 顔と耳は塗りつぶしと
* レイヤーが無く、「図形を描画する順番＝図形の重ね合わせの順序」となるため、描画する順番を少し工夫しました。
 * 目、口、髭は最前面にならないといけないので、一番最後に描画しています。
* コーディングよりも苦労したのは顔の各パーツのサイズや配置でした。
 * 絵心が無いので、顔のパーツとなる図形のサイズや位置を少しずつ変えて調整しました...

```c#:CatDrawer.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpStudy.Image
{
    public class CatDrawer
    {
        /// <summary>
        /// 猫のイラストを描画するBitmapオブジェクト。
        /// </summary>
        Bitmap bmp;

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        public CatDrawer()
        {
            // キャンバスサイズは横500px、縦400px。
            this.bmp = new Bitmap(500, 400);
        }

        /// <summary>
        /// 猫のイラストを描画する。
        /// </summary>
        public void Draw(string filePath)
        {
            using (Graphics g = Graphics.FromImage(bmp))
            {
                // 顔の輪郭を描く。
                g.DrawEllipse(new Pen(GetFaceBorderColor(), 5), GetFacePosition());
                g.DrawPolygon(new Pen(GetFaceBorderColor(), 5), GetLeftEarPosition());
                g.DrawPolygon(new Pen(GetFaceBorderColor(), 5), GetRightEarPosition());

                // 顔の色を塗る。
                g.FillEllipse(new SolidBrush(GetFaceColor()), GetFacePosition());
                g.FillPolygon(new SolidBrush(GetFaceColor()), GetLeftEarPosition());
                g.FillPolygon(new SolidBrush(GetFaceColor()), GetRightEarPosition());

                // その他の顔のパーツを作る。
                g.FillEllipse(Brushes.Black, 190, 200, 15, 50);  // 右目
                g.FillEllipse(Brushes.Black, 295, 200, 15, 50);  // 左目
                g.DrawArc(new Pen(Color.Black, 5), new Rectangle(190, 270, 60, 60), 0, 180);    // 口（右側）
                g.DrawArc(new Pen(Color.Black, 5), new Rectangle(250, 270, 60, 60), 0, 180);    // 口（左側）
                g.DrawLine(new Pen(Color.Black, 5), new Point(5, 210), new Point(120, 225));    // ひげ1（右側）
                g.DrawLine(new Pen(Color.Black, 5), new Point(0, 250), new Point(120, 250));    // ひげ2（右側）
                g.DrawLine(new Pen(Color.Black, 5), new Point(5, 290), new Point(120, 275));    // ひげ3（右側）


                g.DrawLine(new Pen(Color.Black, 5), new Point(380, 225), new Point(495, 210));    // ひげ1（左側）
                g.DrawLine(new Pen(Color.Black, 5), new Point(380, 250), new Point(500, 250));    // ひげ2（左側）
                g.DrawLine(new Pen(Color.Black, 5), new Point(380, 275), new Point(495, 290));    // ひげ3（左側）

            }

            // 画像をPNG形式で保存する。
            bmp.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);
        }

        /// <summary>
        /// 猫の顔の色を返す。
        /// </summary>
        /// <returns></returns>
        private Color GetFaceColor()
        {
            return Color.FromArgb(255, 221, 209, 174);
        }

        /// <summary>
        /// 猫の顔の枠線の色を返す。
        /// </summary>
        /// <returns></returns>
        private Color GetFaceBorderColor()
        {
            return Color.FromArgb(255, 182, 156, 78);
        }
        
        /// <summary>
        /// 顔を描画する座標（外接する長方形）を返す。
        /// </summary>
        /// <returns></returns>
        private Rectangle GetFacePosition()
        {
            return new Rectangle(50, 100, 400, 280);
        }

        /// <summary>
        /// 左耳を描画する座標を返す。
        /// </summary>
        /// <returns></returns>
        private Point[] GetLeftEarPosition()
        {
            return new Point[] {
                new Point(430, 30),
                new Point(430, 210),
                new Point(300, 110)
            };
        }

        /// <summary>
        /// 右耳を描画する座標を返す。
        /// </summary>
        /// <returns></returns>
        private Point[] GetRightEarPosition()
        {
            return new Point[] {
                new Point(70, 30),
                new Point(70, 210),
                new Point(200, 110)
            };
        }
    }
}
```

# 実行用のコード


```c#:CatDrawerTest.cs
using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CSharpStudy.Image;

namespace CSharpStudyTest.Image
{
    /// <summary>
    /// GrayConverterクラスのテストコード
    /// </summary>
    [TestClass]
    public class CatDrawerTest
    {
        private const string BITMAP_PATH = @"C:\Users\NKOJIMA\source\repos\CSharpStudy\CSharpStudy\Image\generated_cat.png";

        [TestMethod]
        public void TestMethod1()
        {
            CatDrawer drawer = new CatDrawer();
            drawer.Draw(BITMAP_PATH);
        }
    }
}
```

# 生成された画像

* 茶トラが好きなので茶色にしました...というのは嘘で、体の模様が三毛猫や鉢割れだと描画するのが大変なので、茶色一色で仕上げました。
* アンチエイリアスをどうやって入れるかが分からなかったので、線の周囲がギザギザになってしまったのが残念な点です。

![generated_cat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312166/de8ce3ce-3871-8196-f383-78c9526dc0e7.png)

# 参考URL

* [長方形、多角形、楕円、円弧、扇形を描く](https://dobon.net/vb/dotnet/graphics/drawrectangle.html)
* [塗りつぶした図形を描く](https://dobon.net/vb/dotnet/graphics/fillrectangel.html)
* [【C#】画像生成の基本（Bitmap）](https://qiita.com/nkojima/items/ab776e03ae80a2b2e3bb)
* [【C#】2つの画像の差分を得る（Bitmap）](https://qiita.com/nkojima/items/1fb901c13f9707808bc0)
* [【C#】市松模様を描画する（Bitmap）](https://qiita.com/nkojima/items/b61599c02f4d91ab6269)