Win32 LayeredWindow gives bad visual effect - user-interface

I'm developing a UI system that has all those smart features like panel tearing off and docking, etc. Right now my task is to create an overlay on the screen that shows the position where the teared off or dockable panel would land. Pretty much same thing that visual studio has got.
For that I'm using a custom layered window class that would show up when it is needed. After that I've started digging to achieve the needed effect.
I was working with standart GDI functions before and basicly they are ok. But this time I followed the documentation advice to use UpdateLayeredWindow for my tasks and to load 32bit image from bitmap instead of drawing it with GDI functions.
So here I have a 128x128pixel wide bmp with 222 in alpha channel and 255 0 0 in RGB
Here are methods which I use for initialization and drawing.
void Init(HDC in_hdc,HWND in_hwnd)
{
bf = { 0, 0, 200, AC_SRC_ALPHA };
hwnd = in_hwnd;
hdc_mem = CreateCompatibleDC(in_hdc);
hBitmap_mem = CreateCompatibleBitmap(in_hdc, canvas_size.cx, canvas_size.cy);
hBitmap_mem_default = (HBITMAP)SelectObject(hdc_mem, hBitmap_mem);
hdc_bitmap = CreateCompatibleDC(in_hdc);
}
void DrawArea(RECT& in_rect)
{
hBitmap_area_default = (HBITMAP)SelectObject(hdc_bitmap, hBitmap_area);
AlphaBlend(hdc_mem, in_rect.left, in_rect.top, in_rect.right, in_rect.bottom, hdc_bitmap, 0, 0, 2, 2, bf);
hBitmap_area = (HBITMAP)SelectObject(hdc_bitmap, hBitmap_area_default);
}
void Update()
{
POINT p = { 0, 0 };
HDC hdc_screen = GetDC(0);
UpdateLayeredWindow(hwnd, hdc_screen, &p, &canvas_size, hdc_mem, &p, 0, &bf, ULW_ALPHA);
}
The window style has this extras
WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOPMOST
And here is what I get.
So as you can see the blending that takes place DOES take into account per-pixel alpha, but it does a bad blending job.
Any ideas how to tune it?

I suspect the problem is in the source bitmap. This is the kind of effect you get when the RGB values aren't premultiplied with the alpha. But ignore that because there is a far simpler way of doing this.
Create a layered window with a solid background colour by setting hbrBackground in the WNDCLASSEX structure.
Make the window partially transparent by calling SetLayeredWindowAttributes.
Position the window where you want it.
That's it.
This answer has code that illustrates the technique for a slightly different purpose.

Related

GetWindowRect returns a size including "invisible" borders

I'm working on an app that positions windows on the screen in a grid style. When Running this on Windows 10, there is a huge gap between the windows. Further investigation shows that GetWindowRect is returning unexpected values, including an invisible border, but I can't get it to return the real values with the visible border.
1) This thread suggests this is by design and you can "fix" it by linking with winver=6. My environment does not allow this but I've tried changing the PE MajorOperatingSystemVersion and MajorSubsystemVersion to 6 with no affect
2) That same thread also suggests using DwmGetWindowAttribute with DWMWA_EXTENDED_FRAME_BOUNDS to get the real coordinates from DWM, which works, but means changing everywhere that gets the window coordinates. It also doesn't allow the value to be set, leaving us to reverse the process to be able to set the window size.
3) This question suggests it's lack of the DPI awareness in the process. Neither setting the DPI awareness flag in the manifest, or calling SetProcessDpiAwareness had any result.
4) On a whim, I've also tried adding the Windows Vista, 7, 8, 8.1 and 10 compatibility flags, and the Windows themes manifest with no change.
This window is moved to 0x0, 1280x1024, supposedly to fill the entire screen, and when querying the coordinates back, we get the same values.
The window however is actually 14 pixels narrower, to take into account the border on older versions of Windows.
How can I convince Windows to let me work with the real window coordinates?
Windows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing. The borders might look like this: 7,0,7,7 (left, top, right, bottom)
When you call SetWindowPos to put the window at this coordinates:
0, 0, 1280, 1024
The window will pick those exact coordinates, and GetWindowRect will return the same coordinates. But visually, the window appears to be here:
7, 0, 1273, 1017
You can fool the window and tell it to go here instead:
-7, 0, 1287, 1031
To do that, we get Windows 10 border thickness:
RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));
//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`
RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;
//border should be `7, 0, 7, 7`
Then offset the rectangle like so:
rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;
//new rect should be `-7, 0, 1287, 1031`
Unless there is a simpler solution!
How can I convince Windows to let me work with the real window coordinates?
You are already working with the real coordinates. Windows10 has simply chosen to hide the borders from your eyes. But nonetheless they are still there. Mousing past the edges of the window, your cursor will change to the resizing cursor, meaning that its still actually over the window.
If you want your eyes to match what Windows is telling you, you could try exposing those borders so that they are visible again, using the Aero Lite theme:
http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/
AdjustWindowRectEx (or on Windows 10 and later AdjustWindowRectExForDpi) might be of use. These functions will convert a client rectangle into a window size.
I'm guessing you don't want to overlap the borders though, so this probably isn't a full solution--but it may be part of the solution and may be useful to other people coming across this question.
Here's a quick snippet from my codebase where I've successfully used these to set the window size to get a desired client size, pardon the error handling macros:
DWORD window_style = (DWORD)GetWindowLong(global_context->window, GWL_STYLE);
CHECK_CODE(window_style);
CHECK(window_style != WS_OVERLAPPED); // Required by AdjustWindowRectEx
DWORD window_style_ex = (DWORD)GetWindowLong(global_context->window, GWL_EXSTYLE);
CHECK_CODE(window_style_ex);
// XXX: Use DPI aware version?
RECT requested_size = {};
requested_size.right = width;
requested_size.bottom = height;
AdjustWindowRectEx(
&requested_size,
window_style,
false, // XXX: Why always false here?
window_style_ex
);
UINT set_window_pos_flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
CHECK_CODE(SetWindowPos(
global_context->window,
nullptr,
0,
0,
requested_size.right - requested_size.left,
requested_size.bottom - requested_size.top,
set_window_pos_flags
));
There are still two ambiguities in the above use case:
My window does have a menu, but I have to pass in false for the menu param or I get the wrong size out. I'll update this answer with an explanation if I figure out why this is!
I haven't yet read about how Windows handles DPI awareness so I'm not sure when you want to use that function vs the non DPI aware one
You can respond to the WM_NCCALCSIZE message, modify WndProc's default behaviour to remove the invisible border.
As this document and this document explain, when wParam > 0, On request wParam.Rgrc[0] contains the new coordinates of the window and when the procedure returns, Response wParam.Rgrc[0] contains the coordinates of the new client rectangle.
The golang code sample:
case win.WM_NCCALCSIZE:
log.Println("----------------- WM_NCCALCSIZE:", wParam, lParam)
if wParam > 0 {
params := (*win.NCCALCSIZE_PARAMS)(unsafe.Pointer(lParam))
params.Rgrc[0].Top = params.Rgrc[2].Top
params.Rgrc[0].Left = params.Rgrc[0].Left + 1
params.Rgrc[0].Bottom = params.Rgrc[0].Bottom - 1
params.Rgrc[0].Right = params.Rgrc[0].Right - 1
return 0x0300
}

How to create an imagelist from a PNG?

I've seen here that you can create an image list with transparency. It works... sort of.
I used this to create an image list for a list control. The results were a little disappointing:
The one on the left is how it should look. The one on the right is how the list control is displaying it. It looks like it just tried to use the alpha as a mask and any blended area is attempted to be approximated by dithering. Is there a way of getting this better so that I get an actual alpha blended image?
Here is the source if that makes any difference:
class CDlg : public CDialog
{
DECLARE_DYNCREATE(CDlg)
public:
CDlg(CWnd* pParent = NULL); // standard constructor
virtual ~CDlg();
// Dialog Data
enum { IDD = IDD_BS_PRINT };
CGdiPlusBitmapResource m_pBitmap;
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
virtual BOOL OnInitDialog();
DECLARE_MESSAGE_MAP()
public:
CListCtrl m_printOptions;
};
BOOL CDlg::OnInitDialog()
{
__super::OnInitDialog();
m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
HBITMAP hBitmap;
m_pBitmap.m_pBitmap->GetHBITMAP(RGB(0, 0, 0), &hBitmap);
CImageList *pList = new CImageList;
CBitmap bm;
bm.Attach(hBitmap);
pList->Create(32, 32, ILC_COLOR32, 0, 4);
pList->Add(&bm, RGB(255, 0, 255));
m_printOptions.SetImageList(pList, LVSIL_NORMAL);
//...
return TRUE;
}
IMPLEMENT_DYNCREATE(CDlg, CDialog)
CBSPrintDlg::CBSPrintDlg(CWnd* pParent /*=NULL*/)
: CBCGPDialog(CBSPrintDlg::IDD, pParent)
{
}
CBSPrintDlg::~CBSPrintDlg()
{
}
void CBSPrintDlg::DoDataExchange(CDataExchange* pDX)
{
CBCGPDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PRINT_OPTIONS, m_printOptions);
}
For source of CGdiPlusBitmapResource implementation look here.
The original image with transparency is this:
#Barmak tried with a different image and it looks fine. I think that is because the transparency is near the edge and not located within the image. See here:
Edit ----------
First parameter in Gdiplus::GetHBITMAP should be the background color. Using RGB(0, 0, 0) as background color causes the semi-transparent pixels to match with black.
Using Gdiplus::Color(255,255,255,255) (white) it will improve the appearance (because ListView background is also white). But it's better to change the background to Gdiplus::Color(0,255,255,255) (transparent) to match any background.
{
CGdiPlusBitmapResource gdibmp;
if (gdibmp.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle()))
{
HBITMAP hBitmap;
gdibmp.m_pBitmap->GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);
ImageList_AddMasked(*pList, hBitmap, 0);
}
}
This assume images are all 32x32 pixels. If images are different sizes, they have to be resized before being added to image list.
{
CGdiPlusBitmapResource gdibmp;
if (gdibmp.Load(id, _T("PNG"), AfxGetResourceHandle()))
{
//resize image to 32x32 pixels
Gdiplus::Bitmap newBmp(32, 32, PixelFormat32bppPARGB);
double oldh = (double)gdibmp.m_pBitmap->GetHeight();
double oldw = (double)gdibmp.m_pBitmap->GetWidth();
double neww = 32;
double newh = 32;
double ratio = oldw / oldh;
if (oldw > oldh)
newh = neww / ratio;
else
neww = newh * ratio;
Gdiplus::Graphics graphics(&newBmp);
graphics.SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQualityBicubic);
graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
graphics.DrawImage(gdibmp.m_pBitmap, 0, 0, (int)neww, (int)newh);
//add `newBmp` to image list
HBITMAP hBitmap;
newBmp.GetHBITMAP(Gdiplus::Color::Transparent, &hBitmap);
ImageList_AddMasked(m_ImageList, hBitmap, 0);
}
}
Using GdiPlus::GetHICON to get the icon handle... With CGdiPlusBitmapResource class, it should be possible to use the following:
HICON hicon;
m_pBitmap.Load(IDB_RIBBON_HOMELARGE, _T("PNG"), AfxGetResourceHandle());
m_pBitmap.m_pBitmap->GetHICON(&hicon);
pList->Add(hicon);
or using GetHBITMAP
Also make sure Visual Styles is enabled for improved appearance of ListView icons.
Test image with transparent background:
The PNG image contains pixels that are partially transparent (alpha < 255). That's a pretty common accident with a program like Photoshop, the most likely cause is overlaying the spyglass image on top of the document image and not merging the layers correctly.
As given, the image can only look good when it is displayed on top of the light-gray or white background. But that didn't happen, the background was black. Now making the anti-aliasing pixels around the spyglass painfully obvious, they turned various shades of dark-gray depending on their alpha value and no longer blend with the white background of the document image. A very typical mishap when you use GDI functions, it does not like alpha.
You can doctor it with GDI+, ensuring that the background color is correct. But that's a fair amount of work and it still leaves you with the trouble of guessing at the original background color correctly.
Just really rather best to go back to whatever painting tool you used and fix the problem there. Quickest fix ought to be re-saving it as a 24bpp BMP image file, ymmv.

Any way to make GtkProgressBar have a transparent background?

So far I've not been able to find any way to make the background of a GtkProgressBar transparent. I've tried setting a transparent image as the background, but the alpha channel is ignored. Can this be done?
Gtk doesn't have mechanisms to handle transparency.
It would create significant performance penalty. Even changes in covered parts of the widgets would have to generate expose events.
I guess there should be a method to composite widgets in your own way. But I think it'd be a lot of work to implement in C/Gtk. In C++/gtkmm it wouldn't be that hard to implement custom widget which does all the rendering itself.
You can try it using gtkrc file.
Try to do something like this.
`style "tc-theme-ProgressBar"
{
xthickness = 1
ythickness = 1
engine "pixmap"
{
image
{
function = BOX
orientation = HORIZONTAL
file = "./buttons/TransparentImage.png"
border = { 0, 0, 0, 0} # = {Left, Right, Top, Bottom}
stretch = TRUE #This stretches the image
}
}
}
class "GtkProgressBar" style "tc-theme-ProgressBar"
'

If window spans multiple monitors, I can't draw to it

If I have a window that spans both monitors on a multimonitor system, I can't seem to erase (paint black) the entire window. Instead, only the primary window is drawn black. The secondary remains the original white color. Has anyone seen this behavior?
wxwidgets:
wxClientDC dc(this);
Erase(dc);
void SpriteWindowFrame::Erase(wxDC& dc)
{
dc.SetBackground(*wxBLACK_BRUSH);
dc.SetBrush(*wxBLACK_BRUSH);
dc.Clear();
//wxLogDebug("Erase called. Rect is %i, %i w:%i, h:%i", GetPosition().x, GetPosition().y, GetSize().GetWidth(), GetSize().GetHeight());
}
Inside dc.Clear() function, there is this code
wxwidgets:
void wxDC::Clear()
{
WXMICROWIN_CHECK_HDC
RECT rect;
if ( m_canvas )
{
GetClientRect((HWND) m_canvas->GetHWND(), &rect);
}
else
{
// No, I think we should simply ignore this if printing on e.g.
// a printer DC.
// wxCHECK_RET( m_selectedBitmap.Ok(), wxT("this DC can't be cleared") );
if (!m_selectedBitmap.Ok())
return;
rect.left = -m_deviceOriginX; rect.top = -m_deviceOriginY;
rect.right = m_selectedBitmap.GetWidth()-m_deviceOriginX;
rect.bottom = m_selectedBitmap.GetHeight()-m_deviceOriginY;
}
#ifndef __WXWINCE__
(void) ::SetMapMode(GetHdc(), MM_TEXT);
#endif
DWORD colour = ::GetBkColor(GetHdc());
HBRUSH brush = ::CreateSolidBrush(colour);
::FillRect(GetHdc(), &rect, brush);
::DeleteObject(brush);
#ifndef __WXWINCE__
int width = DeviceToLogicalXRel(VIEWPORT_EXTENT)*m_signX,
height = DeviceToLogicalYRel(VIEWPORT_EXTENT)*m_signY;
::SetMapMode(GetHdc(), MM_ANISOTROPIC);
::SetViewportExtEx(GetHdc(), VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
::SetWindowExtEx(GetHdc(), width, height, NULL);
::SetViewportOrgEx(GetHdc(), (int)m_deviceOriginX, (int)m_deviceOriginY, NULL);
::SetWindowOrgEx(GetHdc(), (int)m_logicalOriginX, (int)m_logicalOriginY, NULL);
#endif
}
Using the debugger, I checked what GetClientRect returned and sure enough it returns a rectange with location 0 and width/height of the combined two monitors so it's right. Maybe fillrect function is not capable of drawing to two displays?
Can you trace into the constructor of the wxClientDC?
wxClientDC dc(this);
A lot depends on what type of DC wx has given you. The windows API to retrieve a window DC is hdc = GetDC(hwnd), and, on multimonitor systems, it retrieves a handle to a 'mirror driver' DC, thats meant to reflect calls to all the underlying display device DCs that the monitor spans.
The only possible reason I can think of for this behaviour is wx is somehow retrieving a display DC rather than a window DC.
I'm sure Chris is correct, that the "overlapping window" case is handled somewhere for you. But where?
Rendering with windows GDI and "display contexts" such as you mention is very primitive and prone to all sorts of problems. GDI is one of poorest interfaces ever seen, poor even for Microsoft. Since most "window" programs work OK on multiple monitors, think of animating things in a "window" - and how that "window" makes its way to the "display" is best left a mystery.
Maybe DC is fundamentally not multi-monitor capable. Look for anything that allows multiple DCs to be treated uniformly. Rending graphics onto a grid of paper sheets would be like a tiled "printer DC". A video wall would be a tiled "display DC" and you would be happy with a 2-monitor hack, i.e. "multimon dc" echoes to "owning" display and "another one" if a window spans both.
If you want to do "real" animation on windows, you will need to move to DirectX. It is also a lot to learn, but much more capable: scene graphs, textures, video, alpha channels, ...

ImageList Transparency on Listviews?

EDIT: I've offered a bounty, since I doubt I'll be getting any answers otherwise.
Lately I've been working with listviews and I've decided to add an icon for each item indicating whether it's input or output. The icons add fine, but they're not transparent:
As can be seen, the icons are clearly not transparent. I'm currently doing something like this load the icons:
hImageList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 2);
if (hImageList != NULL)
{
iIN = ImageList_AddIcon(hImageList, LoadIcon(hInstance, MAKEINTRESOURCE(101)));
iOUT = ImageList_AddIcon(hImageList, LoadIcon(hInstance, MAKEINTRESOURCE(102)));
}
I've tried messing with the flags for ImageList_Create & LoadIcon/LoadImage but have had no luck and to be honest I've run out of ideas.
Any help would be very appreciated.
First up, ImageList_ReplaceIcon copies the icon data when adding it to an image list. So the HICON needs to be released afterwards.
Next, imagelists are natively bitmaps, not icons. And the way you are creating your imagelist makes the conversion of icon to bitmap very ambiguous. ILC_COLOR32 implies the imagelist should be created as a 32bit dib section, which typically contain transparency information via an embedded alpha channel. ILC_MASK instead implies that the internal bitmaps are DDB bitmaps, with the transparency information stored as a 1bpp mask bitmap.
The quickest solution to your problem - take your two icons:
Merge them into a single bitmap resource thats 32 pels wide by 16 high. Fill the background with a mask color :- purple or something.
Create the bitmap using ILC_COLOR|ILC_MASK
Load the bitmap being sure NOT to use LR_TRANSPARENT.
Add the bitmap using ImageList_AddMasked passing in a COLORREF that represents the mask color.
OR, for a better visual effect...
export your PNG data as a 32x16 32bpp bitmap file containing pre-multiplied alpha channel data.
Create the imagelist using the ILC_COLOR32 value.
LoadImage() with LR_CREATEDIBSECTION to load the bitmap as a 32bpp dib section.
Add the image using ImageList_Add()
(the last option is kind of tricky as the number of tools that support writing out 32bit bmp files with properly pre multiplied alpha channels is rather low).
Edited to add the following code sample. Using a 4bpp bitmap created in the dev environment this works just great :-
HWND hwndCtl = CreateWindowEx(0,WC_LISTVIEW,TEXT("ListView1"),WS_CHILD|WS_VISIBLE|WS_HSCROLL|WS_VSCROLL,0,0,cx,cy,hWnd,(HMENU)101,hModule,NULL);
HBITMAP hbm = (HBITMAP)LoadImage(hModule,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,0);
COLORREF crMask=RGB(255,0,255);
HIMAGELIST himl = ImageList_Create(16,16,ILC_COLOR|ILC_MASK,2,0);
ImageList_AddMasked(himl,hbm,crMask);
ListView_SetImageList(hwndCtl,himl,LVSIL_NORMAL);
You want to make your icons have a background color that isn't used anywhere else in the icon, like a really ugly purple, and then use LoadImage(..., LR_LOADTRANSPARENT); The flag says look at the first pixel at 0,0 and make everything that color transparent.
Your code looks fine to me, I always use LoadImage instead of LoadIcon but I suspect that doesn't matter. Have you checked that the icons do indeed have transparent areas and don't themselves have a solid background?
My LoadImage calls look like:
HICON hIcon = (HICON)LoadImage(hinstResources,MAKEINTRESOURCE(IDI_ICON),IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
Here... Create an ImageList, as suggested, make your icons into a Bitmap, 16 pixels high, by 16*n long, where n= the number of icons...
Set the background color to 255, 0, 255, like you have done.
Then, load it, and add it to the image list as I did here:
m_ImageList.Create(16, 16, ILC_COLOR16 | ILC_MASK, 7, 1);
CBitmap bm;
bm.LoadBitmap(IDB_SUPERTREEICONS);
m_ImageList.Add(&bm, RGB(255, 0, 255));
GetTreeCtrl().SetImageList(&m_ImageList, TVSIL_NORMAL);
Of course, this was written in MFC, but as you know, it's just a wrapper to Win32...
Outside of this, you are going to have to go to a custom draw control, in which you draw the icon over whatever background the icon happens to be sitting on. There isn't really any magic "transparent" color, that I know of, in any of these controls.
In the case of a custom draw, you need to use code like the following:
#define TRANSPARENT_COLOR (255,0,255)
UINT iBitmap = IDB_ICON_UP
CDC *dc = GetDC();
int x = 0, y = 0;
CDC *pDisplayMemDC = new CDC;
CDC *pMaskDC = new CDC;
CDC *pMemDC = new CDC;
CBitmap *pBitmap = new CBitmap;
CBitmap *pMaskBitmap = new CBitmap;
CBitmap *pMemBitmap = new CBitmap;
int cxLogo, cyLogo;
BITMAP bm;
pBitmap->LoadBitmap(iBitmap);
pDisplayMemDC->CreateCompatibleDC(dc);
CBitmap *pOldBitmap = (CBitmap *)pDisplayMemDC->SelectObject(pBitmap);
pBitmap->GetObject(sizeof(bm), &bm);
cxLogo = bm.bmWidth;
cyLogo = bm.bmHeight;
pMaskBitmap->CreateBitmap(cxLogo, cyLogo, 1, 1, NULL);
pMaskDC->CreateCompatibleDC(dc);
CBitmap *pOldMask = (CBitmap *)pMaskDC->SelectObject(pMaskBitmap);
COLORREF oldBkColor = pDisplayMemDC->SetBkColor(TRANSPARENT_COLOR);
pMaskDC->BitBlt(0, 0, cxLogo, cyLogo, pDisplayMemDC, 0, 0, SRCCOPY);
pMemBitmap->CreateCompatibleBitmap(dc, cxLogo, cyLogo);
pMemDC->CreateCompatibleDC(dc);
CBitmap *pOldMem = (CBitmap *)pMemDC->SelectObject(pMemBitmap);
pMemDC->BitBlt(0, 0, cxLogo, cyLogo, dc, x, y, SRCCOPY);
pMemDC->BitBlt(0, 0, cxLogo, cyLogo, pDisplayMemDC, 0, 0, SRCINVERT);
pMemDC->BitBlt(0, 0, cxLogo, cyLogo, pMaskDC, 0, 0, SRCAND);
pMemDC->BitBlt(0, 0, cxLogo, cyLogo, pDisplayMemDC, 0, 0, SRCINVERT);
dc->BitBlt(x, y, cxLogo, cyLogo, pMemDC, 0, 0, SRCCOPY);
delete pMemDC->SelectObject(pOldMem);
delete pMemDC;
delete pMaskDC->SelectObject(pOldMask);
delete pMaskDC;
delete pDisplayMemDC->SelectObject(pOldBitmap);
delete pDisplayMemDC;
This code decides where to draw the icon, and takes a snapshot of the background, creates a mask for the icon, and then draws it over the background, giving it a fully transparent background...
Hope that helps somewhat. If not, please explain in more detail what you are trying to make happen, and what you are seeing, or what you are NOT seeing...
I struggled with the same issue using an ImageList in a Tree View. I eventually got Chris Becke's second solution to work, creating an ImageList using the ILC_COLOR32 flag and using LoadImage() with the LR_CREATEDIBSECTION flag. This solution, and probably also the first solution, requires what is described below.
Transparency (and themes) are only supported with comctl32.dll version 6+, to use the correct version, the pre-processor directive on this page worked for me:
https://learn.microsoft.com/en-us/windows/win32/controls/cookbook-overview

Resources