C++ gdi::Bitmap to PNG Image in memory

2022-01-06 00:00:00 png c++ bitmap gdi+ image-conversion

我正在尝试通过 tcp 将窗口的屏幕截图发送到服务器.获取屏幕截图没有问题(使用 GDIplus).网络对我来说也很容易.问题是试图将 gdi+ 位图转换为 png(在内存中)以从中获取数据并将其发送到服务器.有人可以帮我吗?

I'm trying to send a screenshot of a window over tcp to a server. Getting the screenshot is no problem (using GDIplus). The networking is also easy for me. The problem is trying to convert the gdi+ Bitmap to a png (in memory) to get the data out of it and send it to the server. Can anyone help me please?

推荐答案

Gdiplus 可以保存到文件,或者使用 IStream 保存到内存.请参阅 Gdiplus::Image::Save 方法

Gdiplus can save to file, or save to memory using IStream. See Gdiplus::Image::Save method

//get gdi+ bitmap
Gdiplus::Bitmap bitmap(hbitmap, nullptr);

//write to IStream
IStream* istream = nullptr;
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &istream);
CLSID clsid_png;
CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &clsid_png);
bitmap.Save(istream, &clsid_png);

内存足够小,您可以从 IStream 复制到单个缓冲区:

The memory size is small enough that you can copy from IStream to a single buffer:

//copy IStream to buffer
int bufsize = GlobalSize(hg);
char *buffer = new char[bufsize];

//lock & unlock memory
LPVOID ptr = GlobalLock(hg);
memcpy(buffer, ptr, bufsize);
GlobalUnlock(hg);

//release will automatically free the memory allocated in CreateStreamOnHGlobal 
istream->Release();

PNG 现在可以在 buffer 中使用,其大小为 bufsize.您可以直接处理二进制数据,也可以转换为 Base64 以通过网络发送

PNG is now available in buffer, its size is bufsize. You can work directly with the binary data, or convert to Base64 to send over the network

...
delete[]buffer;

MCVE:

#include <iostream>
#include <fstream>
#include <vector>
#include <Windows.h>
#include <gdiplus.h>

bool save_png_memory(HBITMAP hbitmap, std::vector<BYTE> &data)
{
    Gdiplus::Bitmap bmp(hbitmap, nullptr);

    //write to IStream
    IStream* istream = nullptr;
    CreateStreamOnHGlobal(NULL, TRUE, &istream);

    CLSID clsid_png;
    CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &clsid_png);
    Gdiplus::Status status = bmp.Save(istream, &clsid_png);
    if(status != Gdiplus::Status::Ok)
        return false;

    //get memory handle associated with istream
    HGLOBAL hg = NULL;
    GetHGlobalFromStream(istream, &hg);

    //copy IStream to buffer
    int bufsize = GlobalSize(hg);
    data.resize(bufsize);

    //lock & unlock memory
    LPVOID pimage = GlobalLock(hg);
    memcpy(&data[0], pimage, bufsize);
    GlobalUnlock(hg);

    istream->Release();
    return true;
}

int main()
{
    CoInitialize(NULL);

    ULONG_PTR token;
    Gdiplus::GdiplusStartupInput tmp;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);

    //take screenshot
    RECT rc;
    GetClientRect(GetDesktopWindow(), &rc);
    auto hdc = GetDC(0);
    auto memdc = CreateCompatibleDC(hdc);
    auto hbitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
    auto oldbmp = SelectObject(memdc, hbitmap);
    BitBlt(memdc, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY);
    SelectObject(memdc, oldbmp);
    DeleteDC(memdc);
    ReleaseDC(0, hdc);

    //save as png
    std::vector<BYTE> data;
    if(save_png_memory(hbitmap, data))
    {
        //write from memory to file for testing:
        std::ofstream fout("test.png", std::ios::binary);
        fout.write((char*)data.data(), data.size());
    }
    DeleteObject(hbitmap);

    Gdiplus::GdiplusShutdown(token);
    CoUninitialize();

    return 0;
}

相关文章