Fix adding wxBitmap with mask to wxImageList not supporting masks (wxMSW)

If wxBitmap with mask is added to wxImageList that doesn't support masks
we need to convert a bitmap mask to alpha channel values prior to adding
bitmap to the native list to preserve bitmap transparency.

Closes #19036.
This commit is contained in:
Artur Wieczorek
2021-01-12 17:41:55 +01:00
parent fd5df49920
commit 8f08233a13
2 changed files with 106 additions and 25 deletions

View File

@@ -194,6 +194,7 @@ public:
protected: protected:
WXHIMAGELIST m_hImageList; WXHIMAGELIST m_hImageList;
wxSize m_size; wxSize m_size;
bool m_useMask;
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxImageList); wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxImageList);
}; };

View File

@@ -65,8 +65,9 @@ static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
wxImageList::wxImageList() wxImageList::wxImageList()
: m_hImageList(NULL)
, m_useMask(false)
{ {
m_hImageList = 0;
} }
// Creates an image list // Creates an image list
@@ -94,6 +95,7 @@ bool wxImageList::Create(int width, int height, bool mask, int initial)
wxLogLastError(wxT("ImageList_Create()")); wxLogLastError(wxT("ImageList_Create()"));
} }
m_useMask = (flags & ILC_MASK) != 0;
return m_hImageList != 0; return m_hImageList != 0;
} }
@@ -135,8 +137,7 @@ bool wxImageList::GetSize(int WXUNUSED(index), int &width, int &height) const
// 'bitmap' and 'mask'. // 'bitmap' and 'mask'.
int wxImageList::Add(const wxBitmap& bitmap, const wxBitmap& mask) int wxImageList::Add(const wxBitmap& bitmap, const wxBitmap& mask)
{ {
HBITMAP hbmp; HBITMAP hbmp = NULL;
bool useMask;
#if wxUSE_WXDIB && wxUSE_IMAGE #if wxUSE_WXDIB && wxUSE_IMAGE
// wxBitmap normally stores alpha in pre-multiplied format but // wxBitmap normally stores alpha in pre-multiplied format but
@@ -147,34 +148,73 @@ int wxImageList::Add(const wxBitmap& bitmap, const wxBitmap& mask)
AutoHBITMAP hbmpRelease; AutoHBITMAP hbmpRelease;
if ( bitmap.HasAlpha() ) if ( bitmap.HasAlpha() )
{ {
wxImage img = bitmap.ConvertToImage(); wxImage img;
if ( m_useMask )
// For comctl32.dll < 6 remove alpha channel from image
// to prevent possible interferences with the mask.
if ( wxApp::GetComCtl32Version() < 600 )
{ {
// Remove alpha channel from image to prevent
// possible interferences with the mask.
// The bitmap isn't drawn correctly if we use both.
img = bitmap.ConvertToImage();
img.ClearAlpha(); img.ClearAlpha();
useMask = true;
} }
else else
{ {
useMask = false; wxBitmap bmpWithoutMask = bitmap;
if ( mask.IsOk() || bmpWithoutMask.GetMask() )
{
// Blend mask with alpha channel.
if ( mask.IsOk() )
{
bmpWithoutMask.SetMask(new wxMask(mask));
}
bmpWithoutMask.MSWBlendMaskWithAlpha();
}
img = bmpWithoutMask.ConvertToImage();
} }
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach(); hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach();
hbmpRelease.Init(hbmp); hbmpRelease.Init(hbmp);
} }
else else
#endif // wxUSE_WXDIB && wxUSE_IMAGE
{ {
hbmp = GetHbitmapOf(bitmap); if ( m_useMask )
useMask = true; {
hbmp = GetHbitmapOf(bitmap);
}
else
{
if ( mask.IsOk() || bitmap.GetMask() )
{
// Convert mask to alpha channel.
wxBitmap bmpWithMask;
if ( mask.IsOk() )
{
bmpWithMask = bitmap;
bmpWithMask.SetMask(new wxMask(mask));
}
else
{
bmpWithMask = bitmap;
}
wxImage img = bmpWithMask.ConvertToImage();
img.InitAlpha();
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach();
hbmpRelease.Init(hbmp);
}
else
{
hbmp = GetHbitmapOf(bitmap);
}
}
} }
#else
hbmp = GetHbitmapOf(bitmap);
#endif // wxUSE_WXDIB && wxUSE_IMAGE
// Use mask only if we don't have alpha, the bitmap isn't drawn correctly // Use mask only if we don't have alpha, the bitmap isn't drawn correctly
// if we use both. // if we use both.
AutoHBITMAP hbmpMask; AutoHBITMAP hbmpMask;
if ( useMask ) if ( m_useMask )
hbmpMask.Init(GetMaskForImage(bitmap, mask)); hbmpMask.Init(GetMaskForImage(bitmap, mask));
int index = ImageList_Add(GetHImageList(), hbmp, hbmpMask); int index = ImageList_Add(GetHImageList(), hbmp, hbmpMask);
@@ -255,38 +295,78 @@ bool wxImageList::Replace(int index,
const wxBitmap& bitmap, const wxBitmap& bitmap,
const wxBitmap& mask) const wxBitmap& mask)
{ {
HBITMAP hbmp; HBITMAP hbmp = NULL;
bool useMask;
#if wxUSE_WXDIB && wxUSE_IMAGE #if wxUSE_WXDIB && wxUSE_IMAGE
// See the comment in Add() above. // See the comment in Add() above.
AutoHBITMAP hbmpRelease; AutoHBITMAP hbmpRelease;
if ( bitmap.HasAlpha() ) if ( bitmap.HasAlpha() )
{ {
wxImage img = bitmap.ConvertToImage(); wxImage img;
if ( m_useMask )
if ( wxApp::GetComCtl32Version() < 600 )
{ {
// Remove alpha channel from image to prevent
// possible interferences with the mask.
// The bitmap isn't drawn correctly if we use both.
img = bitmap.ConvertToImage();
img.ClearAlpha(); img.ClearAlpha();
useMask = true;
} }
else else
{ {
useMask = false; wxBitmap bmpWithoutMask = bitmap;
if ( mask.IsOk() || bmpWithoutMask.GetMask() )
{
// Blend mask with alpha channel.
if ( mask.IsOk() )
{
bmpWithoutMask.SetMask(new wxMask(mask));
}
bmpWithoutMask.MSWBlendMaskWithAlpha();
}
img = bmpWithoutMask.ConvertToImage();
} }
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach(); hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach();
hbmpRelease.Init(hbmp); hbmpRelease.Init(hbmp);
} }
else else
#endif // wxUSE_WXDIB && wxUSE_IMAGE
{ {
hbmp = GetHbitmapOf(bitmap); if ( m_useMask )
useMask = true; {
hbmp = GetHbitmapOf(bitmap);
}
else
{
if ( mask.IsOk() || bitmap.GetMask() )
{
// Convert mask to alpha channel.
wxBitmap bmpWithMask;
if ( mask.IsOk() )
{
bmpWithMask = bitmap;
bmpWithMask.SetMask(new wxMask(mask));
}
else
{
bmpWithMask = bitmap;
}
wxImage img = bmpWithMask.ConvertToImage();
img.InitAlpha();
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach();
hbmpRelease.Init(hbmp);
}
else
{
hbmp = GetHbitmapOf(bitmap);
}
}
} }
#else
hbmp = GetHbitmapOf(bitmap);
#endif // wxUSE_WXDIB && wxUSE_IMAGE
AutoHBITMAP hbmpMask; AutoHBITMAP hbmpMask;
if ( useMask ) if ( m_useMask )
hbmpMask.Init(GetMaskForImage(bitmap, mask)); hbmpMask.Init(GetMaskForImage(bitmap, mask));
if ( !ImageList_Replace(GetHImageList(), index, hbmp, hbmpMask) ) if ( !ImageList_Replace(GetHImageList(), index, hbmp, hbmpMask) )