• ベストアンサー

メモリデバイスコンテキストを用いた描画処理

お世話になります。 Borland C++ 5.5.1 for Win32で簡易な画像ビュアーを開発しています。 基本的な動作は完成したのですが、ある条件下で発生する問題を回避するために、メモリデバイスコンテキストを用いようと考えています。 ところが、後述のように変更したところ、画像がまったく表示されなくなりました。(ウィンドウ背景色で塗りつぶされたままです。一瞬たりとも表示されません。) Windowsプログラムに慣れていないため解決に難儀しています。 どなたか原因と対策をご教示くださいますようよろしくお願いします。 ////////////////////////// //変更前 画像表示される// ////////////////////////// case WM_PAINT: hdc = BeginPaint(hWnd, &ps); /* ~省略(画像処理)~ */ StretchDIBits(hdc,~,SRCCOPY);//処理した画像をウィンドウへ EndPaint(hWnd, &ps); break; //////////////////////////// //変更後 画像表示されない// //////////////////////////// case WM_PAINT: hdc = BeginPaint(hWnd, &ps); hmemdc = CreateCompatibleDC(hdc); /* ~省略(画像処理)~ */ StretchDIBits(hdc,~,SRCCOPY);//処理した画像をMemoryDCへ BitBlt(hdc, 0, 0, rc.right, rc.bottom, hmemdc, 0, 0, SRCCOPY);//MemoryDCからウィンドウへ(rcはクライアント領域) DeleteDC(hmemdc); EndPaint(hWnd, &ps); break;

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

  • ベストアンサー
  • nolnil
  • ベストアンサー率100% (1/1)
回答No.4

StretchDIBitsは、hdcへ出力しているぞ hmemdcへは何も出力していないみたいだぞ その後、BitBltでhmemdcをhdcへ出力しているから 何にも表示されないのだ メモリデバイスコンテキストでやりたいなら、 1.CreateCompatibleBitmapで空のHBITMAPを作成する 2.SelectObject(hmemdc, hBitmap)とかで、hmemdcに   空のHBITMAPをくっつける (これで、hmemdcへの描画処理がhBitmapのメモリに 描かれることになる) 3.StretchDIBitsでhmemdcに出力する (画像データ -> hBitmap) 4.BitBltでhmemdcをhdcへ出力する (hBitmap -> ディスプレイ) ※hBitmapとhmemdcの解放を忘れないでちょーだい! 解放する順番によってメモリリークがおきちゃうぞ 後に作成したものを先に解放するのだー おちまい

mrumesuke
質問者

お礼

動きました!表示できました! SelectObject意味や役割の理解が一歩進みました。 目からウロコだのなんだの色々落ちまくりです。 回答ありがとうございました。

その他の回答 (3)

noname#30727
noname#30727
回答No.3

>とりあず付け焼刃の知識でSelectObjectを使ってみたのですが、やはり表示されません。 言葉で説明するのは難しいですね。 おそらく、StretchDIBitsでの画像の拡縮に時間がかかるから、画像の読み込み時に拡縮をやって、あとはBitBltでできるだけ高速に描画させるという方針だと思います。 だとすると、CreateCompatibleBitmapでビットマップを作成するのがいいです。CompatibleBitmapはVRAMに空きがあればそこに置かれるようなので、BitBltが桁違いに速いです。 1.画像の読み込み HBITMAP imageBm = 画像の読み込み(); HDC dispDC = GetDC(NULL); HDC memDC = CreateCompatibleDC(dispDC); HBITMAP memBm = CreateCompatibleBitmap(dispDC, IMAGE_WIDTH, IMAGE_HEIGHT); HGDIOBJ prevMemBm = SelectObject(memDC, (HGDIOBJ)memBm); StretchDIBits(memDC, ...); // memDCを通してmemBmにimageBmを書き込む DeleteDC(dispDC); DeleteObject((HGDIOBJ)imageBm); 2.WM_PAINT PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); BitBlt(hdc, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, memDC, 0, 0, SRCCOPY); EndPaint(hwnd, &ps); 3.画像の開放 SelectObject(memDC, prevMemBm); DeleteDC(memDC); DeleteObject((HGDIOBJ)memBm);

mrumesuke
質問者

お礼

回答ありがとうございます。 すいません、お礼がすっかり遅くなってしまいました。(^^; すばらしい! 必要な関数や変数が一目瞭然で、非常に参考になります。 残念ながら勉強不足で、まだ応用できるレベルに達していないのですが、勉強を終えて応用が利くようになったらぜひ試してみます。 回答本当にありがとうございました。

noname#30727
noname#30727
回答No.2

省略されている部分にあるのかもしれませんが、 メモリDCの書き込み対象は、そのメモリDCにSelectObjectされたビットマップになります。 CreateDIBSectionなどで作成することが多いです。 SelectObjectで戻ってきたビットマップを再度SelectObjectしないと、DeleteDCは失敗します。

mrumesuke
質問者

お礼

アドバイスありがとうございます。 SelectObjectですか。今は使っていないです。 これが原因でしょうか。 とりあず付け焼刃の知識でSelectObjectを使ってみたのですが、やはり表示されません。というか、修正量が多くてもはやどこまで正常なのかさえ分からなくなってしまってるんですけど。 もっと簡単なものかと思っていたのですが、やはり何事も基本を抑えないとだめですね。。。 今回はいったん質問を締め切り、改めて勉強しなおして出直します。 そのときはまたよろしくお願いします。

  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.1

ふつうはWM_PAINTなんかでそんなことしないんですけどね。描画にコストがかかるからこそメモリデバイスコンテキストを使うんですから。 WM_PAINTの中ではBitBltのみ。 CreateCompatibleDCで元にするHDCはBeginPaintではなくGetDCとかですかね。 http://www13.plala.or.jp/kymats/explain/gameprograming/1-4(2).html

参考URL:
http://www13.plala.or.jp/kymats/explain/gameprograming/1-4(2).html
mrumesuke
質問者

お礼

アドバイスありがとうございます。 >ふつうはWM_PAINTなんかでそんなことしないんですけどね。描画にコストがかかるからこそメモリデバイスコンテキストを使うんですから。 勉強不足による設計思想のまずさから、今となっては現在の設計を変更するのが困難な造りになってしまっています。(実は今日も挑戦してみたのですが、連鎖的に問題が増える一方です。根本的解決には設計からやり直した方がよさそうなので、今のところは見送ろうと思います。) >WM_PAINTの中ではBitBltのみ。 >CreateCompatibleDCで元にするHDCはBeginPaintではなくGetDCとかですかね。 おお!! 前者は(前述の理由で)後に回すとして、とりあえず hMemDC = CreateCompatibleDC(GetDC(hWnd)); としてみました・・・・が、まだ同じ状況です。 うーん、作り直したほうが早いかなぁ。。。 アドバイスおよび細部にまでわたるチェックありがとうございます。 これから参考サイトでじっくり腰を据えて勉強します。

関連するQ&A

  • メモリデバイスコンテキストについて(画像が表示されない)

    VisualC++.netのWindowsSDKでゲームを作ろうと思っている者です。 現在、キー入力により、主人公の画像をウィンドウ上で動かすことまでいきましたが、画面のチラつきが気になったため、メモリデバイスコンテキストを導入することにしました。 しかし、画像が表示されません。画面は初期状態のままです。 現在のソースは以下の通りです。 // ウィンドウプロシージャ内 case WM_CREATE: … GetClientRect(hWnd, &rc); hmdc = CreateCompatibleDC( NULL ); hdc = GetDC( hWnd ); hbgr = CreateCompatibleBitmap( hdc, rc.right, rc.bottom ); ReleaseDC( hWnd, hdc ); SelectObject( hmdc, hbgr ); … case WM_PAINT: … hDefBrush = (HBRUSH)SelectObject( hmdc, GetStockObject( WHITE_BRUSH ) ); PatBlt( hmdc, 0, 0, rc.right, rc.bottom, PATCOPY ); SelectObject( hmdc, hDefBrush ); ShowLife(hmdc); // 自作関数 ShowHero(hWnd, hmdc); // 自作関数 hdc = BeginPaint(hWnd, &ps); BitBlt(hdc, 0, 0, rc.right, rc.bottom, hmdc, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); 自作関数ShowLifeやShowHeroは引数のDCに主人公の画像などを出力する関数です。これの引数をhdcにして、BeginPaintの下に置くとちゃんと画面に主人公が現れて、キー入力にも従ってくれます。 何かヒントになるようなことでも教えてくださると幸いです。情報が少ない場合は補足にて説明します。ヨロシクお願いしますm(_ _)m

  • デバイスコンテキストの宣言に関するエラー?について

    Run-Time Check Failure #3 - The variable 'hMemDC' is being used without being defined. というエラーが出ます。 自分としては、WM_CREATEの処理で、初期化を行っているつもりなのですが・・・ 力を貸して頂ければ有難いです。よろしくお願いします。 ちなみに、関係のありそうな部分だけ掲載しています。 この部分だけ残して、他の部分をコメントアウトしてコンパイルしても、同じエラーメッセージが出ました。 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; RECT rect; HDC hDC; PAINTSTRUCT ps; HDC hMemDC; HBITMAP hMemBmp; HDC hMjDC; HBITMAP hMjBmp; BITMAP MjBmp; /* POINT posMJ; int MJshot; POINT posMJshot;*/ switch (message) { case WM_CREATE: GetClientRect(hWnd, &rect); // クライアント領域の大きさをrectに格納 hDC = GetDC(hWnd); hMemDC = CreateCompatibleDC(hDC); // hDCと互換性をとるメモリデバイスコンテキストhMemDCを構築 hMemBmp = CreateCompatibleBitmap(hMemDC, rect.right, rect.bottom); hMjDC = CreateCompatibleDC(hMemDC); // hMemDCと互換性をとるメモリデバイスコンテキストhMjDCを構築 hMjBmp = (HBITMAP)LoadImage( // ビットマップイメージMYJET(リソース)をhMjBmpに設定 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), MAKEINTRESOURCE(IDB_MYJET), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); SetTimer(hWnd, 1, 10, NULL); // 100分の1秒毎にWM_TIMERメッセージを送るタイマー1をセット break; case WM_PAINT: hDC = BeginPaint(hWnd, &ps); // hDCにディスプレイのデバイスコンテキストのハンドルを格納 BitBlt( // hDCにバックバッファhMemDCを転送 hDC, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY ); EndPaint(hWnd, &ps); break;

  • TextOut( ) が動かない

    LRESULT CALLBACK WndProc( HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam){ PAINTSTRUCT ps; HDC hdc; switch(msg){ case WM_KEYDOWN:  if( wParam == VK_ESCAPE ){   hdc = BeginPaint(hWnd, &ps);   TextOut(hdc,0,0,str,strlen(str));   EndPaint(hWnd, &ps);  }  break; case WM_PAINT:  break; エスケープキーで文字表示をやりたいけど TextOut( ) が動作していないみたいでした。 switch(msg){ case WM_KEYDOWN:  if( wParam == VK_ESCAPE ){   hdc = BeginPaint(hWnd, &ps);   TextOut(hdc,0,0,str,strlen(str));   EndPaint(hWnd, &ps);  }  break; case WM_PAINT:  hdc = BeginPaint(hWnd, &ps);  TextOut(hdc,0,0,str,strlen(str));  EndPaint(hWnd, &ps);  break; とすると、常に文字が表示されたから、やっぱり case WM_KEYDOWN: の中の TextOut( ) が 動作していないんだと思いました。 TextOut( ) は case WM_PAINT: からのつながりが ある場合でないと実行されないんですか? ソースのおかしいところがあったら教えてください。

  • 再描画されない

    お世話になります。WS_POPUPで作成したウィンドウにGDI+を用いて画像を表示させています。 そのウィンドウに、他のウィンドウ(最大化されていない)を重ねて動かすと、重なっていた部分が再描画されず真っ白になってしまいます。 最大化されたウィンドで隠した場合は正常に表示されます。 VC+2005です。よろしくお願いします。m(__)m //ウィンドウプロシージャWM_PAINT内 hdc = BeginPaint(hWnd, &ps); Image myImage(fn); MoveWindow(hWnd, 0, 0, myImage.GetWidth(), myImage.GetHeight(), 0); Graphics MyGraphics(hdc); MyGraphics.DrawImage(&myImage, 0, 0, myImage.GetWidth(), myImage.GetHeight()); EndPaint(hWnd, &ps);

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

    画面に描画する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;

  • TextOutについて

    ウィンドウの雛形に、 case WM_PAINT:{ static int t; PAINTSTRUCT ps; char cbuf[100]; HDC hdc = BeginPaint( hWnd, &ps ); wsprintf( cbuf, _T("on WM_PAINT%d"),t); TextOut(hdc,0,100,cbuf,sizeof(cbuf)); SetWindowText( hWnd, cbuf ); t++; EndPaint( hWnd, &ps ); break; } として実行したところ、ウィンドウを任意のところに重ねて、移動すると、SetWindowはタイトルバーにちゃんとの値が表示されるのですが、TextOutのtは再描画されないのですが、通っているのに描画されないのはなぜでしょうか?

  • SDKでウィンドウの中央に文字列を表示させる方法

    ウィンドウの中央に文字列sを表示させる方法を教えて下さい。 /********************************** WM_PAINTで TCHAR s[80]; HDC hDC; PAINTSTRUCT ps; RECT rc; GetClientRect(hWnd, &rc); hDC = BeginPaint(hWnd, &ps); TextOut(hDC, rc.right / 2, rc.bottom / 2, tcDayTime, lstrlen(s)); EndPaint(hWnd, &ps); return FALSE; ***********************************/ GetClientRect(hWnd, &rc);でウィンドウのサイズを取得し、 TextOutの第2、第3引数で ウィンドウ幅/2、ウィンドウ高さ/2 としていますが、これだと中央から表示されてしまいます。 できれば、ウィンドウのサイズを変更してもウィンドウの中央に表示させたいです。 #VC ++ 6.0 & Win98 & SDK で作成してます。

  • BeginPaintの使い方

    現在windowsプログラミングを勉強しています。 まだまだはじめたばかりでwindowsプログラミングの把握できていない部分が多々あるので、何か使い方が根本的に間違えているところがあるかもしれませんが、ご容赦ください。 case WM_PAINT: hdc=BeginPaint(hdc,&ps);/*rc,hdc等は宣言済み*/ TextOut(hdc,0,0,szStr,(int)strlen(szStr));/*szStr="文字列"代入済み*/ EndPaint(hWnd,&ps);/*hWnd等は宣言済み*/ break; において、 1.ダブルクリックしたら表示させよう!と思いまして、 WM_PAINT→WM_RBUTTONDBLCLKSと変更したところ表示されなくなりました。 しかし、hdc取得をGetDCで行うとうまくいきました。この違いがよくわかりません。 2.次はダブルクリックすると文字を右に動かそう!と思いまして、 TextOut(hdc,i,0,・・・・)などといたしましてiを増やしたところ更新前の画像が残りました。そこでInvalidateRectによって背景消去しようとおもいまして、TextOutの前に挿入しましたがこれによっても初めから、クリック後も何も表示されなくなりました。 1と2の2点に関して、どなたかご教授いただけないでしょうか。よろしくお願いします。

  • WinAPIで画像を更新し続けるには

    本を見ながら、WindowsSDKを使ってビットマップを描画する 所まではできたのですが、動かそうとすると上手くいきません。 ソースを簡略かして載せますと、以下のようにしています。 case WM_KEYDOWN: x++; // static MyDrawBitMap( hWnd, x, 10 ); ------------------------------------------------ void MyDrawBitMap( HWND hWnd, int x, int y ) { hdc = BeginPaint(hWnd, &ps); BitBlt( hdc, x, y, bitmap.bmWidth, bitmap.bmHeight, hMemDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); このように書いてますが、ボタンを押しても画面に変化が出ません。 PAINTSTRUCTは再描画が必要な情報に関する構造体だとは解っていま すが、特に変更の必要の無い画像も再描画したい場合の処理はどうす れば良いのでしょうか?よろしくお願いします。

  • WinAPIでRECT構造体の宣言について

    PAINTSTRUCT ps; HDC hdc; RECT rct={200,200,200,200}; char *szStr = "あけまして\n\tおめでとう"; switch (msg) { case WM_PAINT: GetClientRect(hWnd, &rct); hdc = BeginPaint(hWnd, &ps); DrawText(hdc, (LPCTSTR)szStr, -1, &rct, DT_CENTER | DT_WORDBREAK); EndPaint(hWnd, &ps); break; のようにしているのですが文字列が表示される場所が中央上部です。 何故なのでしょう? 自分としてはど真ん中辺りから表示されるようにしているつもりなのですが

専門家に質問してみよう