AFsoft WebSite(エーエフソフト・ウェブサイト)
 

オペレーティング・システムについて

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
Delphi2010 Direct2D(8) 2010/12/14

今回ももう少し、Direct2Dについて見ていきます。
前回は、楕円の描画 DrawEllipseメソッド、および、塗り潰し楕円の描画 FillEllipseメソッドについて見ました。GDIには線・四角形・楕円のほか、折れ線 Polyline・多角形 Polygon・ベジェ曲線 PolyBezierTo・円弧 Arc・弓形 Chord・扇形 Pie等のような機能がありますが、それに相当するDirect2Dのメソッドというのは無さそうです。
その他、DrawGeometryメソッド(図形をキャンバス上に描画)、FillGeometryメソッド(図形を塗り潰してキャンバス上に描画)というメソッドがあるようですが、引数には ID2D1Geometry型を指定するようですが、ヘルプにはそれらしき詳細説明がありませんのでよく分かりません。多角形 Polygonに相当するようなメソッドなのでしょうか? また、RenderTargetプロパティというものがあります。ヘルプによれば
このキャンバス オブジェクトのレンダリング対象を示します。
RenderTargetの値を使用して、キャンバスが描画される対象の具体的なプロパティにアクセスすることができます。RenderTarget は、直接使用することができる Direct2D インターフェイスを返します。
と記載されていますが、これ以上の詳細説明がどうも見当たりません。これを使用すればもう少し細かい処理が出来そうな感じではありますが・・・。
 
これまで記載したテストプログラムでは、例えば
var
 e : D2D1_ELLIPSE ;
begin
 if not(D2DCanvasFlag) then exit;
 with D2DCanvas do begin
  BeginDraw;
  RenderTarget.Clear (D2D1ColorF(clWhite));  // 背景:白色
  Brush.Handle := CreateBrush(clBlue);
  e.point.x := 100 ;
  e.point.y := 120 ;
  e.radiusX := 80 ;
  e.radiusY := 60 ;
  DrawEllipse(e);
  EndDraw;
 end;
end;
のようにしていましたが、RenderTargetを使用して
procedure TForm1.Button11Click(Sender: TObject);
var
 e : D2D1_ELLIPSE ;
 b : ID2D1Brush ;
begin
 if not(D2DCanvasFlag) then exit;
 with D2DCanvas do begin
  b := CreateBrush(clBlue);
  with RenderTarget do begin
   BeginDraw;
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   e.point.x := 100 ;
   e.point.y := 120 ;
   e.radiusX := 80 ;
   e.radiusY := 60 ;
   FillEllipse(e,b);
   EndDraw;
  end;
 end;
end;
というように記述することも出来ます。上記のFillEllipseメソッドでは引数が違っていますが同様に実行出来るようです。そのほか、アンチエイリアスモードの設定・取得、レイヤ、タグ、メッシュ、などあるようですが、ヘルプに記載がありませんので使用方法がよく分かりません。
 
アンチエイリアスモードについては、よく分かりませんが、取り合えず実際に実行をして比較してみます。
procedure TForm1.Button11Click(Sender: TObject);
var
 e : D2D1_ELLIPSE ;
 b : ID2D1Brush ;
begin
 if not(D2DCanvasFlag) then exit;
 with D2DCanvas do begin
  b := CreateBrush(clBlue);
  with RenderTarget do begin
   BeginDraw;
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); // 0値
   e.point.x := 100 ;
   e.point.y := 100 ;
   e.radiusX := 80 ;
   e.radiusY := 60 ;
   DrawEllipse(e,b);
   
   SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); // 1値
   e.point.x := 120 ;
   e.point.y := 120 ;
   e.radiusX := 80 ;
   e.radiusY := 60 ;
   DrawEllipse(e,b);
   
   EndDraw;
  end;
 end;
end;
保存・コンパイル(再構築)・実行をしてみます。

やや左上がアンチエイリアス有りで、右下がアンチエイリアス無しです。やはり、アンチエイリアス有りの方が綺麗ですね。無しだとガタガタしているように見えます。GDI描画の場合にもアンチエイリアス処理はありませんから、GDI描画相当と考えればいいかと思われます。デメリットだけでメリットがないのか?というと、その処理が行われない分、描画速度は速いはずだ、と想像出来ます。ですので速度比較をしてみます。ボタン Button12 を追加してそちらに記述します。なお、ボタンが多くなりすぎたのでボタンの大きさを小さくして Captionプロパティも変更しています。ボタン Button7 の処理の変更版です。
procedure TForm1.Button12Click(Sender: TObject);
var
 t : TDateTime ;
 mes : string ;
 i,j : integer ;
 r : D2D1_ROUNDED_RECT ;
begin
 if not(D2DCanvasFlag) then exit;
 t := Now ;
 with D2DCanvas do begin
  BeginDraw;
  RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
  Pen.Style := psSolid ;
  Pen.Width := 1;
  for j:=1 to 1000 do begin
   for i:=0 to 200 do begin
    Pen.Color := RGB(Random(256),Random(256),Random(256)) ;
    r.rect.left := i ;
    r.rect.top := i ;
    r.rect.right := 200 - i ;
    r.rect.bottom := 200 - i ;
    r.radiusX := 10 ;
    r.radiusY := 5 ;
    DrawRoundedRectangle(r);
   end;
  end;
  EndDraw;
 end;
 mes := 'アンチエイリアス有:' + TimeToStr(Now - t) ;
 
 t := Now ;
 with D2DCanvas do begin
  BeginDraw;
  RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
  Pen.Style := psSolid ;
  Pen.Width := 1;
  for j:=1 to 1000 do begin
   for i:=0 to 200 do begin
    Pen.Color := RGB(Random(256),Random(256),Random(256)) ;
    r.rect.left := i ;
    r.rect.top := i ;
    r.rect.right := 200 - i ;
    r.rect.bottom := 200 - i ;
    r.radiusX := 10 ;
    r.radiusY := 5 ;
    DrawRoundedRectangle(r);
   end;
  end;
  EndDraw;
 end;
 mes := mes + ' 無:' + TimeToStr(Now - t) ;
 
 StatusBar1.SimpleText := mes ;
end;
保存・コンパイル(再構築)・実行をしてみます。



想定通り、アンチエイリアス無しだとガタ付きはありますが、描画速度は速くなっています。次に、線幅を「3」にしてみます。



線幅を変更するだけでは速度的には上記とほぼ変わらないようです。それでは線幅を「1」に戻し、線種を「psDash(破線)」に変更してみます。速度はかなり落ちますので繰り返し回数を「1000」から「100」へ1/10に落とします。



破線の場合には、アンチエイリアス有りだとかなり遅くなってしまいますが、アンチエイリアス無しの場合には速度的に圧倒的な差が出ているのが分かります。アンチエイリアス無しの場合も、1/10にして約5秒という事は、実線時の約4倍の遅さ、という事にはなるのですが。
 
まぁそれはさておき、実線での描画時、
アンチエイリアスを有り:綺麗に描画、約1.5倍遅い
アンチエイリアスを無し:ガタツキが見える描画、約1.5倍速い
のどちらを取るか、というのは中々微妙なラインかもしれません。
 
 
RenderTargetプロパティで次に気になるのは、SetTransformメソッドでしょうか。「Transform」というワードで連想されるのは、変換行列ですが、このメソッドの引数は D2D_MATRIX_3X2_F型となっており、Single型の_11値・_12値・_21値・_22値・_31値・_32値を持ちます。いかにも〜という感じで変換行列そのままです。おそらくは
 (x' y')=(x y 1)(_11 _21)
_21 _22
_31 _32
というお約束のパターンではないでしょうか? という訳で、上記のボタン Button11のテストで試してみます。
 
こういう形式であれば初期値はおそらく
 (x' y')=(x y 1)(1 0)
0 1
0 0
でしょう。GetTransformメソッドがあるとの事ですから確認してみます。
 
procedure TForm1.Button11Click(Sender: TObject);
var
 t : D2D_MATRIX_3X2_F ;
 e : D2D1_ELLIPSE ;
 b : ID2D1Brush ;
begin
 if not(D2DCanvasFlag) then exit;
 with D2DCanvas do begin
  b := CreateBrush(clBlue);
  with RenderTarget do begin
   BeginDraw;
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   
   RenderTarget.GetTransform(t);
   showMessage(
    FloatToStr(t._11)+' '+FloatToStr(t._12)+' '+#13+#10+
    FloatToStr(t._21)+' '+FloatToStr(t._22)+' '+#13+#10+
    FloatToStr(t._31)+' '+FloatToStr(t._32) );
   ・・・
実行すると・・・

となり、思惑通り? 数値は 0 と 1 しかありませんからこの時点では何とも言えないですが、あながち大はずれという感じもしませんので、それでは、これが変換行列であるとすれば、_11にX方向倍率、_22にY方向倍率を指定すれば縦横倍率指定の変形が出来るはずです。試しに・・・
procedure TForm1.Button11Click(Sender: TObject);
   ・・・
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   
   t._11 := 1.5 ;
   t._12 := 0.0 ;
   t._21 := 0.0 ;
   t._22 := 1.0 ;
   t._31 := 0.0 ;
   t._32 := 0.0 ;
   RenderTarget.SetTransform(t);
   ・・・
とすれば横に1.5倍に変形されずはずです。実行してみます。

想定通り、横長になっていますね。それでは今度は、
procedure TForm1.Button11Click(Sender: TObject);
   ・・・
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   
   t._11 := 1.0 ;
   t._12 := 0.0 ;
   t._21 := 0.0 ;
   t._22 := 1.5 ;
   t._31 := 0.0 ;
   t._32 := 0.0 ;
   RenderTarget.SetTransform(t);
   ・・・
とすれば縦に1.5倍に変形されずはずです。実行してみます。

想定通り、縦長になっています。それでは今度は、
procedure TForm1.Button11Click(Sender: TObject);
   ・・・
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   
   t._11 := 1.0 ;
   t._12 := 0.0 ;
   t._21 := 0.0 ;
   t._22 := 1.0 ;
   t._31 := 30.0 ;
   t._32 := 20.0 ;
   RenderTarget.SetTransform(t);
   ・・・
とすれば左に30、下に20、移動するはずです。実行してみます。

これも想定通り、移動しています。それでは次に回転(写像)してみます。回転の変換行列は下記のようになるはずですから
 (x' y')=(x y 1)(cosθ  sinθ)
-sinθ cosθ
moveX  moveY
procedure TForm1.Button11Click(Sender: TObject);
   ・・・
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   
   t._11 := cos(15/180*Pi) ;
   t._12 := sin(15/180*Pi) ;
   t._21 :=-sin(15/180*Pi) ;
   t._22 := cos(15/180*Pi) ;
   t._31 := 0.0 ;
   t._32 := 0.0 ;
   RenderTarget.SetTransform(t);
   ・・・
とすれば15°回転されて作図されるはずです。それでは実行してみます。

回転されていますね。回転の中心は原点(0,0)ですから、左上が回転中心、画面の座標軸のY軸は下がプラスですから、数学グラフ上な反時計回りではなく、時計回りになる、というのは想定済みです。
 
これを利用すれば、回転・変形の描画は容易に出来そうですね。画像ファイルは何でもいいですが、例えば、テスト5で作成した画像テスト(ImageTest.bmp)をコピーしてきて
procedure TForm1.Button11Click(Sender: TObject);
var
 ・・・
 bm : TBitmap ;
begin
   ・・・
   Clear (D2D1ColorF(clWhite)); // 背景:白色
   
   t._11 := cos(15/180*Pi) ;
   t._12 := sin(15/180*Pi) ;
   t._21 :=-sin(15/180*Pi) ;
   t._22 := cos(15/180*Pi) ;
   t._31 := 0.0 ;
   t._32 := 0.0 ;
   RenderTarget.SetTransform(t);
   
   bm := TBitmap.Create ;
   bm.LoadFromFile(ExtractFilePath(Application.ExeName)+'ImageTest.bmp');
   Draw(50,50, bm);
   bm.Free ;
   
   ・・・
のようにすれば、

のような事が簡単に出来てしまいます。まぁ、Direct2D→DirectX→GameAPIというのはもともとラスターオペレーションはかなり強力なのですけれども。勿論、画面に描画できたとしても、印刷(プリンタ)には「?」でありますが。
 
バッチファイル
BASIC
C言語のお勉強
拡張子な話
DOSプログラム
Delphi
>Delphi入門編
>Delphi2010
▲2010/12/13
 2010/12/14
▼2010/12/15
 
シェアウェア
Script!World
データベース
 
お問い合わせ
本サイトはリンクフリーです
リンクバナー
(C)Copyright 1999-2015. By AFsoft All Rights Reserved.