4

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?

Vladimir Kocjancic
  • 1,814
  • 3
  • 22
  • 34

2 Answers2

6
  • 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() :)

Community
  • 1
  • 1
Roel
  • 19,338
  • 6
  • 61
  • 90
1

Based on what you're asking for (simply some set of pixels being transparent) it's probably easiest to use a 24- or 32-bit BMP, and simply pick a color to treat as transparent. Pre-process your picture to take any pixel that precisely matches your transparent color and increase change the least significant bit of one of the channels.

Given 8 bits per channel, this won't normally cause a visible change. Then draw the pixels you want transparent in one exact color. This is easy to do in something like a paint program.

You can then draw that to your DC using TransparentBlt. That lets you specify the color you want to treat as transparent.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    Would that work with anti-aliasing as well? Basically, my image is a painted circle on transparent background. If I change transparent to let's say pink, some pixels will not be pink, but will not have the color of either app background or circle itself. – Vladimir Kocjancic Oct 23 '14 at 06:53