3

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:

Example of icon not being 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.

Spooky
  • 2,966
  • 8
  • 27
  • 41
Irwin
  • 499
  • 1
  • 3
  • 12
  • You have your source available somewhere for us to mess with? I see some comments where people are testing your code.. I'd love to get a look at it, since I've done a lot of work in this area... – LarryF Mar 19 '09 at 23:06
  • Please see my answer (Answer 2) for http://stackoverflow.com/questions/6893351/transparent-images-in-imagelists-for-listviews/ thread. Olexiy – Olexiy Litvinenko Aug 29 '13 at 13:58

5 Answers5

13

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);
Chris Becke
  • 34,244
  • 12
  • 79
  • 148
  • I tried the first option earlier today and I had the same problem. After reading your post I tried the second way, and it made absolutely no difference. Please take a look at this: http://pastebin.com/f5617ac8a – Irwin Mar 13 '09 at 09:31
  • When you provide ILC\_MASK|ILC\_COLOR how are you loading the bitmap? You should probably omit the DIBSECTION flag, and also be aware that loading bitmaps onto DDB surfaces is lossy, so a COLORREF that describes the ideal RGB values in the file can not match the pels in the DDB. Use pure colors. – Chris Becke Mar 13 '09 at 12:07
  • ...You completely neglected to make any comment on the second option. Also, I did omit the LR_CREATEDIBSECTION flag for the first option. My mask color was pink (RGB(255, 0, 255)) so there wouldn't have been any matches. – Irwin Mar 13 '09 at 14:19
  • I can't, unfortunately, download your bmp file as im at work, and there is a websense filter. That said, start by making a simple 16 color bitmap. I can't comment on the second option not working as its a lot more complicated, and this a lot less obvious where it might be failing. – Chris Becke Mar 13 '09 at 14:30
  • Your 32bpp argb bitmap has a purple background. As your screenshot shows, the background is displaying white. Which implies, in both the mask, and 32bpp case, that the imagelist item IS being alpha'd correctly - against a white bk. The question is - how is the alternating grey fill being done? – Chris Becke Mar 13 '09 at 15:10
  • not sure whats up. Finally got you bmp. plugged it into the DDB / Masked version of the code and it worked first time. Tried again with the DIBSection / 32bpp alpha, and that looked even better. The only thing I dont know how to replicate is the grey backfill on your listview. – Chris Becke Mar 13 '09 at 21:06
  • The grey backfill for each other item is a simple aesthetic tweak applied through custom-draw and I'm sure it's not the cause of problems (I've commented it out and tested, no difference to icon drawing). Also, I think you're mistaken, the image isn't being alpha'd; try highlighting an item and see. – Irwin Mar 14 '09 at 06:35
  • Your source image does have a purple background. The icons in the screenshot above have white backgrounds and only the icons on the grey rows are displaying incorrectly. I put together a small app using your bmp and attached code: it seems to work flawlessly. – Chris Becke Mar 14 '09 at 14:39
  • You can create a pre multiplied alpha image by using gimp: http://kevin.c.krinke.ca/2012/04/24/creating-images-with-pre-multiplied-alpha-using-gimp/ – denim Jul 22 '14 at 14:57
0

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.

i_am_jorf
  • 53,608
  • 15
  • 131
  • 222
  • 1
    I've already tried this (with first pixel as RGB(255, 0, 255)) and quoting the documentation: LR_LOADTRANSPARENT does not load the image transparently. It creates an opaque image list that only appears transparent because all the background pixels have been changed to COLOR_WINDOW. – Irwin Mar 10 '09 at 23:05
  • Ah yes, that makes sense. Stupid Windows and transparent Icons. – i_am_jorf Mar 10 '09 at 23:06
0

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);
Murray
  • 680
  • 5
  • 8
  • As stated in my original post, I've already messed with LoadIcon & LoadImage. I also tried using only the LR_DEFAULTCOLOR flag alone and it made no difference. Oh and yes, my icons are transparent: http://img12.imageshack.us/img12/6117/reseditxpirc776.png – Irwin Mar 13 '09 at 07:12
0

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...

LarryF
  • 4,925
  • 4
  • 32
  • 40
0

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

vishayp
  • 70
  • 5