You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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定义匹配:

  1. 修改BITMAPINFO的biHeight设置:
stBmpInfo.bmiHeader.biHeight = bmpHeight; // 改为正值
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 17:39:50