MFC Zeichnung erst anzeigen,wenn komplett in DeviceContext geschrieben, wie?

Albus

Grünschnabel
Einen schönen Abend!
Ich habe ein kleines Programmchen in MFC gebastelt, welches ein bestimmtes Bild in verschiedenen Kanäle splittert.
hier die Funktion:

Code:
CBitmap tempbmp;
	CString fileName;
	CFileDialog dlg(TRUE,NULL,NULL,OFN_ENABLESIZING|OFN_EXPLORER|OFN_FILEMUSTEXIST,
					L"Bitmap(*.bmp)|*bmp||",this,0,TRUE);
	if (dlg.DoModal() == IDOK)
	{
		fileName = dlg.GetPathName();

		HBITMAP hBmp = (HBITMAP)::LoadImage(
                NULL,
                fileName,
                IMAGE_BITMAP,
                0,
                0,
                LR_LOADFROMFILE|LR_CREATEDIBSECTION
                );
	
		tempbmp.Attach(hBmp);

		CClientDC dc(this);
		CDC cDC;
		cDC.CreateCompatibleDC(&dc);
		CBitmap *pOldbmp = cDC.SelectObject(&tempbmp);
		BITMAP  bi;
		tempbmp.GetBitmap(&bi);
		

		dc.BitBlt(0,0,bi.bmWidth,bi.bmHeight,&cDC,0,0,SRCCOPY);

		cDC.SelectObject(pOldbmp);

		for (int i=0; i<bi.bmWidth; i++)
		{
			for (int j=0; j<bi.bmHeight; j++)
			{
				BYTE r,g,b,y,u,v;
				COLORREF rgb= RGB(0,0,0);
				rgb = dc.GetPixel(i,j);
				
				r = GetRValue(rgb);
				g = GetGValue(rgb);
				b = GetBValue(rgb);

				//y = 0,299*r +0,587*g +0,114*b;		//alt
				y = ( (  66 * r + 129 * g +  25 * b + 128) >> 8) +  16;
				u = ( ( -38 * r -  74 * g + 112 * b + 128) >> 8) + 128;
				v = ( ( 112 * r -  94 * g -  18 * b + 128) >> 8) + 128;
				
				/*
				Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16
				U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128
				V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128

				C = Y - 16
				D = U - 128
				E = V - 128

				R = clip(( 298 * C           + 409 * E + 128) >> 8)
				G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
				B = clip(( 298 * C + 516 * D           + 128) >> 8)
				*/

				dc.SetPixel(i+bi.bmWidth,j,RGB(y,y,y));
				dc.SetPixel(i,j+bi.bmHeight,RGB(u,u,u));
				dc.SetPixel(i+bi.bmWidth,j+bi.bmHeight,RGB(v,v,v));		
			}
		}
	}
	else
	{
	}

Das Problem ist nur, dass man sieht, wie die die 3 Kanäle (bilder) gezeichnet werden :

Code:
for (int i=0; i<bi.bmWidth; i++)
		{
			for (int j=0; j<bi.bmHeight; j++)
			{
				BYTE r,g,b,y,u,v;
				COLORREF rgb= RGB(0,0,0);
				rgb = dc.GetPixel(i,j);
				
				r = GetRValue(rgb);
				g = GetGValue(rgb);
				b = GetBValue(rgb);

				//y = 0,299*r +0,587*g +0,114*b;		//alt
				y = ( (  66 * r + 129 * g +  25 * b + 128) >> 8) +  16;
				u = ( ( -38 * r -  74 * g + 112 * b + 128) >> 8) + 128;
				v = ( ( 112 * r -  94 * g -  18 * b + 128) >> 8) + 128;
				
				/*
				Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16
				U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128
				V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128

				C = Y - 16
				D = U - 128
				E = V - 128

				R = clip(( 298 * C           + 409 * E + 128) >> 8)
				G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
				B = clip(( 298 * C + 516 * D           + 128) >> 8)
				*/

				dc.SetPixel(i+bi.bmWidth,j,RGB(y,y,y));     // Hier wird es mit jedem Durchlauf gezeichnet :-(
				dc.SetPixel(i,j+bi.bmHeight,RGB(u,u,u));
				dc.SetPixel(i+bi.bmWidth,j+bi.bmHeight,RGB(v,v,v));		
			}
		}

Wie kann man erst zeichnen, wenn die Schleife komplett durchlaufen ist? (so dass, das BIld gleich "komplett" angezeigt wird )
Noch eine Frage: Wenn ich das Fenster minimiere oder schiebe, verschwindet die Zeichnung. Was kann man dagegen tun?
 
1)
Erstelle dir eine neue Bitmap mit dem richtigen Dimensionen und male die Kanäle da hinein. Nicht direkt auf den ClientDC zeichnen. Daran hängt dann auch das zweite Problem:

2)
In OnPaint (WM_PAINT) wird dann natürlich dein Bild wieder übermalt. In OnPaint machst du dann einfach ein simples BitBlt von deiner Puffer-Bitmap auf den PaintDC. Geht dann auch schneller.

Merke: Man sollte nie ausserhalb von WM_PAINT schreibend auf den DC des Fensters zugreifen.
 
Ich habe mein Code bisschen geändert, nun wird es neu gezeichnet, wenn ich das Fenster minimiere oder seine Größe ändere.
Problem ist nur, dass ich immernoch sehe, wie das BIld aufgebaut wird.

Meine Situation:
Ich taste das Bild aus ClientDC (bei mir dc-Variable), dann schreibe ich die Werte für Y,U,V in Memory Device Context (cDC bei mir)
Code:
CBitmap tempbmp;

	HBITMAP hBmp = (HBITMAP)::LoadImage(
                NULL,
                myDatei,
                IMAGE_BITMAP,
                0,
                0,
                LR_LOADFROMFILE|LR_CREATEDIBSECTION
                );
	
		tempbmp.Attach(hBmp);

		CClientDC dc(this);
		CDC cDC;
		cDC.CreateCompatibleDC(&dc);
		CBitmap *pOldbmp = cDC.SelectObject(&tempbmp);
		BITMAP  bi;
		tempbmp.GetBitmap(&bi);
		

		dc.BitBlt(0,0,bi.bmWidth,bi.bmHeight,&cDC,0,0,SRCCOPY);      // Hier wird das erste (original) Bild sofort gezeigt

		cDC.SelectObject(pOldbmp);
		
		for (int i=0; i<bi.bmWidth; i++)                    //Hier laufe ich durch das Bild (das was auf ClientDC gezeichnet wurde)
		{
			for (int j=0; j<bi.bmHeight; j++)
			{
				BYTE r,g,b,y,u,v;
				COLORREF rgb= RGB(0,0,0);
				rgb = dc.GetPixel(i,j);                    //Hier lese ich jeden Pixel aus!
				
				r = GetRValue(rgb);
				g = GetGValue(rgb);
				b = GetBValue(rgb);

				//y = 0,299*r +0,587*g +0,114*b;		//alt
				y = ( (  66 * r + 129 * g +  25 * b + 128) >> 8) +  16;
				u = ( ( -38 * r -  74 * g + 112 * b + 128) >> 8) + 128;
				v = ( ( 112 * r -  94 * g -  18 * b + 128) >> 8) + 128;
				
				/*
				Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16
				U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128
				V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128

				C = Y - 16
				D = U - 128
				E = V - 128

				R = clip(( 298 * C           + 409 * E + 128) >> 8)
				G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
				B = clip(( 298 * C + 516 * D           + 128) >> 8)
				*/

				cDC.SetPixel(i+bi.bmWidth,j,RGB(y,y,y));                      //Hier schreibe ich alle Werte in Memory Device Context (glaube ich zumindest :-)   )
				cDC.SetPixel(i,j+bi.bmHeight,RGB(u,u,u));
				cDC.SetPixel(i+bi.bmWidth,j+bi.bmHeight,RGB(v,v,v));
			}
		}

Im Post oben stand, dass ich neues BITMAP erstellen soll und da rein malen :). Wie kann ich das erreichen? Gibt es eine Funktion, mit der ich aus CDC in BITMAP schreiben kann und dann alles zeigen (sofort, ohne dem lahmen Aufbau) ?:confused:

Ach ja, wenn ich z.B. das Fenster so schiebe, dass es nur teilweise gezeigt wird und ein Teil davon nicht zu sehen ist--->und dann wieder so auf dem Desktop platziere, dass es komplett zu sehen ist, hängt die Anwendung bisschen, weil die 3 neue Bilder gezeichnet werden. (hier im Code schreibe ich in cDC, so dass man nichts sieht, aber die Belastung durch das Zeichnen spürbar ist.:(
 
Jedes CDC kann an eine Bitmap gehängt werden (SelectObject, hast du ja oben auch schon mal gemacht). Du kannst mit CreateCompatibleBitmap eine erstellen.

Evtl. anderer Vorschlag: Arbeite mit DIBSections. Die kannst du ähnlich wie Bitmaps benutzen, aber du bekommst auch einen Pointer auf die Daten. SetPixel/GetPixel schlagen nämlich nicht wirklich Geschwindigkeitsrekorde.
 
Ich habe mein Programm bisschen geändert, so dass es nun sofort und deutlich schneller angezeigt wird und alle möglichen "Verformungen" des Fensters sind jetzt möglich ohne, dass das Bild verloren geht oder langsam aufgebaut wird.

Problem ist nur, wenn ich mein Menüpunkt wieder aufrufe, kommt es zu einem Fehler.
Code:
void Ccolor2bwView::OnDraw(CDC* /*pDC*/)
{
	Ccolor2bwDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: Code zum Zeichnen der systemeigenen Daten hinzufügen
	CClientDC mainDC(this);

	switch (WasMaleIch)
	{
	case 1:
		{
			//cDC.RestoreDC(savedDcInt);
			mainDC.BitBlt(0,0,2*bi.bmWidth,2*bi.bmHeight,&cDC,0,0,SRCCOPY);
		}
		break;
	}
}
Code:
void Ccolor2bwView::OnToolsB()              //Menüaufruf
{
	Dialog1();    
}
Code:
void Ccolor2bwView::FarbeZuGrau()
{
	CBitmap tempbmp;

	HBITMAP hBmp = (HBITMAP)::LoadImage(
                NULL,
                myDatei,
                IMAGE_BITMAP,
                0,
                0,
                LR_LOADFROMFILE|LR_CREATEDIBSECTION
                );
	
		tempbmp.Attach(hBmp);

		CClientDC dc(this);
		
		cDC.CreateCompatibleDC(&dc);    // Ich glaube der Fehler passiert hier beim zweiten Aufruf (wenn ich den Menüpunkt auswähle)
		

		CBitmap *pOldbmp = cDC.SelectObject(&tempbmp);
		
		tempbmp.GetBitmap(&bi);
				
		dc.BitBlt(0,0,bi.bmWidth,bi.bmHeight,&cDC,0,0,SRCCOPY);

		CBitmap pufferBmp;
		pufferBmp.CreateCompatibleBitmap(&dc,2*bi.bmWidth,2*bi.bmHeight);
		
		cDC.SelectObject(&pufferBmp);
		
		for (int i=0; i<bi.bmWidth; i++)
		{
			for (int j=0; j<bi.bmHeight; j++)
			{
				BYTE r,g,b,y,u,v;
				COLORREF rgb= RGB(0,0,0);
				rgb = dc.GetPixel(i,j);
				
				r = GetRValue(rgb);
				g = GetGValue(rgb);
				b = GetBValue(rgb);

				//y = 0,299*r +0,587*g +0,114*b;		//alt
				y = ( (  66 * r + 129 * g +  25 * b + 128) >> 8) +  16;
				u = ( ( -38 * r -  74 * g + 112 * b + 128) >> 8) + 128;
				v = ( ( 112 * r -  94 * g -  18 * b + 128) >> 8) + 128;
				
				/*
				Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16
				U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128
				V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128

				C = Y - 16
				D = U - 128
				E = V - 128

				R = clip(( 298 * C           + 409 * E + 128) >> 8)
				G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8)
				B = clip(( 298 * C + 516 * D           + 128) >> 8)
				*/
				cDC.SetPixel(i,j,RGB(r,g,b));
				cDC.SetPixel(i+bi.bmWidth,j,RGB(y,y,y));
				cDC.SetPixel(i,j+bi.bmHeight,RGB(u,u,u));
				cDC.SetPixel(i+bi.bmWidth,j+bi.bmHeight,RGB(v,v,v));
			}
		}
		//savedDcInt = cDC.SaveDC();
		dc.BitBlt(0,0,2*bi.bmWidth,2*bi.bmHeight,&cDC,0,0,SRCCOPY);
}
Code:
void Ccolor2bwView::Dialog1(void)
{
	CFileDialog dlg(TRUE,NULL,NULL,OFN_ENABLESIZING|OFN_EXPLORER|OFN_FILEMUSTEXIST,
					L"Bitmap(*.bmp)|*bmp||",this,0,TRUE);
	if (dlg.DoModal() == IDOK)
	{
		myDatei = dlg.GetPathName();
		FarbeZuGrau();
		WasMaleIch = 1;
	}
	else
	{
	}	
}
irgendwie scheint das Programm nicht klar kommen, wenn ich mein cDC beim zweiten Menüaufruf neu kreiere
mit cDC.CreateCompatibleDC(&dc);
Wie kann ich es umgehen? Soll ich cDC.CreateCompatibleDC(&dc); irgendwo beim Start des Programms machen oder liegt es an was anderes?:confused:

P.S. Diese Variablen sind in *.h Datei deklariert.
Code:
CString myDatei;
BITMAP bi;
int savedDcInt;
CDC cDC;
 
Zuletzt bearbeitet:
In OnDraw der Aufruf von CClientDC mainDC(this);ist totaler mist den dc auf dein Window bekommst du doch übergeben, du mußt nur den Kommentar weg nehmen dann hast du in pDC den Zeiger auf den WindowsDC den du zum Zeichnen benötigst.
Die Aussage das du DIBSections benutzen sollst damit du an die Daten des Bitmaps kommst ist ja nun auch nur eine Halbwahrheit, denn bei CBitmap kommt man genau so an die Daten.

Code:
void CPicture::MakeGrayPicture()
{
	if(!this->m_hObject)
	{
		TRACE( _T("CPicture::MakeGrayPicture Class not Initialised or not Picture Loaded\n"));
		return;
	}

	CPalette pal;
	UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);
	LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];

	pLP->palVersion = 0x300;
	pLP->palNumEntries = 256;

	for( int i=0; i < 256; i++)
	{
		pLP->palPalEntry[i].peRed = i;
		pLP->palPalEntry[i].peGreen = i;
		pLP->palPalEntry[i].peBlue = i;
		pLP->palPalEntry[i].peFlags = 0;
	}

	pal.CreatePalette( pLP );

	BITMAP     bm;
	CBitmap::GetObject(sizeof(BITMAP), (LPSTR)&bm);

	BYTE *bmpBuffer = new BYTE[ bm.bmWidthBytes*bm.bmHeight ];//allocate memory for image 
                                              //byte buffer

	ULONG dwValue=CBitmap::GetBitmapBits(bm.bmWidthBytes*bm.bmHeight,
                   bmpBuffer);//Get the bitmap bits  
                              //into a structure*/
	DWORD* buffer = (DWORD*)bmpBuffer;
	for(ULONG Count = 0;Count < dwValue/4;Count++)
	{
		UINT Index = pal.GetNearestPaletteIndex(buffer[Count]);
		buffer[Count] = RGB(pLP->palPalEntry[Index].peRed,
							pLP->palPalEntry[Index].peGreen,
							pLP->palPalEntry[Index].peBlue);
	}

	delete[] pLP;

	dwValue = CBitmap::SetBitmapBits(bm.bmWidthBytes*bm.bmHeight,bmpBuffer);
	delete[] bmpBuffer;
}

das ist mal eine Funktion aus einer Klasse die von CBitmap abgeleitet ist. Wenn du die CBitmap-aufrufe etwas veränderst hast du schon deine fertige Funktion. Das alles noch in ein Bitmap geschrieben was du dann in OnDraw nur noch Blittest und fertig.
Das Zielbitmap kannst du gleich in der Headerdatei erstellen und beim Initialisieren mit erstellen, fertig ist deine Anwendung.

Noch Fragen?
 

Neue Beiträge

Zurück