仅当行填充等于 3 个字节时,C++ 读取 BMP 文件才有效

Posted

技术标签:

【中文标题】仅当行填充等于 3 个字节时,C++ 读取 BMP 文件才有效【英文标题】:C++ reading BMP file works only when row padding is equal to 3 bytes 【发布时间】:2013-04-07 10:00:05 【问题描述】:

我在用 C++ 读取位图文件时遇到问题。我的代码仅在行填充等于 3 个字节时才有效。任何其他填充都会造成奇怪的事情 - 有时我无法读取输出文件,有时我可以打开它,但它看起来像垃圾并且具有不同的宽度和高度。

这是我从位图文件中读取数据的函数:

void read_bmp(ImageFile* Image, const char* filename)
FILE* pFile;
unsigned char* buffer;
unsigned int bufferSize, offset_bitmapData, counter_PixelCounter=0, offset_paddingSum=0;

pFile = fopen(filename, "rb"); 
if(pFile==NULL) throw ERR_FILE_DOES_NOT_EXIST;

fseek(pFile, 0, SEEK_SET);
bufferSize = 54;
buffer = new unsigned char[bufferSize];
if(fread(buffer, sizeof(char), bufferSize, pFile)!=bufferSize) throw ERR_FILE_READING_ERROR;
if(readBytes_int(0, 2, buffer)!=0x4D42) throw ERR_NO_BMP_HEADER;

offset_bitmapData = readBytes_int(0x0A, 4, buffer);
Image->ImageWidth = readBytes_int(0x12, 4, buffer);
Image->ImageHeight = readBytes_int(0x16, 4, buffer);
Image->FileSize = readBytes_int(0x02, 4, buffer);
Image->bbpInfo = readBytes_int(0x1C, 2,buffer);
Image->Padding = (4 - (Image->ImageWidth*3)%4)%4;
cout<<"width "<<Image->ImageWidth<<endl;
cout<<"padding "<<(int)Image->Padding<<endl;

if(readBytes_int(0x0E, 4, buffer)!=40) throw ERR_NO_BITMAPINFOHEADER;

delete[] buffer;
bufferSize = Image->ImageWidth * Image->ImageHeight * 3 + Image->ImageHeight * Image->Padding;
buffer = new unsigned char[bufferSize];
if(buffer==NULL) throw ERR_BAD_ALLOC;
fseek(pFile, offset_bitmapData, SEEK_SET);

if(fread(buffer, sizeof(char), bufferSize, pFile)!=bufferSize) throw ERR_FILE_READING_ERROR;
fclose(pFile);

Image->PixelArray = new unsigned char**[Image->ImageHeight];
counter_PixelCounter = 0;

for(int height = Image->ImageHeight-1; height >= 0; height--)

    Image->PixelArray[height] = new unsigned char*[Image->ImageWidth];

    for(int width = 0; width < Image->ImageWidth; width++)
    
        Image->PixelArray[height][width] = new unsigned char[3];

        Image->PixelArray[height][width][0] = (unsigned char)readBytes_int((counter_PixelCounter ) * 3  + offset_paddingSum + 2, 1, buffer);
        Image->PixelArray[height][width][1] = (unsigned char)readBytes_int((counter_PixelCounter ) * 3 + offset_paddingSum + 1, 1, buffer);
        Image->PixelArray[height][width][2] = (unsigned char)readBytes_int((counter_PixelCounter ) * 3 + offset_paddingSum, 1, buffer);

        counter_PixelCounter++;

     
    offset_paddingSum += Image->Padding;

cout<<counter_PixelCounter<<endl;
cout<<"File loaded successfully\n";

【问题讨论】:

似乎对我有用,在一个零填充的文件上。阅读代码也没有明显的错误。不过确实很丑。 所有这些神奇的数字使代码几乎无法阅读,也许你搞砸了?无论如何,还有压缩位图,您既不检查也不处理。我不确定,但我似乎记得 BMP 具有单独的宽度(像素)和步幅(每行字节数),您也需要考虑这些。顺便说一句,new[] 会抛出失败,不需要自己检查和抛出宏。 【参考方案1】:

The documentation says

DIB 由两个不同的部分组成: BITMAPINFO 结构 描述位图的尺寸和颜色,以及一个数组 定义位图像素的字节。数组中的位是 打包在一起,但每条扫描线必须用零填充以结束 在 LONG 数据类型边界上

您的代码错误地实现了粗体子句。您正在四舍五入像素数,而不是字节数。

【讨论】:

是的...但是每个像素有 3 个字节,所以我将像素计数器乘以 3,这是我的字节数...【参考方案2】:

.BMP 文件中有四个字节用于描述文件地址 0x22 的填充大小。

您可以阅读它并跳过填充。另请参阅此问题和回复: C++: .bmp to byte array in a file

【讨论】:

以上是关于仅当行填充等于 3 个字节时,C++ 读取 BMP 文件才有效的主要内容,如果未能解决你的问题,请参考以下文章

C++ - 从 BMP 文件中读取每个像素的位数

仅当行不存在时才插入 SQL

仅当行不存在时才附加到 csv

在 C++ 中读取 .bmp 文件

仅当行已更改时,MySQL 才在更新后触发

仅当行不存在时,如何在 SQL Server 中执行插入操作? [复制]