概要

GDI+はGDI(Graphics Design Interface)の機能を強化したものです。
jpegファイル等を比較的簡単に扱うことができます。
本プログラムは、image.jpgファイルをウィンドウに等倍で表示するプログラムです。

ウィンドサイズをマウス操作で変化させると、再描画がちらついて見えます。
DrawImageメソッドの動作が遅いことがわかります。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

プロジェクトの作成

Win32プロジェクト Windowsアプリケーション

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

GDI+のオブジェクトは、Gdiplusという名前空間に属しています。
これらを呼び出すには、オブジェクトの前にGdiplus::を付加するか、using namespace Gdiplus;でディフォルトの名前空間をGdiplusに変更する方法があります。
オブジェクトごとにGdiplus::を付加するのは煩雑なので、using namespaceを使用しています。

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。
GdiplusStartup関数でGDI+を初期化します。
ウィンドウを作成する場合は、RegisterClass APIによりウィンドウクラスを定義してからCreateWindow APIを呼び出しウィンドウを作成します。 Windowsは入力等のイベントが発生するとアプリケーションにメッセージを送付します。 メッセージはキューに保管されます。アプリケーションはメッセージを取り出し、該当ウィンドウにメッセージを配信します。 メッセージの取り出しから配信までループで処理を行いウィンドウから終了メッセージが届くと、ループを抜けるように記述します。 これらの一連の処理は、通常にCreateWindow APIの後に記述しこれをメッセージループと呼んでいます。 詳細は以下を参照してください。
メッセージループについて

WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ウィンドウプロシージャー

RegisterClass APIにより登録することによりWindowsから呼び出されます。
第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。
自分で処理しないメッセージはDefWindowProc APIに渡せばWindowsが標準的な処理を行ってくれます。
最後に、GdiplusShutdown関数でGDI+の終了処理をします。

case WM_CREATE:

ウィンドウの初期化時に呼び出されます。
Bitmap::FromFileによりファイル名からBitmapオブジェクトを作成しimagep静的変数に保存します。
ファイル名は、UNICODEでなければならないので、マルチバイトでコンパイルされた場合は、MultiByteToWideChar APIによりUNICODEで変換するコードを有効にします。
ファイルが開けない場合は、FromFileはNULLを返します。
今回のプログラムでは、DefWindowProc APIにそのままメッセージを渡して処理しています。

case WM_DESTROY:

ウィンドウが閉じるときに呼び出されます。
WM_CREATEで作成されたオブジェクトを解放します。
PostQuitMessage APIにより終了コードを指定して、ウィンドウプロシージャーを終了させます。

case WM_PAINT:

ウィンドウを再描画する必要があるときに呼び出されます。
他のウィンドウに隠れ再びフォアグラウンドになった場合などウィンドウの再描画はWindowsが面倒を見ないのでプログラマの仕事となっています。
BeginPaint APIを呼び出して、描画に必要なデバイスコンテキストのハンドルを取得します。
BeginPaint APIの第2引数のポインタにはPAINTSTRUCT構造体のポインタを渡します。
BeginPaint API終了後、PAINTSTRUCT構造体には再描画が必要な領域の座標等の値が格納されています。
GDI+による描画に必要なGraphicsクラスをデバイスコンテキストから作成します。
GraphicsクラスのメンバーDrawImageを用いてイメージを左上0,0位置に描画します。
描画後、Graphicsクラス解放します。
再描画が終了したことをWindowsに知らせるためにEndPaint APIを使用してます。このAPIを呼び出さないと何度もWM_PAINTメッセージが発生し暴走します。

プログラムソース

//       jpegファイルをウィンドウに等倍で表示(GDI+)
//      Visual C++ 2008/2013    32/64bit

#include <windows.h>
#include <gdiplus.h>
#include <tchar.h>

#pragma comment(lib,"gdiplus.lib")

using namespace Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL InitApp(HINSTANCE, WNDPROC, TCHAR*);
BOOL InitInstance(HINSTANCE, TCHAR*, int);
HWND hWnd;


// 最初に呼び出される関数

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,TCHAR* lpsCmdLine, int nCmdShow){
        MSG msg;
        TCHAR szClassName[] = TEXT("jpgview1");
        GdiplusStartup(&gdiToken, &gdiSI, NULL);
        if (!hPrevInst) {
                if (!InitApp(hCurInst,WndProc, szClassName))    //    ウィンドウクラスの登録
                        return FALSE;
        }
        if (!InitInstance(hCurInst, szClassName, nCmdShow)) {    // ウィンドウの作成
                return FALSE;
        }

        while (GetMessage(&msg, NULL, NULL, NULL)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
        GdiplusShutdown(gdiToken);

        return (int)msg.wParam;
}

// ウィンドウを作成/閉じる/移動等のメッセージにより起動される関数

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
        HDC hdc;
        PAINTSTRUCT ps;
        TCHAR* szFile=TEXT("image.jpg");      //      表示するファイル名指定
        static Image* imageP=0;
        switch (msg) {
        case WM_CREATE:{
#ifndef UNICODE
                WCHAR szFileW[MAX_PATH];
                MultiByteToWideChar(932,0,szFile,-1,szFileW,sizeof(szFileW)/sizeof(TCHAR));
                imageP=Bitmap::FromFile(szFileW);
#else
                imageP=Bitmap::FromFile(szFile);
#endif
        }
        break;
        case WM_PAINT: {       // ウィンドウの描画が必要な場合に呼び出される。
                hdc = BeginPaint(hWnd, &ps);
                Graphics MyGraphics(hdc);
                MyGraphics.DrawImage(imageP,0,0);
                MyGraphics.~Graphics();
                EndPaint(hWnd, &ps);
                break;
        }
    case WM_DESTROY:    // ウィンドウを閉じる場合に呼び出される。
                imageP->~Image();
                PostQuitMessage(0);
                break;
    default:
                return (DefWindowProc(hWnd, msg, wp, lp));
    }
        return 0L;
}

//     ウィンドウクラスの登録 1回しか呼ばれないのに関数化しているのは、ウィンドウを2回呼び出す場合に
//  この関数を再利用できるからです。

BOOL InitApp(HINSTANCE hInst,WNDPROC WndProc,TCHAR* szClassName){
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInst;
        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = szClassName;
        return (RegisterClass(&wc));
}

//     ウィンドウの作成 1回しか呼ばれないのに関数化しているのは、ウィンドウを2回呼び出す場合に
//  この関数を再利用できるからです。

BOOL InitInstance(HINSTANCE hInst, TCHAR* szClassName, int nCmdShow){
        hWnd = CreateWindow(szClassName,
                szClassName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInst,
                NULL);
        if (!hWnd)
                return FALSE;
        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);
        return TRUE;
}

ソースファイルと実行ファイルのダウンロード

ダウンロードjpegview1.zip(74.4kByte)

ZIPファイルに含まれるファイル
jpegview1cpp
jpegview1.exe
image.jpg