Don't lose alpha when converting GDI+ bitmap to wxImage

wxGDIPlusBitmapData::ConvertToImage() lost the alpha channel of the image as
it used the bitmap handle returned Gdiplus::Bitmap::GetHBITMAP() which is a
DDB and doesn't contain real alpha channel values.

In order to retrieve actual alpha channel data there is necessary to get
direct access to Gdiplus::Bitmap bits with Bitmap::LockBits and transfer pixel
data to wxImage pixel by pixel.

Closes #17174.
This commit is contained in:
Artur Wieczorek
2015-12-31 15:58:56 +01:00
committed by Vadim Zeitlin
parent 450019dc21
commit b65a3f5520

View File

@@ -1104,20 +1104,37 @@ wxGDIPlusBitmapData::wxGDIPlusBitmapData( wxGraphicsRenderer* renderer,
wxImage wxGDIPlusBitmapData::ConvertToImage() const wxImage wxGDIPlusBitmapData::ConvertToImage() const
{ {
// We could use Bitmap::LockBits() and convert to wxImage directly but // We need to use Bitmap::LockBits() to convert bitmap to wxImage
// passing by wxBitmap is easier. It would be nice to measure performance // because this way we can retrieve also alpha channel data.
// of the two methods but for this the second one would need to be written // Alternative way by retrieving bitmap handle with Bitmap::GetHBITMAP
// first... // (to pass it to wxBitmap) doesn't preserve real alpha channel data.
HBITMAP hbmp; const UINT w = m_bitmap->GetWidth();
if ( m_bitmap->GetHBITMAP(Color(0xffffffff), &hbmp) != Gdiplus::Ok ) const UINT h = m_bitmap->GetHeight();
return wxNullImage;
wxBitmap bmp; wxImage img(w, h);
bmp.SetWidth(m_bitmap->GetWidth()); img.InitAlpha();
bmp.SetHeight(m_bitmap->GetHeight());
bmp.SetHBITMAP(hbmp); BitmapData bitmapData;
bmp.SetDepth(IsAlphaPixelFormat(m_bitmap->GetPixelFormat()) ? 32 : 24); Rect rect(0, 0, w, h);
return bmp.ConvertToImage(); m_bitmap->LockBits(&rect, ImageLockModeRead, PixelFormat32bppARGB, &bitmapData);
const BYTE* pixels = static_cast<const BYTE*>(bitmapData.Scan0);
for( UINT y = 0; y < h; y++ )
{
for( UINT x = 0; x < w; x++ )
{
ARGB c = reinterpret_cast<const ARGB*>(pixels)[x];
img.SetRGB(x, y, (c >> 16) & 0xFF,
(c >> 8) & 0xFF,
(c >> 0) & 0xFF);
img.SetAlpha(x, y, (c >> 24) & 0xFF);
}
pixels += bitmapData.Stride;
}
m_bitmap->UnlockBits(&bitmapData);
return img;
} }
#endif // wxUSE_IMAGE #endif // wxUSE_IMAGE