Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 11
  1. #1
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563

    C - Fare screenshot

    Salve.
    Ho già cercato su questo forum e da altre parti, senza trovare una risposta soddisfacente: come fare uno screenshot funzionante di qualcosa?

    Evitate per favore di suggerirmi freelibrary: il mio obiettivo è imparare e non fare (come ho già provato)

    codice:
    fipWinImage Img;
    Img.capturewindow(GetDesktopWindow(),GetDesktopWindow());
    Img.save("C:/Ciao.jpg",FIF_JPG);
    Ci sono molte guide su come farlo in C#, che sostanzialmente ricalcano lo stesso codice che vedrete sotto, solo che arrivato ad avere un puntatore HBITMAP, usano la funzione Image::FromhBitmap e ha finito poichè la classe ha tutti i metodi necessari per salvare quest'ultima anche in altri formati.

    Usando msdn e qualche altra referenza sono arrivato a scrivere questo codice

    codice:
    int MakeScreenShoot(void)
    {
    	HWND desktop = GetDesktopWindow();
    	HDC ScreenDesktop = GetDC(desktop);
    
    	int ResX = GetSystemMetrics(SM_CXSCREEN);
    	int ResY = GetSystemMetrics(SM_CYSCREEN);
    
    	HDC MemDesktop = CreateCompatibleDC(ScreenDesktop);
    	HBITMAP tmpbitmap = CreateCompatibleBitmap(MemDesktop,ResX,ResY);
    
    	HBITMAP bitmap = (HBITMAP)SelectObject(MemDesktop,tmpbitmap);
    
    
    	BitBlt(MemDesktop,0,0,ResX,ResY,ScreenDesktop,0,0,SRCCOPY);
    	
    	BITMAPINFO BitInfo;
    
    	memset(&BitInfo,0,sizeof(BITMAPINFO));
    
    	BitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    	BitInfo.bmiHeader.biWidth = ResX;
    	BitInfo.bmiHeader.biHeight = ResY;
    	BitInfo.bmiHeader.biPlanes = 1;
    	BitInfo.bmiHeader.biBitCount = 16;
    	BitInfo.bmiHeader.biCompression = BI_RGB;
    	BitInfo.bmiHeader.biSizeImage = (BitInfo.bmiHeader.biWidth * BitInfo.bmiHeader.biHeight) * 2;
    
    	void *bits = malloc(BitInfo.bmiHeader.biSizeImage);
    	GetDIBits(MemDesktop,bitmap,0,BitInfo.bmiHeader.biHeight,bits,&BitInfo,DIB_PAL_COLORS);
    	HANDLE bmfile = CreateFile("C:\\image.bmp",GENERIC_READ | GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    
    	BITMAPFILEHEADER bitheader;
    
    	bitheader.bfReserved1 = 0;
    	bitheader.bfReserved2 = 0;
    	bitheader.bfType = 0x4d42;
    	bitheader.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
    	bitheader.bfSize = bitheader.bfOffBits + BitInfo.bmiHeader.biSizeImage;
    
    	DWORD bytes;
    
    	WriteFile(bmfile,&bitheader,sizeof(BITMAPFILEHEADER),&bytes,NULL);
    	WriteFile(bmfile,&BitInfo.bmiHeader,sizeof(BITMAPINFOHEADER),&bytes,NULL);
    	WriteFile(bmfile,bits,BitInfo.bmiHeader.biSizeImage,&bytes,NULL);
    	
    
    	CloseHandle(bmfile);
    	free(bits);
    	DeleteDC(MemDesktop);
    	ReleaseDC(desktop,ScreenDesktop);
    	DeleteObject(bitmap);
    	DeleteObject(tmpbitmap);
    
    	return 0;
    
    }
    Una cosa non ho capito: perchè devo fare il SelectObject...a cosa serve fare l'associazione, dato che la copia mi sembra esser fatta in modo "raw" da BitBtl? Abilita lo scambio di dati? Ma poi, se la copia dei bit mi arriva su un puntatore che gli do stesso io, perchè mi chiede un'HBITMAP? Non ne capisco il senso: tra l'altro poi questa HBITMAP sembra servire solo a questo e a nient'altro: esser passata come parametro.

    Apparte il fatto che l'unica cosa che vedo è una bitmap nera (anche se non riescoa capire ancora perchè), la mia intenzione sarebbe salvare almeno in jpg.
    Sulla documentazione MSDN sembra che per fare ciò basta cambiare un flag e settare a 0 il valore bits per pixel.
    Riporto il pezzo per intero
    biCompression
    Specifies the type of compression for a compressed bottom-up bitmap (top-down DIBs cannot be compressed). This member can be one of the following values.
    biSizeImage
    Specifies the size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
    Windows 98/Me, Windows 2000/XP: If biCompression is BI_JPEG or BI_PNG, biSizeImage indicates the size of the JPEG or PNG image buffer, respectively.
    Cosa significa il remark del secondo parametro? Che dimensione gli devo mettere? La massima che voglio poi sarà lui a regolarsi su come fare il controllo qualità?
    Ad ogni modo, scrivendo con lo stesso codice, almeno usando bitmap si vedeva nero, qui invece la da proprio come file corrotto.
    Avete qualche idea su come fare?
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  2. #2

    Re: C - Fare screenshot

    Premessa: se non sai cos'è un DC e come funziona a grandi linee la GDI di Windows dai un'occhiata qui e poi eventualmente chiedi.
    codice:
    int MakeScreenShoot(void)
    {
            //Ottiene un device context relativo a tutto lo schermo
    	HDC ScreenDC = GetDC(NULL);
            //Ottiene le dimensioni dello schermo
    	int ResX = GetSystemMetrics(SM_CXSCREEN);
    	int ResY = GetSystemMetrics(SM_CYSCREEN);
            //Crea un device context in cui verrà selezionata la DIB su cui verrà copiato il contenuto del desktop
    	HDC DestDC = CreateCompatibleDC(NULL);
            //Crea una DIB in memoria con la stessa profondità di colori del desktop
    	HBITMAP DestBitmap = CreateCompatibleBitmap(DestDC,ResX,ResY);
            //Salva la bitmap attualmente selezionata nel DC per ripristinarla in seguito (per poter poi eliminare DestBitmap) e seleziona nel DC DestDC la DIB appena creata
    	HBITMAP OldBitmap = (HBITMAP)SelectObject(MemDesktop,DestBitmap);
            //Copia il contenuto del DC del desktop nel DC che contiene la DIB DestBitmap, che quindi ora conterrà una copia del contenuto del desktop
    	BitBlt(DestDC,0,0,ResX,ResY,ScreenDC,0,0,SRCCOPY);
    	//Da qui in poi il codice che hai scritto diventa casinaro e non mi pare molto corretto; rimando a qui per informazioni su come salvare un HBITMAP
            //...
            //Deseleziona dal DC DestDC la DIB, selezionando la vecchia bitmap che in esso era prima contenuta
            SelectObject(DestDC,OldBitmap);
            //Ora che la DIB è deselezionata la si può cancellare
            DeleteObject(DestBitmap);
            //Elimina il DC di destinazione
    	DeleteDC(DestDC);
            //Rilascia il DC relativo allo schermo
            ReleaseDC(NULL,ScreenDC);
    	return 0;
    }
    Per quanto riguarda il salvataggio in JPEG o PNG, non mi pare che sia possibile a meno di grandi casini; il supporto per questi formati si limita alla possibilità di dare dati JPEG o PNG in input a StretchDIBits e SetDIBitsToDevice (http://msdn.microsoft.com/en-us/libr...08(VS.85).aspx).
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    Ho cambiato completamente approccio appoggiandomi quindi sulle GDI+, facendo quest'altro codice

    codice:
    int MakeScreenShot(void)
    {
    	
    	HDC ScreenDesktop = GetDC(NULL);
    	HDC MemDesktop = CreateCompatibleDC(ScreenDesktop);
    
    	int ResX = GetDeviceCaps(ScreenDesktop,HORZRES);
    	int ResY = GetDeviceCaps(ScreenDesktop,VERTRES);
    
    	HBITMAP bitmap = CreateCompatibleBitmap(MemDesktop,ResX,ResY);
    
    	int value = BitBlt(MemDesktop,0,0,ResX,ResY,ScreenDesktop,0,0,SRCCOPY);
    
    	HPALETTE Palette = (HPALETTE)GetCurrentObject(MemDesktop,OBJ_PAL);
    	
    	ULONG_PTR gditoken;
    	GdiplusStartupInput input;
    	
    	GdiplusStartup(&gditoken,&input,NULL);
    
    		CLSID jpgsid;
    		Bitmap GBitmap(bitmap,Palette);
    		GBitmap.SetResolution(300.0f,200.0f);
    		GetEncoderClsid(L"image/jpeg",&jpgsid);
    		GBitmap.Save(L"image.jpeg",&jpgsid,NULL);
    
    	GdiplusShutdown(gditoken);
    	
    	DeleteObject(bitmap);
    	DeleteObject(Palette);
    	DeleteDC(MemDesktop);
    	ReleaseDC(NULL,ScreenDesktop);
    	
    	return 0;
    }
    Il tutto funziona, ma l'immagine visualizzata è sempre nera.
    Ci ho messo 30 anni solo a capire come prendere l'HPALETTE del desktop per poi scorprie per caso che c'è la funzione GetCurrentObject: ho passato altrettanti secoli a capire GetSystemPaletteEntry voleva il riverimento al compatibleDC e non a quello originale.
    Ad ogni modo, CreateCompatibleBitmap da una DIB? Se si allora le palette non servono e non è lì il problema. (tempo sprecato, veramente sprecato)
    Ho controllato i valori di ritorno, la copia dei bit avviene correttamente.

    Sono proprio fuso, avete idee?
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  4. #4
    In primo luogo, stai passando al costruttore di Bitmap una bitmap correntemente selezionata in un DC, il che non va bene (come spiegato nella documentazione del costruttore della classe Bitmap). Inoltre la bitmap creata deve essere compatibile con il DC dello schermo, non con quello di destinazione (visto che esso quando viene creato contiene una bitmap 1x1 1bpp). In sostanza:
    codice:
    int MakeScreenShot_GDIPlus(void)
    {
        //N.B.: codice di gestione degli errori completamente omesso
        
        //Ottiene un device context relativo a tutto lo schermo
        HDC ScreenDC = GetDC(NULL);
        //Ottiene le dimensioni dello schermo
        int ResX = GetSystemMetrics(SM_CXSCREEN);
        int ResY = GetSystemMetrics(SM_CYSCREEN);
        //Crea un device context in cui verrà selezionata la DIB su cui verrà copiato il contenuto del desktop
        HDC DestDC = CreateCompatibleDC(NULL);
        //Crea una DDB in memoria con la stessa profondità di colori del desktop
        HBITMAP DestBitmap = CreateCompatibleBitmap(ScreenDC,ResX,ResY);
        //Salva la bitmap attualmente selezionata nel DC per ripristinarla in seguito (per poter poi eliminare DestBitmap) e seleziona nel DC DestDC la DDB appena creata
        HBITMAP OldBitmap = (HBITMAP)SelectObject(DestDC,DestBitmap);
        //Copia il contenuto del DC del desktop nel DC che contiene la DDB DestBitmap, che quindi ora conterrà una copia del contenuto del desktop
        BitBlt(DestDC,0,0,ResX,ResY,ScreenDC,0,0,SRCCOPY);
        //Deseleziona dal DC DestDC la DIB, selezionando la vecchia bitmap che in esso era prima contenuta
        SelectObject(DestDC,OldBitmap);
        //Elimina il DC di destinazione
        DeleteDC(DestDC);
        //Rilascia il DC relativo allo schermo
        ReleaseDC(NULL,ScreenDC);
        ULONG_PTR gditoken;
        GdiplusStartupInput input;
        GdiplusStartup(&gditoken,&input,NULL);
        CLSID jpgsid;
        {
            //Dichiaro la variabile all'interno di uno scope in modo che venga distrutta appena si esce da esso, così da poter eliminare DestBitmap
            Bitmap GBitmap(DestBitmap,NULL);
            GetEncoderClsid(L"image/jpeg",&jpgsid);
            GBitmap.Save(L"image.jpeg",&jpgsid,NULL);
        }
        GdiplusShutdown(gditoken);
        //Ora che la DDB è deselezionata la si può cancellare
        DeleteObject(DestBitmap);
        return 0;
    }
    In ogni caso puoi ottenere lo stesso risultato (salvando in bmp) anche con le normali GDI, che hanno il pregio di essere presenti su tutte le versioni di Windows, ma devi faticare un po' di più.
    codice:
    int MakeScreenShot_GDI()
    {
        //N.B.: codice di gestione degli errori completamente omesso
        
        //Ottiene un device context relativo a tutto lo schermo
        HDC ScreenDC = GetDC(NULL);
        //Ottiene le dimensioni dello schermo
        int ResX = GetSystemMetrics(SM_CXSCREEN);
        int ResY = GetSystemMetrics(SM_CYSCREEN);
        //Crea un device context in cui verrà selezionata la DIB su cui verrà copiato il contenuto del desktop
        HDC DestDC = CreateCompatibleDC(NULL);
        //Crea una DIB in memoria con la stessa profondità di colori del desktop
        HBITMAP DestBitmap = CreateCompatibleBitmap(ScreenDC,ResX,ResY);
        //Salva la bitmap attualmente selezionata nel DC per ripristinarla in seguito (per poter poi eliminare DestBitmap) e seleziona nel DC DestDC la DIB appena creata
        HBITMAP OldBitmap = (HBITMAP)SelectObject(DestDC,DestBitmap);
        //Copia il contenuto del DC del desktop nel DC che contiene la DIB DestBitmap, che quindi ora conterrà una copia del contenuto del desktop
        BitBlt(DestDC,0,0,ResX,ResY,ScreenDC,0,0,SRCCOPY);
        //Deseleziona dal DC DestDC la DIB, selezionando la vecchia bitmap che in esso era prima contenuta
        SelectObject(DestDC,OldBitmap);
        //Rilascia il DC relativo allo schermo
        ReleaseDC(NULL,ScreenDC);
        //Costruisce la DIB in memoria
        //Headers
        BITMAPINFO BitInfo={0};
        BitInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        BitInfo.bmiHeader.biWidth = ResX;
        BitInfo.bmiHeader.biHeight = ResY;
        BitInfo.bmiHeader.biPlanes = 1;
        BitInfo.bmiHeader.biBitCount = 16;
        BitInfo.bmiHeader.biCompression = BI_RGB;
        BitInfo.bmiHeader.biSizeImage = (BitInfo.bmiHeader.biWidth * BitInfo.bmiHeader.biHeight) * 2;
        BITMAPFILEHEADER bitheader;
        bitheader.bfReserved1 = 0;
        bitheader.bfReserved2 = 0;
        bitheader.bfType = 0x4d42;
        bitheader.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
        bitheader.bfSize = bitheader.bfOffBits + BitInfo.bmiHeader.biSizeImage;
        //Alloca lo spazio per i byte dell'immagine
        void *bits = malloc(BitInfo.bmiHeader.biSizeImage);
        GetDIBits(DestDC,DestBitmap,0,BitInfo.bmiHeader.biHeight,bits,&BitInfo,DIB_PAL_COLORS);
        //Crea il file
        HANDLE bmfile = CreateFile("image.bmp",GENERIC_WRITE,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
        //Scrive i dati su file
        DWORD bytes;
        WriteFile(bmfile,&bitheader,sizeof(BITMAPFILEHEADER),&bytes,NULL);
        WriteFile(bmfile,&BitInfo.bmiHeader,sizeof(BITMAPINFOHEADER),&bytes,NULL);
        WriteFile(bmfile,bits,BitInfo.bmiHeader.biSizeImage,&bytes,NULL);
        //Delloca la memoria
        free(bits);
        //Chiude il file
        CloseHandle(bmfile);
        //Elimina il DC di destinazione
        DeleteDC(DestDC);
        //Ora che la DIB è deselezionata la si può cancellare
        DeleteObject(DestBitmap);
        return 0;
    }
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    [QUOTE]Originariamente inviato da MItaly
    La bitmap creata deve essere compatibile con il DC dello schermo, non con quello di destinazione (visto che esso quando viene creato contiene una bitmap 1x1 1bpp). [/code]

    Giustissimo, non ci avevo pensato.
    Avevo anche io notato che il costrutture chiedeva una bitmap non selezionata nel DC; così ho pensato bastasse evitare di chiamare SelectObject (che ancora adesso rimane una funzione abbastanza ambigua: ma a che serve?)
    Grandissima la documentazione Microsoft: The SelectObject function Select an Object in desidered HDC
    Grazie mille, eh.

    Ad ogni modo, la classe Bitmap richiede di dover chiudere esplicitamente la bitmap e le palette passategli, ma allo stesso tempo non puoi chiuderle finchè hai la bitmap aperta.
    Alla fin fine ho dovuto fare così

    codice:
    		CLSID jpgsid;
    		{
    			Bitmap GBitmap(DestBitmap,NULL);
    			GBitmap.SetResolution(300.0f,200.0f);
    			GetEncoderClsid(L"image/jpeg",&jpgsid);
    			GBitmap.Save(L"image.jpeg",&jpgsid,NULL);
    
    		}
    	
    	GdiplusShutdown(gditoken);
    	DeleteObject(DestBitmap);
    	DeleteObject(OldBitmap);
    Per forzare la chiusura prima di togliere gli oggetti.

    Altra domanda: per ridimensionare l'immagine?
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  6. #6
    Originariamente inviato da XWolverineX
    Avevo anche io notato che il costrutture chiedeva una bitmap non selezionata nel DC; così ho pensato bastasse evitare di chiamare SelectObject (che ancora adesso rimane una funzione abbastanza ambigua: ma a che serve?)
    Grandissima la documentazione Microsoft: The SelectObject function Select an Object in desidered HDC
    Grazie mille, eh.
    La documentazione Microsoft presuppone che tu sappia di cosa si stia parlando; se sai come funzionano i DC (e di conseguenza cosa significhi selezionare un oggetto in un DC) la funzione SelectObject è talmente ovvia da non richiedere documentazione.
    Come già detto prima, fatti una cultura sui DC e vedrai che tutto sarà più chiaro.
    Ad ogni modo, la classe Bitmap richiede di dover chiudere esplicitamente la bitmap e le palette passategli, ma allo stesso tempo non puoi chiuderle finchè hai la bitmap aperta.
    Alla fin fine ho dovuto fare così
    Come peraltro ho scritto nel mio codice.
    Altra domanda: per ridimensionare l'immagine?
    Con GDI o GDI+?
    Nel primo caso devi creare la bitmap fin dall'inizio con le dimensioni che vuoi che abbia l'immagine di destinazione e usare StretchBlt per ridimensionare l'immagine mentre viene copiata sul DC di destinazione.
    Amaro C++, il gusto pieno dell'undefined behavior.

  7. #7
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    GDI+, oramai ci sono dentro.
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  8. #8
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    Hop
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  9. #9
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    hop
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

  10. #10
    Utente di HTML.it L'avatar di XWolverineX
    Registrato dal
    Aug 2005
    residenza
    Prague
    Messaggi
    2,563
    trovato basta fare getthumbnail
    "Se proprio devono piratare, almeno piratino il nostro." (Bill Gates)

    "Non è possibile che 2 istituzioni statali mi mettano esami nello stesso giorno." (XWolverineX)

    http://xvincentx.netsons.org/programBlog

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.