4.7 ブラシ
4.7.1 固定色での塗りつぶし
これまでも何度か利用してきましたが、図形の内部を塗りつぶす方法を提供するにはブラシを利用します。ブラシは抽象クラスである System.Windows.Media.Brush クラスをルートとして、塗りつぶす方法に応じてサブクラスに派生しています。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush
[TypeConverterAttribute(typeof(BrushConverter))] [LocalizabilityAttribute(LocalizationCategory.None, Readability=Readability.Unreadable)] public abstract class Brush : Animatable, IFormattable
これまでは Brushes クラスが提供する静的プロパティが返す固定色のブラシを利用してきましたが System.Windows.Media.SolidColorBrush クラスのオブジェクトを 独自にインスタンス化することも可能です。一般的な色に関しては Brushes から取得した方が簡単ですが、詳細に色を指定したい場合は独自にオブジェクトを作ります。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush System.Windows.Media.SolidColorBrush
public sealed class SolidColorBrush : Brush
SolidColorBrush ブラシクラスのコンストラクタには、塗りつぶしの色を表す Color 構造体のオブジェクトを渡すこともできます。
public SolidColorBrush () public SolidColorBrush (Color color)
コンストラクタで指定した Color オブジェクトは Color プロパティから設定または取得できます。
public Color Color { get; set; }
System.Windows.Media.Color 構造体は、数値を用いて色を表す情報を提供します。色の表現方法は、基本的に HTML やこれまでのプログラミングモデルで使われていた方法と同じで、不透明度、赤要素、緑要素、青要素のそれぞれを 0 ~ 255 までの 1 バイトで表現します。
[TypeConverterAttribute(typeof(ColorConverter))] [LocalizabilityAttribute(LocalizationCategory.None, Readability=Readability.Unreadable)] public struct Color : IFormattable, IEquatable<Color>
Color 構造体のオブジェクトは、コンストラクタからではなく Color 構造体が提供している静的な FromRgb() メソッド、または FromArgb() メソッドから取得します。
public static Color FromRgb (byte r, byte g, byte b)
public static Color FromArgb (byte a, byte r, byte g, byte b)
a パラメータには不透明度を表すアルファ要素、r には赤要素、g には緑要素、b には青要素をそれぞれ指定します。不透明度は 0 であれば完全な透明を表し、255 であれば不透明であることを表します。各色要素は、0 であるほどその色合いが弱く、255 に近いほど強くなります。
Color オブジェクトが表す色は A プロパティ、R プロパティ、G プロパティ、B プロパティからそれぞれ設定・取得することができます。
public byte A { get; set; }
public byte R { get; set; }
public byte G { get; set; }
public byte B { get; set; }
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color = Color.FromArgb(0xFF, 0xE0, 0xE0, 0xFF); SolidColorBrush brush = new SolidColorBrush(color); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード1では、独自に SolidColorBrush クラスのオブジェクトを作成し、それを Window オブジェクトの背景色に設定しています。これまでは Brushes クラスから取得していましたが、Brushes クラスのプロパティで提供されていない色を利用するには、この方法でオブジェクトを用意する必要があります。
Color 構造体は不透明度を表すアルファを設定することができるため、ブラシで塗りつぶす空間を半透明にすることができます。例えば、図形の内部を半透明で塗りつぶすことによって、複数の図形を重ね合わせることができます。
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Shapes; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromArgb(0x80, 0xFF, 0, 0); Color color2 = Color.FromArgb(0x80, 0, 0, 0xFF); SolidColorBrush brush1 = new SolidColorBrush(color1); SolidColorBrush brush2 = new SolidColorBrush(color2); Ellipse ellipse1 = new Ellipse(); ellipse1.Fill = brush1; ellipse1.Width = 400; ellipse1.Height = 200; Canvas.SetLeft(ellipse1, 10); Canvas.SetTop(ellipse1, 10); Ellipse ellipse2 = new Ellipse(); ellipse2.Fill = brush2; ellipse2.Width = 400; ellipse2.Height = 200; Canvas.SetLeft(ellipse2, 200); Canvas.SetTop(ellipse2, 10); Canvas canvas = new Canvas(); canvas.Children.Add(ellipse1); canvas.Children.Add(ellipse2); Window wnd = new Window(); wnd.Content = canvas; Application app = new Application(); app.Run(wnd); } }
コード2は、赤いブラシと青いブラシを用意していますが、それぞれの Color オブジェクトのアルファ値は 0x80 となっているため、これらの色は半透明に描画されます。実行結果を見て分かるように、2 つの楕円は半透明に描画され、楕円が重なる部分は色が合成されています。
4.7.2 グラデーション
SolidColorBrush クラスは単一の固定色による塗りつぶしでしたが System.Windows.Media.GradientBrush クラスを利用することで複数の色を組み合わせた、より美しい演出が可能になります。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush System.Windows.Media.GradientBrush
[ContentPropertyAttribute("GradientStops")] public abstract class GradientBrush : Brush
GradientBrush クラスは、グラデーションを提供するブラシのルートクラスとなる抽象クラスです。どのような形でグラデーションを描画するかは、このクラスのサブクラスに委ねられています。
特定の方向に向かって徐々に色を変化させる一般的なグラデーションは System.Windows.Media.LinearGradientBrush クラスを使います。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush System.Windows.Media.GradientBrush System.Windows.Media.LinearGradientBrush
public sealed class LinearGradientBrush : GradientBrush
このクラスのコンストラクタはいくつかオーバーロードされてますが、この場では最も基本的なパラメータを受け取らないコンストラクタだけを使います。その他の設定は、プロパティからも行うことができます。
public LinearGradientBrush ()
グラデーションの方向は StartPoint プロパティと EndPoint プロパティで設定します。ブラシは、直接描画される長方形を表すものではないので、これらの値は直接ピクセルに対応するものではありません。値が小さいほど左上、大きいほど右下に向かうことを表しています。
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
StartPoint プロパティは、グラデーションを開始する位置を指定します。このプロパティの既定の値は、左上隅を表す (0, 0) となっています。
EndPoint は、グラデーションを終了する位置を指定します。このプロパティの既定の値は、StartPoint プロパティの (0, 0) から相対的に右下を表す (1, 1) となっています。
グラデーションは StartPoint から EndPoint に向かって、徐々に色が変化するように作られます。
グラデーションの色や、変化の具合といった調整は GradientStops プロパティから行います。
public GradientStopCollection GradientStops { get; set; }
GradientStops が返す GradientStopCollection 型のオブジェクトは、グラデーションの色と位置を表す System.Windows.Media.GradientStop クラスのオブジェクトの配列を管理するコレクションです。GradientStop には、任意の数の GradientStop オブジェクトを追加することができるため、2 色のグラデーションだけではなく、任意の数の色でグラデーションを表示することが可能です。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.GradientStop
[LocalizabilityAttribute(LocalizationCategory.None, Readability=Readability.Unreadable)] public sealed class GradientStop : Animatable, IFormattable
GradientStop クラスのコンストラクタには、グラデーションの色と、その色を適用するオフセットを指定することができます。
public GradientStop () public GradientStop (Color color, double offset)
color パラメータには、グラデーションの色を表す Color 構造体のオブジェクトを指定します。offset パラメータは、StartPoint から EndPoint までのどの位置に指定した色を適用するかを表すオフセット値を指定します。例えば、この値が 0 であれば StartPoint に適用することを表し、1 であれば EndPoint に適用することを表します。0.5 であれば StartPoint と EndPoint の中間に適用することになります。
GradientStop クラスのコンストラクタで指定する値は Color プロパティと Offset プロパティを使って設定・取得することも可能です。
public Color Color { get; set; }
public double Offset { get; set; }
グラデーションの性質上、GradientStops プロパティが返す GradientStopCollection オブジェクトには、少なくとも 2 つ以上の GradientStop オブジェクトを追加しなければなりません。StartPoint に対応する色と、EndPoint に対応する色を指定することで、正しいグラデーションを表示することができます。
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromRgb(0xFF, 0, 0); Color color2 = Color.FromRgb(0, 0xFF, 0); Color color3 = Color.FromRgb(0, 0, 0xFF); LinearGradientBrush brush = new LinearGradientBrush(); brush.GradientStops.Add(new GradientStop(color1, 0)); brush.GradientStops.Add(new GradientStop(color2, 0.5)); brush.GradientStops.Add(new GradientStop(color3, 1)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード3は、既定の StartPoint と EndPoint の値で、赤、緑、青の 3 色を用いてグラデーションを表示しています。StartPoint と EndPoint 既定値のままなので、グラデーションは左上から右下に向かって行われています。赤のオフセットは 0 なので左上隅が赤から始まり、緑のオフセットを 0.5 に設定しているため、StartPoint と EndPoint の中間に緑色が適用されています。そして、青のオフセットは 1 なので EndPoint の右下が青となっています。
StartPoint と EndPoint の値を変更することで、グラデーションの方向を調整することができます。
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromRgb(0, 0, 0xFF); Color color2 = Color.FromRgb(0xFF, 0xFF, 0xFF); LinearGradientBrush brush = new LinearGradientBrush(); brush.StartPoint = new Point(0.5, 0); brush.EndPoint = new Point(0.5, 1); brush.GradientStops.Add(new GradientStop(color1, 0)); brush.GradientStops.Add(new GradientStop(color2, 1)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード4は、上から下に向かってグラデーションを行うブラシを作成しています。StartPoint と EndPoint には、中央を表す 0.5 を水平軸に設定しているため、左上から右下に向かうのではなく、上から下に向かってグラデーションが行われます。
GradientBrush クラスを継承するクラスは、LinearGradientBrush クラスの他にもう 1 つ System.Windows.Media.RadialGradientBrush クラスが存在しています。LinearGradientBrush が直線状に色をグラデーションさせるのに対し、このブラシは楕円状にグラデーションさせます。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush System.Windows.Media.GradientBrush System.Windows.Media.RadialGradientBrush
public sealed class RadialGradientBrush : GradientBrush
RadialGradientBrush のコンストラクタはオーバーロードされていますが、オブジェクトに対する設定はインスタンス化後でもプロパティから変更できるので、この場ではパラメータを渡さないコンストラクタのみを使います。
public RadialGradientBrush ()
楕円状にグラデーションをさせるには、やはり GradientStops プロパティから GradientStop オブジェクトを追加した色と位置を指定します。この方法は LinearGradientBrush と同じです。
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromRgb(0xFF, 0, 0); Color color2 = Color.FromRgb(0, 0xFF, 0); Color color3 = Color.FromRgb(0, 0, 0xFF); RadialGradientBrush brush = new RadialGradientBrush(); brush.GradientStops.Add(new GradientStop(color1, 0)); brush.GradientStops.Add(new GradientStop(color2, 0.5)); brush.GradientStops.Add(new GradientStop(color3, 1)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード5はコード3と 1 行しか違わないことに注目してください。このプログラムはコード3の LinearGradientBrush オブジェクトのインスタンス生成式を RadialGradientBrush に書き換えただけです。LinearGradientBrush オブジェクトの場合は、左上隅から右下隅に向かってグラデーションしていたのに対し、このプログラムの実行結果は、中央から外に向かって楕円状にグラデーションしていることが分かります。
もちろん、RadialGradientBrush クラスのプロパティを設定することで楕円の位置やサイズを自由に変更することができます。既定では、領域の中央から外に向かって、長方形サイズ全体に楕円を表示するように設定されています。
楕円のサイズは RadiusX プロパティと RadiusY プロパティから設定することができます。RadiusX プロパティは、楕円の水平方向の半径を、RadiusY は垂直方向の半径を表す値です。
public double RadiusX { get; set; }
public double RadiusY { get; set; }
これらのプロパティのそれぞれの規定値は 0.5 です。この値が 1 であれば、ブラシが描画する領域全体であることを表します。これらのプロパティは直径ではなく半径を意味するため、楕円の半径が 0.5 ということは、領域全体に楕円を描画することを表します。この値が大きいほど楕円は大きくなり、小さいほど楕円は小さくなります。
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromRgb(0, 0, 0xFF); Color color2 = Color.FromRgb(0xFF, 0xFF, 0xFF); RadialGradientBrush brush = new RadialGradientBrush(); brush.RadiusX = 0.4; brush.RadiusY = 0.15; brush.GradientStops.Add(new GradientStop(color1, 0)); brush.GradientStops.Add(new GradientStop(color2, 1)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード6は、RadiusX に 0.4、RadiusY に 0.15 という値を設定して青と白の 2 色のグラデーションを描画しています。特に、RadiusY が 0.15 に設定されているため、楕円の高さの半径は領域全体の 15% に縮められていることが分かります。
楕円の中央となる座標を変更したい場合は Center プロパティを設定します。
public Point Center { get; set; }
このプロパティの既定の値は、長方形の中央を表す (0.5, 0.5) です。楕円状のグラデーションは、このプロパティが表す座標から、RadiusX と RadiusY プロパティの半径に従って位置とサイズが決定されていたのです。
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromRgb(0, 0, 0xFF); Color color2 = Color.FromRgb(0xFF, 0xFF, 0xFF); RadialGradientBrush brush = new RadialGradientBrush(); brush.Center = new Point(0.2, 0.8); brush.GradientStops.Add(new GradientStop(color1, 0)); brush.GradientStops.Add(new GradientStop(color2, 1)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード7では、楕円の中央を表す Center プロパティの座標を (0.2, 0.8) に設定しています。そのため、楕円上のグラデーションは領域の左下方向に描画されるでしょう。しかし、実行結果を見て分かるように、楕円は左下隅に描画されていますが、グラデーションは左下隅ではなく中央から開始されています。
これは、楕円の中央とは別に焦点と呼ばれるプロパティで変更することができます。GradientStop オブジェクトで指定した色は、Center プロパティではなく、焦点を表す GradientOrigin プロパティに設定されている座標からグラデーションを開始します。
public Point GradientOrigin { get; set; }
このプロパティの既定の値は、Center プロパティと同様に領域の中央を表す (0.5, 0.5) となっています。
using System; using System.Windows; using System.Windows.Media; class Test { [STAThread] public static void Main() { Color color1 = Color.FromRgb(0xFF, 0xFF, 0xFF); Color color2 = Color.FromRgb(0, 0, 0); RadialGradientBrush brush = new RadialGradientBrush(); brush.GradientOrigin = new Point(0.3, 0.2); brush.GradientStops.Add(new GradientStop(color1, 0)); brush.GradientStops.Add(new GradientStop(color2, 1)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード8は、楕円は領域の中央から描画していますが、色の焦点は領域の左上隅方向 (0.3, 0.2) に設定しています。このブラシでは、白から黒にグラデーションをさせていますが、最も強い白色は楕円の中央ではなく、焦点の左上にあることが確認できます。
4.7.3 タイルブラシ
ブラシは、色で塗りつぶすものだけではありません。タイルブラシを用いれば、特定のパターンを用いて領域の内部を塗りつぶすことができます。例えば、レンガの壁や絨毯、カーテン、壁紙のような、特定の模様を何度も繰り返しながら内部を塗りつぶしたい場合、タイルブラシを利用するべきです。
特定のパターンを用いて塗りつぶすブラシは System.Windows.Media.TileBrush クラスから派生しています。TileBrush クラスは、パターンを用いるブラシのルートとなる抽象クラスです。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush System.Windows.Media.TileBrush
public abstract class TileBrush : Brush
TileBrush を継承する実装は、イメージをパターンに利用する System.Windows.Media.ImageBrush クラスがあります。このクラスでは、特定のイメージを用いて内部の領域を塗りつぶします。
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable System.Windows.Media.Brush System.Windows.Media.TileBrush System.Windows.Media.ImageBrush
public sealed class ImageBrush : TileBrush
このクラスのコンストラクタはオーバーロードされています。
public ImageBrush () public ImageBrush (ImageSource image)
image パラメータには、領域の内部を塗りつぶすイメージを表す ImageSource クラスのオブジェクトを指定します。ブラシが保有するイメージは ImageSource プロパティからでも設定することができます。
public ImageSource ImageSource { get; set; }
まずは、ImageBrush クラスのオブジェクトを作成し、指定した ImageSource オブジェクトのイメージで内部が塗りつぶされるかどうかを確かめてみましょう。テクスチャ用のイメージには、次のようなビットマップを用意しました。
これを BitmapImage クラスを用いて読み込み、ImageBrush に設定します。この ImageBrush を用いて内部領域を塗りつぶすと、ビットマップイメージが表示されることを確認することができます。
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; class Test { [STAThread] public static void Main() { Uri uri = new Uri("test.bmp", UriKind.RelativeOrAbsolute); ImageBrush brush = new ImageBrush(new BitmapImage(uri)); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード9の実行結果を見ると、指定したイメージが長方形の内部領域全体に伸縮して描画されていることが分かります。単純に 1 舞のイメージで塗りつぶしたい場合はこれで十分ですが、タイルブラシを使うシナリオとしては珍しい方でしょう。一般的なタイルの利用方法は、小さなイメージを領域内に繰り返して描画する方法です。
イメージを領域全体に満たすのか、特定のパターンで繰り返すのかという設定は TileMode プロパティから変更することができます。
public TileMode TileMode { get; set; }
TileMode プロパティには System.Windows.Media.TileMode 列挙体のメンバを指定します。
public enum TileMode
TileMode プロパティの既定では、パターンを繰り返さないことを表す None メンバが設定されていますが、Tile メンバを設定することでイメージが繰り返されるようになります。
イメージなど、ブラシのタイルとなる要素の位置とサイズは Viewport プロパティから設定します。既定では、座標が左上隅を表す (0, 0) に設定されており、幅と高さが共に領域全体を表す 1.0 に設定されているため、TileMode を Tile メンバに変更しても、Viewport プロパティを変更しなければ、結局イメージのサイズが領域全体に伸縮されてしまうので繰り返しされません。Viewport プロパティの値を変更して、イメージのサイズを調整します。
public Rect Viewport { get; set; }
Viewport に指定する Rect オブジェクトは、ピクセル単位ではなく領域に対する相対的な座標値を用います。左上隅が 0.0 で始まり、領域の最も右下隅が 1.0 を表します。Tile メンバを設定した状態のタイルモードでは、イメージのサイズが領域よりも小さい場合、イメージは繰り返されて内部を塗りつぶします。例えば、Viewport プロパティからイメージの幅と高さを 0.2 とすれば、縦横共にイメージが 5 回繰り返されることになります。
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; class Test { [STAThread] public static void Main() { Uri uri = new Uri("test.bmp", UriKind.RelativeOrAbsolute); ImageBrush brush = new ImageBrush(new BitmapImage(uri)); brush.TileMode = TileMode.Tile; brush.Viewport = new Rect(0, 0, 0.2, 0.2); Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード10は、イメージを縦横共に 5 回繰り返して領域を塗りつぶす ImageBrush を作成しています。
ただし、この場合もイメージは領域に対して幅、高さ共に 20% のサイズに伸縮されます。タイルとなるイメージを伸縮させたくないという場合、この方法は採用できません。 ブラシとして利用するイメージを伸縮させずに固定サイズで表示させたい場合は ViewportUnits プロパティの値を変更します。
public BrushMappingMode ViewportUnits { get; set; }
ViewportUnits プロパティには System.Windows.Media.BrushMappingMode 列挙体のいずれかのメンバを設定します。
public enum BrushMappingMode
ViewboxUnits プロパティの既定では、領域全体に対して相対的な値であることを表す RelativeToBoundingBox メンバが設定されています。イメージのサイズを固定させたい場合は、絶対値で位置とサイズを指定することを表す Absolute メンバを設定してください。
using System; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; class Test { [STAThread] public static void Main() { Uri uri = new Uri("test.bmp", UriKind.RelativeOrAbsolute); ImageSource image = new BitmapImage(uri); ImageBrush brush = new ImageBrush(); brush.TileMode = TileMode.Tile; brush.Viewport = new Rect(0, 0, 60, 60); brush.ViewportUnits = BrushMappingMode.Absolute; brush.ImageSource = image; Window wnd = new Window(); wnd.Background = brush; Application app = new Application(); app.Run(wnd); } }
コード11は、ViewportUnits プロパティに BrushMappingMode.Absolute を設定しているため、Viewport プロパティの Rect オブジェクトを絶対的なイメージの領域として解釈します。このプログラムの場合、イメージは (0, 0) 座標から開始される幅 60、高さ 60 ピクセルとして描画されます。