WisdomSoft - for your serial experiences.

2.5 ブラシ

図形の領域内は指定されたブラシによって塗りつぶされます。単色ブラシだけではなく、模様やグラデーションで領域を塗りつぶすことができます。

2.5.1 模様ブラシ

閉じた図形の内部を塗りつぶすブラシは、ペンよりも多少複雑な構造です。「2.4 塗りつぶす コード1」で SolidBrush クラスを利用しましたが、これ以外にもブラシとして利用できるクラスがいくつか存在します。

すべてのブラシは Brush 抽象クラスを継承しています。Fill 系メソッドはブラシを Brush 型として受けるため、Brush 抽象クラスを継承するすべてのクラスをブラシとして利用することができます。SolidBrush 以外のブラシの一部は System.Drawing.Drawing2D 名前空間に配置されています。

何らかの模様で塗りつぶすにはハッチブラシを使います。ハッチブラシは System.Drawing.Drawing2D.HatchBrush クラスで表され、模様、前景色、背景色を提供します。このクラスのオブジェクトを Fill 系メソッドに渡せば、閉じた図形の内部が指定した模様と色で塗りつぶされます。

System.Drawing.Drawing2D.HatchBrush クラス
System.Object
    System.MarshalByRefObject
        System.Drawing.Brush
            System.Drawing.Drawing2D.HatchBrush
public sealed class HatchBrush : Brush

このクラスのコンストラクタは次のようなものがあります。

HatchBrush クラスのコンストラクタ
public HatchBrush(HatchStyle hatchstyle, Color foreColor)
public HatchBrush(HatchStyle hatchstyle, Color foreColor, Color backColor)

hachstyle パラメータには、模様を定義する HatchStyle 列挙体のいずれかのメンバを指定します。foreColor パラメータには模様の前景色、backColor パラメータには背景色を指定します。backColor パラメータを省略した場合は黒で初期化されます。

ハッチブラシの代表的な模様は網の目やクロスだと思われますが、このほかにも多くの模様が定義されています。模様を指定するにはは System.Drawing.Drawing2D.HatchStyle 列挙体を使います。この列挙体では、模様を表すメンバが多く宣言されています。

System.Drawing.Drawing2D.HatchStyle 列挙体
[Serializable]
public enum HatchStyle

例えば、網の目模様のような交差する斜め線は DiagonalCross メンバを、水平線と垂直線の十字模様は Cross を指定します。

コード1
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);
		Brush brush1 = new HatchBrush(HatchStyle.DiagonalCross , Color.Black , Color.White);
		Brush brush2 = new HatchBrush(HatchStyle.Cross , Color.Blue , Color.Red);
		Brush brush3 = new HatchBrush(HatchStyle.ZigZag , Color.CadetBlue , Color.Pink);
		Brush brush4 = new HatchBrush(HatchStyle.Sphere , Color.Green , Color.Khaki);
		e.Graphics.FillRectangle(brush1, 10, 10, 400, 100);
		e.Graphics.FillRectangle(brush2, 10, 120, 400, 100);
		e.Graphics.FillRectangle(brush3, 10, 230, 400, 100);
		e.Graphics.FillRectangle(brush4, 10, 340, 400, 100);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード1 実行結果

コード1は、異なる HatchStyle で複数のハッチブラシを作成して矩形を描画しています。FillRectangle() メソッドの描画結果を見ると、矩形の内部が模様で塗りつぶされていることを確認することができます。

2.5.2 グラデーション

図形の内部を2色以上の線形グラデーションで塗りつぶすには System.Drawing.Drawing2D.LinearGradientBrush クラスを使います。

System.Drawing.Drawing2D.LinearGradientBrush クラス
System.Object
    System.MarshalByRefObject
        System.Drawing.Brush
            System.Drawing.Drawing2D.LinearGradientBrush
public sealed class LinearGradientBrush : Brush

グラデーションとは、例えば黒から赤へ徐々に変化する塗りつぶし方で、その間は2色の中間色となります。ゲームなどのマルチメディアアプリケーションでは、このような演出を頻繁に使うと考えられます。逆に、レイアウトよりも実用性や速度を重視するビジネスアプリケーションでは速度が低下するため不必要な部分で使うべきではありません。

LinearGradientBrush クラスのコンストラクタでは、グラデーションを行う2点と2色を設定します。グラデーションは開始点から開始色で始まり、終了点に向かって徐々に終了色と混ざります。

LinearGradientBrush クラスのコンストラクタ
public LinearGradientBrush(
    Point point1, Point point2,
    Color color1, Color color2
)

point1 パラメータには開始点を、point2 パラメータには終了点を指定します。描画する閉じた図形の位置やサイズに関係なく、描画する図形のコントロールのクライアント座標でこの開始点と終了点が使われます。color1 パラメータには開始色、color2 パラメータには終了色を指定します。

コード2
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);
		Point pt1 = new Point(10, 10);
		Point pt2 = new Point(410, 210);
		Brush brush1 = new LinearGradientBrush(pt1, pt2, Color.Black, Color.Red);
		e.Graphics.FillRectangle(brush1, pt1.X, pt1.Y, pt2.X - pt1.X, pt2.Y - pt1.Y);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード2 実行結果

コード2は、矩形の左上隅が黒、右下隅が赤で、その間の色が徐々にグラデーションで塗りつぶされるというプログラムです。グラデーションの座標と図形の座標は個別に行う必要があるため、双方の座標を適切に調整しなければなりません。このプログラムでは、ブラシに設定する座標と描画に使う座標を、同じ pt1 と pt2 オブジェクトから取得することで整合性を保たせています。

LinearGradientBrush クラスで設定されている開始点と終了点の間はグラデーションになりますが、終了点以降の座標が塗りつぶされた場合は、再び開始点と同じ色から開始してグラデーションを繰り返します。例えば、原点 (0, 0) から開始し、終了点が (50, 0) の場合であれば、X 座標 50 以降の図形は開始点からの色で同じように繰り返します。X 座標 50 から 100 までの塗りつぶしは X 座標 0 から 50 までの塗りつぶしと同じになります。

コード3
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);
		Point pt1 = new Point(0, 0);
		Point pt2 = new Point(0, 50);
		Brush brush = new LinearGradientBrush(pt1, pt2, Color.Blue, Color.White);
		e.Graphics.FillRectangle(brush, 0, 0, 400, 300);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード3 実行結果

コード3は、(0, 0) から (0, 50) に向かってグラデーションを行う LinearGradientBrush オブジェクトを生成し、これで大きな矩形を描画しています。矩形は (0, 0) から (400, 300) までを塗りつぶすため、グラデーションが繰り返していることが確認できます。

上記のような処理をする場合は座標でのグラデーション指定が便利ですが、図形の内部を単純な2色のグラデーションで塗りつぶすだけであれば、図形の描画で指定する座標とサイズが LinearGradientBrush オブジェクトに指定する座標と関係を持っていないことが問題になります。

例えば、プログラムの仕様の変更で矩形のサイズが変更される場合、これに合わせて LinearGradientBrush オブジェクトの座標も変更させなければなりません。LinearGradientBrush の変更を忘れると、矩形の塗りつぶしが正しく行われなくなり、これがバグとして残る可能性があります。これを防ぐには Point ではなく Rectangle を受け取る LinearGradientBrush コンストラクタを使います。

LinearGradientBrush クラスのコンストラクタ
public LinearGradientBrush(
    Rectangle rect, Color color1, Color color2,
    LinearGradientMode linearGradientMode
)

rect パラメータにはグラデーションを行う範囲を表す矩形を、color1 パラメータに開始色、color2 パラメータに終了色を指定します。linearGradientMode パラメータには System.Drawing.Drawing2D.LinearGradientMode 列挙体のメンバを指定します。

System.Drawing.Drawing2D.LinearGradientMode 列挙体
public enum LinearGradientMode

LinearGradientMode 列挙対には、グラデーションの方向を表すメンバが宣言されています。グラデーションの開始点と終了点の位置は linearGradientMode に指定された値によって決定されます。

LinearGradientBrush クラスのコンストラクタは、このようにオーバーロードされているため Point でグラデーションの開始点と終了点を指定する方法と、Rectangle で指定する方法があります。多くの Fill 系メソッドは Rectangle オブジェクトで矩形を選択するので、LinearGradientBrush の設定と描画の Rectangle オブジェクトを共有することで整合性を高めることができるでしょう。

コード4
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);
		Rectangle rect = new Rectangle(10, 10, 400, 200);
		Brush brush = new LinearGradientBrush(
			rect, Color.Black, Color.Red, LinearGradientMode.BackwardDiagonal
		);
		e.Graphics.FillRectangle(brush, rect);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード4 実行結果

コード4では、右上から左下へのグラデーションを指定する LinearGradientMode.BackwardDiagonal を使って LinearGradientBrush オブジェクトを初期化しています。描画する矩形と LinearGradientBrush オブジェクトに設定する矩形は同じ rect オブジェクトなので、すなわち描画する矩形に対して確実にグラデーションをかけることができます。

2.5.3 多色グラデーション

さらに複雑なグラデーション技法に多色グラデーションがあります。多色グラデーションは2色以上の複数の色を一定の間隔で徐々に変化させていきます。例えば赤→青→白というような形で、複数の色で矩形内をグラデーションしたい場合に利用します。

多色グラデーションを行うには LinearGradientBrush クラスの InterpolationColors プロパティを正しく設定しなければなりません。

LinearGradientBrush クラス InterpolationColors プロパティ
public ColorBlend InterpolationColors { get; set; }

このプロパティで受け渡すのは System.Drawing.Drawing2D.ColorBlend クラスのオブジェクトです。ColorBlend クラスは、多色グラデーションで使われる任意の数の Color オブジェクトと、それぞれの色の間隔を比で表す float 値の配列を提供します。

System.Drawing.Drawing2D.ColorBlend クラス
System.Object
    System.Drawing.Drawing2D.ColorBlend
public sealed class ColorBlend

ColorBlend オブジェクトの取り扱いはやや複雑なので注意が必要です。プロパティに不正な値を設定して描画しようとすると冷害が発生してしまいます。このクラスのコンストラクタにはパラメータを渡す必要はありません。あらかじめ色数が確定している場合は数をパラメータに受けるコンストラクタもオーバーロードされています。

このクラスにおいて重要なのは Colors プロパティPositions プロパティです。

ColorBlend クラス Colors プロパティ
public Color[] Colors { get; set; }
ColorBlend クラス Positions プロパティ
public float[] Positions { get; set; }

例えば、赤→青→白とグラデーションする場合は、次のような Colors プロパティで Color.Red、Color.Blue、Color.White の順に格納されている Color クラスの配列を渡します。

Color[] colors = { Color.Red, Color.Blue, Color.White };

ただし、これだけではそれぞれの色を区切る間隔が不明なので、Positions に比で間隔を設定する必要があります。Positions プロパティの制約として、開始色は 0.0F から始まり、終了色は 1.0F でなければなりません。Positions プロパティに設定する float 型の配列は、それぞれの要素が Colors プロパティの配列に対応します。赤→青→白のグラデーションを均等に塗る場合は次のような float 配列を設定することになるでしょう。

float[] positions = { 0.0F, 0.5F, 1.0F };

色が何色指定されているかに問わず Positions プロパティに設定する配列の先頭は 0、末尾は 1 でなければならないということに注意してください。どのような場合でも Positions[0] = 0.0F、Positions[Positions.Length - 1] = 1.0F が成り立ちます。

コード5
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);

		Color[] colors = { Color.Black, Color.Red, Color.Green, Color.Blue, Color.White };
		float[] positions = { 0.0F, 0.25F, 0.5F, 0.75F, 1.0F };

		ColorBlend blend = new ColorBlend();
		blend.Colors = colors;
		blend.Positions = positions;

		LinearGradientBrush brush = new LinearGradientBrush(
			new Point(0, 0), new Point(400, 0) , Color.Black, Color.White
		);
		brush.InterpolationColors = blend;
		e.Graphics.FillRectangle(brush, 0, 0, 400, 300);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード5 実行結果

コード5では、黒→赤→緑→青→白の順番で多色グラデーションを行っています。それぞれが均等になるように、LinearGradientBrush の開始点から終了点までの間で、黒が 0% すなわち開始点から始まって赤が 25%、緑が 50% すなわち中央、青が 75%、白が 100% すなわち終了点に配置されます。

LinearGradientBrush オブジェクトの Colors プロパティと Positions プロパティは両方とも必ず適切な配列を設定しなければなりません。また、今プロパティに設定する配列は互いに関係するため、配列のサイズが同じでなければなりません。