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

Win32使用SetDIBitsToDevice绘制位图时部分尺寸图像显示异常

问题描述

问题概述

我尝试通过输入数组(char* [w×h×3],格式为像素1蓝色、像素1绿色、像素1红色、像素2蓝色……)绘制w×h尺寸的图像。部分尺寸(如196×196)的图像能正常显示,但其他尺寸(如174×174)的图像显示错误,该问题表现完全随机。

Win32绘图实现

生成纯红色图像示例代码

char* imgData = new char[w * h * 3];
char* rData = new char[w * h];
char* gData = new char[w * h];
char* bData = new char[w * h];
for (int i = 0; i < w * h; ++i)
{
    rData[i] = 255;
    gData[i] = 0;
    bData[i] = 0;
}

for (int i = 0; i < w * h; ++i)
{
    imgData[3*i] = bData[i];
    imgData[3*i+1] = gData[i];
    imgData[3*i+2] = rData[i];
}

WM_PAINT消息中的绘图逻辑

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//  WM_PAINT    - Paint the main window

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    RECT rt;
    GetClientRect(hWnd, &rt);

    switch (message) {
        case WM_PAINT:
        {
            hdc = BeginPaint(hWnd, &ps);

            BITMAPINFO bmi;
            CBitmap bitmap;
            memset(&bmi,0,sizeof(bmi));
            bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
            bmi.bmiHeader.biWidth = inImage.getWidth();
            bmi.bmiHeader.biHeight = -inImage.getHeight();  
            // Use negative height.  DIB is top-down.
            bmi.bmiHeader.biPlanes = 1;
            bmi.bmiHeader.biBitCount = 24;
            bmi.bmiHeader.biCompression = BI_RGB;
            bmi.bmiHeader.biSizeImage = inImage.getWidth()*inImage.getHeight();

            SetDIBitsToDevice(
                hdc, 
                0,
                100, 
                inImage.getWidth(), 
                inImage.getHeight(),
                0,
                0,
                0,
                inImage.getHeight(),
                inImage.getImageData(), //imgData
                &bmi,
                DIB_RGB_COLORS);
            EndPaint(hWnd, &ps);
        }
        break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

其他相关设置

窗口类注册代码

//
//  PURPOSE: Registers the window class.
//

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX); 

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = (WNDPROC)WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, (LPCTSTR)IDI_IMAGE);
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = (LPCSTR)IDC_IMAGE;
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

    return RegisterClassEx(&wcex);
}

窗口初始化代码

//
//PURPOSE: Saves instance handle and creates main window

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }
   
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

示例情况

示例1(纯色图像)

  • 174×174尺寸:显示异常
  • 196×196尺寸:显示正常

示例2(含图形图像)

  • 450×450尺寸:显示异常
  • 256×256尺寸:显示正常

解决方案

问题根源在于未遵循Win32 DIB(设备无关位图)的行对齐要求,以及BITMAPINFO参数计算错误。

1. 修复biSizeImage参数计算

Win32要求DIB的每行像素数据字节数必须是4的倍数(DWORD对齐)。24位色深下,原始每行字节数为宽度×3,需向上取整到最近的4的倍数。当前代码中biSizeImage仅计算宽度×高度,完全错误,正确计算方式如下:

int width = inImage.getWidth();
int height = inImage.getHeight();
// 计算对齐后的每行字节数
int rowBytes = ((width * 3 + 3) / 4) * 4;
// 总图像数据大小 = 对齐后每行字节数 × 高度
bmi.bmiHeader.biSizeImage = rowBytes * height;

2. 调整图像数据缓冲区的分配与填充

当前imgData按宽度×3×高度分配,当宽度×3不是4的倍数时,行数据无法对齐,导致SetDIBitsToDevice读取数据错位。需重新分配对齐后的缓冲区,并按行填充:

int w = inImage.getWidth();
int h = inImage.getHeight();
int rowBytes = ((w * 3 + 3) / 4) * 4;
char* imgData = new char[rowBytes * h];

// 按行填充图像数据
for (int y = 0; y < h; ++y) {
    for (int x = 0; x < w; ++x) {
        int pixelIdx = y * w + x;
        int bufferIdx = y * rowBytes + x * 3;
        imgData[bufferIdx] = bData[pixelIdx];     // 蓝色通道
        imgData[bufferIdx + 1] = gData[pixelIdx]; // 绿色通道
        imgData[bufferIdx + 2] = rData[pixelIdx]; // 红色通道
    }
    // 行末尾的对齐字节自动初始化为0,无需额外处理
}

3. 验证参数匹配

确认SetDIBitsToDevice的参数与DIB属性匹配:当前代码中biHeight设为负值表示从上到下的图像顺序,与数据填充逻辑一致,无需修改。

修复后,所有尺寸的图像都能正常显示——之前仅当宽度×3为4的倍数时(如196×3=588、256×3=768)能正常显示,正是因为这些尺寸刚好满足对齐要求。


内容的提问来源于stack exchange,提问作者tonyt95

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 06:13:11