現在C++でGUIアプリケーションを作成しているのですが、ウィンドウに画像を表示させたいです。
調べると、画像を表示するにはメッセージループのWM_PAINTの箇所に該当する記述を追加する必要があるということでしたが、
これらの記述を外部の自作関数内などに移し、関数を呼び出してみたところ、うまく行きませんでした。
なぜWM_PAINT内でないとうまく動かないのでしょうか?また、WM_PAINT内の記述がかなり多くなり煩雑になりそうなのですが、回避する方法はありますか?
記述は以下のとおりです。
C++
1 PAINTSTRUCT ps; 2 HDC hdc = BeginPaint(hWnd, &ps); 3 HDC hMdc = CreateCompatibleDC(hdc); 4 5 SelectObject(hMdc, LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1))); 6 7 BitBlt(hdc, 0, 0, 100, 100, hMdc, 0, 0, SRCCOPY);
よろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答2件
0
なぜWM_PAINT内でないとうまく動かないのでしょうか?
そういう仕様だからとしか言いようがないのですが。
BeginPaint
関数はWM_PAINT
メッセージハンドラーから呼び出すことを前提としています。そのことは公式のリファレンスにも明記されています。APIを利用する際は、ちゃんとリファレンスを読んで使い方を理解しましょう。
そもそも、WM_PAINTメッセージは、ウィンドウを描画する必要があるとシステムが判断したときに当該のウィンドウに送られるものなので、きちんと書かないとウィンドウの表示が乱れます。例えば、あるウィンドウの上に別のウィンドウを重ねて一部あるいは全部を覆い隠したとします。手前のウィンドウを動かして奥のウィンドウが現れたときに、隠されていた部分を描画し直さないといけないわけですが、それを知らせるのがWM_PAINTメッセージです。WM_PAINTメッセージをきちんと処理しないと、表示が欠けたり、ゴミデータが表示されたりします。
WM_PAINT内の記述がかなり多くなり煩雑になりそうなのですが、回避する方法はありますか?
複雑な描画を行う場合は、バックバッファー(オフスクリーンバッファーとも言う。裏画面などと言うことも)というテクニックを使うのが一般的です。これにより、表示と描画が切り離せます。また、WM_PAINTで複雑な描画を行うと表示がちらつくことがあるので、それの抑制にもなります。
まず、ウィンドウの描画領域と同じサイズのビットマップを作成します。これをバックバッファーとします。ビットマップの作成にはCImage
クラスの利用がお手軽で簡単です。Windows SDKに含まれているので、別途何かを用意する必要はありません。
WM_PAINTメッセージでは、バックバッファーを丸ごとウィンドウにコピー(BitBlt
)する処理を書きます。ビットマップに描画した内容が表示に反映されるようになります。
これで、描画処理とWM_PAINTを切り離すことができました。あとは、アプリの都合が良いタイミングでビットマップに描画を行い、InvalidateRect
関数でウィンドウに表示の変更を伝えるだけです。
投稿2018/04/28 01:09
総合スコア5944
0
ベストアンサー
cpp
1// image.h 2#pragma once 3 4#include <Windows.h> 5 6typedef struct tagImage { 7 int nWidth; // 横幅 8 int nHeight; // 縦幅 9 HDC hdc; // デバイスコンテキスト 10 HBITMAP hbmp; // ビットマップハンドル 11}IMAGE, *LPIMAGE; 12 13int CreateImage(IMAGE* image, HWND hWnd, int width, int height); 14int CreateImageFromFileA(IMAGE* image, HWND hWnd, const char* filename); 15int CreateImageFromFileW(IMAGE* image, HWND hWnd, const wchar_t* filename); 16#ifdef UNICODE 17#define CreateImageFromFile CreateImageFromFileW 18#else 19#define CreateImageFromFile CreateImageFromFileA 20#endif 21 22int BltImage(IMAGE* dst, int x, int y, IMAGE* scr, RECT scr_r, DWORD flag); 23int DeleteImage(IMAGE* image);
cpp
1#include "Image.h" 2#include <cassert> 3 4int CreateImage( 5 IMAGE * image, // 画像を作成するためのポインタ 6 HWND hWnd, // ウィンドウハンドル 7 int width, int height) // 作成する横幅と縦幅 8{ 9 HDC hdc; 10 hdc = GetDC(hWnd); 11 12 // 仮想画面とDCの作成 13 image->hbmp = CreateCompatibleBitmap(hdc, width, height); 14 image->hdc = CreateCompatibleDC(hdc); 15 ReleaseDC(hWnd, hdc); 16 17 if (image->hbmp == NULL && image->hbmp == nullptr || 18 image->hdc == NULL && image->hdc == nullptr) 19 { 20 return -1; 21 } 22 23 // 関連付け 24 SelectObject(image->hdc, image->hbmp); 25 26 image->nWidth = width; 27 image->nHeight = height; 28 29 // 画面クリア(白) 30 BitBlt(image->hdc, 0, 0, width, height, nullptr, 0, 0, WHITENESS); 31 return 1; 32} 33 34int CreateImageFromFileA( 35 IMAGE* image, // 画像格納用変数ポインタ 36 HWND hWnd, // ウィンドウハンドル 37 const char* filename) // 読み込むファイル名 38{ 39 HDC hdc; 40 HINSTANCE hInst; 41 BITMAP bmp; 42 43 // ファイルから読み込む 44 hInst = (HINSTANCE)GetWindowLongA(hWnd, GWL_HINSTANCE); 45 image->hbmp = (HBITMAP)LoadImageA(hInst, filename, IMAGE_BITMAP, 0, 0, 46 LR_LOADFROMFILE | LR_CREATEDIBSECTION); 47 48 // DCの作成 49 hdc = GetDC(hWnd); 50 image->hdc = CreateCompatibleDC(hdc); 51 ReleaseDC(hWnd, hdc); 52 53 if (image->hbmp == NULL && image->hbmp == nullptr || 54 image->hdc == NULL && image->hdc == nullptr) 55 { 56 return -1; 57 } 58 59 // 関連付け 60 SelectObject(image->hdc, image->hbmp); 61 62 // ビットマップ情報の取得 63 GetObjectA(image->hbmp, sizeof(BITMAP), (void*)&bmp); 64 image->nWidth = bmp.bmWidth; 65 image->nHeight = bmp.bmHeight; 66 return 1; 67} 68 69int CreateImageFromFileW( 70 IMAGE* image, // 画像格納用変数ポインタ 71 HWND hWnd, // ウィンドウハンドル 72 const wchar_t* filename) // 読み込むファイル名 73{ 74 HDC hdc; 75 HINSTANCE hInst; 76 BITMAP bmp; 77 78 // ファイルから読み込む 79 hInst = (HINSTANCE)GetWindowLongW(hWnd, GWL_HINSTANCE); 80 image->hbmp = (HBITMAP)LoadImageW(hInst, filename, IMAGE_BITMAP, 0, 0, 81 LR_LOADFROMFILE | LR_CREATEDIBSECTION); 82 83 // DCの作成 84 hdc = GetDC(hWnd); 85 image->hdc = CreateCompatibleDC(hdc); 86 ReleaseDC(hWnd, hdc); 87 88 if (image->hbmp == NULL && image->hbmp == nullptr || 89 image->hdc == NULL && image->hdc == nullptr) 90 { 91 return -1; 92 } 93 94 // 関連付け 95 SelectObject(image->hdc, image->hbmp); 96 97 // ビットマップ情報の取得 98 GetObjectW(image->hbmp, sizeof(BITMAP), (void*)&bmp); 99 image->nWidth = bmp.bmWidth; 100 image->nHeight = bmp.bmHeight; 101 return 1; 102} 103 104int BltImage( 105 IMAGE* dst, // 転送先イメージ 106 int x, // 表示先X座標 107 int y, // 表示先Y座標 108 IMAGE* scr, // 表示元イメージ 109 RECT scr_r, // 表示元領域 110 DWORD flag) // 転送フラグ 111 112{ 113 int w = scr_r.right - scr_r.left; 114 int h = scr_r.bottom - scr_r.top; 115 116 BitBlt(dst->hdc, x, y, w, h, scr->hdc, scr_r.left, scr_r.top, flag); 117 return 0; 118} 119 120int DeleteImage(IMAGE* image) 121{ 122 DeleteDC(image->hdc); 123 DeleteObject(image->hbmp); 124 125 image->hdc = nullptr; 126 image->hbmp = nullptr; 127 return 1; 128}
こちらにある CreateImage関数、CreateImageFromFile関数、BltImage関数、DeleteImage関数の4つを使えば簡単に画像の表示ができます。
CreateImage関数でメモリDCを作成し、そのメモリDCに対して BltImage関数で描画、BltBltでウィンドウのデバイスコンテキストに描画、WM_DESTROYメッセージでそのメモリDCを削除します。
投稿2018/04/29 03:01
総合スコア178
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/04/28 03:31