月刊ソフト作り!
Make The Software For VisualBasic


『GDIの知識』
2002.May.11

 Presented by kouta_y
感想等は掲示板、苦情はメールへ。

会社
今回はGDI系の知識です。
筆者のわかる範囲で説明していきます。


GDIとは

Graphics Device Interface
の略です。
英語に詳しい人ならなんとなく分かるでしょう。
簡単に言えば「デバイスに絵を描く為の道具や材料」だと言えます。

例えばデバイスをディスプレイだと考えてみましょう。
そしてそのディスプレイに256色カラーの絵を表示したいと思います。
するとまず必要になってくるのがです。これがデバイスのもっているパレット(物理パレット)になります。
次に必要なのが、表示させたい絵の情報です。これがビットマップ(HBITMAP)です。
上の情報に合わせたその絵の実際の色の値がピクセルビットとなります。
そしてそれらの情報を元に実際にディスプレイに描いてくれる関数となります。BitBltとか。

これら全てはGDIオブジェクトと呼ばれます。(関数は除いて)


デバイスコンテキストってなんなの?

はい。これには筆者も随分頭を悩まされたものです。
一体なんの事を指しているんでしょう。

答えは簡単です。
そのデバイスに対する描画属性のあらゆる情報で、全てのGDIオブジェクトの中核を担っています。
私たちはこのデバイスコンテキストを使って間接的にデバイスに画像を描画する事ができます。
そのデバイスコンテキスにアクセスする為のハンドルが、デバイスコンテキストハンドルです。

では「デバイスに対する情報」という事は、その情報を取得する事ができるはずです。
それを実現してくれるのが、有名な「GetDeviceCaps」API関数です。
逆にGDIオブジェクトを設定したい時には「SelectObject」を使います。
デバイスコンテキストハンドルを取得するには、「GetDC」関数や「BeginPaint」関数などを使います。

関係ありませんが、こないだデバイスコンテキストの事を「デバコン」と略している人をみました。


メモリデバイスコンテキスト

これは前述したデバイスコンテキストをメモリ上に作成したものの事を指します。
メモリ上で扱うという事は、例えばディプレイに互換のあるデバイスコンテキストをメモリ上にロード(作成)したとします。
すると実際のディスプレイと全く同じ情報をもったデバイスがパソコンの中(つまりメモリ内に)存在するという事になります。
そのメモリデバイスコンテキストにアクセスする為のハンドルが、長ったらしい名前のメモリデバイスコンテキストハンドルとなります。

ではデバイスコンテキスト(以下DC)をメモリ上で扱ってなにかメリットはあるのか?
ありありです。
ここではわかりやすいVBを例にあげてみましょう。

VBのフォームオブジェクトには「AutoRedraw」というプロパティがあります。
MSDNには「再描画を勝手にやる」とありますが、実はこれ。内部でメモリデバイスコンテキスト(以下メモリDC)を作成してるんですね。
んでそのメモリDCに画像を描画し、Windowsから再描画命令がきたら内部でメモリDCの内容をフォームに描画します。
メモリDCはメモリ内に画像を展開するので、当たり前な話実際に画面に表示はされません。
AutoRedrawはこの方法で自動再描画を実現しています。
あと、HDCプロパティもメモリDCのハンドルとなります。
AutoRedrawをTrueにした場合、Refreshメソッドを実行するのはこの為なんですね。

ちなみになぜ再描画が必要なのかというと、2つのウィンドウがAとBあったとして、AがBに重なっていたとします。(Aが前面、Bが後面)
するとBウィンドウの一部はAウィンドウに邪魔されて隠れています。
今度はBウィンドウを見たくなったので、Bウィンドウをアクティブしました。
するとどうでしょう。BウィンドウとAウィンドウの重なっていた部分が真っ白くなってしまいました。
これはBウィンドウを作った人が「再描画」するプログラムをサボったからです。
ウィンドウが一旦画面から見えなくなってしまうと、その部分のビットマップ情報は消えてしまいます。
ですので内部で再描画を行わなければならないのです。
VBユーザの方のほとんどが「AutoRedraw」をTrueに設定して「自動再描画」としていると思いますが、実は「手動再描画」する事も可能なんです。


メモリDCを作成するには「CreateCompatibleDC」関数、再描画命令は「WM_PAINT」メッセージ。VBなら「Form_Paint」イベントです。
そのイベント内でメモリDCの内容をフォームに描画してやります。
しかしこの2つだけでは「手動再描画」は実現できません。
メモリDCを作っただけではビットマップまでは作成されませんので、作成後ビットマップをメモリDCに「SelectObject」させ、フォームが閉じられたら「DeleteDC」でメモリDCを解放します。
読んだだけではなかなか理解できないので、次回のサンプルに組み込もうかと思います。


DIBとDDB

前回まででDIBの表示はやりました。
関数は「SetDIBitsToDevice」を使いました。

たぶん
「DIBとDDBって結局なにがどう違うんだ!」
と思っている人は結構いると思います。
「デバイスに依存するとかしないとかで分かるか!」
そうなんですよね。そんな読者の声(?)に筆者も分かる範囲で説明したいと思います。

DIBとDDBの構造は「ビットマップビューアを作る(その1)」で説明しました。
そこで構造体を見てわかる通り、デバイスに依存をしないDIBの構造体の方があきらかに要素が多いです。
という事は、デバイスに依存しないが為、描画に必要な情報が多いからです。
一方DDBの方は、デバイスに依存するので色や高さ、幅などといった情報のみでいいのです。

前回、前々回と渡ってDIBの表示をやりましたが、実は画像を画面に表示するにはそのDIBをDDBに変換してやる必要があるんです。
DIBはデバイスに依存しない、つまり互換がありませんのでDIBをDIBのまま表示する事は不可能です
というより、DIBはビットマップファイルの事を指しているんです。
DDBは画面に表示したビットマップの事をさしています。
「依存するしない」と言っているのはこの事なんです。
これでもうお分かりでしょう。
ですので通常私たちがウィンドウに表示させた画像を操作するという事は、「DDBを操作する」という事になるんです。
BitBlt、StrechBlt関数などは全てのGDI関数はDDBを操作します。


なぜ2つに分けてるんだ?

知りません。(O_o)/~
いろいろと説はあるようですが、現在のウィンドウズではDDBの方が扱いやすいんでしょう。
近い未来。ビットマップは全てDIBに移り変わるといっている人もいます。
そうなった場合、古くからビットマップを扱ってるプログラマさんは「また変わるんかい・・・」などと呟いたりするんでしょうかね?
ちなみに筆者は、これを書いている現在でプログラム歴たった2年です。
16ビットプログラムすら組んだことがないので、ビットマップの移り変わりの歴史は見てきていませぬ。


今回はこれで終わりです。
何か気がついた事があったら随時追加していく予定です。
リクエストしてくれても良いですが、筆者のレベルでわかるに範囲でお願いします。。。