月刊ソフト作り!
Make The Software For VisualBasic
『ビットマップビューアを作る(その3)』
2002.May.13
Presented by kouta_y
感想等は掲示板、苦情はメールへ。
前回の続きです。
今回はそんなに難しくありません。
前回までのあらすじ
さて前回はやっとこさ「ビットマップビューア」と名乗れるものが出来上がりました。
今回は「GDIの知識」でも触れた「再描画」というものを実現してみようと思います。
APIの宣言がまた増えてしまいますが、「VBなので・・・」という事で諦めましょう。。。
再描画とは?
その名の通り、「再び描画」します。
再び描画するというのは、ウィンドウを表示させても、他のウィンドウに邪魔され隠れてしまう事があります。
んで再度そのウィンドウを表示させようとした場合、もう一度ウィンドウの内容を更新しなければなりません。
それを「再描画」と言います。
どうやるのか
ここで紹介する方法は、ちと面倒なやり方かもしれません。
今回に限りませんがソースの書き方なんて人それぞれです。
「再描画はこの方法しかない」とは思わないでください。
もっと簡単は方法もありますし、逆に「こんなやり方もあったのか!」なんて思ってくれれば幸いです。
その中で自分にあったプログラムを見つけてください。
・・・では、高尚な前置きここまでにして(高尚か?)さっそく方法です。
まずメモリデバイスコンテキスト(以下メモリDC)を作成し、そこにビットマップを表示させます。
んで再描画命令がきたら、論理DCつまりメモリDCの内容を物理DC(フォームのDC)にビットマップを転送します。
こんだけです。
しかしこれだけといってもなかなか手順が面倒な上、最初の内は理解しがたいかもしれません。
いろいろソースをいじって「結局こういう動作をするんだ」というのを理解してください。
' hdc互換のメモリDCを作成 Public Declare Function CreateCompatibleDC Lib "gdi32" ( _ ByVal hdc As Long _ ) As Long ' hdc互換のビットマップを作成 Public Declare Function CreateCompatibleBitmap Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal nWidth As Long, _ ByVal nHeight As Long _ ) As Long ' グラフィックオブジェクトを選択 Public Declare Function SelectObject Lib "gdi32" ( _ ByVal hdc As Long, _ ByVal hObject As Long _ ) As Long ' オブジェクトリソースを解放 Public Declare Function DeleteObject Lib "gdi32" ( _ ByVal hObject As Long _ ) As Long ' メモリDCを解放 Public Declare Function DeleteDC Lib "gdi32" ( _ ByVal hdc As Long _ ) As Long ' 画像転送 Public Declare Function BitBlt Lib "gdi32" ( _ ByVal hDestDC As Long, _ ByVal x As Long, _ ByVal y As Long, _ ByVal nWidth As Long, _ ByVal nHeight As Long, _ ByVal hSrcDC As Long, _ ByVal xSrc As Long, _ ByVal ySrc As Long, _ ByVal dwRop As Long _ ) As Long |
CreateCompatibleDC
指定されたDCに互換のあるメモリDCを作成します。
メモリDCを作成する場合、何かしら「元」となるデバイスコンテキストが必要となります。
hdc 互換をとるデバイスコンテキスト。NULL(ゼロ)を指定すると画面に互換のあるメモリDCを作成する。
戻り値は、成功したらメモリDCハンドル、失敗したらNULL(ゼロ)が返ります。
CreateCompatibleBitmap
指定されたDCに互換のあるビットマップを作成します。
ここでいうビットマップというのはDDBです。
hdc 互換をとるDC
nWidth、nHeight ビットマップの幅と高さ。このパラメータに0を指定すると1×1のモノクロビットマップを作成します。
戻り値は、成功したらビットマップのハンドル(HBITMAP)、失敗したらNULL(ゼロ)が返ります。
SelectObject
指定されたGDIオブジェクトをデバイスに選択します。古いオブジェクトは新しいものに置き換わります。
作成したGDIオブジェクトはこの関数によって選択させなければ意味がありません。
hdc 対象となるDC
hObject 新しいオブジェクト
戻り値は、成功したら置き換わる前のオブジェクトのハンドル。失敗したらNULL(ゼロ)が返ります。
DeleteObject
指定されたGDIオウブジェクトを削除し、割り当てられていたリソースなども全て解放します。
使い終わったオブジェクトは必ずこの関数にて削除してください。
hObject 削除するオブジェクト
戻り値は、成功したら0以外、失敗したら0が返ります。
DeleteDC
指定されたデバイスコンテキストを削除します。
使い終わったDCは必ず削除してください。
ReleaseDCとは違いますので気をつけてください。
hdc 削除するデバイスコンテキスト
戻り値は、成功したら0以外、失敗したら0が返ります。
BitBlt
デバイスにビットブロックを転送(コピー)します。
DDBを操作する代表的な関数の1つです。この関数にて画像の転送を行います。
hDestDC コピー先のDC
x、y コピー先の左上端xとy座標
nWidth、nHeight コピー先の幅と高さ
hSrcDC コピー元のDC
xSrc、ySrc コピー元の左上端xとy座標
dwRop ラスタオペレーション。ここについてはこのページを参照してください。
戻り値は、成功したら0以外、失敗したら0が返ります。
Private Sub Form_Paint() ' メモリDCの内容をフォームに転送 Call BitBlt(Me.hdc, 0, 0, Me.ScaleWidth, Me.ScaleHeight, hMemDC, 0, 0, vbSrcCopy) End Sub |
Public hMemDC As Long Public hBmp As Long Public hOldBmp As Long Public Sub Main() Dim BFH As BITMAPFILEHEADER Dim BI As BITMAPINFO Dim Bit() As Byte Dim Cmd As String Dim Argv() As String Dim Argc As Long Dim NO As Integer Cmd = Command$() Argc = ArgvArgc(Cmd, Argv) If Argc <= 0 Then Exit Sub NO = FreeFile() Open Argv(0) For Binary As #NO Get #NO, , BFH If BFH.bfType <> BM Then ' ビットマップではない MsgBox "形式エラー" Close Exit Sub End If Get #NO, , BI.bmiHeader With BI.bmiHeader If .biBitCount < 16 Then ' 8Bit以下の場合カラーテーブルを読み込む Get #NO, , BI.bmiColors End If ' ReDim Bit(.biHeight * .biWidth * 4) As Byte ' 余分に確保 ReDim Bit(((.biWidth * .biBitCount + 31) \ 32) * 4 * .biHeight) As Byte ' バイト数を計算 End With Seek #NO, BFH.bfOffBits + 1 ' ピクセルビットのポインタへ移動 Get #NO, , Bit Close Load Form1 Form1.Width = (BI.bmiHeader.biWidth * Screen.TwipsPerPixelX) + _ (Form1.Width - (Form1.ScaleWidth * Screen.TwipsPerPixelX)) ' Twipに変換 Form1.Height = (BI.bmiHeader.biHeight * Screen.TwipsPerPixelY) + _ (Form1.Height - (Form1.ScaleHeight * Screen.TwipsPerPixelY)) ' Twipに変換 Form1.Caption = Argv(0) ' メモリDCを作成 hMemDC = CreateCompatibleDC(Form1.hdc) ' ビットマップを作成 hBmp = CreateCompatibleBitmap(Form1.hdc, Form1.ScaleWidth, Form1.ScaleHeight) ' ビットマップを選択 hOldBmp = SelectObject(hMemDC, hBmp) ' DIBを転送 Call SetDIBitsToDevice(hMemDC, 0, 0, BI.bmiHeader.biWidth, BI.bmiHeader.biHeight, _ 0, 0, 0, BI.bmiHeader.biHeight, Bit(0), BI, DIB_RGB_COLORS) Form1.Show vbModal ' 前のビットマップに戻す Call SelectObject(hMemDC, hOldBmp) ' ビットマップを解放 Call DeleteObject(hBmp) ' メモリDCを解放 Call DeleteDC(hMemDC) End Sub |
さて、ごちゃごちゃと書いてありますね。
まず、フォームの幅や高さを設定している所までは全部同じです。
その次にメモリDCを作成して、そのハンドルをグローバル変数へ代入しています。
ここで勘違いしないでもらいたいのは、デバイスコンテキストを作成しただけなのでビットマップの内容は同じではありません。
作成時のメモリDCのビットマップは1×1のモノクロビットマップの情報しかもっていません。
そこで今度はビットマップを作成します。
次に、SelectObject関数にて作成したビットマップをメモリDCに選択します。
この時点でやっとメモリDCに絵が描ける状態になりました。
作成したビットマップはメインフォームのビットマップと互換がとれておりますので、カラー情報なども全てメインフォームと同じです。
んでSetDIBitsToDevice関数で、前回はメインフォームに直接描画していましたが、今回はメモリDCへ描画します。
この時点で転送したDIBはDDBに変換されます。
もちろんメモリDCに描画しただけでは、まだ画面には表示されません。
どうやって表示させるかというと、表の上段に書いてあるコードです。
このコードは今回唯一のフォームイベントです。
Paintイベント内で、BitBlt関数を呼び出しメモリDCの内容をフォームに描画します。
Paintイベントは再描画命令がきた場合にのみ発生するイベントで、Showメソッドを実行しウィンドウが表示された直後に呼び出されます。
Showメソッドの所でvbModalを指定していますが、これはMainプロージャが終わらないようにする為です。
メインフォームが閉じられたら、後始末をします。
まずSelectObjectで、前のオブジェクトに戻します。
その後作成したビットマップを削除し、最後にメモリDCを削除してプログラムを終了します。
(人によっては「どうせ破棄するんだからSelectObjectする必要はない」と言う人もいますが、基本ですのでやるようにしてください)
完成
実行させてみて、ウィンドウを隠したりなんなりさせてみてください。
AutoRedrawをTrueにした時と同じ動作をするはずです。
試しにPaintイベントを何もしないとウィンドウには何も表示されません。
ちなみにウィンドウをモーダル表示しているのでタスクバーには表示されなくなっています。
モードレス表示にし、後始末をUnloadイベント内で処理すればこの問題は解決されます。
今回はあまりあっちゃこちゃにコードを散らしたくなかったので、こんなコードとなりました。
終わり
とりあえず今回はメモリDCの使い方と、ついでに再描画のやり方でした。
メモリDCとはどういうものか?
DDBとはどういうものだ?
そんな事がなんとなくわかってもらえれば嬉しいです。
さて、では今回やったサンプルコードです。
実はちょっとした「おまけコード」を付けました。
たいしたものではないですが、何かの参考になればと思います。
vb09.lzh(8.78KB)