Opened 9 years ago

Closed 3 months ago

Last modified 3 months ago

#2395 closed defect (fixed)

Alpha incorrectly (not?) premultiplied in wxBitmap::CopyFromIcon

Reported by: ghazel Owned by:
Priority: normal Milestone:
Component: wxMSW Version: dev-latest
Keywords: wxStaticBitmap wxBitmap alpha transparency Cc: ghazel
Blocked By: Blocking:
Patch: yes

Description

When an icon with an alpha mask is loaded into a
wxBitmap via wxBitmap::CopyFromIcon, the alpha
channel is lost. Attached is an icon with an alpha mask
for demonstration, you should see a checkmark if alpha
is working.
The following pseudo-code demonstrates the problem:

icon = wxIcon("alpha_icon.ico");

incorrect_static_bitmap = new wxStaticBitmap();
incorrect_bitmap = new wxBitmap();
incorrect_bitmap->CopyFromIcon(icon);
incorrect_static_bitmap->SetBitmap(incorrect_bitmap);

correct_static_bitmap = new wxStaticBitmap();
correct_static_bitmap->SetIcon(icon);

(also, wxStaticBitmap::SetIcon is not documented)

Attachments (6)

alpha_icon.ico download (4.2 KB) - added by ghazel 9 years ago.
test icon with alpha layer
icon_vs_bitmap.png download (9.5 KB) - added by vadz 13 months ago.
Screenshot showing the difference
test-case.patch download (880 bytes) - added by awi 3 months ago.
Another sample to reproduce the problem.
test_alpha.png download (427 bytes) - added by awi 3 months ago.
Another image for testing purposes.
icon_cross.png download (510 bytes) - added by awi 3 months ago.
Yet another image for testing purposes.
temp-not-premulti-bitmap.patch download (2.0 KB) - added by awi 3 months ago.
Handling premultiplied bitmaps with transparency.

Download all attachments as: .zip

Change History (14)

Changed 9 years ago by ghazel

test icon with alpha layer

comment:1 Changed 9 years ago by ghazel

I found this, but I can't find a method that lets you specify
PixelFormat.Format32bppArgb.


Solution for DrawImage XP alpha icon bitmap drawing issues.

Author: dr21702
I'm posting this to help anyone else out there with this issue
that
has cost me a days work to research and figure out.

This will address all issues with alpha loss on Windows XP
style icons
(32bppARGP format) with regard to Bitmap.FromHicon and
Graphics.DrawImage.

There appears to be a bug in GDI+ with regard to getting the
bitmap
image of any icon which results in an image being use that is
not the
correct pixel format. This seems to occur with any GDI+
function that
internally has to a convert an image to a bitmap. The solution
below
addresses the issue by using GetIconInfo, creating a bitmap
which will
contain the correct bits but wrong format, then copy the
bitmap bits
to a new bitmap with the correctly format.

The resulting bitmap will work with DrawImage to the screen
or to a
Bitmap Graphics object. I haven't tested this with ImageList,
it is
likely that this will solve problems with that as well since
adding an
icon to an image list requires the flawed internal conversion to
a
bitmap.

Below is the C# code that can be added to a class for a
simple static
method to return a good bitmap from an icon. I only performs
the
special steps if the icon is 32 bit, otherwise it using
Bitmap.HIcon
which appears to work for other format.

public struct ICONINFO
{
public int fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}

public struct BITMAP
{
public int bmType;
public int bmWidth;
public int bmHeight;
public int bmWidthBytes;
public short bmPlanes;
public short bmBitsPixel;
public int bmBits;
}

[DllImport("gdi32")]
public static extern int GetObject(IntPtr hObject, int nCount,
out
BITMAP bitmap);

[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr DeleteObject(IntPtr hObject);

public static Bitmap AlphaBitmapFromIcon(Icon icon)
{
(Overcome Flaw #1: GDI+ Bitmap.FromHICON mangles the
alpha so the
..NET Bitmap.FromHicon will not work)

Call Win32 to get icon info with contains the icon bitmap,
return
null if invalid

ICONINFO iconInfo;
if (GetIconInfo(icon.Handle, out iconInfo)==0)
return null;

Get BITMAP struct from icon bitmap

BITMAP bitmapData;
GetObject(iconInfo.hbmColor, Marshal.SizeOf(typeof
(Win32.BITMAP)),
out bitmapData);

If not a 32bpp then ok to use Bitmap.FromHicon

if (bitmapData.bmBitsPixel!=32)
{
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
return Bitmap.FromHicon(icon.Handle);
}

Create .NET wrapped bitmap from ICONINFO bitmap

Bitmap wrapBitmap=Bitmap.FromHbitmap
(iconInfo.hbmColor);

(Overcome Flaw #2: Bitmap.FromHbitmap creates a bitmap
with the
correct bits but wrong pixel format)

Copy bit form flawed bitmap to new bitmap with correct
format

BitmapData bmData=wrapBitmap.LockBits(new Rectangle
(0,0,
wrapBitmap.Width, wrapBitmap.Height),
ImageLockMode.ReadOnly, wrapBitmap.PixelFormat);
Bitmap dstBitmap=new Bitmap(bmData.Width,
bmData.Height,
bmData.Stride, PixelFormat.Format32bppArgb,
bmData.Scan0);
wrapBitmap.UnlockBits(bmData);

Caller must dispose of bitmaps returned in ICONINFO from
GetIconInfo call

DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);

Return corrected bitmap

return dstBitmap;

}

comment:2 Changed 13 months ago by oneeyeman

Using this code:

    icon = wxIcon( "alpha_icon.ico", wxICON_DEFAULT_TYPE );
    bitmap = new wxBitmap();
    bitmap->CopyFromIcon( icon );
    m_statbmp = new wxStaticBitmap(this, wxID_ANY, *bitmap );

in StatBmpWidgetsPage::RecreateWidget() produces empty static bitmap.

So it would be nice to have a code reproducing the problem in addition to the icon...

Changed 13 months ago by vadz

Screenshot showing the difference

comment:3 Changed 13 months ago by vadz

  • Keywords alpha added
  • Status changed from new to confirmed
  • Summary changed from wxIcon alpha channel lost with wxBitmap::CopyFromIcon to Alpha incorrectly (not?) premultiplied in wxBitmap::CopyFromIcon

The bug is still easily reproducible, it's enough to do this:

  • samples/minimal/minimal.cpp

    diff --git a/samples/minimal/minimal.cpp b/samples/minimal/minimal.cpp
    index a78e462..1251750 100644
    a b bool MyApp::OnInit() 
    172172    CreateStatusBar(2); 
    173173    SetStatusText("Welcome to wxWidgets!"); 
    174174#endif // wxUSE_STATUSBAR 
     175 
     176    wxIcon icon("alpha_icon.ico", wxBITMAP_TYPE_ICO); 
     177 
     178    new wxStaticBitmap(this, wxID_ANY, wxBitmap(icon), wxPoint(5, 5)); 
     179    new wxStaticBitmap(this, wxID_ANY, icon, wxPoint(5, 50)); 
    175180} 
    176181 
    177182 

The alpha is not completely lost any more but something is still wrong as the bitmaps appear differently, see the screenshot:

Screenshot showing the difference

This is almost certainly because alpha is (not) premultiplied somewhere but I don't have the time to debug it further now.

comment:4 Changed 12 months ago by oneeyeman

Isn't it the same as 2474?

comment:5 Changed 12 months ago by vadz

It's not the same as #2474 because we don't support drawing with transparency in wxDC but we do support using bitmaps with alpha channel in wxStaticBitmap.

Changed 3 months ago by awi

Another sample to reproduce the problem.

Changed 3 months ago by awi

Another image for testing purposes.

Changed 3 months ago by awi

Yet another image for testing purposes.

Changed 3 months ago by awi

Handling premultiplied bitmaps with transparency.

comment:6 Changed 3 months ago by awi

  • Keywords wxStaticBitmap wxBitmap transparency added
  • Patch set
  • Version set to dev-latest

The reason for the problem is that wxBitmap stores alpha value in pre-multiplied format and wxStaticBitmap control uses STM_SETIMAGE message to draw the bitmaps/icons. The handler of this message apparently
does pre-multiplication internally (like e.g. ImageList does) and produces artifacts on output for already premultiplied bitmaps with transparency.

One option to fix the issue is to temporary disable premultiplication for bitmaps with transparency before they are passed to the system via STM_SETIMAGE message (e.g. by creating temporary bitmap with not-premultiplied alpha channel).

Second test case with additional images with alpha channel attached.
Patch to fix the issue also attached.

comment:7 Changed 3 months ago by VZ

  • Resolution set to fixed
  • Status changed from confirmed to closed

(In [75727]) Fix display of bitmaps with alpha in wxStaticBitmap under MSW.

Avoid double alpha pre-multiplication by converting the bitmap into a non
pre-multiplied format before passing it to STM_SETIMAGE.

Closes #2395.

comment:8 Changed 3 months ago by VZ

(In [75735]) Fix display of bitmaps with alpha in wxStaticBitmap under MSW.

Avoid double alpha pre-multiplication by converting the bitmap into a non
pre-multiplied format before passing it to STM_SETIMAGE.

Closes #2395.

Note: See TracTickets for help on using tickets.