2011-04-10 118 views
6

我已修改another article here on SO中的此代码。它会截取桌面并将其写入名为“test.jpg”的文件。使用GDI +和C++将JPEG编码的屏幕截图转换为缓冲区

我有兴趣将JPEG数据直接保存到要通过网络发送的缓冲区中。我很确定GdipSaveImageToStream是我需要的,但我无法弄清楚它是如何工作的。 GpImage参数特别令人困惑。

我很感谢您提供的任何帮助。

#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; 
} 

回答

7

简答:使用IStream版本的Gdiplus :: Image :: Save。使用CreateHStreamOnGlobal创建一个临时IStream,您可以将其转换回缓冲区;

带代码示例的长卷版本。

替换此行:

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

有了这个代码块:

// 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

谢谢!它奇妙地工作。 – Smurf64 2011-04-10 18:30:39

+1

@ Smurf64将缓冲区数据发送到服务器端后,如何将其恢复为原始图像 – Ganesh 2014-02-09 14:07:20