C++中如何将gdi::Bitmap转换为内存中的PNG图像

7

我正在尝试将窗口的截图通过TCP发送到服务器。获取截图不是问题(使用GDIplus)。网络也很容易处理。问题在于尝试将gdi+位图转换为PNG格式(在内存中),以便从中获取数据并将其发送到服务器。请问有谁能够帮助我吗?


不是推荐,但FreeImage可能会有所帮助... http://freeimage.sourceforge.net - Mark Setchell
类似的问题在这里 https://dev59.com/vJrga4cB1Zd3GeqPs9mr - seccpur
并不是很相似,因为我想将它作为字符串通过TCP发送,而那个人想将其保存到文件中。 - surreal
OpenCV可以使用imencode()将基于内存的图像转换为PNG格式,但这需要导入一个庞大的依赖项。相反,您可以使用NetPBM工具,并通过popen()进行外壳调用来执行bmptopnm(),然后是pnmtopng() http://netpbm.sourceforge.net/doc/pnmtopng.html,而且NetPBM的东西比OpenCV小得多。 - Mark Setchell
可能会对您有所帮助…… https://dev59.com/G33aa4cB1Zd3GeqPjeeS#22580958 - Mark Setchell
1个回答

9

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

//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 复制到一个单独的缓冲区(有关更多详细信息,请参阅“最小示例”)。
//get memory handle associated with istream
HGLOBAL hg = NULL;
GetHGlobalFromStream(istream, &hg);

//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格式进行网络传输。

最小示例:

#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;
    if (CreateStreamOnHGlobal(NULL, TRUE, &istream) != 0)
        return false;

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

    //get memory handle associated with istream
    HGLOBAL hg = NULL;
    if (GetHGlobalFromStream(istream, &hg) != S_OK)
        return 0;

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

    //lock & unlock memory
    LPVOID pimage = GlobalLock(hg);
    if (!pimage)
        return false;
    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;
}

我该如何正确格式化代码?需要使用换行符吗? - surreal
仍然不起作用,我尝试了你的解决方案,但在Gdiplus :: Bitmap构造函数之后它给了我FileNotFound错误。我正在使用Visual Studio,C++17。这是我的代码:http://prntscr.com/k7wqu8 - surreal
没关系,我已经在CLSIDFromString中更改了字符串,现在我又把它改回来了,现在它可以工作了。非常感谢大家! - surreal
你第二个片段中的 hg 是什么? - Gray Programmerz
1
@GrayProgrammerz 这是 HGLOBAL 的值。请参考“最小示例”获取可工作的代码。 - Barmak Shemirani
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接