c++ - 使用 GDI+ 和 C++ 将 JPEG 编码的屏幕截图保存到缓冲区

标签 c++ windows gdi+ jpeg screenshot

我从 another article here on SO 改编了这段代码.它获取桌面的屏幕截图并将其写入名为“test.jpg”的文件。

我有兴趣将 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;
}

最佳答案

简短回答:使用 Gdiplus::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();
    }
}

关于c++ - 使用 GDI+ 和 C++ 将 JPEG 编码的屏幕截图保存到缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5610075/

相关文章:

C++ opencv Mat 到 QPixmap 错误

C++ 类/结构大小

c# - 使用 GDI+ 和 C# 为站点创建图像

delphi - 使用 GDI+ 淡入淡出图像(即仅更改 TGPGraphic 的 Alpha channel )

c# - GDI+ 图像比 C# 图像快得多

c++ - 向 vector 添加多个值

c++ - 使用 Visual Studio 在发布二进制文件中生成符号

对正在运行的程序的 Windows URL 方案调用

python - ipython 笔记本不工作 : OSError: [Errno None not found] 2

c++ - 找不到 libpng14-14.dll