Problem mit Regions und BitBlt
-
Hallo, nette hilfsbereite Mitmenschen,
ich habe da kleine Probleme mit meiner Anwendung:
Ich will in einem Dialogfenster 2 Diagramme zeichnen lassen.
Diese Diagramme werden mehrmals in der Sekunde gezeichnet.Um das Flackern zu vermeiden, habe ich mir gedacht, ich nutze dafür Regions.
Habe dann erst angefangen mit Gdiplus-Methoden herumzuspielen.
Jedoch fing das Flackern dann trotzdem an. Dann bin ich dazu übergegangen, mit CRgn zu arbeiten -> flackerte immer noch.
Habe mich dann noch an CreateCompatibleDC erinnern können und das Flackern wegbekommen,
ABER:1. mit
CPaintDC dc(this); CDC memDC; CRect copyRect; CBitmap bmp, *pOldBmp; GetClientRect(©Rect); memDC.CreateCompatibleDC(NULL); bmp.CreateCompatibleBitmap(&dc,copyRect.Width(),copyRect.Height()); pOldBmp = memDC.SelectObject(&bmp);
erzeuge ich ja ein Bitmap im SpeicherDC. Darin kopiere ich ja sozusagen mein Fenster und kann dann darin herumzeichnen.
Jedoch immer, wenn ich mitdc.BitBlt(0,0,copyRect.Width(),copyRect.Height(),&memDC,0,0,SRCCOPY);
den SpeicherDC wieder zurück blitte bekomme ich statt eines "Windows-Mausgrauen" Fenster ein schwarzes?!? Dabei benutze ich doch SRCCOPY?
Zudem habe ich das Problem, dass ich nicht weiss, wie ich mit CRgn einen Clipping-Bereich festlege. Bei Gdiplus ist das ja recht simpel. Gdiplus kann ich meiner Meinung nach aber nicht nutzen, da ich ja InvalidateRgn nutzen möchte und er da meckert.
Mein letztes Problem:
Wenn ich nun 2 Regions einsetze und gerne beide zur gleichen Zeit updaten möchte, wie kann ich das Anstellen? InvalidateRgn nimmt ja nur eine?
Und bei Invalidate flackert es wieder?Sorry, falls sich das hier konfus anhört, sitze aber nun fast 2 Tage an dem Problem, was glaub ich aber ganu einfach zu lösen ist.
-
Ok, momentan stört mich nur, dass ich ein schwarz eingefärbtes Fenster erhalte, wenn BitBlt benutze?
Weiss wirklich nicht warum? Kein anderer?
-
Ok. Unter CBitmap::CreateCompatibleBitmap
steht:"When a memory device context is created, GDI automatically selects a monochrome stock bitmap for it."
Nehme ich stattdessen HBITMAP und CreateCompatibleBitmap (nicht von CBitmap)
steht dort sogar ein Beispiel:Note: When a memory device context is created, it initially has a 1-by-1 monochrome bitmap selected into it. If this memory device context is used in CreateCompatibleBitmap, the bitmap that is created is a monochrome bitmap. To create a color bitmap, use the hDC that was used to create the memory device context, as shown in the following code:
HDC memDC = CreateCompatibleDC ( hDC ); HBITMAP memBM = CreateCompatibleBitmap ( hDC, nWidth, nHeight ); SelectObject ( memDC, memBM );
Und als Ergebnis: Wieder ein schwarzes Fenster.
Hilfe?
-
CreateCompatibleBitmap erzeugt die eine bitmap mit den den selben eigenschaften wie das orginal, aber nicht mit selben inhalt. Nach CreateCompatibleBitmap ist die also leer (schwarz)
-
Es wird ja eigentlich alles gezeichnet: Button, meine regions, nur halt die Farbe stimmt nicht.
Ich vermute ja, dass es an demCreateCompatibleBitmap(hDC)
liegt. Ich übergebe da nun
CPaintDC dc(this); HDC memDC = CreateCompatibleDC(dc);
Ich habe als Argument auch schon dc.m_hDC ausprobiert.
Oder wie bekomme ich vom dc ein HDC?
Oder ist das nicht möglich?
-
??
dein code macht das:CPaintDC dc(this); CRect copyRect; CBitmap bmp, *pOldBmp; GetClientRect(©Rect); HDC memDC = CreateCompatibleDC(dc); // memDC ist jetzt ein DC mit den gleichen attributen wie dc bmp.CreateCompatibleBitmap(&dc,copyRect.Width(),copyRect.Height()); // bmp ist ne leere bitmap mit dem selben attributen wie selektierte in dc pOldBmp = memDC.SelectObject(&bmp); // du hast die leere bitmap (schwarz) in den memDC selektiert // du kopierst jetzt die den memDC (schwarz) in den paint-dc dc.BitBlt(0,0,copyRect.Width(),copyRect.Height(),&memDC,0,0,SRCCOPY);
klar dass da was schwarzes raus kommt
-
Kannst du mir denn nen Hinweis geben, wie ich das beheben könnte?
Ich dachte zuerst an SetBkColor aber das geht nicht.
Übersehe ich da irgendwas?
Bin echt verzweifelt und deprimiert, dass ich das nicht hinbekomme.Danke übrigens für deine Hilfe.
-
Wie ich das verstehe ist die Bitmap schuld, oder?
Dann schaue ich da nochmal.
-
Kannst du mir denn nen Hinweis geben, wie ich das beheben könnte?
dazu müsste ich erst mal kapieren was genau du vor hast...
zeig mal bitte deinen ganzen code. wo, wie zeichnest du die diagramme?
-
Ok, im Grunde übernehme ich Daten über die RS-232-Schnittstelle, welche ich dann grafisch darstellen möchte.
Da es dann aber ziemlich flackert, will ich das Doublebuffering nutzen, was ich ja mit CreateCompatibleDC & Co realisiere.Mein Code (zeichne hier nur in die erste Region, Diagram im eigentlichen Sinn noch nicht fertig [Achsen, Beschriftung <- ist aber auch unwichtig]):
CPaintDC dc(this); CRect copyRect; GetClientRect(©Rect); HDC memDC = CreateCompatibleDC(dc); HBITMAP bmp = CreateCompatibleBitmap(dc,copyRect.Width(),copyRect.Height()); SelectObject(memDC,bmp); //Erstelle mit Gdiplus regions Gdiplus::Graphics gdi(memDC); static Gdiplus::Point coords1[500]; static Gdiplus::Point coords2[500]; Gdiplus::Rect speedRect(200,75,400,300); Gdiplus::Rect currentRect(605,75,400,300); Gdiplus::Region gdiSpeed(speedRect); Gdiplus::Region gdiCurrent(currentRect); Gdiplus::Pen plotPen(Gdiplus::Color(255,0,170,0)); Gdiplus::SolidBrush regionBrush(Gdiplus::Color(255,255,255,255)); Gdiplus::Status status; // nur zum Überprüfen status = gdi.FillRegion(®ionBrush, &gdiSpeed); status = gdi.FillRegion(®ionBrush, &gdiCurrent); gdi.SetClip(&gdiSpeed, Gdiplus::CombineModeReplace); // zeichne erst in die erste region if (timercounter == 0) // wird im timer inkrementiert { coords1[timercounter].X = 0; coords1[timercounter].Y = 220; coords2[timercounter].X = 0; coords2[timercounter].Y = 220; } else { coords1[timercounter].X = timercounter; coords1[timercounter].Y = Data*2; //Data enthält die empfangenen Daten coords2[timercounter].X = coords1[timercounter-1].X; coords2[timercounter].Y = coords1[timercounter-1].Y; } for (int i = 0; i <= timercounter;i++) { gdi.DrawLine(&plotPen,coords2[i].X+205,coords2[i].Y+80,coords1[i].X +205,coords1[i].Y+80); } BitBlt(dc,0,0,copyRect.Width(),copyRect.Height(),memDC,0,0,SRCCOPY); CDialog::OnPaint();
Danke, für die Mühe.
-
hmmm.. funktioniert bestens bei mir (bis auf das problem das nach ~500 durchläufen gar nix mehr passiert weil ein DeleteObject(bmp) und DeleteDC (memDC) fehlt => resource fertig
)
-
Und es ist bei dir nicht schwarz???
Die DeleteDC und DeleteObject - Methoden habe ich mittlerweile hinzugefügt.
Vielleicht sollte ich mal versuchen, alles zu rebuilden.
-
Also, ich habe nun alles rebuilded, habe auch die Dateien aus dem Debug-Ordner gelöscht, dann noch mal aufbauen lasse, aber es hilft nichts.
Kann das vielleicht damit zusammenhängen, dass ich diesen Dialog aus einem anderen Dialog öffne? Wüsste zwar nicht warum, aber villeicht wäre das ein Unterschied zu dir.
-
Falls du es mit meinem code testen willst (sorry ist WinAPI keine MFC, aber um schnell was zu testen baue ich immer nur ne WinMain..
):
#include <windows.h> #include <gdiplus.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wcex; memset(&wcex,0,sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)COLOR_BACKGROUND; wcex.lpszMenuName = NULL; wcex.lpszClassName = "test"; RegisterClassEx(&wcex); Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, 0); HWND hWnd = CreateWindow("test", "test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); SetTimer(hWnd,0,100,0); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; } static int timercounter = 0; LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_TIMER: timercounter++; if(timercounter>=500) timercounter = 0; InvalidateRect(hWnd,NULL,FALSE); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); { // dein code (auf WinAPI umgebaut) RECT copyRect; GetClientRect(hWnd,©Rect); long width = copyRect.right - copyRect.left; long height = copyRect.bottom - copyRect.top; HDC memDC = CreateCompatibleDC(hdc); HBITMAP bmp = CreateCompatibleBitmap(hdc,width,height); HBITMAP oldBmp = (HBITMAP)SelectObject(memDC,bmp); Gdiplus::Graphics gdi(memDC); static Gdiplus::Point coords1[500]; static Gdiplus::Point coords2[500]; Gdiplus::Rect speedRect(200,75,400,300); Gdiplus::Rect currentRect(605,75,400,300); Gdiplus::Region gdiSpeed(speedRect); Gdiplus::Region gdiCurrent(currentRect); Gdiplus::Pen plotPen(Gdiplus::Color(255,0,170,0)); Gdiplus::SolidBrush regionBrush(Gdiplus::Color(255,255,255,255)); Gdiplus::Status status; status = gdi.FillRegion(®ionBrush, &gdiSpeed); status = gdi.FillRegion(®ionBrush, &gdiCurrent); gdi.SetClip(&gdiSpeed, Gdiplus::CombineModeReplace); if (timercounter == 0) { coords1[timercounter].X = 0; coords1[timercounter].Y = 220; coords2[timercounter].X = 0; coords2[timercounter].Y = 220; } else { coords1[timercounter].X = timercounter; coords1[timercounter].Y = 100*2; //Data enthält die empfangenen Daten coords2[timercounter].X = coords1[timercounter-1].X; coords2[timercounter].Y = coords1[timercounter-1].Y; } for (int i = 0; i <= timercounter;i++) { gdi.DrawLine(&plotPen,coords2[i].X+205,coords2[i].Y+80,coords1[i].X +205,coords1[i].Y+80); } BitBlt(hdc,0,0,width,height,memDC,0,0,SRCCOPY); SelectObject(memDC,oldBmp); DeleteObject(bmp); DeleteDC(memDC); } EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
zeichnet ne schöne linie
-
Guten Morgen,
danke für den Quellcode. Ich habe den sofort mal ausprobiert. Er zeichnet auch eine schöne Linie in einem weissen Rechteck, jedoch ist der Hintergrund bei mir immer noch schwarz!?!Also, so sieht es aus (kann leider keine Screenshots hochladen):
* = schwarz
| = weiss (ausser an den Rändern)-----------------------------------------------------------------------------------
||
|diese Fläche ist schwarz|
|***** -----------------------******** -------------------------------|
|| ||||||||||||||||||||||||||||||||||||||||||||||||||||||
||||diese ist weiss|||||diese ist auch weiss||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|||hier wird|||||||||||||||||||||||||||||||||||||||||||
||||||gezeichnet!||||||||||||||||||||||||||||||||||||
|***** ---------------------- ******** ------------------------------- |
|**********************************************|
-----------------------------------------------------------------------------------.....
Für nen Augenblick dachte ich, es liegt an COLOR_BACKGROUND, denn in der MSDN stehtCOLOR_BACKGROUND
DesktopUND dann habe ich einfach mal bei mir unter Anzeigeneinstellung geschaut und da ist eingestellt, dass der Desktop die Farbe Schwarz zugewiesen hat. (Ich weiss, ist eigentlich eine saublöde Idee, aber wenn man verzweifelt ist...
) Eine Änderung brachte auch nichts.
Aber es scheint an irgendeiner Einstellung zu liegen, denn sonst würden wir ja auf jeden Fall das Gleiche sehen müssen.
-
Ist dosch klar das der hintergrund schwarz ist, wie gesagt die bitmap ist leer und du zeichnerst da ja nur dein diagramm rein.
Wenn du den hintergrund vom orgrinal fenster haben willst mache ein
BitBlt(memDC,0,0,width,height,hdc,0,0,SRCCOPY);
nach dem
HBITMAP oldBmp = (HBITMAP)SelectObject(memDC,bmp);
-
So langsam sehe ich meinen Fehler.
Also,
1.zuerst erstelle ich ein BITMAP, das ist aber leer
2.dann sollte ich das originale Fenster (DC) dort hinein kopieren, damit es auch dieselbe Hintergrundfarbe besitzt
3.dann zeichne ich da hinein, um
4.letztendlich alles wieder in das original Fenster hinein zu kopieren. ;
Bei deiner Anwendung klappt es auch. Bei meiner kriege ich das richtige DC nicht?!?
CPaintDC dc(this); CRect copyRect; GetClientRect(©Rect); HDC memDC = CreateCompatibleDC(dc); HBITMAP bmp = CreateCompatibleBitmap(dc,copyRect.Width(),copyRect.Height()); HBITMAP oldBmp = (HBITMAP)SelectObject(memDC,bmp); BitBlt(memDC,0,0,copyRect.Width(),copyRect.Height(),dc,0,0,SRCCOPY);
Wenn ich nur dc da übergebe ist immer noch alles schwarz?
Vielleicht noch ein kleiner Hinweis?
Danke.EDIT: Sehe gerade, dass ich was Falsches geschrieben habe: Wenn ich dc übergebe bleibt es nicht schwarz, sondern übernimmt die Farben,Fläche des darunterliegenden Fenster.
-
Ok, kann den Fehler vielleicht eingrenzen:
Und zwar gebe ich in der Behandlung der Nachricht WM_ERASEBKGND normalerweise einfach einTRUE
zurück.
Ändere ich das wieder aufreturn CDialog::OnEraseBkgnd(pDC);
um, dann ist auch der Hintergrund wieder grau!
Leider geht die Performance ziemlich in den Keller!