はじめに
これまでにC#を使った画像処理の記事を3つほど書きましたが、画像の描画については「1ピクセルずつ描画する」「同じ図形を一定のパターンで描画し続ける」というレベルに留まっていました。
そこで今回はお絵かきソフトでイラストを作る時のように、「複数の図形を組み合わせて猫のイラストを描画する」という処理を作ってみました。
コード
- 長方形、多角形、楕円、円弧、扇形を描くと塗りつぶした図形を描くという記事を参考にして、猫の顔のパーツを1つずつ作って重ね合わせました。
- 顔と目は楕円、耳は三角形(=ポリゴン)、口は円弧、髭は直線とそれぞれ異なる図形を使いました。
- 顔と耳は塗りつぶしと
- レイヤーが無く、「図形を描画する順番=図形の重ね合わせの順序」となるため、描画する順番を少し工夫しました。
- 目、口、髭は最前面にならないといけないので、一番最後に描画しています。
- コーディングよりも苦労したのは顔の各パーツのサイズや配置でした。
- 絵心が無いので、顔のパーツとなる図形のサイズや位置を少しずつ変えて調整しました...
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)
};
}
}
}
実行用のコード
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);
}
}
}
生成された画像
- 茶トラが好きなので茶色にしました...というのは嘘で、体の模様が三毛猫や鉢割れだと描画するのが大変なので、茶色一色で仕上げました。
- アンチエイリアスをどうやって入れるかが分からなかったので、線の周囲がギザギザになってしまったのが残念な点です。