EternalWindows
OLE描画 / ピクチャーオブジェクト

アプリケーションが描画するイメージには、ビットマップやアイコン、メタファイルなど様々な種類が存在しますが、 その種類に応じて描画に使用する関数が異なるのは煩わしいことです。 こうした種類の違いを抽象化し、どのような種類でも統一的な方法で描画できるようにするために、 OLEではIPictureと呼ばれるインターフェースが用意されています。 このインターフェースは1つのイメージを表しており、 それがどのようなイメージであっても、IPicture::Renderで描画することができます。 IPictureがどのようなイメージを表しているかを確認したい場合は、IPicture::get_Typeを呼び出すようにします。

アプリケーションがIPictureを取得するには、ファイルパスを指定してOleLoadPicturePathを呼び出すのが最も簡単です。 対象となるファイルは、既に述べたようにビットマップやアイコンなどが可能ですが、 意外にもjepgやgifファイルを指定することも可能になっています。 これらのファイルを指定すれば、IPicture::Renderでjepgやgifを描画できるようになりますから、 グラフィックスアプリケーションにとっては魅力的であるといえるでしょう。 OleLoadPicturePathは、次のように定義されています。

HRESULT OleLoadPicturePath(
  LPOLESTR szURLorPath,
  LPUNKNOWN punkCaller,
  DWORD dwReserved,
  OLE_COLOR clrReserved,
  REFIID riid,
  LPVOID *ppvRet
);

szURLorPathは、イメージを格納したファイルへのフルパスを指定します。 punkCallerは、集約に使用するインターフェースを指定します。 集約を行わない場合は、NULLで問題ありません。 dwReservedは、予約されているため0で問題ありません。 clrReservedは、透過するために予約したい色を指定します。 ビットマップを扱う場合は考慮されません。 riidは、オブジェクトを識別するインターフェースのIIDを指定します。 ppvRetは、オブジェクトを受け取る変数のアドレスを指定します。

IPictureを取得したアプリケーションは、Renderを呼び出すことによってイメージを描画することができます。

HRESULT IPicture::Render(
  HDC hDC,
  LONG x,
  LONG y,
  LONG cx,
  LONG cy,
  OLE_XPOS_HIMETRIC xSrc,
  OLE_YPOS_HIMETRIC ySrc,
  OLE_XSIZE_HIMETRIC cxSrc,
  OLE_YSIZE_HIMETRIC cySrc,
  LPCRECT pRcWBounds
);

hDCは、デバイスコンテキストのハンドルを指定します。 このデバイスコンテキストにイメージが描画されることになります。 xは、イメージの描画先となるx座標を指定します。 yは、イメージの描画先となるy座標を指定します。 cxは、イメージを描画する幅を指定します。 cyは、イメージを描画する高さを指定します。 xSrcは、描画元イメージのどのX座標からコピーの対象にするかを指定します。 ySrcは、描画元イメージのどのY座標からコピーの対象にするかを指定します。 cxSrcは、描画元イメージのコピーする幅を指定します。 cySrcは、描画元イメージのコピーする高さを指定します。 pRcWBoundsは、hDCがメタファイルのデバイスコンテキストである場合に初期化します。 メタファイルのデバイスコンテキストを指定していない場合は、NULLで問題ありません。

今回のプログラムは、jpegファイルをウインドウに描画します。

#include <windows.h>
#include <olectl.h>

void HIMETRICtoDP(LPSIZEL lpSizel);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static IPicture *pPicture = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		HRESULT hr;

		OleInitialize(NULL);

		hr = OleLoadPicturePath(L"C:\\sample.jpg", NULL, 0, 0, IID_PPV_ARGS(&pPicture));
		if (FAILED(hr))
			return -1;

		return 0;
	}

	case WM_PAINT: {
		HDC                hdc;
		PAINTSTRUCT        ps;
		OLE_XSIZE_HIMETRIC cxSrc;
		OLE_YSIZE_HIMETRIC cySrc;
		SIZEL              sizel;

		hdc = BeginPaint(hwnd, &ps);
		
		pPicture->get_Width(&cxSrc);
		pPicture->get_Height(&cySrc);
		sizel.cx = cxSrc;
		sizel.cy = cySrc;
		HIMETRICtoDP(&sizel);
		
		pPicture->Render(hdc, 0, 0, sizel.cx, sizel.cy, 0, cySrc, cxSrc, -cySrc, NULL);

		EndPaint(hwnd, &ps);

		return 0;
	}

	case WM_DESTROY:
		if (pPicture != NULL)
			pPicture->Release();
		OleUninitialize();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void HIMETRICtoDP(LPSIZEL lpSizel)
{
	HDC hdc;
	const int HIMETRIC_INCH = 2540;

	hdc = GetDC(NULL);
	lpSizel->cx = lpSizel->cx * GetDeviceCaps(hdc, LOGPIXELSX) / HIMETRIC_INCH;
	lpSizel->cy = lpSizel->cy * GetDeviceCaps(hdc, LOGPIXELSY) / HIMETRIC_INCH;
	ReleaseDC(NULL, hdc);
}

IPicture::Renderを呼び出す場合は、注意しなければならない点が2つあります。 1つは、描画先の位置と範囲をデバイス単位で指定するのに対して、 描画元の位置と範囲はHIMETRIC単位で指定するという点です。 IPicture::get_Widthとget_Heightは、イメージのサイズをHIMETRIC単位で返しますが、 これを基に描画先の範囲を決定したい場合は、デバイス単位に変換しておく必要があります。 これは、HIMETRICtoDPという自作関数で行われています。 2つ目の注意点は、描画元イメージのY位置の始点がcyから0に向かって始まるという点です。 これは、IPicture::Renderの第7引数に相当します。 cyから0に向かって始まるということは、高さの値はマイナスで指定しなければならないため、 第9引数は-cyを指定しています。

IPictureのメソッドについて

OleLoadPicturePathでjpegやgifファイルをロードした場合、それらは内部でビットマップとして扱われることになります。 これは、IPicture::get_TypeでPICTYPE_BITMAPが返ることからも分かります。 この点に注目すれば、ビットマップを扱う感覚でjepgやgifファイルの情報を取得することができます。

TCHAR      szBuf[256];
BITMAP     bm;
OLE_HANDLE hOle;

pPicture->get_Handle(&hOle);
GetObject((HBITMAP)hOle, sizeof(BITMAP), &bm);

wsprintf(szBuf, TEXT("%d %d"), bm.bmWidth, bm.bmHeight);
MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);

IPicture::get_Handleで返るハンドルの型はOLE_HANDLEとなっていますが、 タイプがPICTYPE_BITMAPである場合は、HBITMAP型にキャストすることができます。 HBITMAP型ということは、ビットマップというGDIオブジェクトを識別していることになりますから、 GetObjectを通じてビットマップの情報を取得することができます。 このようにして取得した幅と高さは既にデバイス単位になっているため、 HIMETRICへの変換を行う必要はなくなります。 また、ビットマップは24ビットのDIBとして扱われており、 bmBitsからビットイメージにアクセスすることができます。

jpegやgifファイルのデータがビットマップとして扱われているということは、 そのデータを保存することでビットマップを作成できることを意味しています。 言い換えれば、jpegやgifファイルをビットマップに変換できるわけです。 このような例を次に示します。

LONG    lSize;
IStream *pStream;

hr = SHCreateStreamOnFile(L"C:\\sample.bmp", STGM_CREATE | STGM_WRITE, &pStream);
if (SUCCEEDED(hr)) {
	pPicture->SaveAsFile(pStream, TRUE, &lSize);
	pStream->Release();

}

IPicture::SaveAsFileを呼び出せばデータを保存することができますが、 このためには保存先となるストリームを第1引数に指定しなければなりません。 SHCreateStreamOnFileを呼び出せば、ファイルに関連したストリームを作成することができるため、 ストリームに書き込まれたデータはファイルに反映されることになります。



戻る