Direct2Dを使ってPNG画像を表示するいい方法はないか模索していたところ、
Windows Imaging Component(WIC)というのを見つけましたが、しばらくこれどうやって使うの?
状態が続いており、いろんなサイトを読んでみるも挫折……
と思ったところ神!!!といえるようなサイトを発見し、そこの解説を読んで、
一応一つのコードにまとめました。長いんでGistに貼り付けました。
作り方はWin32プロジェクト作って変更していくスタイルです。
ひな形をVisual Studioに作らせてADDとかMODとかDELとか書いているところを変更しただけです。
Windows APIの最低限の知識がないと意味不明です。
あとちょっとだけでもCOM知ってないとなんじゃこりゃですw
作成環境
Windows 8.1
Visual Studio 2013 Professional
※ExpressだとATLが使えないので、CComPtrが使えないですので、代替の_com_ptr_tを使えばいいそうです。
※_com_ptr_tは古いのでMicrosoft::WRL::ComPtrのほうが良いです。
作成方法
ファイル > 新規作成 > プロジェクト
Visual C++のWin32 プロジェクトを選択し
プロジェクト名をD2DTest01とかにしておく。
デフォルト設定のままプロジェクトを作成。
プロジェクトを右クリックしてプロパティで
Debug/Releaseともに
構成プロパティのリンカー > 入力 > 追加の依存ファイル
先頭に以下を追加して下さい
d2d1.lib;WindowsCodecs.lib;
あとはD2DTest01.cppのWinMainがあるソースを下記のように変更する。
ソース
// D2DTest01.cpp : アプリケーションのエントリ ポイントを定義します。 // // NOTE : 構成プロパティのリンカー > 入力 > 追加の依存ファイル // 先頭に以下を追加して下さい // d2d1.lib;WindowsCodecs.lib; #include "stdafx.h" #include "D2DTest01.h" // ADD ======================== >>> #include <d2d1.h> // Direct2D #include <d2d1helper.h> // Direct2D #include <atlbase.h> // Direct2D ATL依存 #include <wincodec.h> // WIC #include <wincodecsdk.h> // WIC // ADD ======================== <<< #define MAX_LOADSTRING 100 // グローバル変数: HINSTANCE hInst; // 現在のインターフェイス TCHAR szTitle[MAX_LOADSTRING]; // タイトル バーのテキスト TCHAR szWindowClass[MAX_LOADSTRING]; // メイン ウィンドウ クラス名 // ADD ======================== >>> CComPtr<ID2D1Factory> gFactory; CComPtr<ID2D1HwndRenderTarget> gTarget; CComPtr<ID2D1Bitmap> gBitmap; CComPtr<IWICImagingFactory> gImagingFactory; // ADD ======================== <<< // このコード モジュールに含まれる関数の宣言を転送します: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); // ADD ======================== >>> HRESULT createResource(HWND hWnd); void discardResource(); // ADD ======================== <<< int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: ここにコードを挿入してください。 // ADD ======================== >>> CoInitialize(NULL); if (FAILED(CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, reinterpret_cast<void **>(&gImagingFactory) )) ) { MessageBox(NULL, _T("ImagingFactory作成に失敗"), NULL, MB_OK); return -1; } HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &gFactory); if (FAILED(hr)) { MessageBox(NULL, _T("D2Dファクトリ作成に失敗"), NULL, MB_OK); return -1; } // ADD ======================== <<< MSG msg; HACCEL hAccelTable; // グローバル文字列を初期化しています。 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_D2DTEST01, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // アプリケーションの初期化を実行します: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_D2DTEST01)); // MOD ======================== >>> // メイン メッセージ ループ: //while (GetMessage(&msg, NULL, 0, 0)) //{ // if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) // { // TranslateMessage(&msg); // DispatchMessage(&msg); // } //} do { if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { BOOL ret = GetMessage(&msg, NULL, 0, 0); if (ret == 0 /*WM_QUIT */ || ret == -1 /* ERROR */) { break; } TranslateMessage(&msg); DispatchMessage(&msg); } } while (msg.message != WM_QUIT); gImagingFactory.Release(); CoUninitialize(); // MOD ======================== <<< return (int) msg.wParam; } // // 関数: MyRegisterClass() // // 目的: ウィンドウ クラスを登録します。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_D2DTEST01)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_D2DTEST01); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 関数: InitInstance(HINSTANCE, int) // // 目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します。 // // コメント: // // この関数で、グローバル変数でインスタンス ハンドルを保存し、 // メイン プログラム ウィンドウを作成および表示します。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // グローバル変数にインスタンス処理を格納します。 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); // DEL ======================== >>> //UpdateWindow(hWnd); // DEL ======================== <<< return TRUE; } // // 関数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: メイン ウィンドウのメッセージを処理します。 // // WM_COMMAND - アプリケーション メニューの処理 // WM_PAINT - メイン ウィンドウの描画 // WM_DESTROY - 中止メッセージを表示して戻る // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; // DEL ======================== >>> //PAINTSTRUCT ps; // DEL ======================== <<< HDC hdc; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 選択されたメニューの解析: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; // ADD ======================== >>> case WM_SIZE: { if (gTarget) { RECT rect; GetClientRect(hWnd, &rect); gTarget->Resize( D2D1::Size( static_cast<UINT>(rect.right - rect.left), static_cast<UINT>(rect.bottom - rect.top) ) ); } } break; case WM_DISPLAYCHANGE: { /*解像度、色深度が変わった時は再描画*/ InvalidateRect(hWnd, NULL, FALSE); } break; // ADD ======================== <<< case WM_PAINT: // MOD ======================== >>> { if (SUCCEEDED(createResource(hWnd))) { PAINTSTRUCT ps; hdc = BeginPaint(hWnd, &ps); // 無効領域を有効領域にする EndPaint(hWnd, &ps); if (!( gTarget->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED)) { gTarget->BeginDraw(); gTarget->Clear(D2D1::ColorF(D2D1::ColorF::Green)); D2D1_SIZE_F size = gBitmap->GetSize(); RECT rect; GetClientRect(hWnd, &rect); int width = rect.right - rect.left; int height = rect.bottom - rect.top; // センタリング float x = (width - size.width) / 2.0f; float y = (height - size.height) / 2.0f; gTarget->DrawBitmap(gBitmap, D2D1::Rect<float>(x, y, x + size.width, y + size.height)); // デバイスロストの場合 if (gTarget->EndDraw() == D2DERR_RECREATE_TARGET) { discardResource(); InvalidateRect(hWnd, NULL, FALSE); } } } } break; // MOD ======================== <<< // ADD ======================== >>> // Direct2Dが自前で背景の描画を行うので、このメッセージを無効に case WM_ERASEBKGND: break; // ADD ======================== <<< case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // バージョン情報ボックスのメッセージ ハンドラーです。 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } /// ADD ======================== >>> HRESULT createResource(HWND hWnd) { HRESULT hr; if (!gTarget) { // レンダーターゲットの作成 RECT rect; GetClientRect(hWnd, &rect); hr = gFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties( hWnd, D2D1::Size( static_cast<UINT>(rect.right - rect.left), static_cast<UINT>(rect.bottom - rect.top) ) ), &gTarget ); if (FAILED(hr)) { MessageBox(NULL, _T("レンダーターゲット作成に失敗"), NULL, MB_OK); return E_FAIL; } // PNGの読込み // ファイルからデコーダを作成 // 画像 http://www.gamecradle.net/document/main/content/material/index.html よりDLして使用 CComPtr<IWICBitmapDecoder> dec; hr = gImagingFactory->CreateDecoderFromFilename( _T("pond_1_l.png"), NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &dec); if (FAILED(hr)) { MessageBox(NULL, _T("デコーダー作成に失敗"), NULL, MB_OK); return E_FAIL; } // フレーム取得 CComPtr<IWICBitmapFrameDecode> frame; hr = dec->GetFrame(0, &frame); if (FAILED(hr)) { MessageBox(NULL, _T("フレーム取得に失敗"), NULL, MB_OK); return E_FAIL; } // コンバータでDirect2D用フォーマットに変換 CComPtr<IWICFormatConverter> converter; hr = gImagingFactory->CreateFormatConverter(&converter); if (FAILED(hr)) { MessageBox(NULL, _T("Direct2Dフォーマット変換に失敗(CreateFormatConverter)"), NULL, MB_OK); return E_FAIL; } hr = converter->Initialize( frame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut); if (FAILED(hr)) { MessageBox(NULL, _T("Direct2Dフォーマット変換に失敗(Initialize)"), NULL, MB_OK); return E_FAIL; } // Direct2D用ビットマップを作成 hr = gTarget->CreateBitmapFromWicBitmap(converter, NULL, &gBitmap); if (FAILED(hr)) { MessageBox(NULL, _T("Direct2Dビットマップ作成に失敗"), NULL, MB_OK); return E_FAIL; } } return S_OK; } void discardResource() { gTarget.Release(); } /// ADD ======================== <<<
2014/11/14追記
COM用のスマートポインタにCComPtrとCComQIPtrとを不適切に使っていた箇所がありました。
今回の場合はCComPtrだけで用が足りますので修正しておきました。
なお、CComPtrとCComQIPtrの違いはこのあたりが参考になるかと……
http://msdn.microsoft.com/ja-jp/library/hh279683.aspx
visual c++ - What is the use of CComPtr over CComQIPtr in COM? - Stack Overflow
実行結果
それにしても初見殺しなコード……WICちゃんと理解しないと…いろいろオプションとか機能ありすぎてわけわかりません(;´・ω・)
参考にさせていただいたサイト 感謝!
使わせていただいた画像
※リンク先が切れていましたので、トップページのURLを載せています。