如何将HDC截取的高分辨率屏幕位图缩小至约1MB用于TCP传输?
解决方案:缩小截图体积用于TCP传输
针对高分辨率屏幕截图体积过大的问题,有两种直接的解决思路:缩小截图分辨率或将位图压缩为JPEG/PNG格式,以下是具体实现:
方法一:直接缩小截图分辨率
通过StretchBlt替代BitBlt,直接生成低分辨率的位图,避免生成原始尺寸的大位图。你可以根据目标体积计算合适的缩放比例:
24位未压缩位图体积计算公式:宽 × 高 × 3 字节,要控制在1MB左右,目标像素总数大概在34万左右(1MB=1048576字节 ÷3≈349525)。按原屏幕2736×1824的3:2比例,可设目标宽为800,高约533,体积约1.27MB,接近需求。
修改后的代码:
HDC hdc = GetDC(NULL); int srcWidth = GetSystemMetrics(SM_CXSCREEN); int srcHeight = GetSystemMetrics(SM_CYSCREEN); // 计算目标尺寸,按比例缩小 int destWidth = 800; int destHeight = static_cast<int>(srcHeight * (static_cast<double>(destWidth) / srcWidth)); HDC hDest = CreateCompatibleDC(hdc); HBITMAP hbDest = CreateCompatibleBitmap(hdc, destWidth, destHeight); HBITMAP hbOld = static_cast<HBITMAP>(SelectObject(hDest, hbDest)); // 缩放截取屏幕内容 StretchBlt(hDest, 0, 0, destWidth, destHeight, hdc, 0, 0, srcWidth, srcHeight, SRCCOPY); // 这里可以对hbDest进行后续处理(比如转为字节流发送) // 务必清理资源 SelectObject(hDest, hbOld); DeleteObject(hbDest); DeleteDC(hDest); ReleaseDC(NULL, hdc);
方法二:压缩位图为JPEG/PNG格式
如果需要保留更多画面细节,推荐将原始位图压缩为压缩格式(比如JPEG),即使是原始分辨率,中等质量的JPEG也能将体积控制在1MB以内。这里使用GDI+实现压缩:
#include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; // 先按你的原有代码获取全屏位图HBITMAP hbDesktop HDC hdc = GetDC(NULL); HDC hDest = CreateCompatibleDC(hdc); int width = GetSystemMetrics(SM_CXSCREEN); int height = GetSystemMetrics(SM_CYSCREEN); HBITMAP hbDesktop = CreateCompatibleBitmap(hdc, width, height); SelectObject(hDest, hbDesktop); BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY); // 初始化GDI+ GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); // 将HBITMAP转为GDI+ Bitmap对象 Bitmap* pBitmap = Bitmap::FromHBITMAP(hbDesktop, NULL); // 获取JPEG编码器CLSID CLSID clsidJpeg; GetEncoderClsid(L"image/jpeg", &clsidJpeg); // 设置压缩质量(0-100,值越低体积越小,50左右即可将2736×1824的图压到1MB以内) EncoderParameters encoderParams; encoderParams.Count = 1; encoderParams.Parameter[0].Guid = EncoderQuality; encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong; encoderParams.Parameter[0].NumberOfValues = 1; UINT quality = 50; encoderParams.Parameter[0].Value = &quality; // 将压缩后的图片写入内存流,方便TCP发送 IStream* pStream = NULL; CreateStreamOnHGlobal(NULL, TRUE, &pStream); pBitmap->Save(pStream, &clsidJpeg, &encoderParams); // 获取流中的数据和大小 HGLOBAL hGlobal; GetHGlobalFromStream(pStream, &hGlobal); DWORD dataSize = GlobalSize(hGlobal); BYTE* pData = static_cast<BYTE*>(GlobalLock(hGlobal)); // 这里可以将pData通过TCP发送... // 清理所有资源 GlobalUnlock(hGlobal); pStream->Release(); delete pBitmap; GdiplusShutdown(gdiplusToken); DeleteObject(hbDesktop); DeleteDC(hDest); ReleaseDC(NULL, hdc);
补充说明
- 方法一适合对画质要求不高的场景,无需额外依赖库,操作简单。
- 方法二能在保留更多细节的同时大幅压缩体积,推荐用于需要画面清晰度的场景。
- 所有GDI/GDI+资源必须正确释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Epic Speedy
相关产品推荐
相关产品推荐

