-
Mar 26th, 2015, 01:55 AM
#1
hIcon to hBitmap
Well I'm officially giving up and asking for help. Went to write new shell interfaces never used in VB, turns out just getting an icon to display for something was 1000x harder.
I need to extract an icon from a file, and obtain an hBitmap for it. Have searched countless examples, spent hours porting c/c++ ones to VB, and nothing works. I can either get the colored icon on a black background, or just a 1-bit black outline, i even tried avoiding true transparency with a COLOR_MENUBAR background but still, only black outline drew on top if it.
Here's the main ideas i was working with, but i have no idea if they're even close:
Code:
Call ExtractIconEx(sIcon, i, ByVal 0&, hIcon, 1)
Public Function HBITMAPfromHICON(hIcon As Long) As Long
Dim hScr As Long
Dim hDev As Long
Dim cx As Long, cy As Long
Dim hbmp As Long
Dim hbmpOld As Long
cx = GetSystemMetrics(SM_CXSMICON)
cy = GetSystemMetrics(SM_CYSMICON)
hScr = GetDC(0&)
hDev = CreateCompatibleDC(hScr)
hbmp = CreateCompatibleBitmap(hDev, cx, cy)
hbmpOld = SelectObject(hDev, hbmp)
Call DrawIconEx(hDev, 0, 0, hIcon, cx, cy, 0, 0, DI_NORMAL)
Call SelectObject(hDev, hbmpOld)
Call DeleteDC(hDev)
Call ReleaseDC(0&, hScr)
HBITMAPfromHICON = hbmp
End Function
Public Function GetIconHBMP(hIcon As Long) As Long
Dim hdc As Long
Dim hBackDC As Long
Dim hBitmap As Long
Dim hBackSV As Long
Dim clrback As Long
Dim cx As Long, cy As Long
clrback = GetSysColor(30)
cx = GetSystemMetrics(SM_CXSMICON)
cy = GetSystemMetrics(SM_CYSMICON)
hdc = GetDC(0)
hBackDC = CreateCompatibleDC(hdc)
hBitmap = CreateSolidBitmap(0, cx, cy, clrback)
hBackSV = SelectObject(hBackDC, hBitmap)
Call DrawIconEx(hBackDC, 0, 0, hIcon, cx, cy, 0, 0, 2)
'Call DestroyIcon(hIcon)
Call SelectObject(hBackDC, hBackSV)
Call ReleaseDC(0, hdc)
Call DeleteDC(hBackDC)
GetIconHBMP = hBitmap
End Function
Last edited by fafalone; Mar 27th, 2015 at 08:47 PM.
-
Mar 26th, 2015, 03:36 AM
#2
Re: hIcon to hBitmap
Try using `GetIconInfo` to obtain `ICONINFO` and further `GetObject` API on `ICONINFO`'s `hbmColor` member to populate `BITMAP` struct with type, size, etc. properties of the underlying bitmap.
cheers,
</wqw>
-
Mar 26th, 2015, 03:39 AM
#3
Re: hIcon to hBitmap
Yes the issue is being able to draw that onto an hDC and get an hBitmap with the transparency intact. Simply using .hbmColor will show the icon on top of a black square.
I need to create a transparent background (ideal, solid COLOR_MENUBAR has issues with versions and themes), draw .hbmColor onto it, then use .hbmpMask to determine and then switch pixels to transparency.
Last edited by fafalone; Mar 26th, 2015 at 03:42 AM.
-
Mar 26th, 2015, 04:19 AM
#4
Re: hIcon to hBitmap
How do you switch pixels to transparency?
The idea is first to draw .hbmColor to a mem dc as is, then to draw .hbmpMask *over* it with "on" pixels transparent and "off" pixels set to COLOR_MENUBAR or vbButtonFace or whatever background color you want transparent pixels to turn to.
For truly alpha transparent hBitmap you will probably need different approach but something along the lines of draw .hbmColor as is, then modify alpha based on .hbmpMask (probably by looping ARGB quads).
cheers,
</wqw>
-
Mar 26th, 2015, 04:23 AM
#5
Re: hIcon to hBitmap
FYI, you need to call `DeleteObject` on .hbmColor if present (btw, the presense of this bitmap is optional) and .hbmMask (this bitmap is always returned) or risk leaking GDI objects after calling `GetIconInfo`
cheers,
</wqw>
-
Mar 26th, 2015, 06:40 AM
#6
Re: hIcon to hBitmap
Well after a few more straight hours of trying different things... the solution turned out to be fairly simple... not sure why so many articles and forums posts were going about it in way more complex ways.
Code:
Public Function HIconFromHBitmapReallyThisTime(hIcon As Long) As Long
Dim hdc As Long
Dim hBackDC As Long
Dim hBitmap As Long
Dim hBackSV As Long
hdc = GetDC(0)
hBackDC = CreateCompatibleDC(hdc)
hBitmap = Create32BitHBITMAP(hBackDC, 16)
hBackSV = SelectObject(hBackDC, hBitmap)
DrawIconEx hBackDC, 0, 0, hIcon, 16, 16, 0, 0, DI_NORMAL
Call SelectObject(hBackDC, hBackSV)
Call ReleaseDC(0, hdc)
Call DeleteDC(hBackDC)
HIconFromHBitmapReallyThisTime = hBitmap
End Function
I think all of the other methods with CreateCompatibleBitmap had the flaw that that won't create a 32-bit bitmap.
Code:
Public Function Create32BitHBITMAP(hdc As Long, cxy As Long) As Long
Dim bmi As BITMAPINFO
Dim hdcUsed As Long
bmi.bmiHeader.biSize = Len(bmi.bmiHeader)
bmi.bmiHeader.biPlanes = 1
bmi.bmiHeader.biCompression = 0
bmi.bmiHeader.biWidth = cxy
bmi.bmiHeader.biHeight = cxy
bmi.bmiHeader.biBitCount = 32
Create32BitHBITMAP = CreateDIBSection(hdc, bmi, DIB_RGB_COLORS, ByVal 0&, 0, 0)
End Function
(I turned this into a code bank article since existing solutions were non-existent)
Last edited by fafalone; Mar 26th, 2015 at 07:12 AM.
-
Mar 26th, 2015, 07:08 AM
#7
Re: hIcon to hBitmap
I thought this was the first thing you tried, doh :-)) `DrawIconEx` accepts cx=cy=0 for "use actual icon size".
cheers,
</wqw>
-
Mar 26th, 2015, 07:14 AM
#8
Re: [RESOLVED] hIcon to hBitmap
It basically was the first thing I tried... the only real difference is using Create32bitHBITMAP instead of CreateCompatibleBitmap which causes the black/white only problem.
-
Mar 26th, 2015, 07:58 AM
#9
Re: hIcon to hBitmap
Originally Posted by fafalone
Well after a few more straight hours of trying different things... the solution turned out to be fairly simple... not sure why so many articles and forums posts were going about it in way more complex ways.
Your code assumes that DrawIconEx will draw into the bitmap's alpha channel correctly. On older systems that may not be the case and may explain more complex solutions for code created at that time?
-
Mar 26th, 2015, 09:41 AM
#10
Re: [RESOLVED] hIcon to hBitmap
Could be the case for some of them, although I saw quite a few complex ones for Vista... I'm not too familiar with DrawIconEx. But the project it's for is Vista+ anyway, and I no longer have access to XP... so I'll leave devising a method compatible with XP through 10 to greater minds.
-
Mar 26th, 2015, 11:55 AM
#11
Hyperactive Member
Re: [RESOLVED] hIcon to hBitmap
I can confirm that DrawIconEx works alright on Vista. I had used DrawIconEx to display 32-BPP frames of an ANI file, with the following code lines:
-- Create a 32-BPP DIB.
-- Draw a chessboard on it.
- Call LoadCursorFromFile to obtain hCursor, passing an ANI file containing 32-BPP frames.
-- Call DrawIconEx, passing on above DIB hDC, 0, 0, hCursor, ...... DI_NORMAL.
32-BPP frames were properly drawn and the above-said chessboard shown through.
-
Mar 26th, 2015, 09:56 PM
#12
Re: [RESOLVED] hIcon to hBitmap
Not being Captain GDI here, I have to wonder how many "modern" GDI code snippets floating around these days will fall over unless the display adapter currently has 32-bit color set. I see lots of hDC passing but little hDC creation going on.
Not that people run 16-bit, 15-bit, or even 8-bit color on a desktop anymore... but surely there are some Windows tablets and such floating around that do? Not to mention headless machines with no monitor, and what about code in a service or scheduled task that runs with no user logged on?
Or maybe I'm just paranoid and magical fairies inside GDI smooth everything over?
-
Mar 27th, 2015, 04:26 AM
#13
Re: [RESOLVED] hIcon to hBitmap
Originally Posted by dilettante
Not being Captain GDI here, I have to wonder how many "modern" GDI code snippets floating around these days will fall over unless the display adapter currently has 32-bit color set. I see lots of hDC passing but little hDC creation going on.
Not that people run 16-bit, 15-bit, or even 8-bit color on a desktop anymore... but surely there are some Windows tablets and such floating around that do? Not to mention headless machines with no monitor, and what about code in a service or scheduled task that runs with no user logged on?
Or maybe I'm just paranoid and magical fairies inside GDI smooth everything over?
They do for any variation of true-color -- 15, 16, 24, 32-bits are taken care at OS level. 8-bit color depth is really hard to come by these day (no option in display settings), the only practical way to get it is to RDP to a server and manually lower connection color depth.
Btw, Windows Server 2003 terminal services cannot handle anything but 8-bit colors for resolutions above 1600x1200 which is common these days.
cheers,
</wqw>
-
Mar 27th, 2015, 09:08 PM
#14
Re: hIcon to hBitmap
So gotta withdraw resolved status... I guess it was too good to be true. It's failing with certain types of icons... IrfanView I can understand because it looks like it's from the 90s, but Office2010 too?
I looked at the icons a bit and there's nothing pointing at an obvious difference... they all have 24-bit color (office had other depths also , but irfanview didn't)... but looking at them closely they may be lacking an alpha channel as there's no transparent sections visible. But why would lacking an alpha channel result in the pictured behavior: before mouseover, nothing there (shown with office), then after mouseover, it looks like the image is faded (with office it's completely white, i can't make out any red).
While it would be great to truly solve the problem, I'd settle for just being able to tell if there is an alpha channel- since GetIconInfo's .hbmColor gives me the correct drawing for these.
Edit: There already is such a function in clsMenuImage... but is there a simpler way, preferably without gdi+?
Code:
Public Function pvIsAlphaIcon(ByVal IconHandle As Long) As Boolean
Dim tARGB() As ARGB
Dim tRECT As RECT
Dim tICONINFO As ICONINFO
Dim tBitmapData As BitmapData
Dim lPixelFormat As Long
Dim lngX As Long
Dim lngY As Long
Dim sngWidth As Single
Dim sngHeight As Single
Dim lngArgbBmp As Long
Dim lngColorBmp As Long
Dim bolRet As Boolean
If GetIconInfo(IconHandle, tICONINFO) <> 0 Then
If tICONINFO.hBMColor <> 0 Then
If GdipCreateBitmapFromHBITMAP(tICONINFO.hBMColor, 0&, lngColorBmp) = 0 Then
If GdipGetImagePixelFormat(lngColorBmp, lPixelFormat) = 0 Then
If lPixelFormat <> PixelFormat32bppRGB Then
bolRet = False
Else
If GdipGetImageDimension(lngColorBmp, sngWidth, sngHeight) = 0 Then
With tRECT
.Right = CLng(sngWidth)
.Bottom = CLng(sngHeight)
End With
ReDim tARGB(tRECT.Right - 1&, tRECT.Bottom - 1&)
With tBitmapData
.Scan0 = VarPtr(tARGB(0&, 0&))
.Stride = 4& * tRECT.Right
End With
If GdipBitmapLockBits(lngColorBmp, tRECT, ImageLockModeRead Or ImageLockModeUserInputBuf, lPixelFormat, tBitmapData) = 0 Then
For lngY = 0 To tBitmapData.Height - 1
For lngX = 0 To tBitmapData.Width - 1
If tARGB(lngX, lngY).Alpha > 0 Then
If tARGB(lngX, lngY).Alpha < 255 Then
bolRet = True
Exit For
End If
End If
Next lngX
If bolRet Then Exit For
Next lngY
Call GdipDisposeImage(lngArgbBmp)
Call GdipBitmapUnlockBits(lngColorBmp, tBitmapData)
End If
End If
End If
End If
Call GdipDisposeImage(lngColorBmp)
End If
Call DeleteObject(tICONINFO.hBMColor)
End If
Call DeleteObject(tICONINFO.hBMMask)
Else
bolRet = False
End If
pvIsAlphaIcon = bolRet
End Function
Edit 2: Apparently if the icon only has 256 or less colors, both methods fail. The alpha-icon handler draws nothing at all, and the .hbmpColor method draws a black background again. Found this out when displaying WinRAR's icon.
Edit 3: So I found a solution that covers non-alpha 24bit, and non-alpha 256 or lower... an important question is, and forgive me if it's stupid I'm very new to working with color depths and bitmaps, can there be 256 or less color depth icons that DO have an alpha channel? Not sure what would happen with those... hopefully the existing alpha icon code would handle them right, but who knows.
It does use gdiplus, so if there's a non-gdiplus solution it would be preferred.
Code:
Public Function HBitmapFromHIconNoAlpha(hIcon As Long) As Long
Dim himg As Long
Dim hb As Long
GdipCreateBitmapFromHICON hIcon, himg
GdipCreateHBITMAPFromBitmap himg, hb, &HFF000000
GdipDisposeImage himg
HBitmapFromHIconNoAlpha = hb
End Function
And a final question.. the above code works on SOME 24bit icons with an alpha channel but not others. I can't seem to find a common difference among them either.
Last edited by fafalone; Mar 28th, 2015 at 12:40 AM.
-
Mar 28th, 2015, 10:05 AM
#15
Re: hIcon to hBitmap
Some points
1. I was under the impression that for the bitmap to work correctly on menus as part of the MENUITEMINFO structure, that the bitmap RGB values had to be premultiplied by the alpha channel. Maybe that's part of the issue you are seeing? The alpha channel is either rendered into by mistake, rendered incorrect values, or premultiplication is not there?
2. Test your DIB section after DrawIconEx renders to it. Look at the alpha channel. If it is zero, then the pixel would be interpreted as transparent & if 255 then opaque. Any value between is a percentage of transparency. If RGB values are premultiplied, then each RGB value was re-written as a percentage of its original value, i.e., original RGB value = (RGB value * 255) / Alpha
Hint: To rule out premultiplication on the DIB:
a. If all alpha values are zero, then the image is 100% transparent or the alpha channel is not used
b. If all alpha values are 255, there is no transparency in the image
c. If any R,G,B value is > alpha value then premultiplication is NOT applied
d. If all R,G,B values are <= alpha value then premultiplication is very likely applied
e. Won't be the case 99.9% of the time, but alpha channels can be used for other stuff & should not be used for rendering. This would have to be known in advance.
3. Any icon bit depth can contain transparency, only 32+ bit icons can contain alpha blending (exception may be PNG-encoded icons at lower bit depths, but as hIcon doesn't apply). And icon bit depth < 32 bits can contain what I call simple transparency, either a pixel is 100% transparent or 100% opaque. This is produced by bitwise AND of the icon mask
Edited: If you have specific examples of bitmaps that are having issues, zip them up & identify specifically how each of them are NOT rendering properly. We can take a look at the bitmap's data structure & there may be a pattern. If you can provide the .ico file for the bitmap, that could be helpful too
And for your overall question regarding GDI+... Can a bitmap be created from hIcon without GDI+? Yes it can, but it is not a short routine. And using GDI+ to load icons, you are taking advantage of patches to GDI+ on Vista and higher. GDI+ had lots of issues on XP with loading icons & still may have a couple today in specific scenarios?
Last edited by LaVolpe; Mar 28th, 2015 at 01:34 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|