fix(windows): pre-multiply alpha when setting menu item icons to fix black background#290
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes Windows 11 menu item icons rendering with a black background by generating a 32-bit DIB-backed bitmap (alpha-capable) and pre-multiplying pixel RGB values by alpha before assigning the bitmap to menu items.
Changes:
- Replaced
CreateCompatibleBitmapwithCreateDIBSectionfor menu item icon bitmaps (preserves alpha). - Added per-pixel alpha pre-multiplication on the DIB’s backing buffer prior to returning the
HBITMAP.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if bits != nil { | ||
| pixels := (*[1 << 30]byte)(bits)[:cx*cy*4 : cx*cy*4] | ||
| // Pre-multiply alpha channel before creating HBITMAP |
There was a problem hiding this comment.
cx/cy are uintptr (returned from Proc.Call), but they’re used directly in the slice expression [:cx*cy*4]. Slice bounds must be int, so this won’t compile. Convert cx/cy to int (after sanity/overflow checks) and use those int values when computing the pixel buffer length and when passing sizes around in Go expressions.
| @@ -761,6 +790,18 @@ func (t *winTray) iconToBitmap(hIcon windows.Handle) (windows.Handle, error) { | |||
| if res == 0 { | |||
| return 0, err | |||
| } | |||
There was a problem hiding this comment.
If DrawIconEx fails, the function returns without cleaning up hMemBmp, which leaks a GDI bitmap handle on the error path. Consider adding a DeleteObject proc and deferring cleanup after CreateDIBSection succeeds, then canceling the defer when returning the bitmap successfully.
|
|
||
| if bits != nil { | ||
| pixels := (*[1 << 30]byte)(bits)[:cx*cy*4 : cx*cy*4] | ||
| // Pre-multiply alpha channel before creating HBITMAP |
There was a problem hiding this comment.
The comment says “Pre-multiply alpha channel before creating HBITMAP”, but the HBITMAP (hMemBmp) has already been created at this point. Rewording to indicate this is done before returning/using the bitmap would avoid confusion for future maintenance.
| // Pre-multiply alpha channel before creating HBITMAP | |
| // Pre-multiply alpha channel in-place before returning/using the HBITMAP |
Fixes #267
Problem
Menu item icons render with a black background on Windows 11 when the
icon has transparent pixels. The tray icon itself renders correctly,
and disabled menu items also render correctly — only enabled menu items
are affected.
The root cause is that Windows GDI expects pre-multiplied alpha for
HBITMAP objects used in menus. The previous code used
CreateCompatibleBitmap which doesn't handle alpha, causing transparent
pixels to render as black.
Fix
Replaced CreateCompatibleBitmap with CreateDIBSection and added
alpha pre-multiplication for each pixel before the bitmap is passed
to SetMenuItemInfo:
This matches the format Windows expects for alpha-blended menu bitmaps.
Tested on
Windows 11 23H2