Results 1 to 15 of 15

Thread: hIcon to hBitmap

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,653

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

  2. #2
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,121

    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>

  3. #3

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,653

    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.

  4. #4
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,121

    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>

  5. #5
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,121

    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>

  6. #6

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,653

    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.

  7. #7
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,121

    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>

  8. #8

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,653

    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.

  9. #9
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: hIcon to hBitmap

    Quote Originally Posted by fafalone View Post
    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?
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  10. #10

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,653

    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.

  11. #11
    Hyperactive Member
    Join Date
    Sep 2014
    Posts
    373

    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.

  12. #12
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    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?

  13. #13
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,121

    Re: [RESOLVED] hIcon to hBitmap

    Quote Originally Posted by dilettante View Post
    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>

  14. #14

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    5,653

    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.

  15. #15
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    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.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width