  
  
  
   
   
   
   
   
   
  | 
| DelphiXE3 [FMX] 画面描画 2014/05/12 | 
 
   
前回はライブラリ、各ユニット内のルーチン・変数と定数について見てきました。各クラス・型についてはプログラミングの際には必ず参照しますからその都度ヘルプを確認すればいいでしょう。 
  
さて今回から、FireMonkey(FMX)の 2Dグラフィックスについて見ていきます。[Shapes]内のコンポーネントについては既に見ました。 
その他、アニメーションな機能や、各種効果の機能なども既に見てきた通りです。しかし、お絵描きプログラムやCADプログラム等では、上記のようなコンポーネントを使用して画面作図を行うような事は余り無いと思われます。勿論、動的なオブジェクト生成を行なって画面作図を行う事も可能でしょうし、一部分だけはコンポーネントを利用するという事も有り得ます。 
  
しかし、CADプログラム等は特に描画速度を求められますし、また、大量の図形を作図する必要性も出てきます。動的なオブジェクト生成を何十万回とやって、果たして、高速性が得られるのか、メモリはどれくらい使用するのか、と考えると不安です。 
  
という訳で従来のWindowsプログラムであれば、Windows GDI による描画、これは Delphi でいう所のVCLアプリケーションでの、Imageコンポーネントや PaintBoxコンポーネントの Canvas での作図、という事が行われてきました。しかしこの手法は、Windows Vista 以降のバージョンでは、VGAカード(グラフィックスボード)のハードウェアアクセラレーション機能が有効利用されない為、また、DirectX の介在も原因となって、描画速度が遅くなってしまう、という事がありました。 
  
これを避ける為、DirectX の DirectDraw(※現在では非推奨)での作図、あるいは、Direct2D での作図が行われるようになってきています。Delphiにおいて、DirectX の機能を使うのは、これまで Delphi そのものがサポートしてくれていない感じでしたのでプログラマは扱いにくいものだったと思います(※XE6では DirectX11の扱いがそれなりに出来るようにはなっているらしいですが)。Delphi&DirectXについての書籍も余りありませんでした。DirectX を使う場合は、Visual C++ や Visual BASIC を使う方が多くの書籍等もあり、プログラミングし易い状況だったと思います。 
  
Direct2D については、Windows Vista以降対応となりますが、Delphi でも 2010からサポートされるようになっています。これは既に 
で記述しました。 
  
WindowsGDIにしろ、GDI+にしろ、DirectXにしろ、Direct2Dにしろ、これらは、Windows での機能であり、VCLアプリケーションでの実装でした。 
  
ここでは、FireMonkey(FMX) アプリケーションについての記述を行なっています。Windows専用ではなく、マルチプラットフォーム対応となります。ですので、GDI+、DirectX、Direct2D で直接アクセスして画面に作図させる、という事は出来ません。 
  
プログラミング手法的には、従来のWindowsGDI的な、Canvas への作図、と同様に、FireMonkey(FMX) アプリケーションにて、フォームや画像(Image) 
、ペイントボックス(PaintBox)等の Canvas への作図を行う事になると思われますので、これについて見ていきます。 
  
  
まずは、DelphiXE3 を起動し、 
メニュー「ファイル」→「新規作成」→「FireMonkey デスクトップアプリケーション」を実行し、「HD FireMonkey アプリケーション」を選択します。 
  
フォーム画面上に、[Shapes]内の 画像(Image)コンポーネントを貼り付けます。ClipChildrenプロパティにチェックを入れます。 
  
オブジェクトインスペクタにて、Image1オブジェクトの OnPaintイベント欄をダブルクリックします。 
  
定数「claGray」を使うので、uses節には、「System.UIConsts」を追加しておきます。OnPaintイベントハンドラを下記のように記述します。 
procedure TForm1.Image1Paint(Sender: TObject; Canvas: TCanvas; 
 const ARect: TRectF); 
begin 
 with Canvas do begin 
  Clear(claGray); 
  Canvas.DrawLine(PointF(0,0),PointF(100,100),1.0); 
 end; 
end; | 
 
   
保存・ビルド(コンパイル)・実行を行います。 
  
もしも ClipChildrenプロパティにチェックを入れず、デフォルト状態=チェック無し状態の場合は、下図のように、境界でクリップされず画面全体に作図されてしまいます。 
ですので通常は必ず、ClipChildrenプロパティにチェックを入れておきます。 
  
フォーム画面上にボタンを配置し、ボタンをクリックした時のイベントハンドラを下記のようにしたとします。 
procedure TForm1.Button1Click(Sender: TObject); 
begin 
 With Image1.Bitmap.Canvas do begin 
  BeginScene ; 
  Clear(claLightGray); 
  Canvas.DrawLine(PointF(100,0),PointF(0,100),1.0); 
  EndScene ; 
 end; 
end; | 
 
 保存・コンパイル・実行を行い、ボタンをクリックしてみると、エラーが発生します。 
  
procedure TForm1.Button1Click(Sender: TObject); 
begin 
 With Image1.Canvas do begin 
  BeginScene ; 
  Clear(claLightGray); 
  Canvas.DrawLine(PointF(100,0),PointF(0,100),1.0); 
  EndScene ; 
 end; 
end; | 
 
 のように変更し、保存・コンパイル・実行を行い、ボタンをクリックしてみると、下図のようになります。 
  
画像コンポーネントの表示領域は無視されてしまいます。これは、画像(Image)コンポーネントだけでなく、ペイントボックス(PaintBox)でも同じようになってしまいます。注意が必要のようです。 
  
  
次に、OnPaintイベントハンドラを下記のように記述します。 
procedure TForm1.Image1Paint(Sender: TObject; Canvas: TCanvas; 
 const ARect: TRectF); 
var 
 i : integer ; 
begin 
 with Canvas do begin 
  Clear(claGray); 
  for i := 1 to 10000 do begin 
   Canvas.Stroke.Color := $FF000000 
    + Random(256)*65536 + Random(256)*256 + Random(256) ; 
   Canvas.DrawLine(PointF(Random(200),Random(200)), 
           PointF(Random(200),Random(200)),1.0); 
  end; 
 end; 
end; | 
 
 保存・コンパイル・実行します。 
のように、乱数で 10000本の線を描かせているだけですが、どうも2度書きしている感じがします。試しに、 
procedure TForm1.Image1Paint(Sender: TObject; Canvas: TCanvas; 
 const ARect: TRectF); 
var 
 i : integer ; 
begin 
 with Canvas do begin 
  Clear(claGray); 
  for i := 1 to 10000 do begin 
   Canvas.Stroke.Color := $FF000000 
    + Random(256)*65536 + Random(256)*256 + Random(256) ; 
   Canvas.DrawLine(PointF(Random(200),Random(200)), 
           PointF(Random(200),Random(200)),1.0); 
  end; 
 end; 
 showMessage('描画'); 
end; | 
 
 とやってみると、確かに、2回描画している様子です。 
  
ですので、 
type 
 TForm1 = class(TForm) 
 ・・・ 
 { private 宣言 } 
  firstflag : Boolean ; 
 ・・・ 
 end; 
・・・ 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
 firstflag := False ; 
end; 
  
procedure TForm1.Image1Paint(Sender: TObject; Canvas: TCanvas; 
 const ARect: TRectF); 
var 
 i : integer ; 
begin 
 if (firstflag) then exit ; 
  
 with Canvas do begin 
  Clear(claGray); 
  for i := 1 to 10000 do begin 
   Canvas.Stroke.Color := $FF000000 
    + Random(256)*65536 + Random(256)*256 + Random(256) ; 
   Canvas.DrawLine(PointF(Random(200),Random(200)), 
           PointF(Random(200),Random(200)),1.0); 
  end; 
 end; 
  
 firstflag := True ; 
end; | 
 
 という風に、1回目に描画をして、2回はパス、としてみます。 
保存・コンパイル・実行してみると、線は表示されないですね。 
瞬間的に、書いて、消えた、という事も無さそうです。 
  
procedure TForm1.Image1Paint(Sender: TObject; Canvas: TCanvas; 
 const ARect: TRectF); 
var 
 i : integer ; 
begin 
 if not(firstflag) then begin 
  firstflag := True ; 
  exit ; 
 end; 
  
 with Canvas do begin 
  Clear(claGray); 
  for i := 1 to 10000 do begin 
   Canvas.Stroke.Color := $FF000000 
    + Random(256)*65536 + Random(256)*256 + Random(256) ; 
   Canvas.DrawLine(PointF(Random(200),Random(200)), 
           PointF(Random(200),Random(200)),1.0); 
  end; 
 end; 
end; | 
 
 という風に、1回目はパスして、2回に描画する、としてみます。 
今度は描画しました。 
  
なお、外アプリケーション画面を上に重ねたりしても、画面の再描画は行われない様子です。フォーム画面をリサイズしようとすると、1ドット?動かすたびに画面の再描画を行うようなったりします。描画すべき内容が大量にある場合、このままだとフォーム画面のリサイズをするのが大変になってきますので、何らかの処置を取る必要性が出てくると思われます。 
  
  | 
 
  | 
バッチファイル 
BASIC 
C言語のお勉強 
拡張子な話 
DOSプログラム 
Delphi 
>Dehi入門編 
>Delphi2010 
>DelphiXE3 
▲2014/05/10 
 2014/05/12 
▼2014/05/13 
  
シェアウェア 
Script!World 
データベース 
  |