• ベストアンサー

Windowのちらつき防止

WEB検索をしますと結構情報がヒットしますが、 解決に至ってませんので、よろしくお願いします。 Win32アプリケーションです。 WM_PAINTのタイミングでWindowに文字を描画してますが、 背景の塗りつぶしが原因でちらつきます。 WM_ERASEBKGNDメッセージをつぶせない仕様ですので、 よく用いられる方法で、ビットマップによるダブルバッファリングを行ったところ、 ちらつきが抑えられましたが、極端に遅くなってしまいました。 質問1 ビットマップに描画して画面に転送するという動作は どうしたら早くできるでしょうか? 質問2 どんな方法でも結構ですので、画面のちらつきを抑える方法はないでしょうか? どんなささいなことでもかまいませんので お願いします。 不足している情報がありましたら、ご指摘ください。 補足させていただきます。 こちらの掲示版はなにか返答がなければ、もう本人は何もできませんので。

質問者が選んだベストアンサー

  • ベストアンサー
noname#30727
noname#30727
回答No.6

>スクロールなどによって文字が変わりますので、 >一旦すべて消さないと全部重なって出て来てしまいます。 バッファが不要という意味ではありません。 もの凄く極端ではありますが、例を示します。 CreateCompatibleDCでメモリDCを作成、CreateCompatibleBitmap でデスクトップと同じ大きさのビットマップを作成しメモリDCにSelectObjectしておきます。 このビットマップがバッファとなり、全ての描画は一度ここに行う事とします。 WM_ERASEBKGNDでは何もしないで下さい。 WM_PAINTは、BeginPaintを行う前に、 CreateRectRgn(0, 0, 0, 0); と空のリージョンを作り、GetUpdateRgnで更新領域を取得します。その後にBeginPaintします。 取得した更新領域を、メモリDCにSelectObjectして下さい。 WM_ERASEBKGNDで行っていた処理を先に行い、その後に文字等を普通に描画します。リージョンがSelectされているので、更新された領域以外には描画されません。この事は通常の描画と同じです。 メモリDCをクライアントにBitBltすれば描画は全て終わりです。ウィンドウの更新領域以外は転送されませんので、更新領域が小さい時は短時間で終了します。 最後にSelectObjectでメモリDCのリージョンを戻し、DeleteObjectでリージョンを削除して下さい。 より省メモリにする方法は色々と考える事ができますが、描画を見せない基本的な概念はこんな感じになると思います。 ウソがあるかもしれないので、自信なしです。

milkoX
質問者

お礼

すいません、ありがとうございます。 書かれた内容と同じ処理をやっていたのですが、 なぜか極端に遅くなってしまっていたのです。 由一の違いはメモリDCに更新リージョンをセットしなかったことでした。 やってみたところ、かなりよくなりました。 またいろいろと問題が生じるかもしれませんが、 とりあえず様子見とします。 大変お世話になりました。 ありがとうございました。

その他の回答 (5)

  • macchun
  • ベストアンサー率50% (3/6)
回答No.5

WM_ERASEBKGND メッセージはつぶせませんが、ウィンドウの背景ブラシをなし (NULL) にすれば、WM_ERASEBKGND によるちらつきを抑えることができます。 また、テキストを書くときには ExtTextOut() などを使用して文字と背景色を一度に書いた方が良いと思います。 なお、背景色を無効化していますので、テキストがない領域については本来の背景色で塗りつぶす必要があることをお忘れなく。

milkoX
質問者

お礼

ありがとうございます。 > なお、背景色を無効化していますので、テキストがない領域については本来の背景色で塗りつぶす必要があることをお忘れなく。 試したことがありますが、結局自分で塗りつぶすことになりますので、 ちらつきました。 またなにかありましたらお願いします。 結局正しくダブルバッファを使うしかないような 気もしてきました。。

noname#30727
noname#30727
回答No.4

#3です。 少々追加します。 ダブルバッファリングというのは、描画用と表示用の2枚のビットマップを交互に使用する事を指します。 WindowsのGDIでこういった事を必要とするケースは、WM_PAINTで表示用のビットマップを描画している最中に、別のスレッドが描画用のビットマップに描画する事がある場合です。それ以外ではビットマップは1枚で足りますので、ダブルバッファリングは不要です。 もし、バッファとなるビットマップを毎回作成しているのであれば、最初に1回だけ作成するようにすれば、多少は改善されるかもしれません。

milkoX
質問者

お礼

度々ありがとうございます。 > もし、バッファとなるビットマップを毎回作成しているのであれば、最初に1回だけ作成するようにすれば、多少は改善されるかもしれません。 以前に試してましたが、変わりませんでした。 それと一回のみ作成だと毎回ビットマップをクリアする処理が必要になるので、それはそれで時間がかかりそうです。 また何かありましたらお願いします。

noname#30727
noname#30727
回答No.3

>WM_ERASEBKGNDメッセージをつぶせない仕様ですので、 これがある限り、どんな方法を使っても、ちらつきを抑える事は出来ません。 WM_ERASEBKGNDを受けとる必要があるのはわかりますが、何も描画しなければ受け取らなかったのと同じです。(文字を描画する部分だけ消さないという方法もありますが) それすらも仕様だから出来ないという事なら、ちらつくのも仕様だとするしかないです。 >ちらつきが抑えられましたが、極端に遅くなってしまいました。 ちらつきが抑えられたように見えるのは、極端に遅くなったからではないでしょうか。 10倍くらい遅くなったのなら、やってはいけない事をやっているのだろうと思いますが、2倍くらい遅くなったのであれば、改善できない可能性はあります。

milkoX
質問者

お礼

ありがとうございます。 > >WM_ERASEBKGNDメッセージをつぶせない仕様ですので、 > これがある限り、どんな方法を使っても、ちらつきを抑える事は出来ません。 確かにおっしゃるとおりです。 しかしファイルの内容をWindowに描画してますので、 スクロールなどによって文字が変わりますので、 一旦すべて消さないと全部重なって出て来てしまいます。 > 10倍くらい遅くなったのなら、やってはいけない事をやっているのだろうと思いますが、2倍くらい遅くなったのであれば、改善できない可能性はあります。 おかしいことをしているに違いないです。 ですが、どこがどうなのかが。。。 描画がロジックは全く同じで違いは描画先が BeginPaintで取得してDCか CreateCompatibleDCで作成したビットマップに関連付けられたDCかです。 同じロジックでも後者に対する描画は遅くなるということかもしれません。正しい知識があれば、判断ができますが、私では力不足です。知識を借りに来ました。

noname#16765
noname#16765
回答No.2

「ちらつく=なんども書かれている」で、 ダブルバッファリング(私はどんなものか知らない)が ただ書き込み処理速度を遅くしただけなのでちらつきが減ったけど根本的な解決になっていない。 という安楽的な考えじゃ答えは出ませんね。 すみません、ギブです。

noname#16765
noname#16765
回答No.1

こういったものが私は苦手なんですが、 これと似たようなものですかね?

参考URL:
http://oshiete1.goo.ne.jp/kotaeru.php3?q=1780918
milkoX
質問者

補足

ありがとうございます。 同じようなものですね。 ビットマップ作成 ↓ ビットマップに描画 ↓ ビットマップを画面にコピー というだけの作業ですが、どこで遅くなったのが よくわからないです。。。

関連するQ&A

  • ダブルバッファの作り方

    画面に描画するBCC5.5 のCプログラムがあります。画面がちらつくので、ダブルバッファにしたいのですが、具体的に、どの関数を呼んで実装したらよいのかわかりません。WEB検索をしますと結構情報がヒットしますが、解決に至ってませんので、よろしくお願いします。 具体的にやったことは、現在動いているプログラムの case WM_PAINT:  hdc=BeginPaint(hWnd,&ps);  paint(hdc); // 自作の描画プログラム本体  ReleaseDC(hWnd,hdc);  EndPaint(hWnd,&ps);  break; の部分を、「画面サイズのビットマップイメージhBitmapをつくり、そこにpaint関数で描き込み、最終画面を一気に出力する」というつもりで以下のプログラムに書き換えたのですが、表示すらしなくなってしまいました。何が悪いのかお教えください。 case WM_PAINT:  GetClientRect(hWnd,&rt);   h = (int)rt.bottom;   w = (int)rt.right;  hBuffer = CreateCompatibleDC(NULL);  hBitmap = CreateCompatibleBitmap(hBuffer, w, h);  SelectObject(hBuffer, hBitmap);  paint(hBuffer);  hdc=BeginPaint(hWnd,&ps);   BitBlt(hdc, 0,0,w,h, hBuffer,0,0, SRCCOPY);  ReleaseDC(hWnd,hdc);  EndPaint(hWnd,&ps);  DeleteDC(hBuffer);  DeleteObject(hBitmap);  break;

  • MFC ビットマップ描画で残像が残る

    MFCです。 ワーカースレッドで取得したDIBを定期的に描画するプログラムを作っています。 ダブルバッファリングで実現しているのですが、画面リサイズを繰り返すと、再描画の後に、拡大して発生した新しいクライアント領域に古い画像が残ってしまいます。 SelectObject()で選択したコンパチDCのビットマップを選択前のものに戻してなかったのですが、これが原因なんでしょうか?

  • タブをオーナードローすると、ちらついてしまいます

    質問させていただきます。 WindowsSDKを用いて、画面を作成しています。 MFCは使用していません。 タブを使っていますが、タブは作成時にTCS_OWNERDRAWFIXEDを指定しています。 WM_DRAWITEMメッセージが来たら、タブを描画していますが どうしてもちらつきが目立ってしまいます。 WM_DRAWITEMでの処理は、初めにタブの裏画面に描画し 最後にタブに通知された、DRAWITEMSTRUCTのhDCに裏画面の内容をBitBltで転送しています。 メイン画面もダブルバッファリングを行っておりますが、メイン画面はちらついていません。 タブ側で、WM_ERASEBKGNDで何もせずreturnすると、ちらつきはなくなりますが 背景色で塗りつぶされなくなるため、タブが表示される箇所にウィンドウを上に置いたりすると そこが残ってしまいます。 WM_DRAWITEMが頻繁によばれているため、これはちらついているのでしょうか? 解消方法に頭を悩ませております。 どうぞご教授の程お願いいたします。

  • JavaのJFrameについて

    いまJavaでパソコンに表示する時計を作っています。 JFrameを使っているのですが、ダブルバッファリングで描写しながらSwingのJButtonを使いたいのですが、今自分の知ってる方法だとうまく行きません。 ひとつは、 repaint()関数とpaint()関数を使う方法です。 この方法だとダブルバッファリングのままJButtonと同梱させることができるのらしいですが、表示が滑らかじゃありません。自分的にはFPSが60はほしいです。 もう一つの方法は、graphicsクラスを使って描写する方法です。 こちらはFPS的に問題はないのですがJButtonが置けません。 要点をまとめると、 Graphicsによる描写とSwingのJButtonを同時に一つのwindowでつかいたい、 描写速度をFPS 60程度にしたい です。 書き方が雑なので質問の意図が伝わりにくいかと思いますが、回答よろしくお願いします

  • WM_PAINTとBitBlt

    レベルはWindowsプログラミングを始めて1月半、それ以前にプログラミング経験はありません。 環境は、VC++6.0 WindowsXP SP2です。 WINAPIしか使えません(MFCは分かりません)。 宜しくお願い致します。 定期的に図形を動かすプログラムを副スレッドを用いて作りました。 副スレッドにはSleep(5msスリープ)を入れてあり、スレッドの最後にInvalidateRectを実行し、WM_PAINTを発行して再描画させているのですが、動作は所望な通りなものの、画面がちらついてしまいます。 所持している参考書を読むと、図形を動かす処理を直接ディスプレイに出すのではなく、メモリDCに一度出力し、その後BitBltでディスプレイに出力すればよい(ダブルバッファのことらしい?)、と書いてありました。 また、あるサイトには、WM_PAINTが実行されるとOnEraseBkgndが走るからちらつくとも書いてあり、何だか良く分かりません。 (別のサイトにはOnPaintが走るとか、OnPaintBackgroundが走ると書いてあり、何が本当なのか???) 自分の知りたいことは以下の通りです。 1)WM_PAINTで画面の再描画を行うと画面が何故チラつくのですか? また、ちらつかない様にする方法はあるのでしょうか? 2)BitBltを用いるとチラつかないのは何故でしょうか? 3)参考までにですが、メモリDCでBitBltで転送、以外に画面をチラつかせずに画面を更新させる方法はあるでしょうか? 色々サイトを探してみてのですが、断片的にしか書いておらず、結局自分が所望する回答は得られませんでした。 初心者レベルなので、分かりやすく説明して頂けると大変嬉しいです。 以上、宜しくお願い致します。

  • クライアント領域を再描画させない方法

    クライアント領域で左クリックを押したままマウスを移動させた時の処理 (WM_MOUSMOVE)として、マウスの位置情報を、 InvalidateRect(hWnd, NULL, FALSE); でWM_PAINTに送り、WM_PAINTで描画処理をさせます。 右クリックでの処理として、 case WM_RBUTTONDOWN:    InvalidateRect(NULL, NULL, TRUE);    return 0; とした時、WM_PAINTでは 右クリックによるWM_PAINTでは、クライアント領域を 再描画させない様にしたいのですが、 どのようにしたら良いのでしょうか? クライアント領域に描画されたのを再描画させないことで、 消したいのです。 MS VC++ & Win98 の環境で作成しています。

  • 文字のちらつきを抑えるには

    VisualStudio2005でAPIをを使ってマウスの位置を表示するソフトを作っています。WM_MOUSEMOVEメッセージがきたら位置を取得して、InvalidateRect関数を使って再描画しています。再描画する際に文字のちらつきが気になります。HPを調べたりいろいろやってみたのですが、ちらつきを起こす原因と対処法(ダブルバッファリング)の大まかな部分しか分かりませんでした。もし、参考になるサイトや詳しい方がいらっしゃったらご教授ください。※MFCは使っていません。

  • 子Windowの描画

    CでWindowプログラミングをしています。 作っているものは、親Windowのボタンを押すと子Windowを表示するプログラムです。 問題点&質問は、子Windowが作成されたときに、子Window内のコントロールが何もない部分に子Windowの後ろの背景(デスクトップや親Window)が描画されてしまうということです。子Windowに作成したコントロールはちゃんと表示されますし、押すことも出来ます。何もない部分だけ、後ろが透過されたみたいになってしまいます。マウスで子Windowを動かすと、作成時に透過した状態で描画された画が、そのままついて動きます。WM_CREATEの最後にメッセージボックスを入れて止めてみた所、その時点で透過されています。原因がまったく判らず、原因と対処方法がお解かりの方がいたら教えてください。 気になる点は、もともと書き始めは1つのファイルに全て書いていました。そして一通り動くようになったので、子Windowの処理を別ファイルに分けて実行したところ、上記のような問題が起こりました(ほかは全て正常に動きます)。子Windowの処理の部分を元に戻すと問題は起こりませんでした。つまり物理ファイルを分けた事で、何か問題が発生していると思うのですが、WindowプログラミングもCも不馴れで原因が解らず、どなたかの助けをお借りしたいと思い質問させてもらいました。「この辺が怪しい」という手掛かりを挙げてもらうだけでも助かります。 足りない情報がありましたらご指摘ください。よろしくお願いします。

  • 画像表示について。

    現在SDKにてBMPやRAWなどの画像表示ソフトを作成している段階です。 そして、一通り作成することができました。 ここで質問に答えてくださった皆様に感謝します。 新たに些細な問題が出てきてしまったので質問します。 作成したソフトはMDI型のソフトであり、その構成はフレーム、クライアント、ドキュメントウィンドウといった構成になっています。 もちろん読み込んだ画像はドキュメントウィンドウに表示をするのですが、このドキュメントウィンドウのサイズをマウスを使って変更した場合、画像にちらつきが生じてしまいます。 原因はプログラム上 WM_PAINT メッセージがきた場合に描画をおこなうことに他ならないのですが、解決する手段はないでしょうか? 例えばダブルバッファリングなどの手段があればよいのですが・・・

  • Windowsプログラミング 画面描画 ちらつき

    SetDIBitsToDevice関数を使って画面に描画をした後、LineTo関数やEllipse関数を使って その画面上に線や丸を表示させる処理をしているのですが、描画処理を一度にできていないためか ちらつきが発生してしまいます。 調べてみるとビットマップのマルチバッファリングなどが解決策にあるのですが、 私は一次元配列で画素値を格納していてビットマップに情報は保持していないため よく解決策で使われているBITBLTが使えない状態です。 裏画面に描画しておいて、最終的に描画は一度だけにするという考えは分かるんですが、 これをSetDIBitsToDevice関数を用いてちらつきが発生しないようにできるんでしょうか? プログラムとしては画像の上に線と円をひたすら描画していくようなイメージをしています。 アドバイスお願いします! case WM_PAINT: hDC=BeginPaint(hParent,&ps); SetDIBitsToDevice( ps.hdc, 0, 0,// コピー先x,y座標 pimg -> bih.biWidth,// DIBの幅 pimg -> bih.biHeight,// DIBの高さ 0, 0,// DIBの座標 0,// 走査線 pimg -> bih.biHeight,/ 走査線数 pimg -> lpBmpData, (BITMAPINFO *)&( pimg -> bih),// BITMAPINFOにキャスト DIB_RGB_COLORS ); for(int i=0; i<number-1; i++){ if(i==0){  hPen1 = CreatePen(PS_SOLID, 1, RGB(255,0,0));  electObject(hDC,hPen1);  MoveToEx(hDC,Xs,Ys,NULL);  Ellipse(hDC,Xs-2,Ys-2,Xs+2,Ys+2); }else{  LineTo(hDC,Xe,Ye);  Ellipse(hDC,Xs-2,Ys-2,Xs+2,Ys+2); } }