在 C 中处理 BMP 文件中的填充

Posted

技术标签:

【中文标题】在 C 中处理 BMP 文件中的填充【英文标题】:Dealing with padding in a BMP file in C 【发布时间】:2016-07-15 16:17:05 【问题描述】:

我正在尝试用 C 语言编辑 BMP 文件。我的代码适用于没有填充的 BMP 文件,但我无法处理填充。

我读过一些关于 BMP 文件的其他问题,但大多数都使用其他语言,如 C# 和 Java,所以我觉得它们不是很有用。

这是像素阵列的样子,但要大得多:

char bmpArray[MAX] = B,G,R,B,G,R,B,G,R,0,0,0,
                      B,G,R,B,G,R,B,G,R,0,0,0,
                      B,G,R,B,G,R,B,G,R,0,0,0

零用于填充字节以使每行可被 4 整除,这取决于图像的像素宽度。我想要做的是将这些填充字节保留在相同位置,并且只处理 B、G、R 字节。如果我对填充值应用编辑,生成的图像将会失真。

我做了一个函数,根据宽度生成填充字节的数量。 它使用了这个公式4 - ((width * 3) % 4),当我用不同宽度的图像测试它时,它就可以工作了。

我成功提取了 BMP 文件的 B、G、R 数据并将其放入数组中,所以我只会发布我遇到问题的部分代码。

int c = 0;
for (int a = 0; a < height; a++) 
    for (int b = 0; b < width*3; b++) 
        if (bmpArray[a*(width*3)+b] < 127) 
            bmpArray[a*(width*3)+b] = 0;
         else 
            bmpArray[a*(width*3)+b] = 255;
        
        c++;
    
    for (int pad = 0; pad < padding; pad++) 
        bmpArray[c++] = 0x00;
    

我要做的是“绘制”输出 BMP 文件的每一行,然后在到达行尾时立即停止,即宽度 * 3,然后在去之前绘制填充字节下一行像素。

或者,有没有一种方法可以使用单个 for 循环识别填充像素,然后使用 if 语句不修改填充像素?例如:

for (int a = 0; a < bmpArraySize; a++) 
    paddingBytes = ??? //for example for the first row
                       // paddingBytes are i + width*3 + 1
                       // and i + width*3 + 2 and i + width*3 + 3 if padding = 3
    if (a = paddingBytes) 
        bmpArray[a] = 0x00;
    
    else if (bmpArray[a] < 127) 
        bmpArray[a] = 0;
    
    else 
        bmpArray[a] = 255;
    

【问题讨论】:

imgArray有什么用? 我解决了这个问题。我从另一个堆栈溢出问题中复制了那部分代码,忘记更改数组名称。 在遍历列时,您应该增加c 我在代码中添加了这个,对吗? 不...上移一行 【参考方案1】:

问题出在这部分:

int c = 0;
for (int a = 0; a < height; a++) 
    for (int b = 0; b < width*3; b++) 
        if (bmpArray[a*(width*3)+b] < 127) 
            bmpArray[a*(width*3)+b] = 0;
         else 
            bmpArray[a*(width*3)+b] = 255;
        
    
    for (int pad = 0; pad < padding; pad++) 
        bmpArray[c++] = 0x00;  /* ONLY HERE is 'c' updated! */
    

在每一行的末尾,您填写从c 开始的填充,它从0 开始,因此会覆盖第一行的前几个字节。然后,每个 next 行都会被复制,但您会继续从头开始覆盖(c 最初指向的位置)。

应在每一行添加填充。在循环中,您调整了ab,但您忘记调整填充。

我建议更直接的代码(未经测试!):

for (int a = 0; a < height; a++) 
    for (int b = 0; b < width*3; b++) 
        if (bmpArray[a*(width*3 + padding)+b] < 127) 
            bmpArray[a*(width*3 + padding)+b] = 0;
         else 
            bmpArray[a*(width*3 + padding)+b] = 255;
        
    
    for (int pad = 0; pad < padding; pad++) 
        bmpArray[a*(width*3 + padding) + 3*width + pad] = 0x00;
    

有什么方法可以识别填充像素..

是的——在我上面的循环中调整了填充,它会自动跳过填充本身。最后,您可以安全地删除显式的“将填充设置为 0”循环。

【讨论】:

太棒了!您认为使用第二种方法是否可行(1 个循环)? @user6005857:是的,但我建议不要这样做。您可以在 height * (width*3 + padding) 上执行一次循环,但是需要识别像素和填充的内部表达式变得相当复杂。【参考方案2】:

类似:

...

int originalLineSize = width * 3;
int workLineSize = originalLineSize + 4 - originalLineSize % 4;

for (int a = 0; a < bmpArraySize; ++a) 
    if ((a % workLineSize) >= originalLineSize)
        bmpArray[a] = 0x00;
    
    else if (bmpArray[a] < 127) 
        bmpArray[a] = 0;
    ...

【讨论】:

【参考方案3】:

“填充字节”是扫描线像素之后的字节。您对填充不像扫描线大小和像素大小那么感兴趣:

iScanlineSize  = ((width * bitsperpixel) + 31) / 32 * 4;
iBytesperPixel = bitsperpixel / 8;

现在您可以遍历扫描线并按如下方式处理像素和像素部分(颜色):

for (int a = 0; a < height; a++) 
    for (int b = 0; b < width; b++) 
        for (int c = 0; c < iBytesperPixel; c++) 
            pixelPart= bmpArray[a*iScanlineSize + b*iBytesperPixel + c];
        
    ]

【讨论】:

以上是关于在 C 中处理 BMP 文件中的填充的主要内容,如果未能解决你的问题,请参考以下文章

在 C 中创建 bmp 文件

如何在c语言 读取BMP图片的信息

C 中的 .BMP 文件 - DIB 标头返回图像大小 0,即使 BMP 标头返回文件大小

谁能解释一下 RowSize 和 PixelArraySize 在 BMP 文件格式中的含义?

用C语言编写程序处理图片bmp文件 1.读取图片的宽度,高度,每个像素所需的位数,水平分辨率,垂直

用标准c读取bmp文件的长宽?