Fix incorrect alpha rendering in wxToolBar (MSW)

wxToolBar::Realize() code for handling bitmaps with alpha channel was
incorrectly blending them with the toolbar’s background color, resulting
in much lighter appearance and broken antialiasing.

Fix it by clearing the composite bitmap to be initially transparent if
bitmaps with alpha channel are used. Doing so uncovered another bug in
how the composite RGBA bitmap was passed to native toolbar control, so
fix that as well.
This commit is contained in:
Václav Slavík 2017-01-11 13:02:05 +01:00 committed by Václav Slavík
parent 0649b0d028
commit 195df9af7f

View File

@ -46,10 +46,12 @@
#include "wx/artprov.h"
#include "wx/sysopt.h"
#include "wx/dcclient.h"
#include "wx/rawbmp.h"
#include "wx/scopedarray.h"
#include "wx/msw/private.h"
#include "wx/msw/dc.h"
#include "wx/msw/dib.h"
#if wxUSE_UXTHEME
#include "wx/msw/uxtheme.h"
@ -734,10 +736,10 @@ bool wxToolBar::Realize()
// remap the buttons on 8bpp displays as otherwise the bitmaps usually look
// much worse after remapping
static const wxChar *remapOption = wxT("msw.remap");
const int remapValue = wxSystemOptions::HasOption(remapOption)
? wxSystemOptions::GetOptionInt(remapOption)
: wxDisplayDepth() <= 8 ? Remap_Buttons
: Remap_None;
int remapValue = wxSystemOptions::HasOption(remapOption)
? wxSystemOptions::GetOptionInt(remapOption)
: wxDisplayDepth() <= 8 ? Remap_Buttons
: Remap_None;
// delete all old buttons, if any
@ -768,9 +770,43 @@ bool wxToolBar::Realize()
// Create a bitmap and copy all the tool bitmaps into it
wxMemoryDC dcAllButtons;
wxBitmap bitmap(totalBitmapWidth, totalBitmapHeight);
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
{
wxToolBarToolBase *tool = node->GetData();
if ( tool->IsButton() &&
tool->GetNormalBitmap().IsOk() && tool->GetNormalBitmap().HasAlpha() )
{
// By default bitmaps don't have alpha in wxMSW, but if we
// use a bitmap tool with alpha, we should use alpha for
// the combined bitmap as well.
bitmap.UseAlpha();
#ifdef wxHAS_RAW_BITMAP
// Clear the combined bitmap to have (0,0,0,0) pixels so that
// alpha blending bitmaps onto it doesn't change their appearance.
wxAlphaPixelData data(bitmap);
if ( data )
{
wxAlphaPixelData::Iterator p(data);
for (int y = 0; y < totalBitmapHeight; y++)
{
wxAlphaPixelData::Iterator rowStart = p;
for (int x = 0; x < totalBitmapWidth; ++x, ++p)
{
p.Red() = p.Green() = p.Blue() = p.Alpha() = 0;
}
p = rowStart;
p.OffsetY(data, 1);
}
#endif
}
break;
}
}
dcAllButtons.SelectObject(bitmap);
if ( remapValue != Remap_TransparentBg )
if ( remapValue != Remap_TransparentBg && !bitmap.HasAlpha() )
{
dcAllButtons.SetBackground(GetBackgroundColour());
dcAllButtons.Clear();
@ -809,12 +845,6 @@ bool wxToolBar::Realize()
if ( bmp.IsOk() )
{
// By default bitmaps don't have alpha in wxMSW, but if we
// use a bitmap tool with alpha, we should use alpha for
// the combined bitmap as well.
if ( bmp.HasAlpha() )
bitmap.UseAlpha();
int xOffset = wxMax(0, (m_defaultWidth - w)/2);
int yOffset = wxMax(0, (m_defaultHeight - h)/2);
@ -882,8 +912,23 @@ bool wxToolBar::Realize()
dcAllButtons.SelectObject(wxNullBitmap);
// don't delete this HBITMAP!
bitmap.SetHBITMAP(0);
#if wxUSE_WXDIB
if ( bitmap.HasAlpha() )
{
// Strangely, toolbar expects bitmaps with transparency to not
// be premultiplied, unlike most of the rest of win32. Without this
// conversion, e.g. antialiased lines would be subtly, but
// noticeably misrendered.
hBitmap = wxDIB(bitmap.ConvertToImage(),
wxDIB::PixelFormat_NotPreMultiplied).Detach();
}
else
#endif
{
hBitmap = GetHbitmapOf(bitmap);
// don't delete this HBITMAP!
bitmap.SetHBITMAP(0);
}
if ( remapValue == Remap_Buttons )
{