Desktop Screenshot als Textur für OpenGL verwenden (Windows)



  • Hallo,

    ich wollte angefangen, für Windows Bildschirmschoner mit OpenGL zu schreiben. Dazu habe ich auch Tutorials usw. gefunden, mit denen das alles soweit klappt.

    Da ich aber nur relativ wenig Ahnung von C / C++ habe (und noch wesentlich weniger von der WinAPI, scheitere ich jetzt daran, vom Bildschirm einen Screenshot zu erstellen und als Textur zu verwenden.

    Und da ich jetzt schon seit ca. 2 Wochen fast täglich das Internet mit verschiedenen Schlagworten (screenshot, capture screen, desktop, opengl, ...) und immer noch nichts gefunden habe, was mir weiterhilft bzw. das was ich ausprobiert habe hat nicht funktioniert.

    Wie verhält sich mein Programm jetzt also? Wenn ich im Quelltext an den mit /* A / bzw. / C */ markierten Stellen die das entsprechend austausche, (also die LoadGLScreenshot() und screenshot nehme), startet das Programm, aber der komplette Bildschirm bleibt schwarz, nix passiert.
    Wenn ich die LoadGLTextures() verwende, funktionierts und alles wird so gezeigt wie ich das mir vorstelle.

    Wenn ich an der mit /* B */ markierten Stelle einen Breakpoint setze, zeigt er mir folgendes an:

    bm.bmType = 0
    bm.bmWidth = 1280
    bm.bmHeight = 1024
    bm.bmWidthBytes = 5120
    bm.Planes = 1
    bm.bmBitsPixel = 32
    bm.bmBits = 0x0

    Ich vermute, dass irgendwie die Bilddaten nicht kopiert werden in das BITMAP, da meines Wissens nach ja 0x0 (bm.bmBits) der NULL-Pointer ist.

    Ich komme aber echt nicht weiter 😕 😞

    Zur Information:
    Wenn ich den Bildschirm per Hand capture (per Druck-Taste) und als *.bmp speichere, funktioniert das Ganze, die Texture-größe dürfte also keine Problem sein (Das Bild lädt übrigens die LoadGLTextures()).

    Ich bin mittlerweile echt am Verzweifeln ...
    (vor allem da der Suchbegriff screenshot vor allem Ergebnisse mit Screenshots von irgendwelchen Programmen liefert 😉 )

    Vielen Dank schon mal für eure Antworten.

    PS: Ich benutze Dev-C++.

    Hier der (relevante) Quelltext:

    #define TEXTURE_COUNT 1
    // textures
    static GLuint texture[TEXTURE_COUNT];
    static GLuint screenshot;
    static GLuint screenshotwidth, screenshotheight;
    
    // prototypes
    void glResetView();
    int LoadGLTextures();  // from NeHe's OpenGL-tutorial
    int LoadGLScreenshot();
    
    void SetupAnimation()
    {
              ...
    
    //  	  if (loadsuccess = LoadGLTextures() )          /* A */                    
      	  if (loadsuccess = LoadGLScreenshot() )        /* A */                    
    	  {
    		  glEnable(GL_TEXTURE_2D); // Texture Mapping aktivieren	
    	  }
    
              ...
    }
    
    int LoadGLScreenshot()
    {
    	HBITMAP 	hBitmap = NULL;
    	BITMAP 		bmp;
    	HDC     	hDCMem = NULL;
    	RECT 		rect;
    
    	int Status = FALSE;
    
    	 // Vorbereitungen
    	hDC = GetDC(GetDesktopWindow());
    	GetClientRect(hWnd, &rect); 
    	int width = rect.right;
    	int height = rect.bottom;
    
            hBitmap = CreateCompatibleBitmap(hDC, width, height);
            hDCMem  = CreateCompatibleDC(hDC);
            SelectObject(hDCMem, hBitmap);
    
     	// Screenshot des Desktops machen
       	BitBlt(hDCMem, 0, 0, width, height, hDC, 0, 0, SRCCOPY);
    
    	if (hBitmap != 0)
    	{
    		GetObject(hBitmap, sizeof(bmp), &bmp); 
    		screenshotwidth = bmp.bmWidth;             /* B */
    		screenshotheight = bmp.bmHeight;
    
    		glGenTextures(1, &screenshot);
    		glBindTexture(GL_TEXTURE_2D, screenshot);
    	        glTexImage2D(GL_TEXTURE_2D, 0, 3, screenshotwidth,
                                 screenshotheight, 0, GL_RGB, 
                                 GL_UNSIGNED_BYTE, bmp.bmBits);		
    
                    // GL_LINEAR wird für GL_TEXTURE_MIN_FILTER genutzt
        	        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    
        	        // GL_LINEAR wird für GL_TEXTURE_MAG_FILTER genutzt
        	        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    
    	        DeleteObject(hBitmap);
    
      	       Status = TRUE;
            }
            return Status;
    }
    
    void drawGL()
    {
    
                    ...
    
    // 		glBindTexture(GL_TEXTURE_2D, texture[0]); /* C */
     		glBindTexture(GL_TEXTURE_2D, screenshot); /* C */
    
                    ...
    
    }
    


  • hmm, schade dass bisher niemand geantwortet hat... hätte echt gedacht, mir kann da jemand helfen. Naja mal sehen wie es weiter geht...

    Denn ich habe mittlerweile ein wenig weitergeforscht und habe bei NeHe eine Funktion gefunden, mit der man über ein HBITMAP (das mit LoadImage aus einer Datei geladen wird) eine Textur erstellt wird. Das funktioniert wunderbar (Quellcode siehe unten). Auch habe ich Funktionen gefunden, die einen Screenshot erstellen und diesen in eine Datei speichern. Auch diese funktioniert.

    Nun habe ich versucht, diese beiden Funktionen zu vereinen und damit mein Problem zu lösen. Dazu wollte ich zum Kopieren der Bilddaten von dem DC des Desktop in den DC des HBITMAP statt der Funktion BitBlt die Funktion StretchBlt verwenden, damit das resultierende BITMAP anschließend die Größe 1024x1024 hat.

    Nur leider funktioniert das nicht, ich erhalte nur ein schwarzes Bild. Die MessageBox (siehe Quelltext unten) gibt folgendes aus:
    BMP is(1024x1024) with 1bpp, 128 byte width

    Auch wenn ich nur die Methode zum Abspeichern eines Screenshot anpasse und das BitBlt durch ein StretchBlt ersetze, wird nur ein vollkommen schwarzes Bitmap mit einer Größe von 1024x1024 gespeichert... 😞

    Ich nehme an, dass ich beim StretchBlt irgendwas nicht richtig mache oder beachte, aber ich weiß wirklich nicht mehr weiter, gerade auch weil ich nur sehr wenig Kenntnis über die WINAPI habe...

    Ich hoffe, dass mir da jemand weiterhelfen kann...

    Danke schon mal für eure Hilfe.

    Ach so, mittlerweile benutze ich kein Dev-C++ mehr, sonder Code::Blocks mit MinGW, aber ich denke, das tut nix zur Sache.

    Quelltext meiner angepassten Funktion:

    BOOL LoadScreenshot(GLuint &texid, GLuint size) {
        HBITMAP hBMP;                           // Handle of The Bitmap
        BITMAP  BMP;                            // Bitmap Structure
    
        HDC     hdcDesktop;                     // Device Context of the desktop
                                                // from where we will copy data
        HDC     hdcBMP;                         // Device Context we will copy
                                                // data to
    
        HWND    hwnd;                           // the desktop window
        RECT    rc;                             // the desktop dimensions
    
        int Width;                              // the width of the desktop
        int Height;                             // the height of the desktop
    
        // get desktop and device context
        hwnd        = GetDesktopWindow();
        hdcDesktop  = GetDC(hwnd);
        Width       = rc.right  - rc.left;
        Height      = rc.bottom - rc.top;
    
        // get desktop dimension
        GetWindowRect(hwnd, &rc);
        Width       = rc.right;
        Height      = rc.bottom;
    
        // prepare HBITMAP
        hdcBMP      = CreateCompatibleDC(hdcDesktop);
        hBMP        = CreateCompatibleBitmap(hdcBMP, size, size);
        HBITMAP hBMPTMP = (HBITMAP) SelectObject(hdcBMP, hBMP);
    
        if (!hBMP) {                            // Does The Bitmap Exist?
                return FALSE;                   // If Not Return False
        }
    
        // copy data from desktop DC to BMP DC with stretching
        // to get 2^n square size
        SetStretchBltMode(hdcDesktop, HALFTONE);                // setup StretchBlt
        StretchBlt( hdcBMP, 0, 0, size, size,                   // Destination
                    hdcDesktop, rc.left, rc.top, Width, Height, // Source
                    SRCCOPY                                     // what to do
                );
    
        // Get The Object, Parameters are:
        // hBMP:        handle to graphics object
        // sizeof(BMP): size of buffer for object information
        // &BMP:        buffer for object information
        GetObject(hBMP, sizeof(BMP), &BMP);
    
        char buffer[1024];
        sprintf(buffer, "BMP is (%ix%i) with %ibpp, %i byte width.", BMP.bmWidth, BMP.bmHeight, BMP.bmBitsPixel, BMP.bmWidthBytes);
        MessageBox(NULL, buffer, "BMP Info", MB_OK);
    
        // switch type
        GLuint type;
        if(BMP.bmBitsPixel == 24) {
            type = GL_BGR_EXT;
        } else if(BMP.bmBitsPixel == 32) {
            type = GL_BGRA_EXT;
        } else {
            return FALSE;                       // BMP is not usable
        }
    
        // Pixel Storage Mode (Word Alignment / 4 Bytes)
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    
        // Typical Texture Generation Using Data From The Bitmap
        glBindTexture(GL_TEXTURE_2D, texid);    // Bind To The Texture ID
        glTexParameteri(GL_TEXTURE_2D,          // Linear Min Filter
                        GL_TEXTURE_MIN_FILTER, GL_LINEAR
                        );
        glTexParameteri(GL_TEXTURE_2D,          // Linear Mag Filter
                        GL_TEXTURE_MAG_FILTER, GL_LINEAR
                        );
        glTexImage2D(   GL_TEXTURE_2D, 0, 3,
                        BMP.bmWidth, BMP.bmHeight,
                        0, GL_BGR_EXT,
                        GL_UNSIGNED_BYTE,
                        BMP.bmBits
                    );
    
        // Release desktop device context
        ReleaseDC(hwnd, hdcDesktop);
    
        // Delete device contexts no longer used
        DeleteDC(hdcBMP);
    
        // Delete The Object
        DeleteObject(hBMP);
        DeleteObject(hBMPTMP);
    
        return TRUE;
    }
    

    Quelltext der Methode zum Laden einer Textur aus einer Datei
    (zu googlen mit "NeHeLoadBitmap"), funktioniert wunderbar:

    BOOL LoadBitmap(LPTSTR szFileName, GLuint &texid)
    {
            HBITMAP hBMP;                           // Handle of The Bitmap
            BITMAP  BMP;                            // Bitmap Structure
    
            glGenTextures(1, &texid);               // Create The Texture
            hBMP = (HBITMAP) LoadImage( GetModuleHandle(NULL),
                                        szFileName,
                                        IMAGE_BITMAP,
                                        0, 0,
                                        LR_CREATEDIBSECTION | LR_LOADFROMFILE );
    
            if (!hBMP) {                            // Does The Bitmap Exist?
                    return FALSE;                   // If Not Return False
            }
    
            // Get The Object, Parameters are:
            // hBMP:        handle to graphics object
            // sizeof(BMP): size of buffer for object information
            // &BMP:        buffer for object information
            GetObject(hBMP, sizeof(BMP), &BMP);
    
            // Pixel Storage Mode (Word Alignment / 4 Bytes)
            glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    
            // Typical Texture Generation Using Data From The Bitmap
            glBindTexture(GL_TEXTURE_2D, texid);    // Bind To The Texture ID
            glTexParameteri(GL_TEXTURE_2D,          // Linear Min Filter
                            GL_TEXTURE_MIN_FILTER, GL_LINEAR
                            );
            glTexParameteri(GL_TEXTURE_2D,          // Linear Mag Filter
                            GL_TEXTURE_MAG_FILTER, GL_LINEAR
                            );
            glTexImage2D(   GL_TEXTURE_2D, 0, 3,
                            BMP.bmWidth, BMP.bmHeight,
                            0, GL_BGR_EXT,
                            GL_UNSIGNED_BYTE,
                            BMP.bmBits
                        );
    
            DeleteObject(hBMP);                     // Delete The Object
    
            return TRUE;                            // Loading Was Successful
    }
    

    Quelltext der Methode zum Speichern eines Screenshots in einer Datei:

    void SaveWindowBitmap(HWND hWnd, LPCTSTR lpszFile) {
        /////////////////////////////////////////////////////////////////
        // Bild aufnehmen
        RECT rc;
        ::GetWindowRect(hWnd, &rc);
        int nWidth  = rc.right  - rc.left;
        int nHeight = rc.bottom - rc.top;
    
        HWND    hWndSc = ::GetDesktopWindow();
        HDC     hdc    = ::GetDC(hWndSc);
        HDC     memDC  = ::CreateCompatibleDC(hdc);
        HBITMAP hbm    = ::CreateCompatibleBitmap(hdc, nWidth, nHeight);
        HBITMAP hbmOld = (HBITMAP)::SelectObject(memDC, hbm);
    
        ::BringWindowToTop(hWnd);
        ::BitBlt(memDC, 0, 0, nWidth,  nHeight, hdc, rc.left, rc.top, SRCCOPY);
        /////////////////////////////////////////////////////////////////
        // ::StretchBlt(memDC, 0, 0, 1024, 1024, hdc, 0, 0, rc.left, rc.top, SRCCOPY);
    
        // Bilddaten ermitteln
        BITMAPINFO bmi;
        ZeroMemory(&bmi, sizeof(bmi));
        bmi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth        = nWidth;
        bmi.bmiHeader.biHeight       = nHeight;
        bmi.bmiHeader.biBitCount     = 24;
        bmi.bmiHeader.biPlanes       = 1;
        bmi.bmiHeader.biCompression  = BI_RGB;
        bmi.bmiHeader.biSizeImage    = 32 * nWidth * nHeight / 8;
    
        BYTE *pbBits = new BYTE[bmi.bmiHeader.biSizeImage];
    
        ::GetDIBits(    memDC,
                        hbm,
                        0,
                        bmi.bmiHeader.biHeight,
                        pbBits,
                        &bmi,
                        DIB_RGB_COLORS
                    );
    
        BITMAPFILEHEADER bfh;
        bfh.bfType      = ('M' << 8) + 'B';
        bfh.bfSize      = sizeof(BITMAPFILEHEADER)
                           + bmi.bmiHeader.biSizeImage
                           + sizeof(BITMAPINFOHEADER);
        bfh.bfReserved1 = 0;
        bfh.bfReserved2 = 0;
        bfh.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    
        /////////////////////////////////////////////////////////////////
        // Bilddaten speichern
        HANDLE hfile = CreateFile(  lpszFile,
                                    GENERIC_WRITE,
                                    0,
                                    0,
                                    OPEN_ALWAYS,
                                    0,
                                    0
                                );
    
        DWORD dwWritten;
        WriteFile(hfile, &bfh,           sizeof(bfh),               &dwWritten, NULL);
        WriteFile(hfile, &bmi.bmiHeader, sizeof(BITMAPINFOHEADER),  &dwWritten, NULL);
        WriteFile(hfile, pbBits,         bmi.bmiHeader.biSizeImage, &dwWritten, NULL);
        CloseHandle(hfile);
    
        ::SelectObject(memDC, hbmOld);
        ::DeleteDC(memDC);
        ::ReleaseDC(hWndSc,hdc);
        ::DeleteObject(hbm);
        delete[] pbBits;
    }
    

Anmelden zum Antworten