2010年10月30日土曜日

SlimDXでDirect2D

Direct2DというのはDirectXの一部なのですが、

え、DirectX? うわぁ。

となる必要は全くありません。
というのも、2Dだけ使いたいのに難しい3DのDirectXなんて使ってられるかー! という人のために作られたのがDirect2Dなのです(知らんけど少なくともユーザーから見たらそう)。

フォームアプリを作った人なら、PictureBoxに触れたことがありますよね。あんな感じで、

DrawLine
DrawEllipse
DrawRectangle

なんてプログラムして線を描いたり、円を描いたり、四角形を描きます。

実際1日2日で習得できるレベル。だから講座と呼べるものはネットにほとんどありません。プログラミングの熟練者なら、いくつかのサンプルとリファレンスを読めばすぐに使えるようになります。

でも

SlimDXからDirectXを始める私のような人への足がかりとなるものがほとんど無かったので、SlimDXからDirect2Dを使いたいという方が、「リファレンスは英語だし~、サンプルはC++だし、なんか難しそうだな」と思って挫折されないように、私が人柱となってやってみたことを書いておきます。


SlimDXのインストールとかはこちらの記事で。

それでは、今回はとりあえず、基本となるサンプルソースを示します。

using System;
using System.Drawing;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct2D;
using SlimDX.Windows;

namespace Direct2DSample
{
    static class Program
    {         [STAThread]         static void Main()         {             var form = new RenderForm("SlimDX - Direct2D Sample");             var factory = new Factory();             var target = new WindowRenderTarget(factory,new WindowRenderTargetProperties(){                 Handle = form.Handle,                 PixelSize = form.ClientSize             });             MessagePump.Run(form, () =>             {               target.BeginDraw();               target.Clear(Color.Black);               target.EndDraw();             });             foreach (var item in ObjectTable.Objects)                 item.Dispose();         }     } }


さて、プログラムの説明をしましょう。

            var form = new RenderForm("SlimDX - Direct2D Sample");
この文でフォームを作っています。フォームのサイズを変えたい場合はここで設定したほうがいいと思います。

            var factory = new Factory();
ここでファクトリ(工場)を生成します。このファクトリの存在が私にはいまいち分からなかったのですが、とにかくDirect2Dで何か作るときは工場で作らないといけないということです。

            var target = new WindowRenderTarget(factory,new WindowRenderTargetProperties(){
                Handle = form.Handle,
                PixelSize = form.ClientSize
            });
ここでレンダーターゲットを作っています。レンダーターゲットをそのまま日本語にすると描画標的になります。つまり、レンダーターゲットに向かって絵が描かれるということです。
レンダーターゲットにはいくつか種類がありますが、ウィンドウに何か表示させたいので、WindowRenderTargetを作っています。
レンダーターゲットも工場で作るので、ファクトリを指定しています。
WindowRenderTargetPropertiesというのがありますが、これはウィンドウレンダーターゲットに色々な初期化情報を与えるためのクラスです。このクラスに前作ったフォームと、そのサイズを設定しています。これで前作ったフォームがレンダーターゲットになります。
ちなみにここではC#3.0の初期化子偉い人の解説)という技法を用いています。この技法を使うことで、本当はWindowRenderTargetPropertiesを仲介しているのに、WindowRenderTargetに直接設定を与えている気分になれます。私の気分の問題です。こういうのが嫌いなら自分なりに書き換えてください。

ところで、DirectXでは初期化情報を独立したクラス(構造体)にしていることがよくあります(この場合は~Propertiesという感じ)。理由はおそらく、色々な言語で利用できるようにAPI関数として提供するときに、関数の引数が長くなりすぎるのは困るからでしょう。


なんか疲れてきた。説明の文章が長すぎてシンプルじゃない。説明が長々書いてあると読みたくなくなるよね。一応優先順位が高い情報を上に書くようにしてるけど、ちゃんと説明できてるのかなぁ。

            MessagePump.Run(form, () =>
            {
              target.BeginDraw();
              target.Clear(Color.Black);

              target.EndDraw();
            });
ここがメッセージループです。メッセージループって何?って感じですが、古いWindowsプログラミングの用語で正直詳しくないのですが、とにかく、何度も何度も実行される所です。
BeginDrawが描画開始で、EndDrawが描画終了です。まんまやん! この間に描きたいものをどんどん追加していきます。このプログラムでは真っ黒にクリアしているだけで、何も描いていません。BlackをRedやWhiteに変えると画面の色が変わります。黒でよければtarget.Clear()のように省略できます。
フォームアプリケーションのピクチャーボックスなどでは、DrawLineなんて命令を出すとその度に線が描かれますが、Direct2Dでは命令を与えた時点ではまだ描画されずに、命令を沢山貯めておいてEndDrawになったときに効率の良い方法で一気に描画します。だから速いのです。ここが一番のポイントなのです。
ちなみに、ここでもまた分かりにくいC#3.0の匿名メソッド偉い人の解説)という機能を使っています。プログラムをシンプルで美しく、かつ分かりやすくするにはC#3.0は欠かせません。匿名メソッドが理解できなくてもこのプログラムの意味は理解できるはずです。

            foreach (var item in ObjectTable.Objects)
                item.Dispose();
SlimDXのサンプルがこんなふうになっていたので、そのままです。使ったオブジェクトの後処理をここで一括でやってもらっています。
後処理は色々あるんじゃないかと思いますが、例えば画像ファイルを参照するときに途中で他のプログラムから書き換えられたら困るので、ファイルにロックをかけることがあります。そのままロックを解除せずに終了したら、そのファイルは誰も書き換えることができなくなってしまいます。だからちゃんとロックを解除しないといけないのですが、プログラマはよく忘れます(なんか今忘却のプログラマというかっこいいフレーズを思いついた)。だから忘れても大丈夫なようにここで全てお任せするのです。


これで説明はお終いです。後は頑張ってね。



ちょっと雑記になります。
SlimDXではRenderFormというフォームを使っています。
RenderFormって普通のFormとどう違うのかといいますと。ほんのちょっとだけコードを綺麗に書けるようにしたというだけです。
もしもRenderFormを使わずに普通のFormで同じコードを書きたい場合は次のようになります。

using System;
using System.Drawing;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct2D;

namespace Direct2DSample
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var form = new Form(){
              Text = "SlimDX - Direct2D Sample"
            };
            var factory = new Factory();

            var target = new WindowRenderTarget(factory,new WindowRenderTargetProperties(){
                Handle = form.Handle,
                PixelSize = form.ClientSize
            });

            Application.Idle += (sender,e) =>
            {
              target.BeginDraw();
              target.Clear(Color.Red);

              target.EndDraw();
            };
            Application.Run(form);

            foreach (var item in ObjectTable.Objects)
                item.Dispose();
        }
    }
}

Application.Idleというのは、調べていただければなんだかよくわからないでしょうが、とにかく、フォームのメッセージループの中で呼びだされます。この名前からメッセージループを連想するのが難しいのでSlimDXではMessagePump.Runに置き換えられたのではないでしょうか。


0 件のコメント:

コメントを投稿