CMemDC aus dem Artikel Flicker free drawing with MFC ist wohl der Klassiker für Doublebuffering und ist wohl jedem MFC Entwickler bekannt.
Er macht was er soll und ich habe ihn selbst über Jahre hinweg unverändert verwendet. Solange man einfache Sachen macht und ausschließlich den MM_TEXT Mappingmode verwendet ist alles OK.
Hat man sich aber was spezielles – zoomfähiges – zusammengebaut mit MM_ANISOTROPIC dann funktioniert das Ganze nicht mehr, weil Keith an den Window Origin Koordinaten dreht und nicht am Viewport.
Meine abgewandelte Variante trägt einer entsprechende Verwendung auch für andere Mappingmodes Rechnung. Es müsste IMHO für jeden funktionieren. Intensiv getestet habe ich es mit MM_ANISOTROPIC.
Will/muss man auch an den Viewport Koordinaten drehen, dann sollten die alten Viewport Koordinaten gelesen werden und dann entsprechend durch ein Offset geändert werden.
Alles anderen Werte wie die Extents können beliebig verändert werden.
Bei der Verwendung sollte MM_TEXT eingeschaltet sein (entsprechende ASSERTs habe ich ergänzt). MM_TEXT ist normalerweise gesetzt, wenn CMemDC direkt nach Instanzierung des CPaintDC zum Einsatz kommt. Der Destruktor setzt dann den Mappingmode auf MM_TEXT zurück.
Dieser neue, modifizierte CMemDC kann einfach die bisherige Implementierung ersetzen.
Have fun & Happy coding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | class CMemDC : public CDC { public: // constructor sets up the memory DC CMemDC(CDC* pDC) : CDC() , m_pDC(pDC) , m_pOldBitmap(NULL) , m_bMemDC(!m_pDC->IsPrinting()) { ASSERT(pDC != NULL); if (m_bMemDC) { // Create a Memory DC. // At this moment we should have mapping mode text ASSERT(m_pDC->GetMapMode()==MM_TEXT); ASSERT(m_pDC->GetWindowOrg()==CPoint(0,0)); ASSERT(m_pDC->GetViewportOrg()==CPoint(0,0)); // Get the clip box and create bitmap for this size m_pDC->GetClipBox(&m_rect); CreateCompatibleDC(m_pDC); m_bitmap.CreateCompatibleBitmap(m_pDC, m_rect.Width(), m_rect.Height()); m_pOldBitmap = SelectObject(&m_bitmap); // Adjust the view port so that we hit the clip rect. SetViewportOrg(-m_rect.left, -m_rect.top); IntersectClipRect(m_rect); // Fill background in case the user has overridden // WM_ERASEBKGND. We end up with garbage otherwise. FillSolidRect(m_rect, m_pDC->GetBkColor()); } else { // Make a copy of the relevant parts of the current DC for printing m_bPrinting = m_pDC->m_bPrinting; m_hDC = m_pDC->m_hDC; m_hAttribDC = m_pDC->m_hAttribDC; } } // Destructor copies the contents of the mem DC to the original DC ~CMemDC() { if (m_bMemDC) { // The mapping mode might effect the BitBlt. So we need to return to // MM_TEXT. This makes m_rect.left and m_rect.top again the // coordinates of our clipped rectangle. And to make sure that the // coordinates are really used we just clear the ViewportOrg and the // WindowOrg SetMapMode(MM_TEXT); SetViewportOrg(0,0); SetWindowOrg(0,0); // Copy the off screen bitmap onto the screen. // For this we just make sure that we really target the rectangle of // our temporal bitmap. m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(), this, 0, 0, SRCCOPY); //Swap back the original bitmap. SelectObject(m_pOldBitmap); } else { // All we need to do is replace the DC with an illegal value, // this keeps us from accidentally deleting the handles associated // with the CDC that was passed to the constructor. m_hDC = m_hAttribDC = NULL; } } // Allow usage as a pointer CMemDC* operator->() { return this; } // Allow usage as a pointer operator CMemDC*() { return this; } private: CBitmap m_bitmap; // Offscreen bitmap CBitmap* m_pOldBitmap; // bitmap originally found in CMemDC CDC* m_pDC; // Saves CDC passed in constructor CRect m_rect; // Rectangle of drawing area. bool m_bMemDC; // true if CDC really is a Memory DC. }; |
2 Kommentare zu “Der etwas bessere CMemDC”
Link für diesen Beitrag | RSS-Feed zu diesem Beitrag
Hinterlassen sie einen Kommentar:
Beachten sie bitte, dass Kommentare evtl. nicht sofort hier erscheinen. Die Kommentare werden zur Moderation an den Webmaster gesendet. Es kann also etwas dauern, bis Ihr Kommentar hier veröffentlicht wird!
on Do 01 Apr 2010 um 12:48 #
Knut
Dein CMemDC beschränkt die Zeichenfläche prinzipiell auf die Clipping Region. s. Konstruktor
// Get the clip box and create bitmap for this size
m_pDC->GetClipBox(&m_rect);
Das wird zum Problem, wenn Windows unvermutet eine Clipbox setzt. Das passiert z.B. wenn man ein anderes Fenster über dem Zeichenfenster (schnell) bewegt. CMemDC kopiert (BitBlt im Destruktor) dann immer in den Bereich, der unter dem sich bewegenden Fenster frei wird, dessen Koordinaten mit der eigentlichen Zeichnung nichts zu tun haben.
Vielleicht ist die alte Lösung von Keith besser, welche im Konstruktor den Zeichenbereich übernehmen kann.
CMemDC(CDC* pDC, const CRect* pRect = NULL) : CDC()
{
// …
// Get the rectangle to draw
if (pRect == NULL) {
pDC->GetClipBox(&m_rect);
} else {
m_rect = *pRect;
}
// …
}
So kann man im Notfall selber festlegen, wohin das Kunstwerk am Ende kopiert wird.
on Sa 06 Nov 2010 um 20:33 # Martin's Blog » Erstaunen: CMemDC ist Bestandteil der MFC!
[...] habe meine Erweiterung hier im Blog vorgestellt und die liegt normalerweise in einem separaten Namespace, wie alle meine [...]