Fix drawing wxBitmap with both alpha channel and mask
For 32 bpp wxBitmap with both alpha channel and mask we have to apply mask on our own while drawing the bitmap because MaskBlt() API doesn't work properly with 32 bpp RGBA bitmaps. To do so we need to create a temporary bitmap with copy of original RGB data and with alpha channel being a superposition of the original alpha values and the mask. See #18498.
This commit is contained in:
parent
b2cdcb6824
commit
2d15218c9d
@ -205,6 +205,9 @@ public:
|
||||
// values in its alpha channel.
|
||||
void MSWUpdateAlpha();
|
||||
|
||||
// Blend mask with alpha channel and remove the mask
|
||||
void MSWBlendMaskWithAlpha();
|
||||
|
||||
public:
|
||||
#if WXWIN_COMPATIBILITY_3_0
|
||||
wxDEPRECATED_INLINE(void SetHBITMAP(WXHBITMAP bmp), SetHandle((WXHANDLE)bmp); )
|
||||
|
@ -734,6 +734,10 @@ public:
|
||||
|
||||
@remarks The bitmap object owns the mask once this has been called.
|
||||
|
||||
@note A mask can be set also for bitmap with an alpha channel but
|
||||
doing so under wxMSW is not recommended because performance of drawing
|
||||
such bitmap is not very good.
|
||||
|
||||
@see GetMask(), wxMask
|
||||
*/
|
||||
virtual void SetMask(wxMask* mask);
|
||||
@ -777,6 +781,10 @@ wxBitmap wxNullBitmap;
|
||||
When associated with a bitmap and drawn in a device context, the unmasked
|
||||
area of the bitmap will be drawn, and the masked area will not be drawn.
|
||||
|
||||
@note A mask can be associated also with a bitmap with an alpha channel
|
||||
but drawing such bitmaps under wxMSW may be slow so using them should be
|
||||
avoided if drawing performance is an important factor.
|
||||
|
||||
@library{wxcore}
|
||||
@category{gdi}
|
||||
|
||||
|
@ -1259,6 +1259,53 @@ void wxBitmap::MSWUpdateAlpha()
|
||||
#endif // wxUSE_WXDIB/!wxUSE_WXDIB
|
||||
}
|
||||
|
||||
void wxBitmap::MSWBlendMaskWithAlpha()
|
||||
{
|
||||
#if defined(wxHAS_RAW_BITMAP)
|
||||
if ( !HasAlpha() || !GetMask() )
|
||||
return;
|
||||
// Update alpha channel by blending original alpha values with mask and then remove the mask.
|
||||
// For non-masked pixels alpha channel values will remain intact and for masked pixels
|
||||
// they will be set to the transparent value (0).
|
||||
AllocExclusive();
|
||||
|
||||
{
|
||||
wxBitmap bmpMask = GetMask()->GetBitmap();
|
||||
wxNativePixelData maskData(bmpMask);
|
||||
wxCHECK_RET(maskData, "No access to bitmap mask data");
|
||||
|
||||
wxAlphaPixelData bmpData(*this);
|
||||
wxCHECK_RET(bmpData, "No access to bitmap data");
|
||||
|
||||
const int w = GetWidth();
|
||||
const int h = GetHeight();
|
||||
|
||||
wxNativePixelData::Iterator maskRowStart(maskData);
|
||||
wxAlphaPixelData::Iterator bmpRowStart(bmpData);
|
||||
for ( int y = 0; y < h; y++ )
|
||||
{
|
||||
wxNativePixelData::Iterator pMask = maskRowStart;
|
||||
wxAlphaPixelData::Iterator pBmp = bmpRowStart;
|
||||
for ( int x = 0; x < w; x++, ++pBmp, ++pMask )
|
||||
{
|
||||
// Masked pixel is not drawn i.e. is transparent,
|
||||
// non-masked pixel is untouched.
|
||||
if ( pMask.Red() == 0 )
|
||||
{
|
||||
pBmp.Red() = pBmp.Green() = pBmp.Blue() = 0; // pre-multiplied
|
||||
pBmp.Alpha() = wxALPHA_TRANSPARENT;
|
||||
}
|
||||
}
|
||||
maskRowStart.OffsetY(maskData, 1);
|
||||
bmpRowStart.OffsetY(bmpData, 1);
|
||||
}
|
||||
}
|
||||
GetBitmapData()->SetMask((wxMask*)NULL);
|
||||
wxASSERT(HasAlpha());
|
||||
wxASSERT(!GetMask());
|
||||
#endif // wxHAS_RAW_BITMAP
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxBitmap setters
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -1242,10 +1242,31 @@ void wxMSWDCImpl::DoDrawBitmap( const wxBitmap &bmp, wxCoord x, wxCoord y, bool
|
||||
|
||||
if ( bmp.HasAlpha() )
|
||||
{
|
||||
MemoryHDC hdcMem;
|
||||
SelectInHDC select(hdcMem, GetHbitmapOf(bmp));
|
||||
// Make a copy in case we would neeed to remove its mask.
|
||||
// If this will not be necessary, the copy is cheap as bitmaps are reference-counted.
|
||||
wxBitmap curBmp(bmp);
|
||||
|
||||
if ( AlphaBlt(this, x, y, width, height, 0, 0, width, height, hdcMem, bmp) )
|
||||
// For bitmap with both alpha channel and mask we have to apply mask on our own
|
||||
// because MaskBlt() API doesn't work properly with 32 bpp RGBA bitmaps.
|
||||
// To do so we will create a temporary bitmap with copy of RGB data and with alpha channel
|
||||
// being a superposition of the original alpha values and the mask - for non-masked pixels
|
||||
// alpha channel values will remain intact and for masked pixels they will be set to the transparent value.
|
||||
if ( curBmp.GetMask() )
|
||||
{
|
||||
if ( useMask )
|
||||
{
|
||||
curBmp.MSWBlendMaskWithAlpha();
|
||||
}
|
||||
else
|
||||
{
|
||||
curBmp.SetMask(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryHDC hdcMem;
|
||||
SelectInHDC select(hdcMem, GetHbitmapOf(curBmp));
|
||||
|
||||
if ( AlphaBlt(this, x, y, width, height, 0, 0, width, height, hdcMem, curBmp) )
|
||||
{
|
||||
CalcBoundingBox(x, y);
|
||||
CalcBoundingBox(x + bmp.GetWidth(), y + bmp.GetHeight());
|
||||
|
Loading…
Reference in New Issue
Block a user