读取大小不是8的倍数的位图导致像素的移位(和换行)

时间:2015-03-03 19:31:56

标签: c++ windows bitmap

我正在努力了解如何输入/输出/处理图像,并且从错误到错误我得到了以下内容:

位图输出:

void createBMPFile(PBYTE image, BITMAPINFOHEADER bmi)
{
    //DWORD stride = (((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3;
    //bmi.biSizeImage = bmi.biHeight * stride;

    BITMAPFILEHEADER    bmf;
    memset(&bmf, 0, sizeof(bmf));

    // Fill BitmapFileHeader
    INT cbHeaderOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    DWORD dwTotalBytes = cbHeaderOffBits + bmi.biSizeImage;        // File size
    bmf.bfType = 0x4d42;     // Signature = 'BM'
    bmf.bfSize = dwTotalBytes;  // Bytes in whole file.
    bmf.bfReserved1 = 0;
    bmf.bfReserved2 = 0;
    bmf.bfOffBits = cbHeaderOffBits; // Offset to bits in file.

    // Flip the biHeight member so that it denotes top-down bitmap 
    // bmi.biHeight *= -1;  

    DWORD      dwWritten = 0;
    HANDLE     hFile = NULL;

    WCHAR wFileName[MAX_PATH] = TEXT("output.bmp");
    hFile = CreateFileW(wFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return;

    // Dump headers first
    if (!WriteFile(hFile, &bmf, sizeof(BITMAPFILEHEADER), &dwWritten, NULL))
        return;
    if (!WriteFile(hFile, &bmi, sizeof(BITMAPINFOHEADER), &dwWritten, NULL))
        return;

    VERBOSE(TEXT("createBMPFile24: imageSize=%d width=%d height=%d \nbitCount=%d image=0x%08x\n"),
        bmi.biSizeImage, bmi.biWidth, bmi.biHeight, bmi.biBitCount, image);

    // Dump the data now
    if (!WriteFile(hFile, image, bmi.biSizeImage, &dwWritten, NULL))
        return;

    CloseHandle(hFile);
}

位图输入:

PBYTE inputBMP(LPCWSTR filename, BITMAPINFOHEADER *bmi)
{
    BITMAPFILEHEADER bmf;
    memset(&bmf, 0, sizeof(bmf));
    DWORD bytesread = 0;

    HANDLE  file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 
        NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);  

    if (file == INVALID_HANDLE_VALUE)
    {
        ERR(TEXT("Error creating file\n"));
        return NULL;
    }

    if (!ReadFile(file, &bmf, sizeof(BITMAPFILEHEADER), &bytesread, NULL))
    {
        CloseHandle(file);
        return NULL;
    }

    if (!ReadFile(file, bmi, sizeof(BITMAPINFOHEADER), &bytesread, NULL))
    {
        CloseHandle(file);
        return NULL;
    }

    LONG width = bmi->biWidth;
    LONG height = abs(bmi->biHeight);

    if (bmi->biCompression != BI_RGB)
    {
        CloseHandle(file);
        return NULL;
    }

    if (bmi->biBitCount != 24)
    {
        CloseHandle(file);
        return NULL;
    }

    unsigned long size = bmi->biSizeImage - bmf.bfOffBits;
    PBYTE Buffer = new BYTE[size];
    if (SetFilePointer(file, bmf.bfOffBits, NULL, FILE_BEGIN) == 0xFFFFFFFF)
    {
    }

    if (!ReadFile(file, Buffer, size, &bytesread, NULL) || bytesread == 0)
    {
        delete[] Buffer;
        CloseHandle(file);
        return NULL;
    }
    CloseHandle(file);

    return Buffer;
}

调用者 - 输入位图,显示其内容,将其发送回输出

int main()
{
    BITMAPINFOHEADER bitmapInfoHeader;
    memset(&bitmapInfoHeader, 0, sizeof(bitmapInfoHeader));
    PBYTE pSrcBitmap = inputBMP(TEXT("input.bmp"), &bitmapInfoHeader);

    if (!pSrcBitmap)
        return 1;

    createBMPFile(pSrcBitmap, bitmapInfoHeader);

    unsigned int h = abs(bitmapInfoHeader.biHeight);
    unsigned int w = bitmapInfoHeader.biWidth;
    for (unsigned int y = 0; y < h; ++y)
    {
        for (unsigned int x = 0; x < w; ++x)
        {
            VERBOSE(TEXT("(%2d %2d %3d %3d %3d) "), x, y,
                pSrcBitmap[3 * (x + y * w)],
                pSrcBitmap[3 * (x + y * w) + 1],
                pSrcBitmap[3 * (x + y * w) + 2]);
        }
        VERBOSE(TEXT("\n"));
    }

    delete[] pSrcBitmap;
    pSrcBitmap = NULL;

    return 0;
}

我收到了非常奇怪的信息。

为了让所有人都很容易看到,我使用Paint制作了一个小矩形(黑白,但图像类型为24 bpp)。

enter image description here

输出似乎有颜色......

enter image description here

但这不是我最大的问题。显示屏显示不均匀的信息(参见x = 14,y = 5。

enter image description here

看起来我的字节发生了变化......我不明白为什么,或者如何能够在一行上看到整行。如果图像不是矩形而是其他形状,则会导致数据的奇怪包装(第1行的第1行字节放置,移动更多字节......)

我怀疑它与步幅有关......但我不明白如何,因为图像是24bpp ......

仍然,我试图在这个特定的bmp(w = 35,h = 10)

上加1
unsigned int w = bitmapInfoHeader.biWidth + 1;

突然我的像素列表似乎很好(没有移位或换行):

enter image description here

我不明白为什么......或者如何对任何尺寸的图像进行校正。

我试过

if (w != (w / 4) * 4)   w = (w / 4) * 4 + 1;

没有工作。

我需要能够遍历图像数据,而不是将其移位....有人可以解释一下这个移位/换行/字节未对齐的逻辑以及我如何修复它们?

1 个答案:

答案 0 :(得分:2)

位图扫描线用0,1,2或3个字节进行零填充,这样就可以了 scanlinesize%4 == 0

这是你(概念上)读取位图(未压缩,24位)

的方式
// bmi is a BITMAPINFOHEADER 
// bmf is a BITMAPFILEHEADER
// fp is a FILE*
int w = bmi.biWidth;
int h = bmi.biHeight;
int scanlinesize = w*3;
while( scanlinesize%4 ) ++scanlinesize;
for(int y=0;y<h;++y)
{
    fseek( fp, bmf.bfOffBits + scanlinesize*y, SEEK_SET );
    for(int x=0;x<w;++x)
    {
        unsigned char rgb[3];
        fread( rgb, 1, 3, fp );
        // put rgb in the output here
    }
}