リソースのPNG画像を使う方法

透過PNG画像をダイアログ上のコントロールとして表示する必要が出来たので、リソースからPNG画像を読み込み Gdiplus::Bitmap で取得するコードを書きました。
ついでにビットマップ(BMP)画像も Gdiplus で扱いやすいように Bitmap で取得するようにと思ったのですが、何故か同じ方法ではうまく行かず。
Gdiplus::Bitmap にはリソースから Bitmap を作成するメソッドが用意されているのでそれを使うようにしました。
(ただインスタンスも指定しなければビットマップを作れないので別段これに実装する必要は無かったかも・・・)
手順としては以下のようになります。
1.FindResource()でリソースを探します。
2.SizeofResource()でリソースのサイズを取得。
3.LoadResource()でリソースを読み込んで LockResource() でメモリーをロックします。
 (LoadResource()はHGLOBALを返しますが実際はグローバルメモリーに読み込まないため、LockResource()でポインターを取得します。)
4.グローバルメモリーを作り、3で取得したリソースデータをコピーします。
5.4のグローバルメモリーからIStreamポインターを作成します
6.5の IStream から Gdiplus::Bitmap を作成します。
途中で失敗した場合は例外を投げて処理を中断します。
実際にコードにするとこんな感じです。
Gdiplus::Bitmap* LoadImageFromResource(UINT nID, LPCTSTR pResourceType = RT_BITMAP, HMODULE hInstance = NULL)
{
    HGLOBAL hRcBuf = NULL;
    void* pRcBuf = NULL;
    IStream* pIStream = NULL;
    Gdiplus::Bitmap *pBitmap = NULL;
    try
    {
        LPCTSTR pName = MAKEINTRESOURCE(nID);
    
        if (pResourceType == RT_BITMAP) {
            pBitmap = Gdiplus::Bitmap::FromResource(hInstance, pName);
            if (pBitmap == NULL || pBitmap->GetLastStatus() != Gdiplus::Ok) {
                delete pBitmap;
                pBitmap = NULL;
                throw std::runtime_error("ビットマップを作成できません。");
            }
            return pBitmap;
        }

        HRSRC hRc = FindResource(hInstance, pName, pResourceType);
        if (!hRc) {
            throw std::runtime_error("リソースが見つかりません。");
        }
        DWORD dwRcSize = SizeofResource(hInstance, hRc);
        if (!dwRcSize) {
            throw std::runtime_error("リソースのサイズが 0 です。");
        }
        const void* pRcData = LockResource(LoadResource(hInstance, hRc));
        if (!pRcData) {
            throw std::runtime_error("リソースの読み込みに失敗しました。");
        }
        hRcBuf = GlobalAlloc(GMEM_MOVEABLE, dwRcSize);
        if (!hRcBuf) {
            throw std::runtime_error("メモリーが確保できません。");
        }
        pRcBuf = GlobalLock(hRcBuf);
        if (!pRcBuf) {
            throw std::runtime_error("メモリーがロックできません。");
        }

        CopyMemory(pRcBuf, pRcData, dwRcSize);

        HRESULT hr = CreateStreamOnHGlobal(hRcBuf, FALSE, &pIStream);
        if (hr != S_OK) {
            throw std::runtime_error("ストリームの作成に失敗しました。");
        }
        pBitmap = Gdiplus::Bitmap::FromStream(pIStream);
        if (pBitmap == NULL || pBitmap->GetLastStatus() != Gdiplus::Ok) {
            delete pBitmap;
            pBitmap = NULL;
            throw std::runtime_error("ストリームからビットマップを作成できません。");
        }
    }
    catch (const std::runtime_error &e)
    {
        std::string str_message = e.what(); str_message += "\r\n";
        OutputDebugStringA(str_message.data());
    }
    if( NULL != pIStream )
        pIStream->Release();
    if (NULL != hRcBuf) {
        GlobalUnlock(hRcBuf);
        GlobalFree(hRcBuf);
    }
    return pBitmap;
}
呼び出す側は、下記のようにします。
// PNG画像をリソースから読み込む
Gdiplus::Bitmap *pPngImage = LoadImageFromResource(IDB_PNG1, _T("PNG"));
if (pPngImage) {
    Gdiplus::Graphics gr(hdc);
    gr.DrawImage(pPngImage, 0, 200);
}
delete pPngImage;
また、リソースには下記のようにPNG画像を追加しておきます。
IDB_PNG1                PNG                     "res¥¥image.png"

※ここでは Gdiplus の初期化処理などの説明は省いています。

コメント

このブログの人気の投稿

Django Webアプリ を AWS Elastic Beanstalk にデプロイする際にDBを初期化する