Screenshot unter 500KB



  • Was ist denn nun dein Problem mit GDI+? Alternativ kannst du WIC verwenden. Oder einen eigenen PNG-Encoder schreiben.



  • GDI+ ist doch (seit Windows XP) Teil der WinAPI.
    Was ist dein Problem damit?



  • Th69 schrieb:

    GDI+ ist doch (seit Windows XP) Teil der WinAPI.
    Was ist dein Problem damit?

    So ist es.

    Im MSDN Beispiel "Setting JPEG Compression Level" ist (fast) alles drin, was man braucht um ein BMP als
    JPG zu speichern. Man muss noch die GetEncoderClsid() dazukopieren und die Gdiplus.lib dazulinken.
    Windows GDI+ kann Images als BMP,GIF,JPEG,PNG,TIFF und einiges andere speichern. Als Feature kann
    man im Beispiel sogar noch die quality zwischen 0 .. 100 einstellen!

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms533814.aspx

    Das ganze kompiliert als Win32 Konsolenanwendung mit VS2010 unter XP fehlerfrei.



  • Du gehst das falsch an. Du meinst, du kannst dir aus dem Internet einfach was zusammenkopieren ohne es zu verstehen und das funzt. Das funzt aber leider nicht.
    Das einzige was du brauchst, ist das hier:

    // get the device context of the screen
    HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);     
    // and a device context to put it in
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
    
    int x = GetDeviceCaps(hScreenDC, HORZRES);
    int y = GetDeviceCaps(hScreenDC, VERTRES);
    
    // maybe worth checking these are positive values
    HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDc, x, y);
    
    // get a new bitmap
    HBITMAP hOldBitmap = SelectObject(hMemoryDC, hBitmap);
    
    BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
    hBitmap = SelectObject(hMemoryDC, hOldBitmap);
    
    // clean up
    DeleteDC(hMemoryDC);
    DeleteDC(hScreenDC);
    

    http://stackoverflow.com/questions/3291167/how-to-make-screen-screenshot-with-win32-in-c

    Das ist der Code um einen Screenshot vom Desktop zu machen.
    Wenn du das als BMP abspeicherst, hast du natürlich eine riesige Datei.
    Dann der Spruch "ich will das aber nur mit WinAPI machen und ohne Library" entspricht ungefähr dem equivalent von "klar ich laufe gerne über heisse kohlen mit einem Messer im Rücken". Auf Low Level Shit zu bestehen bringt dich auch nicht weiter. Als lass den scheiss und machs richtig. Benutz ne image lib wie freeimage oder libpng und speichers als PNG, JPG oder was auch immer um die Zielgröße zu erreichen. Oder benutz halt GDI+.
    Aber hör auf dir Code zusammenzukopieren ohne ihn im Ansatz zu verstehen.
    Vor allem sowas schreckliches wie dein erster Code Teil mit den Haufen fake keyboard inputs.
    Rechtschreibfehler sind mir grade wurscht, is spät. Gute Nacht.



  • Scorcher24 schrieb:

    Das einzige was du brauchst, ist das hier:

    HWND DesktopHwnd = GetDesktopWindow(); 
        RECT DesktopParams; 
        HDC DevC = GetDC(DesktopHwnd); 
        GetWindowRect(DesktopHwnd,&DesktopParams); 
        DWORD Width = DesktopParams.right - DesktopParams.left; 
        DWORD Height = DesktopParams.bottom - DesktopParams.top;
    

    Das ist der Code um einen Screenshot vom Desktop zu machen.

    Der Code macht wohl noch keinen Screenshot, aber es ist der richtige Weg als Basis des "gefundenen" Code aus

    void ScreenShot(char* BmpName)
    

    zu verwenden und die Bitmap platzsparend in einem effizienten Format wie JPG zu speichern.

    Alle dafür notwendigen Quellen wurden hier bereits angesprochen.

    Scorcher24 schrieb:

    Vor allem sowas schreckliches wie dein erster Code Teil mit den Haufen fake keyboard inputs.

    Der Code ist so schrecklich, das ich ganz schnell drüber wegscrollen musste. Das man sowas überhaupt
    versucht und es tatsächlich zu einer Lösung kommt - wenn auch einer eher schlechten - ist auf jeden Fall
    mal ein Lösungsansatz.

    Viel schlimmer finde ich Leute, die garnichts recherchiert haben und die "gebratenen Tauben" erwarten.

    Den Code mit den keyboard inputs bitte schnell abhaken und den anderen Code
    verwenden.



  • Hab den Code ersetzt :P.
    Sorry, meine 4 Uhr morgens Antworten sind nicht meine besten haha.
    Zudem war ich grad angepisst, aber das is ne andere Geschichte.



  • Scorcher24 schrieb:

    Hab den Code ersetzt :P.
    Sorry, meine 4 Uhr morgens Antworten sind nicht meine besten haha.
    Zudem war ich grad angepisst, aber das is ne andere Geschichte.

    Kann ja mal passieren ...

    Leider scheint mittags auch der Wurm drin zu sein.

    1. width, height und x, y durcheinander
    2. SelectObject() muss auf (HBITMAP) gecastet werden

    // get a new bitmap
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
    

    So hat es vermutlich nie gelaufen 😕

    Mit den (minimalen) Änderungen passt es jetzt fast. Wenn man es jetzt noch etwas nett einpackt ist es auch
    schon in dem Format das benötigt wird ...

    Bitmap* GetScreenDump()
    {
        // get the device context of the screen
    
        ...
    
        Bitmap *pbmp = Bitmap::FromHBITMAP((HBITMAP)hBitmap, NULL); 
        DeleteObject(hBitmap); 
    
        return pbmp;
    }
    

    Und noch eben eingesetzt:

    // Get an image from the disk.
       // Image* image = new Image(L"Shapes.bmp");
    
       Image* image = GetScreenDump(); // The Bitmap class inherits from Image
    

    Ein JPG mit quality=100 ist bei 1920x1080 dann etwas über 600kB gross.



  • Danke für die vielen Antworten, ich habe jetzt eingesehen das ich die GDI+ verwenden muss, war ein bisschen durch den Wind 😉
    Jetzt mal mein Code der hoffentlich nicht so schrecklich ist wie der mit dem Paint-Versuch 😃

    BOOL GetEncoderClsid(wchar_t *wchMimeType, CLSID& clsidEncoder)
    {
    	UINT uiNum   = 0;
    	UINT uiSize  = 0;
    	BOOL bOk     = FALSE;
    
    	Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
    	Gdiplus::GetImageEncodersSize(&uiNum, &uiSize);
    
    	if( uiSize > 0 )
    	{
    		pImageCodecInfo = (Gdiplus::ImageCodecInfo *)new char[uiSize];
    
    		if( pImageCodecInfo )
    		{
    			Gdiplus::GetImageEncoders(uiNum, uiSize, pImageCodecInfo);
    
    			for( UINT i = 0; i < uiNum; i++ )
    			{
    				if( wcscmp(pImageCodecInfo[i].MimeType, wchMimeType) == 0 )
    				{
    					clsidEncoder = pImageCodecInfo[i].Clsid;
    					bOk = TRUE;
    				}
    			}
    		}
    
    		delete pImageCodecInfo;
    	}        
    
    	return bOk;
    }
    
    void BMPtoPNG(const char* filenamebmp,const wchar_t* filenamepng){
    
    	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    	ULONG_PTR                    gdiplusToken;
    	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
    	HBITMAP hbm = (HBITMAP)LoadImage( NULL,
    		convertCharArrayToLPCWSTR(filenamebmp),
    		IMAGE_BITMAP,
    		0,
    		0,
    		LR_LOADFROMFILE );
    
    	if( hbm )
    	{
    		Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromHBITMAP(
    			hbm, (HPALETTE)GetStockObject(DEFAULT_PALETTE) );
    
    		if( pBitmap )
    		{
    			CLSID clsidEncoder;
    
    			if( GetEncoderClsid(L"image/png", clsidEncoder) )
    			{
    
    				pBitmap->Save(filenamepng, &clsidEncoder, NULL);
    			}
    
    			delete pBitmap;
    		}
    
    		DeleteObject(hbm);
    	}
    
    	Gdiplus::GdiplusShutdown(gdiplusToken);
    }
    
    void ScreenBMP(char*BmpName)
    {
    	HWND DesktopHwnd = GetDesktopWindow();
    	RECT DesktopParams;
    	HDC DevC = GetDC(DesktopHwnd);
    	GetWindowRect(DesktopHwnd,&DesktopParams);
    	DWORD Width = DesktopParams.right - DesktopParams.left;
    	DWORD Height = DesktopParams.bottom - DesktopParams.top;
    
    	DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)+1*(Width*Height*3));
    	char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize);
    
    	PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
    	PBITMAPINFOHEADER BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
    
    	BFileHeader->bfType = 0x4D42; // BM
    	BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
    	BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    
    	BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
    	BInfoHeader->biPlanes = 1;
    	BInfoHeader->biBitCount = 24;
    	BInfoHeader->biCompression = BI_RGB;
    	BInfoHeader->biHeight = Height;
    	BInfoHeader->biWidth = Width;
    
    	RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)];
    	RGBTRIPLE color;
    
    	HDC CaptureDC = CreateCompatibleDC(DevC);
    	HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height);
    	SelectObject(CaptureDC,CaptureBitmap);
    	BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY|CAPTUREBLT);
    	GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,(LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
    
    	DWORD Junk;
    	HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
    	WriteFile(FH,BmpFileData,FileSize,&Junk,0);
    	CloseHandle(FH);
    	GlobalFree(BmpFileData); 
    }
    

    Aufrufen tuh ich das ganze dann einfach hiermit :

    ScreenBMP("test.bmp");	
    BMPtoPNG("test.bmp","test.png");             
    DeleteFile("test.bmp");
    

    Hoffentlich ist der Code soweit inordnung 🙂

    Was mich allerdings wundert, der Screenshot ist manchmal nur 200KB groß und manchmal ganze 1.12MB. Woran liegt das ? 😕



  • Danke Scorcher24 für deine Wach rüttelnden Worte & für das Codebeispiel.
    Das mit den Fake Keyboards tut mir leid, aber ich war am verzweifeln.
    Außerdem wollte ich ohne einen Lösungsansatz nicht hierher kommen (auch wenn er grottig ist).

    An alle anderen die es bis hierher mit mir ausgehalten haben auch ein großes DANKE 🙂



  • TimDerVerzweifelte schrieb:

    Danke für die vielen Antworten, ich habe jetzt eingesehen das ich die GDI+ verwenden muss, war ein bisschen durch den Wind 😉
    Jetzt mal mein Code der hoffentlich nicht so schrecklich ist wie der mit dem Paint-Versuch 😃

    Schön für die Einsicht - besser spät als nie.

    Allerdings wurde auch bereits erwähnt, das es Bibliotheken wie LibPNG gibt, die das auch ohne Zutun von Microsoft hinbekommen.
    http://de.wikipedia.org/wiki/Libpng

    Leider ist Dein Code immer noch grottig.

    Es wird zwar kein externes Programm mehr benötigt, dafür wird aber aber nun eine unnötige BMP-Datei erzeugt.
    Ich hatte Dir bereits alle relevanten Codefragmente vorgestellt, um den Screendump DIREKT komprimiert speichern.

    Leider wird bei Dir das Image erst als BMP gespeichert

    HANDLE FH = CreateFileA( BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
    WriteFile(FH,BmpFileData,FileSize,&Junk,0);
    

    nur um es direkt wieder zu laden und umzuformen.

    HBITMAP hbm = (HBITMAP)LoadImage( NULL, convertCharArrayToLPCWSTR(filenamebmp), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
    

    TimDerVerzweifelte schrieb:

    Hoffentlich ist der Code soweit inordnung 🙂

    Nee, nicht wirklich.

    TimDerVerzweifelte schrieb:

    Was mich allerdings wundert, der Screenshot ist manchmal nur 200KB groß und manchmal ganze 1.12MB. Woran liegt das ? 😕

    Das hängt natürlich vom Inhalt ab. Ein leerer Desktop hat eben weniger Informationen, die komprimiert werden müssen.


Anmelden zum Antworten