none
Win32 API 描画処理について RRS feed

  • 質問

  • 質問です.

    現在Windows API により,クライアント領域に描画した画像がマウスの動きに応じて移動するプログラムを書いています.

    マウスの動きに対して画像が追従するようにはできたのですが,

    画像が動いた後のすべてに,画像が描画されてしまいます.

    これを現在のマウスの位置だけに対して描画する処理を行いたいのですが,

    どのように実装すればよいのか詰まっています.

    どなたか回答お願いいたします.

    2012年2月20日 10:28

回答

  • 具体的に、(1)マウスの動きに追従する描画はどのように実施し、(2)マウスの位置以外の場所のクライアント領域の描画はどのように実施しているのでしょうか。

    「マウスの位置だけに対して描画する」ということは、それ以外のところは「クライアント領域の描画をやり直す」ということが必要です。
    それが単に InvalidateRect で済むのか、もう一工夫が必要なのかは、先に書いたようにあなたがどのような方法を使っているかによりま
    す。WM_MOUSEMOVE あたりで差分しか描いていないのであれば、InvalidateRect を呼ぶと「マウスの位置だけに描画する」とした内容が見えなくなるかもしれませんね。(その場合、差分描画をやめてすべて WM_PAINT で描く、差分描画時に一度クライアント領域を描き直して差分を描画するなどかなぁ)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年2月20日 13:46
    モデレータ

すべての返信

  • 具体的に、(1)マウスの動きに追従する描画はどのように実施し、(2)マウスの位置以外の場所のクライアント領域の描画はどのように実施しているのでしょうか。

    「マウスの位置だけに対して描画する」ということは、それ以外のところは「クライアント領域の描画をやり直す」ということが必要です。
    それが単に InvalidateRect で済むのか、もう一工夫が必要なのかは、先に書いたようにあなたがどのような方法を使っているかによりま
    す。WM_MOUSEMOVE あたりで差分しか描いていないのであれば、InvalidateRect を呼ぶと「マウスの位置だけに描画する」とした内容が見えなくなるかもしれませんね。(その場合、差分描画をやめてすべて WM_PAINT で描く、差分描画時に一度クライアント領域を描き直して差分を描画するなどかなぁ)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。

    2012年2月20日 13:46
    モデレータ
  • BitBlt などによる「描画」は不可逆な操作です。

    背景画像に何かを「描く前の状態」に戻すような機能は Win32 レベルでは提供されていません。

    では、どうするか?ですが、まず、「背景画像」と「そこに描画するべき何かの画像」を直接画面に描画することはしません。

    「オフスクリーン」や「DIB」などのキーワードで検索すれば、適当なサンプルが見つかるのではないかと思います。

     

    2012年2月20日 13:54
  • 実装によりますね。
    (A)全ての描画をWM_PAINTで行っており、他の場所では行っていない。
      つまり、CPaintDC( = BeginPaint())のみ使用しているケース。
      この場合の実装ではWM_MOUSEMOVEでは、InvalidateRect()のみを行っている。
    (B)WM_MOUSEMOVEタイミングなどでCClientDC(キャッシュDC=GetDC())
      を用いて画像を描画しているケース。

    まず、当然ですが、両者の場合共、画像の矩形を保持していなければ話になりません。
    (A.1) WM_MOUSEMOVEがきたら
    (A.2) 画像描画フラグをOFF。
    (A.3) 現在の画像の矩形RcPitureに対して
       InvalidateRect( &RcPiture, TRUE);//背景消去する
    (A.4) UpdateWindow(); // 即時にWM_PAINTを発行。画像描画フラグがOFFなので
       背景の塗りつぶしのみで、画像は表示されないものとする。
    (A.5) 新しい位置にRcPitureを移動。
    (A.6) 画像描画フラグをON。
    (A.7) 新しい位置の画像の矩形RcPitureに対して
       InvalidateRect( &RcPiture, FALSE);//背景消去不要
    (A.8) UpdateWindow(); // 即時にWM_PAINTを発行。画像描画フラグがONなので
       画像が表示される。
    てな感じ。うまくコードすると(A.4)が不要になります。

    ケース(B)の場合は
    (B.1) WM_MOUSEMOVEがきたら、
    (B.2) HDC  hdcCash = GetDC()する。または、CClientDC hdcCash(this);
    (B.3) hdcCashに対して、RcPitureを背景色で塗りつぶす。
    (B.4) 新しい位置にRcPitureを移動。
    (B.5) その位置に画像を描画(BitBlt等)。
    (B.6) ReleaseDC()する。
    (B.7) ValidateRect( hwnd, NULL);でクライアント領域全体を「有効」にする。
       これにより、WM_PAINTが発生するのを抑制する。

    ケース(B)の方が簡単ですが、移動しないつまり静的状態と、
    移動時で描画方法が変わる点がやや制御が難しい。モードありということです。
    ケース(A)は描画の仕組みが簡単ですが、無効にする
    矩形の制御と、画像描画フラグの制御にやや手間がかかります。
    参考になれば幸いです。

    2012年2月21日 2:01
  • 描画に関する記述はすべてWM_PAINTにしております.

    まずフラグを定義し,

    WM_LBUTTONDOWN時にフラグをonにして,

    WM_LBUTTONUP時にフラグをoffにし,マウスクリック時にだけ描画が行われるようにしています.

    WM_MOUSEMOVE時にフラグがonならば,InvalidateRect関数を呼び出して描画を行っています.

    WM_PAINTでは,マウスの位置に対して,BitBlt関数を用いて画像の描画を行っています.

    ウィンドウ最小化時や,ウィンドウサイズ変更時に移動時に描画された画像が消え,

    最後に描画された画像だけ残るため,単純にクライアント領域の更新ができていないのだと思っていました.

    2012年2月21日 2:08
  • それは、自分の発言の(A)のアルゴリズムとは異なりますね。
    (A)の手順をみればわかるとおり、この描画フラグはWM_MOUSEMOVE専用で
    なければなりません。他の物と共用してはなりません。

    また、WM_MOUSEMOVE時の再描画のデバッグは他の場合と比べて困難です。
    WM_MOUSEMOVE時の描画に関する挙動と関係ない、描画に影響を与える
    コードはコメントアウトしておくことをお勧めします。

    また、HDCの描画は自動的にキャッシュされます。
    ブレーク時に、当該描画コードの結果をリアルタイムに確認するには、
    その直後に GdiFlush()を行うと良いでしょう。

    2012年2月21日 2:23
  • 回答ありがとうございます.

    オフスクリーンの概念などを知らなかったため参考になりました.

    2012年2月21日 2:45
  • 非常に詳しい回答ありがとうございます.

    今までDCについては,私自身ほとんど触れてこなかったため,

    非常に参考になりました.

    また,回答していただいた手順を見るとわからない語句とがあったため

    DCについて私の不勉強さがわかりました.

    描画を行う上でDC(描画)に対する基礎をもう一度見直そうかと思います.

    ありがとうございました.

    2012年2月21日 2:58
  • WM_PAINT発生時に

    毎回,クライアント領域を初期化する方法をためしてみました.

    結果実行結果が想定通りになりました.

    ありがとうございました.

    2012年2月21日 3:00