diff --git a/include/wx/msw/bitmap.h b/include/wx/msw/bitmap.h index eb16dd47e6..61707d8866 100644 --- a/include/wx/msw/bitmap.h +++ b/include/wx/msw/bitmap.h @@ -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); ) diff --git a/interface/wx/bitmap.h b/interface/wx/bitmap.h index 57ce3e3ff3..0dbbc428ff 100644 --- a/interface/wx/bitmap.h +++ b/interface/wx/bitmap.h @@ -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} diff --git a/src/msw/bitmap.cpp b/src/msw/bitmap.cpp index 3ceea0022c..077ae40e83 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -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 // ---------------------------------------------------------------------------- diff --git a/src/msw/dc.cpp b/src/msw/dc.cpp index 6710f8c2ae..9652af4ccf 100644 --- a/src/msw/dc.cpp +++ b/src/msw/dc.cpp @@ -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());