StretchDIBits坐标原点偏移问题:如何始终保持左上原点?
问题:StretchDIBits坐标原点异常偏移
当BITMAPINFO.bmiHeader.biHeight传入负值,且xSrc、ySrc为(0,0)时,StretchDIBits的坐标原点位于左上角;但当xSrc或ySrc自增1后,坐标原点会变为左下角,导致绘图位置不符合预期。
相关绘图代码如下:
void CMFCTestDlg::DrawImage(const CString& imagePath, int32_t x, int32_t y) { Gdiplus::GdiplusStartupInput gdiplusStartupInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); m_pBitmap = new Gdiplus::Bitmap(imagePath); CStatic* pStaticCtrl = (CStatic*)GetDlgItem(IDC_STATIC); CRect staticRect; pStaticCtrl->GetClientRect(staticRect); HWND hWnd = pStaticCtrl->GetSafeHwnd(); HDC hDC = ::GetDC(hWnd); HDC hMemDC = NULL; HBITMAP hBitmap = NULL; HGDIOBJ hOldBitMap = NULL; HBRUSH hBkBrush = NULL; do { hMemDC = ::CreateCompatibleDC(hDC); if (NULL == hMemDC) { break; } RECT stWndRect; BOOL bGetClientRect = ::GetClientRect(hWnd, &stWndRect); if (FALSE == bGetClientRect) { break; } int nWndWidth = stWndRect.right - stWndRect.left + 1; int nWndHeight = stWndRect.bottom - stWndRect.top + 1; hBitmap = CreateCompatibleBitmap(hDC, nWndWidth, nWndHeight); if (NULL == hBitmap) { break; } ::SelectObject(hMemDC, hBitmap); unsigned char chBkRed = static_cast<unsigned char>(0xCC); unsigned char chBkGreen = static_cast<unsigned char>(0xCC); unsigned char chBkBlue = static_cast<unsigned char>(0xCC); COLORREF nBkColor = RGB(chBkRed, chBkGreen, chBkBlue); hBkBrush = ::CreateSolidBrush(nBkColor); if (NULL == hBkBrush) { break; } unsigned char chBkRed = static_cast<unsigned char>(0xCC); unsigned char chBkGreen = static_cast<unsigned char>(0xCC); unsigned char chBkBlue = static_cast<unsigned char>(0xCC); COLORREF nBkColor = RGB(chBkRed, chBkGreen, chBkBlue); hBkBrush = ::CreateSolidBrush(nBkColor); if (NULL == hBkBrush) { break; } int nFillRect = ::FillRect(hMemDC, &stWndRect, hBkBrush); if (0 == nFillRect) { break; } Gdiplus::BitmapData bitmapData; Gdiplus::Rect rect(0, 0, m_pBitmap->GetWidth(), m_pBitmap->GetHeight()); m_pBitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &bitmapData); int bmpWidth = m_pBitmap->GetWidth(); int bmpHeight = m_pBitmap->GetHeight(); BITMAPINFO stBmpInfo; memset(&stBmpInfo, 0, sizeof(stBmpInfo)); stBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); stBmpInfo.bmiHeader.biWidth = bmpWidth; stBmpInfo.bmiHeader.biHeight = -bmpHeight; stBmpInfo.bmiHeader.biPlanes = 1; stBmpInfo.bmiHeader.biBitCount = 24; stBmpInfo.bmiHeader.biCompression = BI_RGB; ::SetStretchBltMode(hMemDC, COLORONCOLOR); int stride = bitmapData.Stride; BYTE* pBits = new BYTE[stride * bmpHeight]; memcpy(pBits, bitmapData.Scan0, stride * bmpHeight); m_pBitmap->UnlockBits(&bitmapData); ::StretchDIBits(hMemDC, 0, 0, staticRect.Width(), staticRect.Height(), x, y, staticRect.Width(), staticRect.Height(), pBits, &stBmpInfo, DIB_RGB_COLORS, SRCCOPY); ::BitBlt(hDC, stWndRect.left, stWndRect.top, nWndWidth, nWndHeight, hMemDC, 0, 0, SRCCOPY); } while (0); pStaticCtrl->ReleaseDC(pDC); }
解决方案:统一坐标原点为左上角
这个问题源于StretchDIBits处理负biHeight(表示图像顶行在前)时,非(0,0)起始点的源坐标计算逻辑混淆。可通过以下两种方式修复:
方式一:调整源y坐标计算逻辑
当biHeight为负值时,源图像y轴从上到下,需将传入的y转换为从图像底部反向计算的偏移值,确保起始点始终对应左上角:
// 替换原StretchDIBits调用中的y参数 int adjustedYSrc = bmpHeight - y - staticRect.Height(); ::StretchDIBits(hMemDC, 0, 0, staticRect.Width(), staticRect.Height(), x, adjustedYSrc, staticRect.Width(), staticRect.Height(), pBits, &stBmpInfo, DIB_RGB_COLORS, SRCCOPY);
此方式直接适配负biHeight的图像存储格式,让源坐标偏移始终基于左上角计算。
方式二:设biHeight为正值并翻转图像数据
若不想修改坐标计算逻辑,可将biHeight设为正值(表示图像底行在前),同时翻转GDI+获取的图像数据,让数据存储顺序与biHeight定义匹配:
- 修改BITMAPINFO的biHeight设置:
stBmpInfo.bmiHeader.biHeight = bmpHeight; // 改为正值
- 在memcpy之后、UnlockBits之前添加图像翻转逻辑:
memcpy(pBits, bitmapData.Scan0, stride * bmpHeight); // 新增:上下翻转图像数据 BYTE* pTemp = new BYTE[stride]; for (int i = 0; i < bmpHeight / 2; i++) { memcpy(pTemp, pBits + i * stride, stride); memcpy(pBits + i * stride, pBits + (bmpHeight - 1 - i) * stride, stride); memcpy(pBits + (bmpHeight - 1 - i) * stride, pTemp, stride); } delete[] pTemp;
设置后,StretchDIBits按常规左下角原点逻辑处理,但图像数据已翻转,最终显示效果等价于原点在左上角。
额外代码提示
原代码存在两处重复创建背景画刷的冗余代码,建议删除其中一处,避免资源泄漏风险。
内容的提问来源于stack exchange,提问作者eular
相关产品推荐
相关产品推荐

