【プログラム】難しい・・・
- カテゴリ:パソコン/インターネット
- 2012/08/28 21:22:57
僕は Visual Basic .NET を使って色々とプログラムを組んでる。
んで、目標(?)として、RPGを作ることを目指してたりする。
んで、VBの、Graphicsクラスの、DrawImageメソッドを使って
描画してるんだけど、それよりも、
Win32APIの「BitBlt」が、描画の速度が速いらしい。
前から使いたいなと思ってたけれど、なかなかうまく行かなかった…
やりたいこと
「Graphics1」, 「Graphics2」と、「Graphics」オブジェクトを作って、
「Graphics1」に、ゲームで使う画像
(地面となる画像とか、キャラクターの画像とか、いろいろ)を順番に描画していって、
「Graphics2」に 描画が終わった「Graphics1」の画像を 描画したい。
つまりは ダブルバッファリングってやつ。
んで、この描画に「BitBlt」を使いたかったんだけど なぜかうまく出来なかった。
できなかった時のコード↓
'定数などの宣言
Public Const SRCCOPY As Integer = &HCC0020
Public Declare Function BitBlt Lib "gdi32" _
(ByVal hdcDest As IntPtr, _
ByVal nXDest As Integer, _
ByVal nYDest As Integer, _
ByVal nWidth As Integer, _
ByVal nHeight As Integer, _
ByVal hdcSrc As IntPtr, _
ByVal nXSrc As Integer, _
ByVal nYSrc As Integer, _
ByVal dwRop As Integer) As Boolean
'画像
Dim srcImage, destImage As System.Drawing.Bitmap
'BitBlt実行
Sub Draw()
BitBlt(Graphics2.GetHdc, 0, 0, 640, 480, Graphics1.GetHdc, 0, 0, SRCCOPY)
Graphics2.ReleaseHdc()
Graphics1.ReleaseHdc()
End Sub
これで実行しても 何も変わらなかった。
多分分かる方はここでもう分かるんだろうけど…
僕は答えを知ってもなぜかがよくわかんない^^;
とりあえず、成功した時のコード↓
'定数などの宣言
Public Const SRCCOPY As Integer = &HCC0020
Public Declare Function BitBlt Lib "gdi32" _
(ByVal hdcDest As IntPtr, _
ByVal nXDest As Integer, _
ByVal nYDest As Integer, _
ByVal nWidth As Integer, _
ByVal nHeight As Integer, _
ByVal hdcSrc As IntPtr, _
ByVal nXSrc As Integer, _
ByVal nYSrc As Integer, _
ByVal dwRop As Integer) As Boolean
Declare Function SelectObject Lib "gdi32" _
(ByVal hdc As IntPtr, ByVal hgdiobj As IntPtr) As IntPtr
Declare Function DeleteObject Lib "gdi32" _
(ByVal hgdiobj As IntPtr) As Boolean
'画像
Dim srcImage, destImage As System.Drawing.Bitmap
'BitBlt実行
Sub Draw()
Dim dest As Graphics = Graphics.FromImage(destImage)
Dim src As Graphics = Graphics.FromImage(srcImage)
Dim destHdc As IntPtr = grDest.GetHdc()
Dim srcHdc As IntPtr = grSrc.GetHdc()
Dim hBitmap As IntPtr = srcImage.GetHbitmap()
Dim oldBitmap As IntPtr = SelectObject(srcHdc, hBitmap)
BitBlt(dest, 0, 0, srcImage.Width, srcImage.Height, srcHdc, 0, 0, SRCCOPY)
SelectObject(srcHdc, oldBitmap)
DeleteObject(hBitmap)
src.ReleaseHdc(srcHdc)
dest.ReleaseHdc(destHdc)
End Sub
どうやら、「SelectObject」と「DeleteObject」をやらないとダメっぽい。
これをしないとダメな理由が本当にわからない^^;
やっぱりWin32APIって難しいなぁ…
「DeleteObject」は「SelectObject」してるから戻すためなんだろうけど
その「SelectObject」はなんだ…?
知っている人いたら教えてくださ~いm(_ _)m
Win32関数 説明 .NET Framework API
BitBlt ビットブロック転送を実行します。 System.Drawing.Graphics.DrawImage
もともと使ってたのと一緒だった……
難しいのぉ~~~ わからんわ(*^o^*)
でも、情報ありがとうございました^^
ゆっくりとだけど、なんとかやってみるw
(^人^)感謝♪
9月ですね
今日成績が出るそうです(^q^)
まだ見てません(`・ω・´) シャキーン
どうしましょ(V)・∀・(V) かにぱん。
SelectObjectは なんだ? という質問部分だけ 読んで 答えてたけど・・・
そもそも 上のコードって どっちも 何か おかしいような気がするな…
> どうやら、「SelectObject」と「DeleteObject」をやらないとダメっぽい。
と 決めつけているようだけど…
第1引数のハンドルは 仮想画面でも 実画面でも いいけど とにかく描画先の キャンバスへのハンドル
第6引数は、描画(仮想又は実画面へ転送)するビットマップやらパターンへのハンドル
が 正しくセットされていれば bitblt は 正常に動作するハズなんで
ハンドルが 示す先のオブジェクトが 正しい形式なら 別に SelectObject は 必須では 無いハズだけどな?
もうすこし 研究してみたらどうかな?
ちなみに ダブルバッファリングする時には まず 最終的に描画するウィンドウと互換性のある
メモリ・デバイスコンテキストを CreateCompatibleDC で生成したり
同様に 互換性のあるビットマップを CreateCompatibleBitmap で生成してやって
bitbltで扱うことが可能な状態に お膳立てしてやるのが おまじない的な儀式で 必要じゃなかったけ?
Compatibleって互換性のあるって意味だったよね?
これで引数として使える正しい形式のデータが作成できるようになる
そうしたら bitblt へ転送する背景データを先に Patblt 等で 仮想画面として 生成して
そうして生成した仮想画面の背景の上に bitblt で キャラクタパターンを 重ねて描画して
それを 今度は 実画面へ 再度 bitblt で 転送してやることで ちらつきの少ない 描画を・・・
という理屈で プログラムすることが 多いと思うのだが (つまり SelectObjectは 必ずしも必要ない)
上の コードは そうなってないような気がする・・・
※ 昨日書きこんだコメント 寝ぼけてて bitblt が bitbit ってなってた…
恥ずかしいんで、修正して投稿しなおしました。 てへぺろ!
btbltが ビットパターンを 直接描画するような 仕様にしちゃうと
せっかく 文字コードという 記号化された 少ない情報になっているデータを
毎回、ビットパターンに展開する手間をかけて、メモリも圧迫しながら btbltに描画させるなんて アホだよね?
いや 結局 内部的には 毎回 そういうことが 行われているとしても
そのフォントのビットマップ展開を ユーザーごとに 別々のプログラムを用意させて まちまちの速度で…
なんて 無駄な仕様には しないでしょ?
あらかじめ用意された ブラシや ペンのパターンも 番号やら何やらで 記号化したまま扱ったほうが
プログラムを作っているユーザーにとっては メリットあるよね?
で その ビットマップデータへの展開は SelectObjectとかに任せちゃったほうが 楽だし 効率が良いし
安定する。
でも 下のコメントでも書いたように SelectObject 部分を btblt の中に 含めちゃうことも 可能。
でも それをしないのは 同じブラシパターンを 連続して描画する場合に
btblt の中に パターン展開機能も含めちゃうと 毎回 種類を判断して また 同じ展開処理をして …
と 確実に 無駄が 発生して ループが 遅くなるよね?
だから 最低限描画に必要な部分だけを ループの中で実行できるように シェイプアップして ライブラリ化してある
Win32API は ウィンドウズの 基本的動作を 司るライブラリだから これが できるだけ高速に実行できるよう
さまざまな 工夫や配慮がされて 設計されている ・・・ というように 考える ことも できるかな?
自分が 描画用の ライブラリーを 作成する場合を考えたとき
描画する可能性のあるオブジェクトが ビットマップ、ブラシ、フォント、ペン、リージョン
のように 複数が 想定される場合で
仕様を 練っていったら 最終的に描画する部分の プログラムが
ほとんど 同じになったとき
ビットマップ描画専用の btblt_bitmap() 関数と
ブラシ描画専用の btblt_blush() 関数と
フォント描画専用の btblt_font() 関数と
ペン描画専用の btblt_pen() 関数と
リージョン描画専用の btblt_region() 関数と
って 5種類 用意したりするかな?
普通は 汎用の btblt() 関数1個で どの種類の描画も できるようにするよね?
で
その方法として btblt() の引数に どんな型でも 渡せるようにして
btblt() の中で 渡された引数が ビットマップなのか 何なのか判断する…
って手もあるけど そうすると ビットマップとかのデータフォーマットに
btblt() が判別するための 何らかの 冗長な識別データを 埋め込む必要が出て
大量の 描画データを 扱おうとするときに メモリ容量やらなんやらで 不利だよね?
だったら btblt() には、何を描画する場合でも 同一の引数を渡した方がいいじゃん?
で btblt() は 描画 という動作だけに 専念する仕様にして > だからこそ 描画が高速になる。
そのために 描画する種類やパターンを指定するのは 別の関数に任せた方がよくない?
ってことで SelectObject() に その役目を 任せちゃうかなって感じに ライブラリをつくるんちゃう?
用意したビットマップは 必要最小限のデータ。
SelectObject で メモリ上の仮想領域に、ビットマップ+種類情報等の必要な情報を付加して、決められたフォーマットにしてセット = セレクト。
btblt()で、細かいことは気にせず描画!=高速!
(特に 同じパターンを連続で描画する時に SelectObjectは 1回だけで 描画部分だけ繰り返せるから お得!)
ふ~む。 まぁ API の仕様を デザインした エンジニアが そのような仕様にしていれば 可能だったでしょうが…
ビットマップは メモリ・デバイスコンテキストで 通常のデバイスコンテキストと 違って
メモリ上に用意された 仮想的な状態なので そのまま bitblt にハンドルを 渡しても 描画できません。
デバイスコンテキストは 下のコメントの例のように 描画用のパターンだけを指すのではなく
実際にウィンドウに表示されるキャンバスとしての描画領域と 筆の種類に相当するもののセット のことです。
筆だけのことでは ありません。
だから メモリ・デバイスコンテキストの状態から SelectObjectで 通常のデバイスコンテキストの状態に
してやって その結果の ハンドルを bitblt に渡す必要があるのです。
かなり 乱暴な 説明ですが ・・・ どうでしょう?
マウスで 描画エリア の上を なぞると どうなりますか?
おそらく 太さ数ドットの 黒色の線が 描画されたと思います。
この描画が btblt によって 行われているかどうかは まぁ 気にせずに…考えてみましょう。
なぜ 太さ数ドットの 黒色の線が 描画されるのでしょうか?
こう考えてみましょう プログラム起動時に デフォルトの デバイスコンテキストとして
白いキャンバス と 数ドットの黒い筆先の パターンが セットされているのかな?と
・・・
では NotePad に マウスで 同じように入力エリアを なぞってみます。
何も起きませんね。 それは グラフィックを描画する ビットイメージが
初期状態の デバイスコンテキストとして セレクト されていないからでは ないでしょうか?
そして NotePad には、 そもそも プログラムのメニューに ビットイメージを デバイスコンテキストに
セットするという命令を 持っていません。
その代わり キーボードを タイプするだけで ゴシック文字のフォントが 描画されますね?
通常のウィンドウには 通常のデバイスコンテキストとして テキスト入力エリアと 文字フォントが
セレクトされている状態だと考えてみましょう。
・・・
では あなたが Paint に文字を 描画することは できないでしょうか?
できますよね。 「A」のメニューを クリックすると 文字フォントを描画パターンに持つ デバイスコンテキストが
セレクトできますから キーボードから入力した文字を 描画することが 可能となりますね。
そして ブラシ メニューから 様々なパターンを 選んで 描画することも可能です。
・・・
あなたが お金持ちで もっと高価で 高性能な グラフィックツール を 持っていれば
自分で 作成した ビットパターンや 一定のサイズのグラフィックなどを 筆先として選択し
ちょっと 面白い 線を描画することも可能です。 selectObjectの意味は もう 分かりますよね?
まぁ、ちょっとインチキな説明ですが 正解は 自分で調べてください。
人によって 例え方は 色々あるので
自分が しっくり くるのが スタンプの例えなら
それで 良いのでは ないかな? しっくりクッキー しっくりかぁ~?
描画元の画像をスタンプみたいな感じで扱うってことで、
そのスタンプの一部(もしくは全部)を描画先に描画するってこと・・・でいいのかな。