+wxDCCacheEntry::wxDCCacheEntry(WXHDC hDC, int depth)
+{
+ m_bitmap = 0;
+ m_dc = hDC;
+ m_width = 0;
+ m_height = 0;
+ m_depth = depth;
+}
+
+wxDCCacheEntry::~wxDCCacheEntry()
+{
+ if (m_bitmap)
+ ::DeleteObject((HBITMAP) m_bitmap);
+ if (m_dc)
+ ::DeleteDC((HDC) m_dc);
+}
+
+wxDCCacheEntry* wxDC::FindBitmapInCache(WXHDC dc, int w, int h)
+{
+ int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
+ wxList::compatibility_iterator node = sm_bitmapCache.GetFirst();
+ while (node)
+ {
+ wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
+
+ if (entry->m_depth == depth)
+ {
+ if (entry->m_width < w || entry->m_height < h)
+ {
+ ::DeleteObject((HBITMAP) entry->m_bitmap);
+ entry->m_bitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
+ if ( !entry->m_bitmap)
+ {
+ wxLogLastError(wxT("CreateCompatibleBitmap"));
+ }
+ entry->m_width = w; entry->m_height = h;
+ return entry;
+ }
+ return entry;
+ }
+
+ node = node->GetNext();
+ }
+ WXHBITMAP hBitmap = (WXHBITMAP) ::CreateCompatibleBitmap((HDC) dc, w, h);
+ if ( !hBitmap)
+ {
+ wxLogLastError(wxT("CreateCompatibleBitmap"));
+ }
+ wxDCCacheEntry* entry = new wxDCCacheEntry(hBitmap, w, h, depth);
+ AddToBitmapCache(entry);
+ return entry;
+}
+
+wxDCCacheEntry* wxDC::FindDCInCache(wxDCCacheEntry* notThis, WXHDC dc)
+{
+ int depth = ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
+ wxList::compatibility_iterator node = sm_dcCache.GetFirst();
+ while (node)
+ {
+ wxDCCacheEntry* entry = (wxDCCacheEntry*) node->GetData();
+
+ // Don't return the same one as we already have
+ if (!notThis || (notThis != entry))
+ {
+ if (entry->m_depth == depth)
+ {
+ return entry;
+ }
+ }
+
+ node = node->GetNext();
+ }
+ WXHDC hDC = (WXHDC) ::CreateCompatibleDC((HDC) dc);
+ if ( !hDC)
+ {
+ wxLogLastError(wxT("CreateCompatibleDC"));
+ }
+ wxDCCacheEntry* entry = new wxDCCacheEntry(hDC, depth);
+ AddToDCCache(entry);
+ return entry;
+}
+
+void wxDC::AddToBitmapCache(wxDCCacheEntry* entry)
+{
+ sm_bitmapCache.Append(entry);
+}
+
+void wxDC::AddToDCCache(wxDCCacheEntry* entry)
+{
+ sm_dcCache.Append(entry);
+}
+
+void wxDC::ClearCache()
+{
+ WX_CLEAR_LIST(wxList, sm_dcCache);
+ WX_CLEAR_LIST(wxList, sm_bitmapCache);
+}
+
+// Clean up cache at app exit
+class wxDCModule : public wxModule
+{
+public:
+ virtual bool OnInit() { return TRUE; }
+ virtual void OnExit() { wxDC::ClearCache(); }
+
+private:
+ DECLARE_DYNAMIC_CLASS(wxDCModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
+
+#endif // wxUSE_DC_CACHEING
+
+// ----------------------------------------------------------------------------
+// alpha channel support
+// ----------------------------------------------------------------------------
+
+static bool AlphaBlt(HDC hdcDst,
+ int x, int y, int width, int height,
+ HDC hdcSrc,
+ const wxBitmap& bmp)
+{
+ wxASSERT_MSG( bmp.Ok() && bmp.HasAlpha(), _T("AlphaBlt(): invalid bitmap") );
+ wxASSERT_MSG( hdcDst && hdcSrc, _T("AlphaBlt(): invalid HDC") );
+
+ // do we have AlphaBlend() and company in the headers?
+#if defined(AC_SRC_OVER) && wxUSE_DYNLIB_CLASS
+ // yes, now try to see if we have it during run-time
+ typedef BOOL (WINAPI *AlphaBlend_t)(HDC,int,int,int,int,
+ HDC,int,int,int,int,
+ BLENDFUNCTION);
+
+ // bitmaps can be drawn only from GUI thread so there is no need to
+ // protect this static variable from multiple threads
+ static bool s_triedToLoad = FALSE;
+ static AlphaBlend_t pfnAlphaBlend = NULL;
+ if ( !s_triedToLoad )
+ {
+ s_triedToLoad = TRUE;
+
+ // don't give errors about the DLL being unavailable, we're
+ // prepared to handle this
+ wxLogNull nolog;
+
+ wxDynamicLibrary dll(_T("msimg32.dll"));
+ if ( dll.IsLoaded() )
+ {
+ pfnAlphaBlend = (AlphaBlend_t)dll.GetSymbol(_T("AlphaBlend"));
+ if ( pfnAlphaBlend )
+ {
+ // we must keep the DLL loaded if we want to be able to
+ // call AlphaBlend() so just never unload it at all, not a
+ // big deal
+ dll.Detach();
+ }
+ }
+ }
+
+ if ( pfnAlphaBlend )
+ {
+ BLENDFUNCTION bf;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.BlendFlags = 0;
+ bf.SourceConstantAlpha = 0xff;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+
+ if ( pfnAlphaBlend(hdcDst, x, y, width, height,
+ hdcSrc, 0, 0, width, height,
+ bf) )
+ {
+ // skip wxAlphaBlend() call below
+ return TRUE;
+ }
+
+ wxLogLastError(_T("AlphaBlend"));
+ }
+#endif // defined(AC_SRC_OVER)
+
+ // AlphaBlend() unavailable of failed: use our own (probably much slower)
+ // implementation
+#ifdef wxHAVE_RAW_BITMAP
+ wxAlphaBlend(hdcDst, x, y, width, height, bmp);
+
+ return TRUE;
+#else // !wxHAVE_RAW_BITMAP
+ // no wxAlphaBlend() neither, fall back to using simple BitBlt() (we lose
+ // alpha but at least something will be shown like this)
+ return FALSE;
+#endif // wxHAVE_RAW_BITMAP
+}
+
+
+// wxAlphaBlend: our fallback if ::AlphaBlend() is unavailable
+#ifdef wxHAVE_RAW_BITMAP
+
+static void
+wxAlphaBlend(HDC hdcDst, int xDst, int yDst, int w, int h, const wxBitmap& bmpSrc)
+{
+ // get the destination DC pixels
+ wxBitmap bmpDst(w, h, 32 /* force creating RGBA DIB */);
+ MemoryHDC hdcMem;
+ SelectInHDC select(hdcMem, GetHbitmapOf(bmpDst));
+
+ if ( !::BitBlt(hdcMem, 0, 0, w, h, hdcDst, 0, 0, SRCCOPY) )
+ {
+ wxLogLastError(_T("BitBlt"));
+ }
+
+ // combine them with the source bitmap using alpha
+ wxAlphaPixelData dataDst(bmpDst),
+ dataSrc((wxBitmap &)bmpSrc);
+
+ wxCHECK_RET( dataDst && dataSrc,
+ _T("failed to get raw data in wxAlphaBlend") );
+
+ wxAlphaPixelData::Iterator pDst(dataDst),
+ pSrc(dataSrc);
+
+ for ( int y = 0; y < h; y++ )
+ {
+ wxAlphaPixelData::Iterator pDstRowStart = pDst,
+ pSrcRowStart = pSrc;
+
+ for ( int x = 0; x < w; x++ )
+ {
+ // note that source bitmap uses premultiplied alpha (as required by
+ // the real AlphaBlend)
+ const unsigned beta = 255 - pSrc.Alpha();
+
+ pDst.Red() = pSrc.Red() + (beta * pDst.Red() + 127) / 255;
+ pDst.Blue() = pSrc.Blue() + (beta * pDst.Blue() + 127) / 255;
+ pDst.Green() = pSrc.Green() + (beta * pDst.Green() + 127) / 255;
+
+ ++pDst;
+ ++pSrc;
+ }
+
+ pDst = pDstRowStart;
+ pSrc = pSrcRowStart;
+ pDst.OffsetY(dataDst, 1);
+ pSrc.OffsetY(dataSrc, 1);
+ }
+
+ // and finally blit them back to the destination DC
+ if ( !::BitBlt(hdcDst, xDst, yDst, w, h, hdcMem, 0, 0, SRCCOPY) )
+ {
+ wxLogLastError(_T("BitBlt"));
+ }
+}