我可以将 bmp 内存数据转换为更小的 png 内存数据吗?
can I convert bmp in-memory data into a much more smaller png in-momory data?
我的程序的目的是每 1/16 秒进行一次屏幕捕获,并通过套接字将其发送到远程服务器。
目前这个程序可以使用BMP格式的屏幕截图,但是,BMP格式的数据发送的字节太多,发送和接收过程明显变慢。
我的想法是:如果我能把BMP转换成PNG,然后在发送前压缩,也许程序运行起来会更流畅。
这是我的代码,select 来自 gh0st 项目
LPVOID m_lpvFullBits = NULL;
HDC m_hFullDC, m_hFullMemDC;
LPBITMAPINFO m_lpbmi_full;
m_hFullDC = GetDC(NULL);
int m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN);
int m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN);
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC);
m_lpbmi_full = (BITMAPINFO *) new BYTE[40];
BITMAPINFOHEADER *lpbmih = &(m_lpbmi_full ->bmiHeader);
lpbmih->biSize = sizeof(BITMAPINFOHEADER);
lpbmih->biWidth = m_nFullWidth ;
lpbmih->biHeight = m_nFullHeight ;
lpbmih->biPlanes = 1;
lpbmih->biBitCount = 32; // 32 bit per pixel
lpbmih->biCompression = BI_RGB;
lpbmih->biXPelsPerMeter = 0;
lpbmih->biYPelsPerMeter = 0;
lpbmih->biClrUsed = 0;
lpbmih->biClrImportant = 0;
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;
HBITMAP m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL);
::SelectObject(m_hFullMemDC, m_hFullBitmap);
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY);
直到这条语句:
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY);
BMP像素数据保存在m_lpvFullBits开始的地址,对吧?
现在我想知道的是,我可以使用当前已知的宽度,高度,BMP 像素数据..,重建一个新的 png 格式的内存数据,它比当前的 BMP 数据小得多,并通过套接字将新的 png 格式数据发送到远程服务器?
感谢您的帮助
是的,你可以。在我看来,GDI+ 足以胜任这项任务。首先,从 HBITMAP
创建一个 Gdiplus::Bitmap
,然后将其保存到 IStream
。从 IStream
您可以通过 IStream::Stat()
获得字节数。最后用IStream::Read()
读入字节数组。现在您的 PNG 已保存在内存中。
这是一个最小示例,您可以将其复制并粘贴到 CPP 文件并进行编译。
请注意,省略了错误处理和清理代码。
#include <Windows.h>
#include <gdiplus.h>
#include <iostream>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib") // or you specify it in linker option
IStream * PngMemStreamFrom(HBITMAP hbm)
{
// image/png : {557cf406-1a04-11d3-9a73-0000f81ef32e}
const CLSID clsidPngEncoder =
{ 0x557cf406, 0x1a04, 0x11d3,
{ 0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e } };
IStream *stream = NULL;
Bitmap *bmp = Bitmap::FromHBITMAP(hbm, NULL);
CreateStreamOnHGlobal(NULL, TRUE, &stream);
bmp->Save(stream, &clsidPngEncoder);
delete bmp;
return stream;
}
void ScreenshotTest(LPCWSTR szPath)
{
LPVOID m_lpvFullBits = NULL;
HDC m_hFullDC, m_hFullMemDC;
LPBITMAPINFO m_lpbmi_full;
m_hFullDC = GetDC(NULL);
int m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN);
int m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN);
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC);
m_lpbmi_full = (BITMAPINFO *) new BYTE[40];
BITMAPINFOHEADER *lpbmih = &(m_lpbmi_full ->bmiHeader);
lpbmih->biSize = sizeof(BITMAPINFOHEADER);
lpbmih->biWidth = m_nFullWidth ;
lpbmih->biHeight = m_nFullHeight ;
lpbmih->biPlanes = 1;
lpbmih->biBitCount = 32; // 32 bit per pixel
lpbmih->biCompression = BI_RGB;
lpbmih->biXPelsPerMeter = 0;
lpbmih->biYPelsPerMeter = 0;
lpbmih->biClrUsed = 0;
lpbmih->biClrImportant = 0;
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;
HBITMAP m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL);
::SelectObject(m_hFullMemDC, m_hFullBitmap);
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY);
IStream *stream = PngMemStreamFrom(m_hFullBitmap);
STATSTG stat = {0};
stream->Stat(&stat, STATFLAG_NONAME);
UINT64 cbSize = stat.cbSize.QuadPart;
std::cout << "mem stream byte count = " << cbSize << "\n";
LPBYTE buffer = new BYTE[cbSize];
// IMPORTANT! must seek to offset zero before read it
LARGE_INTEGER offZero = {0};
stream->Seek(offZero, STREAM_SEEK_SET, NULL);
stream->Read(buffer, cbSize, NULL);
// do something with buffer, such as save to disk
HANDLE hfile = CreateFile(szPath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
DWORD cbWritten = 0;
WriteFile(hfile, buffer, cbSize, &cbWritten, NULL);
CloseHandle(hfile);
// TODO: release m_hFullDC, m_hFullMemDC... here
}
int main()
{
ULONG_PTR token = NULL;
GdiplusStartupInput gdipIn;
GdiplusStartupOutput gdipOut;
GdiplusStartup(&token, &gdipIn, &gdipOut);
ScreenshotTest(L"D:\Test.png");
GdiplusShutdown(token);
return 0;
}
我的程序的目的是每 1/16 秒进行一次屏幕捕获,并通过套接字将其发送到远程服务器。
目前这个程序可以使用BMP格式的屏幕截图,但是,BMP格式的数据发送的字节太多,发送和接收过程明显变慢。
我的想法是:如果我能把BMP转换成PNG,然后在发送前压缩,也许程序运行起来会更流畅。
这是我的代码,select 来自 gh0st 项目
LPVOID m_lpvFullBits = NULL;
HDC m_hFullDC, m_hFullMemDC;
LPBITMAPINFO m_lpbmi_full;
m_hFullDC = GetDC(NULL);
int m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN);
int m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN);
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC);
m_lpbmi_full = (BITMAPINFO *) new BYTE[40];
BITMAPINFOHEADER *lpbmih = &(m_lpbmi_full ->bmiHeader);
lpbmih->biSize = sizeof(BITMAPINFOHEADER);
lpbmih->biWidth = m_nFullWidth ;
lpbmih->biHeight = m_nFullHeight ;
lpbmih->biPlanes = 1;
lpbmih->biBitCount = 32; // 32 bit per pixel
lpbmih->biCompression = BI_RGB;
lpbmih->biXPelsPerMeter = 0;
lpbmih->biYPelsPerMeter = 0;
lpbmih->biClrUsed = 0;
lpbmih->biClrImportant = 0;
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;
HBITMAP m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL);
::SelectObject(m_hFullMemDC, m_hFullBitmap);
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY);
直到这条语句:
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY);
BMP像素数据保存在m_lpvFullBits开始的地址,对吧?
现在我想知道的是,我可以使用当前已知的宽度,高度,BMP 像素数据..,重建一个新的 png 格式的内存数据,它比当前的 BMP 数据小得多,并通过套接字将新的 png 格式数据发送到远程服务器?
感谢您的帮助
是的,你可以。在我看来,GDI+ 足以胜任这项任务。首先,从 HBITMAP
创建一个 Gdiplus::Bitmap
,然后将其保存到 IStream
。从 IStream
您可以通过 IStream::Stat()
获得字节数。最后用IStream::Read()
读入字节数组。现在您的 PNG 已保存在内存中。
这是一个最小示例,您可以将其复制并粘贴到 CPP 文件并进行编译。 请注意,省略了错误处理和清理代码。
#include <Windows.h>
#include <gdiplus.h>
#include <iostream>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib") // or you specify it in linker option
IStream * PngMemStreamFrom(HBITMAP hbm)
{
// image/png : {557cf406-1a04-11d3-9a73-0000f81ef32e}
const CLSID clsidPngEncoder =
{ 0x557cf406, 0x1a04, 0x11d3,
{ 0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e } };
IStream *stream = NULL;
Bitmap *bmp = Bitmap::FromHBITMAP(hbm, NULL);
CreateStreamOnHGlobal(NULL, TRUE, &stream);
bmp->Save(stream, &clsidPngEncoder);
delete bmp;
return stream;
}
void ScreenshotTest(LPCWSTR szPath)
{
LPVOID m_lpvFullBits = NULL;
HDC m_hFullDC, m_hFullMemDC;
LPBITMAPINFO m_lpbmi_full;
m_hFullDC = GetDC(NULL);
int m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN);
int m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN);
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC);
m_lpbmi_full = (BITMAPINFO *) new BYTE[40];
BITMAPINFOHEADER *lpbmih = &(m_lpbmi_full ->bmiHeader);
lpbmih->biSize = sizeof(BITMAPINFOHEADER);
lpbmih->biWidth = m_nFullWidth ;
lpbmih->biHeight = m_nFullHeight ;
lpbmih->biPlanes = 1;
lpbmih->biBitCount = 32; // 32 bit per pixel
lpbmih->biCompression = BI_RGB;
lpbmih->biXPelsPerMeter = 0;
lpbmih->biYPelsPerMeter = 0;
lpbmih->biClrUsed = 0;
lpbmih->biClrImportant = 0;
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight;
HBITMAP m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL);
::SelectObject(m_hFullMemDC, m_hFullBitmap);
::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY);
IStream *stream = PngMemStreamFrom(m_hFullBitmap);
STATSTG stat = {0};
stream->Stat(&stat, STATFLAG_NONAME);
UINT64 cbSize = stat.cbSize.QuadPart;
std::cout << "mem stream byte count = " << cbSize << "\n";
LPBYTE buffer = new BYTE[cbSize];
// IMPORTANT! must seek to offset zero before read it
LARGE_INTEGER offZero = {0};
stream->Seek(offZero, STREAM_SEEK_SET, NULL);
stream->Read(buffer, cbSize, NULL);
// do something with buffer, such as save to disk
HANDLE hfile = CreateFile(szPath, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
DWORD cbWritten = 0;
WriteFile(hfile, buffer, cbSize, &cbWritten, NULL);
CloseHandle(hfile);
// TODO: release m_hFullDC, m_hFullMemDC... here
}
int main()
{
ULONG_PTR token = NULL;
GdiplusStartupInput gdipIn;
GdiplusStartupOutput gdipOut;
GdiplusStartup(&token, &gdipIn, &gdipOut);
ScreenshotTest(L"D:\Test.png");
GdiplusShutdown(token);
return 0;
}