StretchBlt函数生成位图出现异常白点,求排查解决
Windows API位图缩放后仅显示白点的问题排查与解决
核心问题分析
ResizeBitmap函数生成全白位图的主要原因是创建目标位图时使用了错误的DC:
CreateCompatibleDC(NULL)创建的内存DC默认是单色(1位)格式,用它调用CreateCompatibleBitmap生成的hNewBitmap是单色位图,而非与源位图同深度的真彩色位图。当StretchBlt将真彩色源位图绘制到单色目标位图时,颜色会被强制转换为黑白,最终出现全白的结果。
此外还有两处潜在问题:
- 未检查
SelectObject的返回值,无法及时发现对象选择失败的情况 - 目标DC(hdcDest)未还原初始GDI对象,可能导致资源泄漏
修复步骤
- 替换CreateCompatibleBitmap的DC参数
使用关联了源位图的hdcSrc来创建目标位图,保证目标位图与源位图的位深度、颜色格式一致。 - 还原目标DC的初始对象
保存SelectObject(hdcDest, hNewBitmap)的返回值,在删除DC前将其选回,避免资源泄漏。 - 添加关键函数的返回值检查
对SelectObject、StretchBlt等函数的返回值进行校验,提前捕获错误。
修改后的ResizeBitmap函数
BOOL ResizeBitmap(HBITMAP hOriginalBitmap, const char resizedFilename[], BitmapAngle NewAngle, BitmapAngle BmAngle){ HDC hdcSrc = CreateCompatibleDC(NULL); if (!hdcSrc) { GetErrorCode("CreateCompatibleDC(hdcSrc)"); ExitProcess(0); } HGDIOBJ originalObjectSrc = SelectObject(hdcSrc, hOriginalBitmap); if (originalObjectSrc == NULL || originalObjectSrc == HGDI_ERROR) { GetErrorCode("SelectObject(hdcSrc)"); DeleteDC(hdcSrc); ExitProcess(0); } HDC hdcDest = CreateCompatibleDC(NULL); if (!hdcDest) { GetErrorCode("CreateCompatibleDC(hdcDest)"); SelectObject(hdcSrc, originalObjectSrc); DeleteDC(hdcSrc); ExitProcess(0); } // 关键修复:使用hdcSrc创建兼容位图,保证颜色格式与源一致 HBITMAP hNewBitmap = CreateCompatibleBitmap(hdcSrc, NewAngle.Width, NewAngle.Height); if (!hNewBitmap) { GetErrorCode("CreateCompatibleBitmap"); SelectObject(hdcSrc, originalObjectSrc); DeleteDC(hdcSrc); DeleteDC(hdcDest); ExitProcess(0); } HGDIOBJ originalObjectDest = SelectObject(hdcDest, hNewBitmap); if (originalObjectDest == NULL || originalObjectDest == HGDI_ERROR) { GetErrorCode("SelectObject(hdcDest)"); DeleteObject(hNewBitmap); SelectObject(hdcSrc, originalObjectSrc); DeleteDC(hdcSrc); DeleteDC(hdcDest); ExitProcess(0); } // 设置高质量缩放模式 SetStretchBltMode(hdcDest, HALFTONE); SetBrushOrgEx(hdcDest, 0, 0, NULL); // 执行缩放绘制 BOOL bltResult = StretchBlt(hdcDest, 0, 0, NewAngle.Width, NewAngle.Height, hdcSrc, 0, 0, BmAngle.Width, BmAngle.Height, SRCCOPY); if (!bltResult) { GetErrorCode("StretchBlt"); } // 保存缩放后的位图 SaveBitmapToFile(hNewBitmap, NewAngle, resizedFilename); // 还原并释放资源 SelectObject(hdcSrc, originalObjectSrc); SelectObject(hdcDest, originalObjectDest); DeleteDC(hdcSrc); DeleteDC(hdcDest); DeleteObject(hNewBitmap); return bltResult; }
额外优化建议
- 在
SaveBitmapToFile中,不要硬编码24位像素格式,应该通过GetDIBits先获取位图的真实位深度,再计算内存大小,避免因源位图位深度不同导致的错误。 - 尽量避免使用
ExitProcess直接终止程序,可以改为返回错误码让调用方处理,提升程序的健壮性。
内容的提问来源于stack exchange,提问作者Daleth
相关产品推荐
相关产品推荐

