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?
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() :)
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.
Related
I'm trying to display images with overlays in a 'CListCtrl' within an MFC dialog box. The list control is in report/details mode.
I cannot find good documentation for showing overlays on some of my item images.
The code that is failing is shown below. I have a 64x32 bitmap with a folder icon in the first 32x32 pixels, and an overlay image in the second 32x32 pixels (IDB_FOLDERS32_OVERLAY). The bitmap has transparencies that seem to work fine.
CBitmap bm;
bm.LoadBitmap(IDB_FOLDERS32_OVERLAY);
m_ImageList.Create(32, 32, ILC_COLOR32, 2, 1);
int index = m_ImageList.Add(&bm, RGB(0, 0, 0));
ASSERT(index >= 0);
m_ImageList.SetOverlayImage(index, 2);
The last line returns 0, which indicates an error. GetLastError() returns 6 (ERROR_INVALID_HANDLE).
I can't for the life of me find reasonable documentation for how to do this anywhere on the web. Can anyone see what I'm missing?
Jonathan Potter was correct that I needed to include the ILC_MASK flag in order for SetOverlayImage() to returns a value that indicates success.
However, of all the documentation I could find online, nothing provided a complete description of how to do this. I didn't find a single source, for example, that showed what my bitmap should look like.
To make it more complicated, I am using ILC_COLOR32 to signify a 32-bit color bitmap with alpha (transparency) channel. So I don't have a mask and a mask does not appear compatible with alpha channels.
So in the end, I just created a bitmap with two images: The first is a folder, and the second is a folder with my overlay image on top of it. Problem solved.
At this point, it seems pointless to try and figure out if, or how, masks can be created with 32-bit alpha channel bitmaps.
I can't seem to get the CBitmap to actually apear on the screen... Here's the code in OnPaint:
CRect frm;
GetClientRect(frm);
CClientDC dc(this);
//dc.FillSolidRect(CRect(0, 0, 1000, 1000), RGB(255, 255, 255));
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap* cache = dcMem.SelectObject(&components.icon.bmp);
dc.BitBlt(0, 0, 55, 55, &dcMem, 0, 0, SRCCOPY);
dc.SelectObject(cache);
The definition of components.icon.bmp is...
components.icon.bmp.LoadBitmap(BMP_BOARDER);
BITMAP icon;
components.icon.bmp.GetBitmap(&icon);
There shouldn't be problem here. The Bitmap is a 32bit alpha bitmap and LoadBitmap returned TRUE.
It doesn't work with 24 bit either.
---- Got it! Problem with my resources
This code seems good to me but there are still many things that can be wrong. Test your code to know where the problem is:
- If you draw anything else (a rectangle for instance) does it appears on the frame window?
- Try to save the bmp to a file and see if it's ok.
- Try to convert the bmp to 24bits using a tool and see if it works.
Once you know where the problem is you'll be able to focus on the problem.
Probably you won't be able to draw a 32bits bmp using GDI but you should get something (a black square at least). Anyway, if you need to draw 32bits alpha-channel bmps using GDI you have to pre-multiply the image and use AlphaBlend method instead of BitBlt.
I'm displaying a bitmap using GDI+. After loading the bitmap from a DLL resource I set the background colour (blue - #0000FF) to transparent using TransparentBlt. On Windows Vista and later this works as expected.
However, on a Windows XP system we're testing on this only works when any tooltip (e.g. the "title" property in IE, or Windows Explorer's tooltip shown when hovering the mouse over a file, etc) is displayed. The rest of the time the background colour is still blue.
Has anyone encountered this before, or know of a way to stop this occurring and for the blue to be properly made transparent?
Edit: After further investigation I found that setting colour depth in Windows XP to 16 bit colours instead of 32 bit colours caused TransparentBlt to start working normally again. Obviously this isn't an ideal solution, specifying what colour depth must be used, but does this give any hint to what might be happening?
Edit2: Code sample included.
m_pGDIBitmap = new Gdiplus::Bitmap(_Module.m_hInst, MAKEINTRESOURCE(lImageResource));
m_hMemDC = CreateCompatibleDC(hdc);
Gdiplus::Graphics myGraphics(m_hMemDC);
myGraphics.DrawImage(m_pGDIBitmap,
Gdiplus::Rect(0, 0, m_pGDIBitmap->GetWidth(), m_pGDIBitmap->GetHeight()),
0,
0,
m_pGDIBitmap->GetWidth(),
m_pGDIBitmap->GetHeight(),
Gdiplus::UnitPixel, &imAtt);
SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, rcBounds.left, rcBounds.top, NULL);
TransparentBlt(hdc, rcBounds.left, rcBounds.top, iScaledWidth, iScaledHeight, m_hMemDC, 0, 0, iBitmapWidth, iBitmapHeight, GetPixel(m_hMemDC, 0, 0));
You must show some sample code - the code that loads the bitmap and the code that blits it to display.
From the symptoms that you describe, my guess is that you load the bitmap not in its native format, but in the current display format. This means, that when the bit depth of the bitmap differs from the bit depth of the display, an automatic color space conversion is made. When this happens, the color that you provide to TransparentBlt is actually different from the color in the bitmap.
Possible alternative solutions:
Make sure that the bitmap is loaded in its native format without conversion.
Allow the conversion to take place, but instead of providing a hardcoded color value to TransparentBlt, make a GetPixel of a known "transparent" pixel of the bitmap (like top-left), and provide this value to the TransparentBlt.
What I ended up doing was more of a workaround, but it did work. I changed the background colour to black and added the following code before the DrawImage call:
Gdiplus::ImageAttributes imAtt;
imAtt.SetColorKey(Gdiplus::Color(0, 0, 0), Gdiplus::Color(0, 0, 0), Gdiplus::ColorAdjustTypeBitmap);
For some reason using this with blue as the background didn't work and using TransparentBlt on its own with either colour didn't work, but the combination applied the transparency correctly on the various OSes and colour depths that I tried.
If I had access to a paint program that supported bitmap files with an alpha channel, i.e. 32 bit bitmaps, I suspect making the background explicitly transparent and then using AlphaBlend would have worked as well but I didn't have the ability to try that at the time.
It's a while since I've done this, but I'm trying to add a custom button graphic to a windows button, with some transparent areas. I've tried various schemes but can't seem to get the transparent areas to show. Here's my code:
hbmpUpDisabled = LoadImage(instance,MAKEINTRESOURCE(IDB_UPARROWDISABLED), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS );
SendMessage(GetDlgItem(hWndDlg, IDC_MOVEUP),BM_SETIMAGE,(WPARAM)IMAGE_BITMAP,(LPARAM)hbmpUpDisabled);
Does anyone notice any problems here? It works if my bitmap is a 1-bit bitmap. I couldn't get a 32 bit bitmap to work, and I'm not sure how to setup a 24 bit or 8 bit bitmap to do it.... I tried a custom 255,0,255 color (which IIRC is a default transparent value), but so far no joy....
LR_LOADMAP3DCOLORS should map grey - in the source image - to to the current button face color. Buttons do not use AlphaBlt or TransparentBlt so there is no way to actually (short of custom painting) set a bitmap with transparent or alpha'd areas onto a button and expect it to work. You just have to pre-prepare the bitmap with the correct button color in its background areas.
That said - I suspect that some of these restrictions may be lifted for buttons implemented by common controls v6. Add commctl 6 as a dependend assembly to your exe and see if the behaviour changes.
I can't draw a pattern with a transparent background. This is my snippet :
bitmap.CreateBitmap(8, 8, 1, 1, &bits)
brush.CreatePatternBrush(&bitmap)
hbrush = pCgrCurrentDC->SelectObject(&brush);
// set text color
TextCol = pCgrCurrentDC->SetTextColor(CgrColourPalRGB);
int oldBkgrdMode = pCgrCurrentDC->SetBkMode(TRANSPARENT);
//draw polygon
pCgrCurrentDC->Polygon(CgrBuffer, n);
The doc on msdn doesn't mention anything about transparency. I guess this mode could be used? Or is this a bug ?
Thanks!
Mode TRANSPARENT means that background will not be filled before your brush is drawn. But your brush does not contain any transparent pixels in it and it redraws background pixels anyway. Fourth argument in CreateBitmap was set to 1 in your sample. That means bitmap is monochrome.
You need to use 32-bit bitmap to use transparency in brushes. GDI supports transparency with some limits. Use GDI+ for full transparency support.