使用GDI +和C ++将JPEG编码的屏幕截图转换为缓冲区

我已经从SO的另一篇文章中修改了这段代码。 它将桌面截图并将其写入名为“test.jpg”的文件。

我有兴趣将JPEG数据直接保存到要通过networking发送的缓冲区中。 我很确定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; } 

简短的回答:使用Gtiplus :: Image :: Save的IStream版本。 使用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(); } }