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

Dealing with padding in a BMP file in C

我正在尝试用 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 数据并将其放入数组中,所以我只 post 我遇到问题的部分代码。

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;
    }
}

我想要做的是 "draw" 输出 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;
    }
}

问题出在这部分:

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;
    }
}

is there a way I can identify the padding pixels ..

是的——在我上面的调整填充的循环中,它会自动跳过填充本身。您可以安全地删除末尾的显式 'set padding to 0' 循环。

类似于:

...

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;
    ...
}

"padding bytes" 是扫描线像素后面的字节。您对填充不如对扫描线大小和像素大小感兴趣:

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];
        }
    ]
}