これまでの作業ではATL/WTLアプリケーションウイザードの標準設定で「ImageViewer」というプロジェクトを作成、自動生成されたソースコードのうち「ImageViewerView.h」を書き換えて以下のようにした。
// ImageViewerView.h : CImageViewerView クラスのインターフェイス
//
/////////////////////////////////////////////////////////////////////////////
#pragma once
#include "gdiplus.h"
#pragma comment(lib,"Gdiplus.lib")
using namespace Gdiplus;
class CImageViewerView : public CWindowImpl<CImageViewerView>
{
public:
DECLARE_WND_CLASS(NULL)
BOOL PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
BEGIN_MSG_MAP(CImageViewerView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
END_MSG_MAP()
// ハンドラーのプロトタイプ (引数が必要な場合はコメントを外してください):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CPaintDC dc(m_hWnd);
//GDI+初期化
ULONG_PTR _nToken;
GdiplusStartupInput _sGdiplusStartupInput;
GdiplusStartup(&_nToken,&_sGdiplusStartupInput,NULL);
//画像読み込み
Bitmap* pImage;
Graphics cGraphics(dc.m_hDC);
pImage = Bitmap::FromFile(L"test.jpg",TRUE);
//画像表示
RECT rect;
GetClientRect(&rect);
cGraphics.DrawImage(pImage,(REAL)0,(REAL)0,(REAL)(rect.right - rect.left),(REAL)(rect.bottom - rect.top));
//画像開放
delete pImage;
//GDI+開放処理
// GdiplusShutdown(_nToken);
return 0;
}
};
たったこれだけのソースコードで画像ファイルを読み込んで画面いっぱいに表示できた。しかし前にも述べたように、このソースコードは誤った使い方をしている部分が少なからずある。また描画するごとに画像ファイルを読み込んでいるため効率も悪い。ここではこれらの点を修正する。
まず先頭にある以下の3行。
#include "gdiplus.h"
#pragma comment(lib,"Gdiplus.lib")
using namespace Gdiplus;
これは画像描画に用いるGDI+を利用するための宣言だ。これらはソースコード中のどこからも参照できるようにする。

画面左側のソリューションエクスプローラにある「stdafx.h」をダブルクリックして開く。このヘッダーファイルはプロジェクト内で共通の宣言を置く場所だ。

ここに先ほどの3行を移動する。
少し飛んでOnPaint内の以下の2箇所の部分。
//GDI+初期化
ULONG_PTR _nToken;
GdiplusStartupInput _sGdiplusStartupInput;
GdiplusStartup(&_nToken,&_sGdiplusStartupInput,NULL);
//GDI+開放処理
// GdiplusShutdown(_nToken);
これらはそれぞれGDI+を利用するための初期化と終了処理だ。このソースコードでは描画毎に初期化を行い、終了処理は行っていない。
本来描画ごとに行うべきではない。GDI+の初期化と終了処理はアプリケーションの起動時と終了時に1回ずつ行えばいい。そのためこれらの処理も移動する。

画面左側のソリューションエクスプローラにある「ImageViewer.cpp」をダブルクリックして開く。このcppファイルはアプリケーションの起動処理が書かれている場所だ。

ImageViewer.cpp内の_tWinMain()を見ると、「int nRet = Run(lpstrCmdLine, nCmdShow);」という部分がある。このRun()の中でアプリケーションのウインドウが生成・表示されている。そのためこの部分を挟んで初期化と終了処理を行う。

このとき今まで実行しないようにコメントアウトしていた終了処理のコメントを外しておく。
具体的には以下のようにする。
//GDI+初期化
ULONG_PTR _nToken;
GdiplusStartupInput _sGdiplusStartupInput;
GdiplusStartup(&_nToken,&_sGdiplusStartupInput,NULL);
int nRet = Run(lpstrCmdLine, nCmdShow);
//GDI+開放処理
GdiplusShutdown(_nToken);
場所を戻って「ImageViewerView.h」内の以下の項目。
BEGIN_MSG_MAP(CImageViewerView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
END_MSG_MAP()
// ハンドラーのプロトタイプ (引数が必要な場合はコメントを外してください):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

この部分はメッセージマップと呼ばれる。ここではWM_PAINTというメッセージの処理が定義されている。
WM_PAINTは描画(ペイント)用のメッセージで、ウインドウの表示内容を描画しなおさなければならないときに送られるメッセージだ。そのWM_PAINTメッセージをOnPaint()という関数で処理することが定義されている。

このメッセージマップの部分にウインドウが生成されるときと破壊されるときのメッセージ処理を定義する。
ウインドウが生成するときに送られるメッセージはWM_CREATE、破壊するときはWM_DESTROYになる。それぞれOnCreate()とOnDestroy()で処理する。
BEGIN_MSG_MAP(CImageViewerView)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
END_MSG_MAP()
// ハンドラーのプロトタイプ (引数が必要な場合はコメントを外してください):
// LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
// LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
//ウインドウ生成時処理
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return 0;
}
//ウインドウ破壊時処理
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return 0;
}

ウインドウ生成時と破壊時処理用の関数を用意しただけでは意味がない。次にウインドウの生成時に画像を読み込み、破壊時に画像を開放する処理を入れる。これによりOnPaint()内もだいぶスマートになった。
Bitmap* pImage;
//ウインドウ生成時処理
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
//画像読み込み
pImage = Bitmap::FromFile(L"test.jpg",TRUE);
return 0;
}
//ウインドウ破壊時処理
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
//画像開放
delete pImage;
return 0;
}
LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CPaintDC dc(m_hWnd);
Graphics cGraphics(dc.m_hDC);
//画像表示
RECT rect;
GetClientRect(&rect);
cGraphics.DrawImage(pImage,(REAL)0,(REAL)0,(REAL)(rect.right - rect.left),(REAL)(rect.bottom - rect.top));
return 0;
}

これでソースコードの修正が済んだ。ビルドを行い、デバッグする。

ソースコードではGDI+を起動時、画像読み込みをウインドウ生成時、GDI+終了処理を最後に行うようにし、処理効率が上がった。しかしウインドウサイズを変更すると...まだちらついてしまう。
次回はこの"ちらつき"を解消する。
プロジェクトファイルをダウンロード