gmmo
gmmo

Reputation: 2811

Can't get BITMAPINFOHEADER data to display odd width bmp images correctly

I am trying to display a 24-bit uncompressed bitmap with an odd width using standard Win32 API calls, but it seems like I have a stride problem.

According to the msdn:

https://msdn.microsoft.com/en-us/library/windows/desktop/dd318229%28v=vs.85%29.aspx

"For uncompressed RGB formats, the minimum stride is always the image width in bytes, rounded up to the nearest DWORD. You can use the following formula to calculate the stride:

stride = ((((biWidth * biBitCount) + 31) & ~31) >> 3)"

but this simply does not work for me and below is is the code:

void Init()
{
    pImage = ReadBMP("data\\bird.bmp");
    size_t imgSize = pImage->width * pImage->height * 3;
    BITMAPINFOHEADER bmih;
    bmih.biSize = sizeof(BITMAPINFOHEADER);
    bmih.biBitCount = 24;
    // This is probably where the bug is
    LONG stride = ((((pImage->width * bmih.biBitCount) + 31) & ~31) >> 3);
    //bmih.biWidth = pImage->width;
    bmih.biWidth = stride;
    bmih.biHeight = -((LONG)pImage->height);
    bmih.biPlanes = 1;
    bmih.biCompression = BI_RGB;
    bmih.biSizeImage = 0;
    bmih.biXPelsPerMeter = 1;
    bmih.biYPelsPerMeter = 1;
    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

    BITMAPINFO dbmi;
    ZeroMemory(&dbmi, sizeof(dbmi));
    dbmi.bmiHeader = bmih;
    dbmi.bmiColors->rgbBlue = 0;
    dbmi.bmiColors->rgbGreen = 0;
    dbmi.bmiColors->rgbRed = 0;
    dbmi.bmiColors->rgbReserved = 0;

    HDC hdc = ::GetDC(NULL);

    mTestBMP = CreateDIBitmap(hdc,
        &bmih,
        CBM_INIT,
        pImage->pSrc,
        &dbmi,
        DIB_RGB_COLORS);

    hdc = ::GetDC(NULL);
}

and here the drawing fuction

RawBMP *pImage;
HBITMAP mTestBMP;
void UpdateScreen(HDC srcHDC)
{
    if (pImage != nullptr && mTestBMP != 0x00)
    {
        HDC hdc = CreateCompatibleDC(srcHDC);
        SelectObject(hdc, mTestBMP);
        BitBlt(srcHDC,
            0,  // x
            0,  // y
            // I tried passing the stride here and it did not work either
            pImage->width, // width of the image
            pImage->height, // height
            hdc,
            0,   // x and
            0,   // y of upper left corner
            SRCCOPY);
        DeleteDC(hdc);
    }
}

If I pass the original image width (odd number) instead of the stride

LONG stride = ((((pImage->width * bmih.biBitCount) + 31) & ~31) >> 3);
//bmih.biWidth = stride;
bmih.biWidth = pImage->width;

the picture looks skewed, below shows the differences:

enter image description here

and if I pass the stride according to msdn, then nothing shows up because the stride is too large.

any clues? Thank you!

Upvotes: 1

Views: 532

Answers (1)

gmmo
gmmo

Reputation: 2811

thanks Jonathan for the solution. I need to copy row by row with the proper padding for odd width images. More or less the code for 24-bit uncompressed images:

const uint32_t bitCount = 24;
LONG strideInBytes;

// if the width is odd, then we need to add padding
if (width & 0x1)
{
    strideInBytes = ((((width * bitCount) + 31) & ~31) >> 3);
}
else
{
    strideInBytes = width * 3;
}

// allocate the new buffer
unsigned char *pBuffer = new unsigned char[strideInBytes * height];
memset(pBuffer, 0xaa, strideInBytes * height);

// Copy row by row
for (uint32_t yy = 0; yy < height; yy++)
{
    uint32_t rowSizeInBytes = width * 3;
    unsigned char *pDest = &pBuffer[yy * strideInBytes];
    unsigned char *pSrc = &pData[yy * rowSizeInBytes];

    memcpy(pDest, pSrc, rowSizeInBytes);
}

rawBMP->pSrc = pBuffer;
rawBMP->width = width;
rawBMP->height = height;
rawBMP->stride = strideInBytes;

Upvotes: 2

Related Questions