Merge branch 'imaglist-mask'

Fixes for using mask and alpha in wxMSW wxImageList.

See #22101.
This commit is contained in:
Vadim Zeitlin 2022-02-08 20:52:20 +01:00
commit bd058106a3
2 changed files with 52 additions and 103 deletions

View File

@ -24,7 +24,7 @@ public:
* Public interface * Public interface
*/ */
wxImageList(); wxImageList() { Init(); }
// Creates an image list. // Creates an image list.
// Specify the width and height of the images in the list, // Specify the width and height of the images in the list,
@ -32,7 +32,7 @@ public:
// from icons), and the initial size of the list. // from icons), and the initial size of the list.
wxImageList(int width, int height, bool mask = true, int initialCount = 1) wxImageList(int width, int height, bool mask = true, int initialCount = 1)
{ {
m_hImageList = NULL; Init();
Create(width, height, mask, initialCount); Create(width, height, mask, initialCount);
} }
virtual ~wxImageList(); virtual ~wxImageList();
@ -198,8 +198,16 @@ public:
protected: protected:
WXHIMAGELIST m_hImageList; WXHIMAGELIST m_hImageList;
wxSize m_size; wxSize m_size;
private:
bool m_useMask; bool m_useMask;
void Init()
{
m_hImageList = NULL;
m_useMask = false;
}
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxImageList); wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxImageList);
}; };

View File

@ -36,6 +36,7 @@
#include "wx/imaglist.h" #include "wx/imaglist.h"
#include "wx/dc.h" #include "wx/dc.h"
#include "wx/scopedptr.h"
#include "wx/msw/dc.h" #include "wx/msw/dc.h"
#include "wx/msw/dib.h" #include "wx/msw/dib.h"
#include "wx/msw/private.h" #include "wx/msw/private.h"
@ -64,12 +65,6 @@ static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask);
// wxImageList creation/destruction // wxImageList creation/destruction
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
wxImageList::wxImageList()
: m_hImageList(NULL)
, m_useMask(false)
{
}
// Creates an image list // Creates an image list
bool wxImageList::Create(int width, int height, bool mask, int initial) bool wxImageList::Create(int width, int height, bool mask, int initial)
{ {
@ -88,7 +83,10 @@ bool wxImageList::Create(int width, int height, bool mask, int initial)
// For comctl32.dll < 6 always use masks as it doesn't support alpha. // For comctl32.dll < 6 always use masks as it doesn't support alpha.
if ( mask || wxApp::GetComCtl32Version() < 600 ) if ( mask || wxApp::GetComCtl32Version() < 600 )
{
m_useMask = true;
flags |= ILC_MASK; flags |= ILC_MASK;
}
// Grow by 1, I guess this is reasonable behaviour most of the time // Grow by 1, I guess this is reasonable behaviour most of the time
m_hImageList = (WXHIMAGELIST) ImageList_Create(width, height, flags, m_hImageList = (WXHIMAGELIST) ImageList_Create(width, height, flags,
@ -98,7 +96,6 @@ 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;
} }
@ -146,74 +143,48 @@ void GetImageListBitmaps(const wxBitmap& bitmap, const wxBitmap& mask, bool useM
AutoHBITMAP& hbmpRelease, AutoHBITMAP& hbmpMask, HBITMAP& hbmp) AutoHBITMAP& hbmpRelease, AutoHBITMAP& hbmpMask, HBITMAP& hbmp)
{ {
#if wxUSE_WXDIB && wxUSE_IMAGE #if wxUSE_WXDIB && wxUSE_IMAGE
// wxBitmap normally stores alpha in pre-multiplied format but // We can only use directly bitmaps without alpha and without mask unless
// ImageList_Draw() does pre-multiplication internally so we need to undo // the image list uses masks and need to modify bitmap in all the other
// the pre-multiplication here. Converting back and forth like this is, of // cases, so check if this is necessary.
// course, very inefficient but it's better than wrong appearance so we do if ( bitmap.HasAlpha() || (!useMask && (mask.IsOk() || bitmap.GetMask())) )
// this for now until a better way can be found.
if ( useMask )
{ {
if ( bitmap.HasAlpha() ) wxBitmap bmp(bitmap);
if ( mask.IsOk() || bmp.GetMask() )
{ {
// Remove alpha channel from image to prevent // Explicitly specified mask overrides the mask associated with the
// possible interferences with the mask. // bitmap, if any.
// The bitmap isn't drawn correctly if we use both. if ( mask.IsOk() )
wxImage img = bitmap.ConvertToImage(); bmp.SetMask(new wxMask(mask));
img.ClearAlpha();
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach(); // Get rid of the mask by converting it to alpha.
hbmpRelease.Init(hbmp); if ( bmp.HasAlpha() )
} bmp.MSWBlendMaskWithAlpha();
else
{
hbmp = GetHbitmapOf(bitmap);
} }
hbmpMask.Init(GetMaskForImage(bitmap, mask)); // wxBitmap normally stores alpha in pre-multiplied format but
// ImageList_Draw() does pre-multiplication internally so we need to undo
// the pre-multiplication here. Converting back and forth like this is, of
// course, very inefficient but it's better than wrong appearance so we do
// this for now until a better way can be found.
wxImage img = bmp.ConvertToImage();
if ( !img.HasAlpha() )
img.InitAlpha();
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach();
hbmpRelease.Init(hbmp);
// In any case we'll never use mask at the native image list level as
// it's incompatible with alpha and we need to use alpha.
} }
else else
{
if ( bitmap.HasAlpha() )
{
wxBitmap bmp(bitmap);
if ( mask.IsOk() || bmp.GetMask() )
{
// Blend mask with alpha channel.
if ( mask.IsOk() )
{
bmp.SetMask(new wxMask(mask));
}
bmp.MSWBlendMaskWithAlpha();
}
wxImage img = bmp.ConvertToImage();
hbmp = wxDIB(img, wxDIB::PixelFormat_NotPreMultiplied).Detach();
hbmpRelease.Init(hbmp);
}
else
{
if ( mask.IsOk() || bitmap.GetMask() )
{
// Convert mask to alpha channel.
wxBitmap bmp(bitmap);
if ( mask.IsOk() )
{
bmp.SetMask(new wxMask(mask));
}
wxImage img = bmp.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 #endif // wxUSE_WXDIB && wxUSE_IMAGE
{
hbmp = GetHbitmapOf(bitmap);
if ( useMask )
hbmpMask.Init(GetMaskForImage(bitmap, mask));
}
} }
}; } // anonymous namespace
// Adds a bitmap, and optionally a mask bitmap. // Adds a bitmap, and optionally a mask bitmap.
// Note that wxImageList creates new bitmaps, so you may delete // Note that wxImageList creates new bitmaps, so you may delete
@ -488,39 +459,16 @@ wxIcon wxImageList::GetIcon(int index) const
static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask) static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask)
{ {
#if wxUSE_IMAGE
wxBitmap bitmapWithMask;
#endif // wxUSE_IMAGE
HBITMAP hbmpMask; HBITMAP hbmpMask;
wxMask *pMask; wxScopedPtr<wxMask> maskDeleter;
bool deleteMask = false;
if ( mask.IsOk() ) if ( mask.IsOk() )
{ {
hbmpMask = GetHbitmapOf(mask); hbmpMask = GetHbitmapOf(mask);
pMask = NULL;
} }
else else
{ {
pMask = bitmap.GetMask(); wxMask* pMask = bitmap.GetMask();
#if wxUSE_IMAGE
// check if we don't have alpha in this bitmap -- we can create a mask
// from it (and we need to do it for the older systems which don't
// support 32bpp bitmaps natively)
if ( !pMask )
{
wxImage img(bitmap.ConvertToImage());
if ( img.HasAlpha() )
{
img.ConvertAlphaToMask();
bitmapWithMask = wxBitmap(img);
pMask = bitmapWithMask.GetMask();
}
}
#endif // wxUSE_IMAGE
if ( !pMask ) if ( !pMask )
{ {
// use the light grey count as transparent: the trouble here is // use the light grey count as transparent: the trouble here is
@ -532,19 +480,12 @@ static HBITMAP GetMaskForImage(const wxBitmap& bitmap, const wxBitmap& mask)
pMask = new wxMask(bitmap, col); pMask = new wxMask(bitmap, col);
deleteMask = true; maskDeleter.reset(pMask);
} }
hbmpMask = (HBITMAP)pMask->GetMaskBitmap(); hbmpMask = (HBITMAP)pMask->GetMaskBitmap();
} }
// windows mask convention is opposite to the wxWidgets one // windows mask convention is opposite to the wxWidgets one
HBITMAP hbmpMaskInv = wxInvertMask(hbmpMask); return wxInvertMask(hbmpMask);
if ( deleteMask )
{
delete pMask;
}
return hbmpMaskInv;
} }