Andrew Truckle
Andrew Truckle

Reputation: 19207

How to load a PNG from RESOURCES into a CImage

According to this documentation for LoadFromResource it states:

Loads an image from a BITMAP resource

So, I have this in my code:

rImage.LoadFromResource(AfxFindResourceHandle(), IDB_PNG1);

Doesn't work. I then realised I am using PNG files and not BMP files. I assume this is the reason the resources can't be found.

I have also tried using AfxGetInstanceHandle(). But that also doesn't work.

Thus, at the moment I am using external PNG files. It works fine. But is there any way to load a PNG from the resources into a CImage?

Update

The comment provided to me was helpful.

It led me here. So, if I have this method:

IStream* CreateStreamOnResource(LPCTSTR lpName, LPCTSTR lpType)
{
    IStream * ipStream = NULL;

    HRSRC hrsrc = FindResource(NULL, lpName, lpType);
    if (hrsrc == NULL)
        goto Return;

    DWORD dwResourceSize = SizeofResource(NULL, hrsrc);
    HGLOBAL hglbImage = LoadResource(NULL, hrsrc);
    if (hglbImage == NULL)
        goto Return;

    LPVOID pvSourceResourceData = LockResource(hglbImage);
    if (pvSourceResourceData == NULL)
        goto Return;

    HGLOBAL hgblResourceData = GlobalAlloc(GMEM_MOVEABLE, dwResourceSize);
    if (hgblResourceData == NULL)
        goto Return;

    LPVOID pvResourceData = GlobalLock(hgblResourceData);

    if (pvResourceData == NULL)
        goto FreeData;

    CopyMemory(pvResourceData, pvSourceResourceData, dwResourceSize);

    GlobalUnlock(hgblResourceData);

    if (SUCCEEDED(CreateStreamOnHGlobal(hgblResourceData, TRUE, &ipStream)))
        goto Return;

FreeData:
    GlobalFree(hgblResourceData);

Return:
    return ipStream;
}

And call it like this:

rImage.Load(CreateStreamOnResource(MAKEINTRESOURCE(IDB_PNG1), _T("PNG")));

Works fine ... Thanks guys.

Update:

Based on comment provided. This better now? I see no leaks:

IStream *pStream = CreateStreamOnResource(MAKEINTRESOURCE(uPNGResourceID), _T("PNG"));
if (pStream != nullptr)
{
    rImage.Load(pStream);
    rImage.SetHasAlphaChannel(true);
    pStream->Release();
}

Side note: Do I really need to call SetHasAlphaChannel if these are my own embedded resources and I know the are transparent 32 bit resources?

Upvotes: 4

Views: 3993

Answers (2)

xMRi
xMRi

Reputation: 15375

  1. AfxFindResource
  2. LoadResource
  3. GlobalAlloc
  4. CopyMemory
  5. CreateStreamOnHGlobal
  6. CImage::Load(IStream*...)

Or you write your own IStream implementation that takes a pointer and a size from LoadResource. Also CSharedFile is an option.

EDIT: Also the new MFC provides a CPngBitmap that allows to load a PNG file as a HBITMAP directly from a memory source.

Upvotes: 3

Michael Haephrati
Michael Haephrati

Reputation: 4245

You can load an image from a hardcoded path or from a Stream. To load it from a Stream, use the following code. First, add a function for creating the stream from the resource.

IStream* ZDlg::CreateStreamOnResource(LPCTSTR lpName, LPCTSTR lpType)
{
    IStream * ipStream = NULL;

    HRSRC hrsrc = FindResource(NULL, lpName, lpType);
    if (hrsrc == NULL)
        goto Return;

    DWORD dwResourceSize = SizeofResource(NULL, hrsrc);
    HGLOBAL hglbImage = LoadResource(NULL, hrsrc);
    if (hglbImage == NULL)
        goto Return;

    LPVOID pvSourceResourceData = LockResource(hglbImage);
    if (pvSourceResourceData == NULL)
        goto Return;

    HGLOBAL hgblResourceData = GlobalAlloc(GMEM_MOVEABLE, dwResourceSize);
    if (hgblResourceData == NULL)
        goto Return;

    LPVOID pvResourceData = GlobalLock(hgblResourceData);

    if (pvResourceData == NULL)
        goto FreeData;

    CopyMemory(pvResourceData, pvSourceResourceData, dwResourceSize);

    GlobalUnlock(hgblResourceData);

    if (SUCCEEDED(CreateStreamOnHGlobal(hgblResourceData, TRUE, &ipStream)))
        goto Return;

FreeData:
    GlobalFree(hgblResourceData);

Return:
    return ipStream;
}

Then use the function as follow:

pImage = new CImage;
IStream *pStream = CreateStreamOnResource(MAKEINTRESOURCE(IDB_MYIMAGE), _T("PNG"));
if (pStream != nullptr)
{
    pImage->Load(pStream);
    pImage->SetHasAlphaChannel(true);
    pStream->Release();
}

Upvotes: 1

Related Questions