diff --git a/include/wx/msw/bitmap.h b/include/wx/msw/bitmap.h index 09f9a85ce3..5bf3a15ba5 100644 --- a/include/wx/msw/bitmap.h +++ b/include/wx/msw/bitmap.h @@ -136,6 +136,8 @@ public: #if wxUSE_WXDIB // copies from a device independent bitmap bool CopyFromDIB(const wxDIB& dib); + bool IsDIB() const; + bool ConvertToDIB(); #endif virtual bool Create(int width, int height, int depth = wxBITMAP_SCREEN_DEPTH); diff --git a/src/common/dcgraph.cpp b/src/common/dcgraph.cpp index 6e2d0192ab..d42209b8cd 100644 --- a/src/common/dcgraph.cpp +++ b/src/common/dcgraph.cpp @@ -18,6 +18,7 @@ #if wxUSE_GRAPHICS_CONTEXT #include "wx/dcgraph.h" +#include "wx/rawbmp.h" #ifndef WX_PRECOMP #include "wx/icon.h" @@ -171,6 +172,61 @@ wxGCDCImpl::wxGCDCImpl( wxDC *owner, const wxWindowDC& dc ) : wxGCDCImpl::wxGCDCImpl( wxDC *owner, const wxMemoryDC& dc ) : wxDCImpl( owner ) { +#ifndef NEVER_USE_DIB + // It seems that GDI+ sets invalid values for alpha channel when used with + // a compatible bitmap (DDB). So we need to convert the currently selected + // bitmap to a DIB before using it with any GDI+ functions to ensure that + // we get the correct alpha channel values in it at the end. + + wxBitmap bmp = dc.GetSelectedBitmap(); + wxASSERT_MSG( bmp.IsOk(), "Should select a bitmap before creating wxGCDC" ); + + // We don't need to convert it if it can't have alpha at all (any depth but + // 32) or is already a DIB with alpha. + if ( bmp.GetDepth() == 32 && (!bmp.IsDIB() || !bmp.HasAlpha()) ) + { + // We need to temporarily deselect this bitmap from the memory DC + // before modifying it. + const_cast(dc).SelectObject(wxNullBitmap); + + bmp.ConvertToDIB(); // Does nothing if already a DIB. + + if( !bmp.HasAlpha() ) + { + // Initialize alpha channel, even if we don't have any alpha yet, + // we should have correct (opaque) alpha values in it for GDI+ + // functions to work correctly. + { + wxAlphaPixelData data(bmp); + if ( data ) + { + wxAlphaPixelData::Iterator p(data); + for ( int y = 0; y < data.GetHeight(); y++ ) + { + wxAlphaPixelData::Iterator rowStart = p; + + for ( int x = 0; x < data.GetWidth(); x++ ) + { + p.Alpha() = wxALPHA_OPAQUE; + ++p; + } + + p = rowStart; + p.OffsetY(data, 1); + } + } + } // End of block modifying the bitmap. + + // Using wxAlphaPixelData sets the internal "has alpha" flag but we + // don't really have any alpha yet, so reset it back for now. + bmp.ResetAlpha(); + } + + // Undo SelectObject() at the beginning of this block. + const_cast(dc).SelectObjectAsSource(bmp); + } +#endif // !NEVER_USE_DIB + Init(wxGraphicsContext::Create(dc)); } diff --git a/src/msw/bitmap.cpp b/src/msw/bitmap.cpp index a702165509..c6e825a237 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -68,8 +68,15 @@ public: virtual void Free(); + // Creates a new bitmap (DDB or DIB) from the contents of the given DIB. void CopyFromDIB(const wxDIB& dib); +#ifndef NEVER_USE_DIB + // Takes ownership of the given DIB. + bool AssignDIB(wxDIB& dib); +#endif // !NEVER_USE_DIB + + // set the mask object to use as the mask, we take ownership of it void SetMask(wxMask *mask) { @@ -118,6 +125,11 @@ public: private: void Init(); + // Use the given bitmap handle and take the rest of the characteristics + // (size, depth, ...) from the given DIB. + void InitFromDIB(const wxDIB& dib, HBITMAP hbitmap); + + // optional mask for transparent drawing wxMask *m_bitmapMask; @@ -249,6 +261,22 @@ void wxBitmapRefData::Free() wxDELETE(m_bitmapMask); } +void wxBitmapRefData::InitFromDIB(const wxDIB& dib, HBITMAP hbitmap) +{ + m_width = dib.GetWidth(); + m_height = dib.GetHeight(); + m_depth = dib.GetDepth(); + + m_hBitmap = (WXHBITMAP)hbitmap; + +#if wxUSE_PALETTE + wxPalette *palette = dib.CreatePalette(); + if ( palette ) + m_bitmapPalette = *palette; + delete palette; +#endif // wxUSE_PALETTE +} + void wxBitmapRefData::CopyFromDIB(const wxDIB& dib) { wxCHECK_RET( !IsOk(), "bitmap already initialized" ); @@ -264,20 +292,26 @@ void wxBitmapRefData::CopyFromDIB(const wxDIB& dib) m_isDIB = true; #endif // SOMETIMES_USE_DIB/ALWAYS_USE_DIB - m_width = dib.GetWidth(); - m_height = dib.GetHeight(); - m_depth = dib.GetDepth(); - - m_hBitmap = (WXHBITMAP)hbitmap; - -#if wxUSE_PALETTE - wxPalette *palette = dib.CreatePalette(); - if ( palette ) - m_bitmapPalette = *palette; - delete palette; -#endif // wxUSE_PALETTE + InitFromDIB(dib, hbitmap); } +#ifndef NEVER_USE_DIB + +bool wxBitmapRefData::AssignDIB(wxDIB& dib) +{ + if ( !dib.IsOk() ) + return false; + + Free(); + + m_isDIB = true; + InitFromDIB(dib, dib.Detach()); + + return true; +} + +#endif // !NEVER_USE_DIB + // ---------------------------------------------------------------------------- // wxBitmap creation // ---------------------------------------------------------------------------- @@ -457,6 +491,26 @@ bool wxBitmap::CopyFromDIB(const wxDIB& dib) return true; } +bool wxBitmap::IsDIB() const +{ + return GetBitmapData() && GetBitmapData()->m_isDIB; +} + +bool wxBitmap::ConvertToDIB() +{ + if ( IsDIB() ) + return true; + + wxDIB dib(*this); + if ( !dib.IsOk() ) + return false; + + // It is important to reuse the current GetBitmapData() instead of creating + // a new one, as our object identity shouldn't change just because our + // internal representation did, but IsSameAs() compares data pointers. + return GetBitmapData()->AssignDIB(dib); +} + #endif // NEVER_USE_DIB wxBitmap::~wxBitmap()