It can't be a rendered bitmap since you specified PictureBox or Image control.
Sounds like what you really have is a StdPicture object but you haven't told us explicitly what it wraps. You say "icon" but do you mean "icon" or might it be something else?
It might be a PNG icon image.
Recoloring may increase the color depth.
Even ignoring those two issues it might take nearly twice the API calls to do this for an icon rather than a bitmap. A normal icon contains either two bitmaps or a PNG.
May not be ideal, might have handle leaks I didn't track down and lacks thorough error checking just like the bitmap version. But going over it for bugs and adding more checking might be a good way to learn.
Changes:
Found hIcon leak, fixed. Also optimized things, reducing some code (no longer creating another copy of the mask bitmap, re-using it).
Last edited by dilettante; Mar 8th, 2021 at 05:37 PM.
Reason: New attachment.
GDI objects are referenced by "handles." These almost always need to be freed up explicitly when no longer needed. Otherwise memory gets eaten up over time or you might even exceed the limit and crash the system.
GDI objects support only one handle per object. Handles to GDI objects are private to a process. That is, only the process that created the GDI object can use the object handle.
There is a theoretical limit of 65,536 GDI handles per session. However, the maximum number of GDI handles that can be opened per session is usually lower, since it is affected by available memory.
For example creating a DC creates a solid color brush but GDI caches solid color brushes and reuses them a needed. It looks like you also get cached 1x1 pixel bitmaps when you create DCs. You can't clean them up.
This all gets far simpler if you didn't use icons, and even simpler yet if you didn't use Image controls.
For example, using a PictureBox or UserControl instead, you can easily use PaintPicture, Render, etc. to draw an icon StdPicture there. Once there it is simpler to recolor the resulting image just using VB statements (no API calls). That can be slow, but then even using API calls to recolor is a lot simpler. Image controls do not support VB drawing/painting operations.
Think of Windows as a large swimming pool. You have wandered under the rope that marks the shallow end.
Somebody may come along and tighten up the code I offered, add more error checks (X and Y bounds checks, API call return code processing, etc.), squash silly bugs, and so on.
Also, I'm doing the recoloring by filling a new color bitmap with the new color and then TransparentBlt()-ing the old color bitmap over it using the old color as the transparent color.
There are other possibilities too, for example: fetching with GetDIBits(), then looping over the pixel bits replacing the color one pixel at a time, then creating the new color bitmap using CreateDIBSection(), then copying the recolored bits into that bitmap. That gets you a DIB though, and I'm not sure that CreateIconIndirect() will accept a DIB or if it does whether the mask bitmap must be a DIB too.
There is also GDI+ using GdipSetImageAttributesRemapTable(), etc. That's another alternative using quite different code. I suspect the GDI approach I used above might be faster though.
That gets you a DIB though, and I'm not sure that CreateIconIndirect() will accept a DIB or if it does whether the mask bitmap must be a DIB too.
It does accept 32-bit premultiplied alpha DIBs and the mask color has to be a dummy 1-bit monochrome completely transparent one (for compatibility reasons I guess) otherwise CreateIconIndirect fails.
Code:
With uInfo
.fIcon = 1
.hbmColor = hDib
.hbmMask = CreateBitmap(sngTargetWidth, sngTargetHeight, 1, 1, ByVal 0)
End With
hIcon = CreateIconIndirect(uInfo)
With uDesc
.lSize = Len(uDesc)
.lType = vbPicTypeIcon
.hBmp = hIcon
End With
'--- IID_IPicture
aGUID(0) = &H7BF80980
aGUID(1) = &H101ABF32
aGUID(2) = &HAA00BB8B
aGUID(3) = &HAB0C3000
If OleCreatePictureIndirect(uDesc, aGUID(0), 1, pvLoadPicture) <> 0 Then
GoTo QH
End If
Good to know, but I don't think an Image control can handle an XP format icon very well. The transparent areas turn black when I try it, but I wasn't using a CC6 manifest either so maybe that's the issue.
Good to know, but I don't think an Image control can handle an XP format icon very well. The transparent areas turn black when I try it, but I wasn't using a CC6 manifest either so maybe that's the issue.
It's the StdPicture's Render method *not* using DrawIconEx for no apparent reason and it tries to interpret ICONINFO's bitmap and mask on it's own.
I understand the reason, but like it or not it's there. So I doubt anyone stuffing icons into an Image control is going to be dealing with 32-bpp XP icon images.
I guess if they're willing to use a PictureBox or a custom UserControl to display those properly then my code above is too simple.
Yes, it's an unfortunate shortcoming of the Render method.
That's the only reason I cobbled up a single-file poor man's AlphaBlendImage control -- load and display 32-bit premultiplied alpha DIBs stuffed innside an icon in an StdPicture.