Search code examples
c++mfcpng

MFC displaying png with transparent background


I need a way to display a PNG image with transparent background on a background with gradient color.

I have tried this:

CImage img;
CBitmap bmp;
img.Load(_T(".\\res\\foo.png"));
bmp.Attach(img.Detach());
CDC dcStatus;
dcStatus.CreateCompatibleDC(&dc);
dcStatus.SelectObject(&bmp);
dcStatus.SetBkColor(TRANSPARENT);
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcStatus, 0, 0, SRCCOPY);
bmp.DeleteObject();

but foo.png gets black background wherever it is transparent in original image.

I did try to do a new bitmap that was painted with transparent color and did all possible operations on it, but that didn't help. Sample of one permutation:

CImage img;
CBitmap bmp;
img.Load(_T(".\\res\\foo.png"));
bmp.Attach(img.Detach());
CBitmap bmpMaska;
bmpMaska.CreateBitmap(14, 14, 1, 1, NULL);
CDC dcStatus;
dcStatus.CreateCompatibleDC(&dc);
dcStatus.SelectObject(&bmp);
CDC dcMaska;
dcMaska.CreateCompatibleDC(&dc);
dcMaska.SelectObject(&bmpMaska);
dcMaska.SetBkColor(dcStatus.GetPixel(0, 0));
//TODO: Bitmap ni transparent
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcMaska, 0, 0, SRCCOPY);
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcStatus, 0, 0, SRCAND);  
bmp.DeleteObject();
bmpMaska.DeleteObject();

This did not do the trick. Either, there was all black square on the screen, or the result was the same as original.

I have also checked AlphaBlend API, but my code must be pure MFC + C++ witthout any additional APIs. [Edit]: Company policy is as little APIs as possible. The code is supposed to run on embedded windows systems in real time.

[Edit 2]: I am not bound to PNG image format, meaning, anything that will display as transparent, goes.

Please, tell me what am I doing wrong?


Solution

    • Convert the png to a 32 bit bmp file. Google 'AlphaConv' and use that. PNG's are a PITA to work with - several API's claim to support them but actually don't, or only partially, or only on some platforms etc.
    • Load your 32-bit bmp using CBitmap::LoadBitmap (from resources - this is by far the easiest)
    • Then use CDC::AlphaBlend() to draw your bitmap. TransparentBlt() doesn't work with an alpha channel - it's just either draw the pixel or not. AlphaBlend is part of the win32 API, and you could still investigate CImage::AlphaBlend if you'd like, but that has a bug somewhere (I forgot what exactly) so I always use the raw ::AlphaBlend.
    • But beware - you need to premultipy the alpha channel for correct display. See my answer on How to draw 32-bit alpha channel bitmaps? .

    No variation of what you described will work. You need another API to get this to work. Or, you could use GetDIBits and do your own version of ::AlphaBlend() :)