SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

WriteableBitmapExを使ってみた

WPFで、画像データをにピクセル単位でアクセスする場合にはWriteableBitampを使いますが、
このクラス、いまいち使い勝手が悪い。。。
ピクセルデータに連続してアクセスする場合とかは、
わざわざunsafeコンテキストでBackBufferプロパティにアクセスしたり、、、と面倒だし。


このライブラリを使えば、ピクセル操作や、DrawLine, DrawCurveなどの図形描画の便利なメソッドで使えるようになります。
WriteableBitmapクラスの拡張メソッドとして作られているので、
「普通にWriteableBitmapクラスにこんなメソッドがあればいいのに」と思っていたのがそのまま実現された感じです。

WriteableBitmapEx

http://writeablebitmapex.codeplex.com/


Codeplexのページに使い方のサンプルが載ってますが、
WPFではうまく動作しない部分もあったので、備忘録として色々メモしときます。

対応環境

Silverlight/WindowsPhone/WPF/Metroアプリ(Windowsストアアプリ)に対応してるとのこと。
Silverlightで使ってるのはよく見かけますが、WPFでの使用例をあまり見かけないので、
簡単にまとめてみました。

準備

Nugetでインポートできるので、Nugetで検索して追加するだけでOK


・使い方

WriteableBitmapのインスタンス作成

WriteableBitmap wb = BitmapFactory.New(512, 512);

WriteableBitmapのコンストラクタは、WPF/Silverlightで形式が異なるけど、
このFactoryを使用することで、どっちでも使える書き方になります。
また、WPF版のWriteableBitampExでは、Pbgr32のフォーマットをで画像を扱わないといけないのですが、
そのフォーマットで作ってくれます。

別のフォーマットのWriteableBitmapのインスタンスは、以下のメソッドでフォーマットを変換してから使います。
ConvertToPbgra32Format

画像のクリア

指定した色で塗りつぶすときは、以下のように書く。
wb.Clear(Colors.Black);

画面をクリアするだけのサンプル

(xamlファイルには、imgTargetという名前のImageコントロールだけを配置)

    public partial class MainWindow : Window
    {
        WriteableBitmap wb;

        public MainWindow()
        {
            InitializeComponent();

            wb = BitmapFactory.New(512, 512);
            using (wb.GetBitmapContext())
            {
                wb.Clear(Colors.Black);
            }
            imgTarget.Source = wb;
        }
    }

画像の読み込み

FromResourceというメソッドもあるみたいだけど、
WPFで使うとなぜか実行時に例外が出て落ちてしまう。。。

WriteableBitmapExのサンプルで用いている方法

このライブラリのソースと一緒に公開されてるサンプルでは、以下のようなメソッドを使ってリソースの画像を読み込んでました。

      WriteableBitmap LoadBitmap(string path)
      {
         var img = new BitmapImage();
         img.BeginInit();
         img.CreateOptions = BitmapCreateOptions.None;
         var s = Application.GetResourceStream(new Uri(path, UriKind.Relative)).Stream;
         img.StreamSource = s;
         img.EndInit();
         return BitmapFactory.ConvertToPbgra32Format(img);
      }
BitmapImageで読み込んでから変換する方法

アプリのリソースから画像を読み込みたい場合などでは、以下のような書き方をすると楽かも。
ファイルシステム上の画像ファイルを指定する場合
wb = BitmapFactory.ConvertToPbgra32Format(new BitmapImage(new Uri(画像ファイルのパス, UriKind.Absolute)));
・リソースとしてアセンブリに含めた画像を参照する場合
wb = BitmapFactory.ConvertToPbgra32Format(new BitmapImage(new Uri("pack://application:,,,/Images/sample.jpg")));
↑リソースの画像を参照するときはpack URIで指定しないといけないっぽい。

GetBitmapContextメソッド

ピクセル操作の前に、GetBitmapContextメソッドを呼んで、最後にDisposeを呼ぶ必要がある。
⇒操作する範囲を、usingステートメントで囲う!!

using (bmp.GetBitmapContext())
{
    // ここで、描画に関する処理を書く
}

描画

ピクセル単位の操作

SetPixel/GetPixelメソッド
こんな風に使える。
wb.SetPixel(10, 10, Colors.Red);
Color c = wb.GetPixel(10, 10);

線の描画とか

wb.DrawLine(0, 0, 512, 512, Colors.Red);
他にも、アンチエイリアスを行って描画する、DrawLineAaや、ブレゼンハムのアルゴリズムで描画するDrawLineBresenhamなどがある。

三角形/四角形/ポリゴン描画用メソッドもある。
wb.DrawTriangle(255, 0, 0, 511, 511, 511, Colors.Blue);

任意の4頂点の四角形
wb.DrawQuad(10, 0, 0, 511, 511, 511, 500, 0, Colors.Red);
幅・高さを指定して四角形を描画
wb.DrawRectangle(10, 10, 200, 300, Colors.Green);

楕円や曲線を描画するメソッド
wb.DrawEllipse(20, 20, 80, 50, Colors.Yellow);

他にも、ベジェ曲線の描画などのメソッドもあるけど省略。。。


塗りつぶしをする場合は、Draw○○ではなくFill○○というメソッドを使用する。
wb.FillRectangle(20, 20, 100, 100, Colors.White);

その他、画像に対する処理

・トリミング
画像の一部をトリミングして、別のインスタンスに代入するメソッド
var cropped = wb.Crop(50, 10, 200, 200);

・リサイズ
var resized = wb.Resize(200, 200, WriteableBitmapExtensions.Interpolation.Bilinear);

・回転
↓のメソッドは、90度単位での回転用のメソッド
var rotated = wb.RotateFree(90);

自由度回転を行うメソッド
var rotated = wb.RotateFree(20);

・反転
var flipped = wb.Flip(WriteableBitmapExtensions.FlipMode.Horizontal);


他にも、画像処理などで便利そうなメソッドもいっぱいあるみたい。