領域を指定した再描画について

返信する


答えを正確に入力してください。答えられるかどうかでスパムボットか否かを判定します。

BBCode: ON
[img]: ON
[url]: ON
スマイリー: OFF

トピックのレビュー
   

展開ビュー トピックのレビュー: 領域を指定した再描画について

レスありがとうございます。

by jacoby » 2006年9月22日(金) 19:40

マティさん、再びのレスありがとうございます。
WM-PAINTを発生するために、InvalidateRectを使用していますよね!
このときInvalidateRect(hMainWnd, rc2, FALSE)で再描画を行う範囲を明示していると思います。

システムは、この範囲を再描画を行う領域と認識(描画命令を実行する範囲をhMainWndのパラメータとして保持)、ドライバーレベルで解読します。
かなり突っ込んで説明して頂いた内容なので、中々自分には難しいのですが、
こういうことでしょうか、
つまり、「InvalidateRect」を実行したとき、その時点で、
そこで指定されている描画範囲指定値(ここでは"rc2")の
値からクリッピングが行われ、ここで最右辺と最下辺はクリップされてしまう。

ただそこから先が、難しいです。。。

 ここからペイント・イベントが呼ばれて、BitBltが実行されるのですが、
例えば上の四角表示テストのように、rc2から受け渡されたPAINTSTRUCTの値
を無視して、強制的に範囲を改めてセットして

コード: 全て選択


BitBlt(hDC,0,0,301,301,hMemDC,0,0,SRCCOPY) 
(301,301ではなく、あるいはもっと極端に400,400とか)
と書いてあってもあくまで四角の右辺と下辺は表示されない。
かといって、BitBltで指定されている範囲指定は「無視」されている
のかというと、右辺と下辺部分が描画されてないだけで、その外側は改めて
きっちり範囲分転送されている。。。ここらがやっぱり分からないとこです。
 恐らくその辺りの動作の根拠は、
bitbltに限りますが、良いドライバーは描画範囲をドライバー側で再計算し転送量を最低限にするように内部でパラメータを補正しますが、適当に作成したドライバーでは、H/WでクリッピングをONにした後、指定領域を全て転送します。
つまり、データ転送量が大幅に異なる場合があり、検証するには一部領域にクリッピングをかけて、全領域をBitBLTすると違いがはっきりします。
(大昔の話なので、こんな腐ったドライバーは存在しないと思いますが・・・)
ここに書かれているんじゃないかと思うのですが、、、


あまりズルズルと訊いてしまうのも申し訳ないので、ここでは
とりあえず先に書いた様に、

コード: 全て選択


 rc2.right++ 
 rc2.bottom++ 
 InvalidateRect(hMainWnd, rc2, FALSE) 
 Sleep(1) 
とインクリメントして、問題なければ以降こう書いていこうと
思います。
 
 返信遅れてしまいましたが、
 レス、ありがとうございました。またよろしくお願いします。

by マティ » 2006年9月20日(水) 20:29

クリッピングに関する質問

細かい制御方法は忘れてしまったので、うる覚えでお答えします。
(この辺をいじっていたのは10年以上前なので、細かいことは・・・)

WM-PAINTを発生するために、InvalidateRectを使用していますよね!
このときInvalidateRect(hMainWnd, rc2, FALSE)で再描画を行う範囲を明示していると思います。

システムは、この範囲を再描画を行う領域と認識(描画命令を実行する範囲をhMainWndのパラメータとして保持)、ドライバーレベルで解読します。
(描画可能な場合はH/Wでクリッピングを設定し、不可能な場合はGDIで分割後、再処理する)

bitbltに限りますが、良いドライバーは描画範囲をドライバー側で再計算し転送量を最低限にするように内部でパラメータを補正しますが、適当に作成したドライバーでは、H/WでクリッピングをONにした後、指定領域を全て転送します。
つまり、データ転送量が大幅に異なる場合があり、検証するには一部領域にクリッピングをかけて、全領域をBitBLTすると違いがはっきりします。
(大昔の話なので、こんな腐ったドライバーは存在しないと思いますが・・・)

描画範囲の設定
上記を踏まえて、BitBLTを行うには、相応のコスト(時間)がかかります。
常に右辺と底辺を+1すると、右から左(or下から上)の描画時に、余分に1ドット転送を行います。
性能改善に追われていた自分には、余分な転送は・・・

まぁ、いいか!

つたない文章ですが以上です

レスありがとうございます。

by jacoby » 2006年9月20日(水) 02:20

マティさん、レスありがとうございます。
私の記憶が正しければ、クリッピングは右辺と底辺を描画対象にしません。
 それを受けて、次のようにプログラムを書き直してみると、うまく描画されました。

コード: 全て選択


 'MainWndに描画 
  rc.right=x 
  rc.bottom=y 
  DrawLine(hMemDC)  

 ' Dim rc2 As RECT
  If rc.left>rc.right Then
      rc2.left =rc.right
      rc2.right=rc.left
    Else
      rc2.left =rc.left
      rc2.right=rc.right
  End If

  If rc.top>rc.bottom Then
      rc2.top   =rc.bottom
      rc2.bottom=rc.top
    Else
      rc2.top   =rc.top
      rc2.bottom=rc.bottom
  End If

  rc2.right++
  rc2.bottom++
  InvalidateRect(hMainWnd, rc2, FALSE) 
  Sleep(1) 
rc2.right++
rc2.bottom++
この2行を追加しました。
恐らくマティさんの言われていることがこれでも実行できてる
のではないかと思うのですが、
初めはペイントイベント内のxv,yvの値を更に+1したりしてみたりして
(それでは変わらず)、あれこれ考えながらの結果でした。
クリッピングという処理(Clipping:不要な部分を除去する処理だと思います。)が
どのタイミングで行われているのか自分ではまだハッキリ分かってないので、
ちょっとまだ消化しきれてないあやふや感が残っています。

 それで次のようにプログラムをより単純にして実験をしてみたのですが、
(プロジェクト名「UzumakiTest424_B」で製作)
教えて頂いた+1はここではあえてやらない状態にして、

プログラムは「渦巻き描画」をやめて、
単に(0,0)-(300,300)の四角(ボックス)を描画して、
ついでにその対角線のラインを描画する、というだけのものです。

コード: 全て選択


  rc.left=0
  rc.top=0
  rc.right=300 
  rc.bottom=300
  penColor=RGB(255,255,0)
  DrawBox(hMemDC)
  penColor=RGB(255,0,0) 
  DrawLine(hMemDC)  
そしてペイントイベントでは

コード: 全て選択


 BitBlt(hDC,0,0,301,301,hMemDC,0,0,SRCCOPY) 
このように強制的に
(0,0)-(300,300) つまり幅、高さ共に301ドットの大きさで
常にコピーするように書きました。

 これで対角線の入ったボックスが表示される筈だと
思ったのですが、結果はボックスの右辺、下辺が描画されない。

 試しに思い切って、

コード: 全て選択


 BitBlt(hDC,0,0,400,400,hMemDC,0,0,SRCCOPY) 
とコピーの大きさを400,400に変えても、結果
右辺、下辺が表示されない。

 ここらへんの動作がまだ、ハッキリ分かっていないんです。
よろしければまた教えて下さい。
 レス、ありがとうございました。

by マティ » 2006年9月18日(月) 19:40

私の記憶が正しければ、クリッピングは右辺と底辺を描画対象にしません。
rc2.left =rc.left + 1
rc2.right=rc.right + 1
rc2.top =rc.top + 1
rc2.bottom=rc.bottom + 1
左から右、上から下へ描画する際に領域を1だけ大きくするとちゃんと描画を行えると思います。

プログラムにバグがありました。

by jacoby » 2006年9月17日(日) 16:03

 どうやらInvalidateRectに渡すRECT構造体「rc」の設定に
まず誤りがありました。
rc.leftとrc.topには「左上頂角」、
残りのrc.rightとrc.bottomには「右下頂角」の座標を
入れなければならないのですが、そこがあのプログラムでは必ずしも
その値になっておらず、間違ったセットになっていました。
スレッド1の描画ループの箇所を下のように修正をして、

コード: 全て選択


'MainWndに描画 
  rc.right=x 
  rc.bottom=y 
  DrawLine(hMemDC)  

'左上頂角、右下頂角かどうか判定。
   Dim rc2 As RECT
  If rc.left>rc.right Then
      rc2.left =rc.right
      rc2.right=rc.left
    Else
      rc2.left =rc.left
      rc2.right=rc.right
  End If

  If rc.top>rc.bottom Then
      rc2.top   =rc.bottom
      rc2.bottom=rc.top
    Else
      rc2.top   =rc.top
      rc2.bottom=rc.bottom
  End If

  InvalidateRect(hMainWnd, rc2, FALSE) 
  Sleep(1) 
  rc.left=rc.right 
  rc.top=rc.bottom 
やってみると、前のように「円周の右上部分しか描画されない」と
いうことは無くなりました。
が、ただまだ部分部分で「かすれた」ようになってしまっています。

 どこかにまだ修正があるものと思います。
また報告します。

領域を指定した再描画について

by jacoby » 2006年9月16日(土) 02:50

描画速度の高速化のために
InvalidateRectで領域を指定した再描画を
させようと以下のようにプログラムを書いてみたのですが、、、
(AB 4.24でプロジェクト名「UzumakiTest424」で製作。)


プログラムは以前のスレ「描画速度の違いについて」で作った
「ラインで画面に序々に渦巻き模様を描いて行く」というものです。

 まず自分で「PAINTSTRUCT構造体」を参照するため
本来CallBack.wbpの中で書かれている
EventCall_MainWnd()の中身をごっそり
MainWnd_Proc()の方へコピーして持って来て、
(その際MainWnd_Proc()の中ではEventCall_MainWnd()へ
行かないように流れを切りました。)
ウインドウメッセージ処理のSelectCase分岐で
WM_PAINTのところ、

コード: 全て選択


'---------- 今回「PAINTSTRUCT」参照の為、ここを変更 ---------
       Case WM_PAINT
                Dim ps As PAINTSTRUCT
                Dim hDC As HDC
                hDC=BeginPaint(hWnd,ps)
                MainWnd_MyPaint(hDC,ps)
                EndPaint(hWnd,ps)
'-----------------------------------------------------
このように変えて、
更にMainWnd_MyPaint()という自前のペイントイベント処理サブを
新たに作り、そこでPAINTSTRUCT構造体を参照するようにしました。

コード: 全て選択


'
'●マイ・ペイント・イベント(領域を指定した再描画。今回これを使用する)
'
Sub MainWnd_MyPaint(ByVal hDC As HDC, ByRef ps As PAINTSTRUCT)
 Dim xa As Long
 Dim ya As Long
 Dim xv As Long
 Dim yv As Long

 xa=ps.rcPaint.left
 ya=ps.rcPaint.top
 xv=ps.rcPaint.right-xa+1
 yv=ps.rcPaint.bottom-ya+1
 BitBlt(hDC,xa,ya,xv,yv,hMemDC,xa,ya,SRCCOPY)
End Sub
とりあえずこのようにしてinvalidateRectの領域指定で

コード: 全て選択


'MainWndに描画
  rc.right=x
  rc.bottom=y
  DrawLine(hMemDC)  
  InvalidateRect(hMainWnd, rc, FALSE)
  Sleep(1)
として再描画させようと思ったのですが、
実行結果は、上のソースを実際実行していただければ一目瞭然なのですが
「再描画される所、されない所が出てきてしまい」、このままでは使い物になりません。

 PAINTSTRUCT構造体の再描画領域の取得そのものは
出来ているように思えるのですが、実際に描画されるのは
渦巻き円の右上部分だけで、後はまるで線が「かすれる」ように消えていく
感じです。

 InvalidateRect命令の再描画領域の指定を
行うにはどうすれば良いのでしょうか?
(その他でもプログラム中で直す所がありましたら教えて下さい)
(環境 WinMe / AB 4.24)

ページトップ