Padding for BMP files with Dynamic Memory

So the goal of this program is to invert a picture's pixels in a BMP format.

I'm not really understanding how the padding part of the code works as far as fseeking to skip the padding and also reading those padded lines and the ones that do not need to be padded from a 2D array into a 1D array in dynamic memory.

essentially what I'm asking is how do you tell the computer to skip the padding you have to add to some of the lines and is it just read into the dynamic array using fread?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118



#include <cstdint>
#include <cstdio>
using namespace std;

#pragma pack(push, 2)

//offset 0

struct BitmapFileHeader {
    uint16_t type;
    uint32_t size;
    uint16_t reserve1;
    uint16_t reserve2;
    uint32_t offset;
};

//offset 14

struct BitmapInfoHeader {
    uint32_t size;
    uint32_t width;
    uint32_t height;
    const uint16_t planes = 1;
    uint16_t bitcount;
    const uint32_t compression = 0;
    uint32_t imagesize; //seek end to find this one
    uint32_t xPPM;
    uint32_t yPPM;
    uint32_t colorUse;
    uint32_t const color_important = 0;
};

struct Pixel {
    uint8_t R;
    uint8_t G;
    uint8_t B;
};

#pragma pack(pop)

int main (int argc, char **argv) {
    FILE *fin;

    BitmapFileHeader BM1;
    BitmapInfoHeader info1;
    Pixel pix;

    int px_padding = 0;

    if (argc != 3) {
        printf("Invalid input; implement correct usage of the program.\n");
        printf("Usage: %s (inputfile) (outputfile) \n", argv[0]);   
        return 1;
    } 

    fin = fopen(argv[1], "rb");
    if (fin == nullptr) {
        perror("FILE NOT OPEN\n");
        return 1;
    }

    // displays size of the file header --- 14 bytes

    printf("Size of BitmapFileHeader Struct: %u\n", fread(&BM1, 1, sizeof(BM1), fin)); 
    printf("Size of BitmapInfoHeader Struct: %u\n", fread(&info1, 1, sizeof(info1), fin));
    printf("Size of Pixel Struct: %u\n", fread(&pix, 1, sizeof(pix), fin));


    fseek(fin, 10, SEEK_SET);
    int offset = fread(&BM1, 1, 4, fin); // reading offset -- 4 bytes

    printf("Read %u Bytes for offset, width, and height\n", offset);

    fseek(fin, 18, SEEK_SET);
    int width = fread(&info1, 1, 4, fin); // reading in width -- 4 bytes


    fseek(fin, 22, SEEK_SET);
    int height = fread(&info1, 1, 4, fin); // reading in height -- 4 bytes

    printf("Offset: %u, Height: %u, Width: %u\n", BM1.offset, info1.height, info1.width); // IN BYTES
    
    
    int px_width = info1.width * sizeof(Pixel); // width of file times size of pixel struct to get pixel width

    int px_height = info1.height * sizeof(Pixel); // height of file times size of pixel struct to get pixel height
    
    Pixel* pixel = new Pixel[(px_width * px_height)];
    
    
    fseek(fin, BM1.offset, SEEK_SET); // seeking to where the pixel struct starts


    if (px_width % 4 != 0) {
        px_padding = (4 - (px_width % 4)) % 4; // add second mod 4 so multiples of four get No padding
        fread(&pixel, 1, px_padding, fin); // reads padding
        fseek(fin, px_padding, SEEK_CUR); // then skips padding
        printf("padding is %u\n", px_padding);
    }



   



    fclose(fin);

    delete[] pixel;

    return 0;
}


Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
#include <cstdint>

using std::uint32_t, std::uint16_t, std::uint8_t;

struct BitmapFileHeader
{
    uint16_t type;
    uint32_t size;
    uint16_t reserved1, reserved2;
    uint32_t offset;
};

struct BitmapInfoHeader
{
    uint32_t size, width, height;
    uint16_t planes, bitcount;
    uint32_t compression, imagesize;
    uint32_t xPPM, yPPM;
    uint32_t colorUse, important;
};

struct Pixel { uint8_t b, g, r; };

void read(std::istream&) { }

template <typename T, typename ...Types>
void read(std::istream& in, const T& value, const Types&... values)
{
    in.read((char*)&value, sizeof value);
    read(in, values...);
}

void write(std::ostream&) { }

template <typename T, typename ...Types>
void write(std::ostream& in, const T& value, const Types&... values)
{
    in.write((char*)&value, sizeof value);
    write(in, values...);
}

std::vector<Pixel>
readBMP(
    const std::string& filename,
    BitmapFileHeader& bm,
    BitmapInfoHeader& info)
{
    std::ifstream fin(filename, fin.binary);
    if (!fin)
    {
        std::cerr << "Cannot open input file\n";
        std::exit(EXIT_FAILURE);
    }

    // Read headers
    read(fin, bm.type, bm.size, bm.reserved1, bm.reserved2, bm.offset);
    read(fin, info.size, info.width, info.height, info.planes,
        info.bitcount, info.compression, info.imagesize,
        info.xPPM, info.yPPM, info.colorUse, info.important);

    if (info.bitcount != 24)
    {
        std::cerr << "The bitmap must use 24-bit color.\n";
        std::exit(EXIT_FAILURE);
    }

    // Seek to start of pixel data
    fin.seekg(bm.offset);

    // Read pixel data
    std::vector<Pixel> pixels;
    pixels.reserve(info.width * info.height);
    uint32_t pad = 0, padding = (4 - (info.width * sizeof(Pixel) % 4)) % 4;
    for (uint32_t row = 0; row < info.height; ++row)
    {
        for (uint32_t col = 0; col < info.width; ++col)
            fin.read((char*)&pixels[row * info.width + col], sizeof(Pixel));
        if (padding) fin.read((char*)&pad, padding);
    }

    return pixels;
}

void
writeBMP(
    const std::string& filename,
    BitmapFileHeader bm,
    const BitmapInfoHeader& info,
    const std::vector<Pixel>& pixels)

{
    std::ofstream fout(filename, fout.binary);
    if (!fout)
    {
        std::cerr << "Cannot open output file\n";
        std::exit(EXIT_FAILURE);
    }

    bm.offset = sizeof bm + sizeof info;

    // Write headers
    write(fout, bm.type, bm.size, bm.reserved1, bm.reserved2, bm.offset);
    write(fout, info.size, info.width, info.height, info.planes,
        info.bitcount, info.compression, info.imagesize,
        info.xPPM, info.yPPM, info.colorUse, info.important);

    // Write pixel data
    uint32_t pad = 0, padding = (4 - (info.width * sizeof(Pixel) % 4)) % 4;
    for (uint32_t row = 0; row < info.height; ++row)
    {
        for (uint32_t col = 0; col < info.width; ++col)
            fout.write((char*)&pixels[row * info.width + col], sizeof(Pixel));
        if (padding) fout.write((char*)&pad, padding);
    }
}

int main (int argc, char **argv)
{
    if (argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << " inputfile outputfile\n";
        std::exit(EXIT_FAILURE);
    }

    BitmapFileHeader bm;
    BitmapInfoHeader info;
    auto pixels = readBMP(argv[1], bm, info);

    // Print header info
    std::cout << "Filesize: "   << bm.size
              << "  Width: "    << info.width
              << "  Height: "   << info.height
              << "  Planes: "   << info.planes
              << "  Bitcount: " << info.bitcount << '\n';

    // Modify pixels ("invert" colors)
    for (uint32_t row = 0; row < info.height; ++row)
        for (uint32_t col = 0; col < info.width; ++col) {
            auto p = &pixels[row * info.width + col];
            p->r = 255 - p->r;
            p->g = 255 - p->g;
            p->b = 255 - p->b;
        }

    writeBMP(argv[2], bm, info, pixels);
}

Last edited on
I did pretty much the same thing whenever I read in the pixel data but with this section and I've made sure my padding is 1 for a width of 1381, but every time i compile with that printf statement at the end of the nested loop just to test where the file pointer is, it infinitely loops, despite you have the exact same logic and it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

fseek(fin, BM1.offset, SEEK_SET);

    for(int i = 0; i < info1.height; i++) {

        for (int z = 0; z < info1.width; z++){

            fread(&pixel[(i*info1.width) + z], 1, info1.width, fin); // read in an entire row
            fseek(fin, px_padding, SEEK_CUR);
            printf("My dick is %d inches long\n", ftell(fin));
        }

    }

Last edited on
You don't want the inner loop if you are reading a row at a time. And you need to read info1.width * sizeof(Pixel) bytes per row (and then the padding).
I haven't tested this, but something like:

1
2
3
4
5
    fseek(fin, BM1.offset, SEEK_SET);
    for (int i = 0; i < info1.height; i++) {
        fread(&pixel[i * info1.width], info1.width, sizeof(Pixel), fin);
        fseek(fin, px_padding, SEEK_CUR);
    }

@dutch

That last post ended up working, and I've pretty much done the same thing for writing the file.

The images are dashingly close but using diff in the console tells me that they differ.

and then I wrote a bash script to tell me where the differences are, and it looks like the file I'm writing is missing 2 zeros of padding; however, I'm not sure how to implement two zeros into the code... I could just hard code it, but then that would mess up the read, or perhaps the problem is in reading the padding in the first place... Although I'm sure I've calculated the padding correctly because I've done it on paper for a multitude of widths and it works just fine... I'm thinking I might need to designate specific rows that need the padding?

for reference what I've revised is right here based on your help and my own wits:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

#include <cstdint>
#include <cstdio>
using namespace std;

#pragma pack(push, 2)

// offset 0

struct BitmapFileHeader {
    uint16_t type;
    uint32_t size;
    uint16_t reserve1;
    uint16_t reserve2;
    uint32_t offset;
};

// offset 14

struct BitmapInfoHeader {
    uint32_t size;
    uint32_t width;
    uint32_t height;
    const uint16_t planes = 1;
    uint16_t bitcount;
    const uint32_t compression = 0;
    uint32_t imagesize; 
    uint32_t xPPM;
    uint32_t yPPM;
    uint32_t colorUse;
    uint32_t const color_important = 0;
};

// offset 54

struct Pixel {
    uint8_t R;
    uint8_t G;
    uint8_t B;
};

#pragma pack(pop)

int main (int argc, char **argv) {
    
    FILE *fin, *fout;
    BitmapFileHeader BM1;
    BitmapInfoHeader info1;
    Pixel pix;
    unsigned int px_padding = 0;

    if (argc != 3) {
        printf("Invalid input; implement correct usage of the program.\n"); // error checking program usage
        printf("Usage: %s (inputfile) (outputfile) \n", argv[0]);   
        return 1;
    } 

    fin = fopen(argv[1], "rb");
    if (fin == nullptr) {
        perror("INPUT FILE FILE NOT OPEN\n"); // error checking file
        return 1;
    }

    fread(&BM1, 1, sizeof(BM1), fin);
    fread(&info1, 1, sizeof(info1), fin);
    fread(&pix, 1, sizeof(pix), fin);

    Pixel* pixel = new Pixel[(info1.width * info1.height)]; // allocating new memory for how many pixels we got width x height
    
    if (info1.width % 4 != 0) {
        px_padding = (4 - ((info1.width * sizeof(pix)) % 4)); //calculating le padding which is 3
    }

    fseek(fin, BM1.offset, SEEK_SET);

    for (unsigned int i = 0; i < info1.height; i++) {
        fread(&pixel[i * info1.width], 1, info1.width * sizeof(Pixel), fin); // reading bytes on the rows                                                             
        fseek(fin, px_padding, SEEK_CUR); // skipping padding from current location
    }
    
    
    fclose(fin);

    // START WRITE

    fout = fopen(argv[2], "wb");

    if (fout == nullptr) {
        perror("OUTPUT FILE NOT OPEN\n");
        return 1;
    }

    fwrite(&BM1, 1, sizeof(BM1), fout);
    fwrite(&info1, 1, sizeof(info1), fout);
    fwrite(&pix, 1, sizeof(pix), fout);

    fseek(fout, BM1.offset, SEEK_SET);

    for(unsigned int i = 0; i < info1.height; i++) { // does the same thing as the read above except this is now writing
        fwrite(&pixel[(i * info1.width)], 1, info1.width * sizeof(Pixel), fout); 
        fseek(fin, px_padding, SEEK_CUR);     
    }

    fclose(fout);

    delete[] pixel;

    return 0;
}

Last edited on
The 'if' part needs the same test as the calculation

1
2
3
    if ((info1.width * sizeof(pix)) % 4) != 0) {
        px_padding = (4 - ((info1.width * sizeof(pix)) % 4));
    }

And when writing you shouldn't just "seek" but should actually write the zeroes.

Also, you have these strange statements:

[/code]
fread(&pix, 1, sizeof(pix), fin);
// and
fwrite(&pix, 1, sizeof(pix), fout);
[/code]
I have no idea why you would read the very first pixel separately but you should get rid of those statements.

So 'pix' is useless. Get rid of it and use Pixel in the sizeof expressions.

And at the beginning of the write section you should set BM1.offset to sizeof BM1 + sizeof info1 since even if there was some extra stuff in the input file between the headers and the pixel data you aren't dealing with it. And you should get rid of the seek to the offset in the write section. The pixels come right after the headers in the output file even if they weren't in that position in the input file.

You should have a test in the read section after reading the headers that bitcount is 24 and stop the program if it isn't since the program assumes 24 bit color.
Topic archived. No new replies allowed.