Fix crash when using wxDataObjectComposite with bitmap in wxMSW

This reverts the changes of b1fad4da44 (Convert 0RGB wxBitmaps to RGB
when copying them to clipboard, 2017-04-30) that added a cast from
wxDataObject to wxBitmapDataObject for any object supporting bitmaps,
which resulted in a crash when this happened to be wxDataObjectComposite
and not wxBitmapDataObject, and does the same thing in
wxBitmapDataObject itself which now converts 0RGB bitmaps to RGB ones
when asked for the bitmap data.

The main change is that the code using wxDataObjectComposite doesn't
crash any longer, but another nice side effect is that this is possibly
more efficient as well because the conversion could be skipped entirely
if the bitmap is never pasted.

See #17640.

Closes #23564.
This commit is contained in:
Vadim Zeitlin 2023-06-20 20:42:26 +01:00
parent cec057519e
commit 675728e1c0
2 changed files with 38 additions and 22 deletions

View file

@ -34,7 +34,6 @@
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/dataobj.h"
#include "wx/dcmemory.h"
#endif
#if wxUSE_METAFILE
@ -585,26 +584,6 @@ bool wxClipboard::AddData( wxDataObject *data )
wxCHECK_MSG( data, false, wxT("data is invalid") );
const wxDataFormat format = data->GetPreferredFormat();
if ( format == wxDF_BITMAP || format == wxDF_DIB )
{
wxBitmapDataObject* bmpData = (wxBitmapDataObject*)data;
wxBitmap bmp = bmpData->GetBitmap();
wxASSERT_MSG( bmp.IsOk(), wxS("Invalid bitmap") );
// Replace 0RGB bitmap with its RGB copy
// to ensure compatibility with applications
// not recognizing bitmaps in 0RGB format.
if ( bmp.GetDepth() == 32 && !bmp.HasAlpha() )
{
wxBitmap bmpRGB(bmp.GetSize(), 24);
wxMemoryDC dc(bmpRGB);
dc.DrawBitmap(bmp, 0, 0);
dc.SelectObject(wxNullBitmap);
bmpData->SetBitmap(bmpRGB);
}
}
#if wxUSE_OLE_CLIPBOARD
HRESULT hr = OleSetClipboard(data->GetInterface());
if ( FAILED(hr) )

View file

@ -23,6 +23,7 @@
#if wxUSE_DATAOBJ
#ifndef WX_PRECOMP
#include "wx/dcmemory.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/utils.h"
@ -1042,9 +1043,41 @@ const wxChar *wxDataObject::GetFormatName(wxDataFormat format)
// wxBitmapDataObject supports CF_DIB format
// ----------------------------------------------------------------------------
namespace
{
// Modify bitmap if necessary, i.e. if it uses 0RGB format in which alpha
// channel is present but is entirely 0, to make it just plain RGB, i.e.
// without alpha channel at all, to ensure compatibility with the applications
// not recognizing the special case of 0RGB and handling such bitmaps as
// completely transparent, see #17640.
void RemoveAlphaIfNecessary(wxBitmap& bmp)
{
// Replace 0RGB bitmap with its RGB copy to ensure compatibility with
// applications not recognizing bitmaps in 0RGB format, see #17640.
if ( bmp.GetDepth() == 32 && !bmp.HasAlpha() )
{
wxBitmap bmpRGB(bmp.GetSize(), 24);
{
wxMemoryDC dc(bmpRGB);
dc.DrawBitmap(bmp, 0, 0);
}
bmp = bmpRGB;
}
}
} // anonymous namespace
size_t wxBitmapDataObject::GetDataSize() const
{
#if wxUSE_WXDIB
wxBitmap& bmp = const_cast<wxBitmapDataObject*>(this)->m_bitmap;
// Note that we need to do this here too and not just in GetDataHere()
// because the size of the bitmap without the alpha channel is different.
RemoveAlphaIfNecessary(bmp);
return wxDIB::ConvertFromBitmap(nullptr, GetHbitmapOf(GetBitmap()));
#else
return 0;
@ -1054,9 +1087,13 @@ size_t wxBitmapDataObject::GetDataSize() const
bool wxBitmapDataObject::GetDataHere(void *buf) const
{
#if wxUSE_WXDIB
wxBitmap& bmp = const_cast<wxBitmapDataObject*>(this)->m_bitmap;
RemoveAlphaIfNecessary(bmp);
BITMAPINFO * const pbi = (BITMAPINFO *)buf;
return wxDIB::ConvertFromBitmap(pbi, GetHbitmapOf(GetBitmap())) != 0;
return wxDIB::ConvertFromBitmap(pbi, GetHbitmapOf(bmp)) != 0;
#else
wxUnusedVar(buf);
return false;