2011-04-10 15 views
6

He adaptado este código de another article here on SO. Toma una captura de pantalla del escritorio y la escribe en un archivo llamado "test.jpg".Tomando una captura de pantalla codificada en JPEG a un búfer usando GDI + y C++

Me interesa guardar los datos JPEG directamente en un buffer que se enviará a través de la red. Estoy bastante seguro de que GdipSaveImageToStream es lo que necesito, pero no puedo entender cómo funciona. El parámetro GpImage es particularmente confuso.

Agradezco cualquier ayuda que pueda proporcionar.

#include "stdafx.h" 
#include "windows.h" 
#include "gdiplus.h" 
using namespace Gdiplus; 
using namespace Gdiplus::DllExports; 

int GetEncoderClsid(WCHAR *format, CLSID *pClsid) 
{ 
     unsigned int num = 0, size = 0; 
     GetImageEncodersSize(&num, &size); 
     if(size == 0) return -1; 
     ImageCodecInfo *pImageCodecInfo = (ImageCodecInfo *)(malloc(size)); 
     if(pImageCodecInfo == NULL) return -1; 
     GetImageEncoders(num, size, pImageCodecInfo); 
     for(unsigned int j = 0; j < num; ++j) 
     { 
       if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0){ 
         *pClsid = pImageCodecInfo[j].Clsid; 
         free(pImageCodecInfo); 
         return j; 
       }  
     } 
     free(pImageCodecInfo); 
     return -1; 
} 

int GetScreeny(LPWSTR lpszFilename, ULONG uQuality) // by Napalm 
{ 
     ULONG_PTR gdiplusToken; 
     GdiplusStartupInput gdiplusStartupInput; 
     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 
     HWND hMyWnd = GetDesktopWindow(); // get my own window 
     RECT r;    // the area we are going to capture 
     int w, h;   // the width and height of the area 
     HDC dc;    // the container for the area 
     int nBPP; 
     HDC hdcCapture; 
     LPBYTE lpCapture; 
     int nCapture; 
     int iRes; 
     CLSID imageCLSID; 
     Bitmap *pScreenShot; 
     HGLOBAL hMem; 
     int result; 

     // get the area of my application's window  
     //GetClientRect(hMyWnd, &r); 
     GetWindowRect(hMyWnd, &r); 
     dc = GetWindowDC(hMyWnd);// GetDC(hMyWnd) ; 
     w = r.right - r.left; 
     h = r.bottom - r.top; 
     nBPP = GetDeviceCaps(dc, BITSPIXEL); 
     hdcCapture = CreateCompatibleDC(dc); 


     // create the buffer for the screenshot 
     BITMAPINFO bmiCapture = { 
        sizeof(BITMAPINFOHEADER), w, -h, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0, 
     }; 

     // create a container and take the screenshot 
     HBITMAP hbmCapture = CreateDIBSection(dc, &bmiCapture, 
       DIB_PAL_COLORS, (LPVOID *)&lpCapture, NULL, 0); 

     // failed to take it 
     if(!hbmCapture) 
     { 
       DeleteDC(hdcCapture); 
       DeleteDC(dc); 
       GdiplusShutdown(gdiplusToken); 
       printf("failed to take the screenshot. err: %d\n", GetLastError()); 
       return 0; 
     } 

     // copy the screenshot buffer 
     nCapture = SaveDC(hdcCapture); 
     SelectObject(hdcCapture, hbmCapture); 
     BitBlt(hdcCapture, 0, 0, w, h, dc, 0, 0, SRCCOPY); 
     RestoreDC(hdcCapture, nCapture); 
     DeleteDC(hdcCapture); 
     DeleteDC(dc); 

     GpImage *bob; 
     IStream *ssStr; 

     // save the buffer to a file  
     pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL); 
     EncoderParameters encoderParams; 
     encoderParams.Count = 1; 
     encoderParams.Parameter[0].NumberOfValues = 1; 
     encoderParams.Parameter[0].Guid = EncoderQuality; 
     encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong; 
     encoderParams.Parameter[0].Value = &uQuality; 
     GetEncoderClsid(L"image/jpeg", &imageCLSID); 
     iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok); 

     delete pScreenShot; 
     DeleteObject(hbmCapture); 
     GdiplusShutdown(gdiplusToken); 
     return iRes; 

} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    GetScreeny(L"test.jpg", 75); 
    return 0; 
} 

Respuesta

7

Respuesta corta: Use la versión IStream de Gdiplus :: Image :: Save. Use CreateHStreamOnGlobal para crear un IStream temporal que pueda convertir de nuevo a un búfer;

Versión de largo aliento con código de muestra.

Reemplazar esta línea:

iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok); 

Con este bloque de código:

// Note: For the sake of brevity and readability, I'm deliberately not checking 
// the return value of any of these calls. In production code, you should do diligent error 
// checking on each function call. Also, there may be an optimization where you can just 
// use the memory of the stream itself (by calling GetHGlobalFromStream and GlobalLock) 
// But that's an exercise left to the reader. 

{ 
    IStream *pStream = NULL; 
    LARGE_INTEGER liZero = {}; 
    ULARGE_INTEGER pos = {}; 
    STATSTG stg = {}; 
    ULONG bytesRead=0; 
    HRESULT hrRet=S_OK; 

    BYTE* buffer = NULL; // this is your buffer that will hold the jpeg bytes 
    DWORD dwBufferSize = 0; // this is the size of that buffer; 


    hrRet = CreateStreamOnHGlobal(NULL, TRUE, &pStream)) 
    hrRet = pScreenShot->Save(pStream, &imageCLSID, &encoderParams) == 0 ? S_OK : E_FAIL; 
    hrRet = pStream->Seek(liZero, STREAM_SEEK_SET, &pos); 
    hrRet = pStream->Stat(&stg, STATFLAG_NONAME); 

    // allocate a byte buffer big enough to hold the jpeg stream in memory 
    buffer = new BYTE[stg.cbSize.LowPart]; 
    hrRet = (buffer == NULL) ? E_OUTOFMEMORY : S_OK; 
    dwBufferSize = stg.cbSize.LowPart; 

    // copy the stream into memory 
    hrRet = pStream->Read(buffer, stg.cbSize.LowPart, &bytesRead); 

    // now go save "buffer" and "dwBufferSize" off somewhere. This is the jpeg buffer 
    // don't forget to free it when you are done 

    // After success or if any of the above calls fail, don't forget to release the stream 
    if (pStream) 
    { 
     pStream->Release(); 
    } 
} 
+0

Gracias! Funciona maravillosamente. – Smurf64

+1

@ Smurf64 después de enviar los datos del búfer al lado del servidor cómo lo recuperó en la imagen original – Ganesh

Cuestiones relacionadas