user6005857
user6005857

Reputation: 631

Dealing with padding in a BMP file in C

I am trying to edit a BMP file in C. My code works for BMP files with no padding but I am having trouble dealing with padding.

There are a few other questions on BMP files that I have read but most of them use other languages like C# and Java so I didn't find them very useful.

Here is what the pixel array looks like, but much larger:

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}

The zeros are for padding bytes to make each row divisible by 4, it depends on the pixel width of the image. What I am trying to do is leave these padding bytes the way they are in the same position and only deal with the B,G,R bytes. If I apply edits to the padding values, the resulting image will be distorted.

I made a function that generates the amount of padding bytes based on the width. It uses this formula 4 - ((width * 3) % 4) and it works as I tested it with images with different width.

I successfully extracted the B, G, R data of the BMP file and put it into an array so I will only post the part of the code I am having trouble with.

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

What I am trying to do is "draw" each row of the output BMP file and then stop as soon as I reach the end of the row, that is width*3, then after that draw the padding bytes before going to the next row of pixels.

Alternatively, is there a way I can identify the padding pixels using a single for loop and then use an if statement to not modify the padding pixels? For example:

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

Upvotes: 2

Views: 9898

Answers (3)

Paul Ogilvie
Paul Ogilvie

Reputation: 25286

The "padding bytes" are the bytes following the pixels of a scanline. You are not so interested in the padding as in the scanline size and pixel size:

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

Now you can loop over scanlines and adress pixels and pixel parts (colors) as follows:

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

Upvotes: 1

tvorez
tvorez

Reputation: 460

Something like:

...

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

Upvotes: 3

Jongware
Jongware

Reputation: 22457

The problem is in this part:

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! */
    }
}

At the end of each line, you fill out the padding starting at c, which starts out at 0 and so overwrites the first few bytes of the first line. Then, each next line gets copied but you continue overwriting from the start (where c initially pointed to).

The padding should be added on each line. In the loops, you adjust a and b but you forget to adjust for the padding.

I suggest the more straightforward code (untested!):

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

Yes – in my loop above with adjustments for padding, it automatically skips the padding itself. You can safely remove the explicit 'set padding to 0' loop at the end.

Upvotes: 3

Related Questions