2

I'm having problems with the header manipulation. My code seems to factor well images but it doesn't calculate well bi.biSizeImage and bf.bfSize.

This is what I've done:

// read infile's BITMAPFILEHEADER. Is going to read 1 object of size BITMAPFILEHEADER from inptr and place it in the adress pointed by &bf
BITMAPFILEHEADER bf;
fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);

// read infile's BITMAPINFOHEADER. Is going to read 1 object of size BITMAPINFOHEADER from inptr and place it in adress pointed by &bi
BITMAPINFOHEADER bi;
fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);

// save BITMAPFILEHEADER and BITMAPINFOHEADER from infile
BITMAPFILEHEADER bf_original = bf;
BITMAPINFOHEADER bi_original = bi;
int original_padding =  (4 - (bi_original.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

// update outfile's BITMAPINFOHEADER by factor
bi.biWidth = bi.biWidth * factor;
bi.biHeight = bi.biHeight * factor;
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
bi.biSizeImage = bi.biWidth * abs(bi.biHeight) * sizeof(RGBTRIPLE) + padding * abs(bi.biHeight);         
bf.bfSize = bi.biSizeImage + sizeof(BITMAPFILEHEADER);

// write outfile's BITMAPFILEHEADER. Is going to write from buffer bf, 1 object of size BITMAPFILEHEADER to outputr
fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);

// write outfile's BITMAPINFOHEADER. Is going to write from bugger bf, 1 object of size BITMAPINFOHEADER to outputr.
fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);

Now, this code gives me completely wrong metadata info. For example, for a 3x3 image resized by a factor of 4, these are the results:

offset  type   name              staff       me
2       DWORD  bfSize           000001e6  00000032
34      DWORD  biSizeImage      000001b0  00000024

Been trying to debug this for a while now and I'm burnout and frustated. I just don't understand why it doesn't work. It should be working.

1
  • What specific problems are you seeing with this? – Luke Van In Aug 9 '14 at 20:57
7

At least one of the problems is caused by the way fwrite is being used, e.g:

fwrite(&triple, sizeof(RGBTRIPLE), factor, outptr);

This looks like it's intended to duplicate the value in triple the number of times defined by factor. So if factor has the value of 4, this should write out 4 triples.

Unfortunately this is not how fwrite works. Here's what man fwrite says:

The function fwrite() writes nitems objects, each size bytes long, to the stream pointed to by stream, obtaining them from the location given by ptr.

So fwrite will write the the number of triples you specify, but it will read the values from the values provided pointer &triple. The code in your example only reads one triple, with the result that the first triple will be correct, but the others will contain random data, assuming the program doesn't crash due to a segfault.

To write multiple triples fwrite needs to be called multiple times, writing only one triple each time. Alternatively the triples can be duplicated into an array before being written.


Regarding the header size calculation:

bi.biSizeImage = bi.biWidth * bi.biHeight * sizeof(RGBTRIPLE) + padding * abs(bi.biHeight);         

The code is missing an abs() statement on the first bi.biHeight term, and so the result will be a negative number which is not correct for the image size.

The file size depends on the image size calculation, so if the first calculation is incorrect, the second one will be too:

bf.bfSize = bi.biSizeImage + 54;

Tip: Instead of using the hard-coded constant 54, you might use sizeof() to ensure the correct size is used, e.g.

bf.bfSize = bi.biSizeImage + sizeof(bf);

It may help to convert the numbers in the comparison report into decimal (shown in brackets below):

offset  type   name             staff           me
2       DWORD  bfSize           000001e6 (491)  00000032 (50)
34      DWORD  biSizeImage      000001b0 (432)  00000024 (36)

Tip: Use the calculator app on mac or windows set to 'programmer' mode to easily convert between hexadecimal and decimal.

So here you can see that the staff's file size is 491 bytes, and the image size is 432 bytes. Your file size is calculated to be 50 bytes, and the image size is 46 bytes. So where do these numbers come from?

First, let's see how the correct numbers are calculated.

  • The image width is 3 pixels.
  • The scan line size is image width * scale factor

    = 3 * 4

    = 12

  • 12 is a multiple of 4 so no padding is needed, padding = 0.

  • scan line bytes is scan line size * sizeof(RGBTRIPLE) + padding

    = 12 * 3

    = 36

  • Total number of bytes is scan line bytes * number of rows * scale factor

    = 36 * 3 * 4

    = 432

432 is the value reported by the staff's implementation so we know the calculation is correct. Notice along the way that we also got the value 36, which is the same value in the header in your image file. This may just be coincidence, or it may indicate where the error is in your calculation.

Regarding the bfSize field, recall from the specification that it needs to reflect the entire size of the file. At the moment it has the (incorrect) size of the image (36 bytes) plus 14 bytes which is the size of the BITMAPFILEHEADER. The staff implementation seems to add another 45 bytes on top of these values. What else is included in the file that is not accounted for in your calculation?


Been trying to debugg this for a while now and I'm burnout and frustated.

You're so close, keep going! The feeling of accomplishment when you solve this is a gratifying reward in itself.

7
  • Thank you. I tried a new method and updated the thread because I'm still having problems unfortunately. – FranGoitia Aug 10 '14 at 1:46
  • Could you describe what problems you are still having, or could you narrow down the cause to a specific case or condition? You can edit your question to add extra information if necessary. – Luke Van In Aug 10 '14 at 9:38
  • Yes, I don't know what else to say. The code doesn't work, it doesn't pass any test. – FranGoitia Aug 10 '14 at 13:26
  • A great deal of the skill in programming is in identifying errors and their cause. It won't be helping you much if I debug the code for you. See if you can't at least describe the problem. Try using xxd to display the output data to see where it's going wrong, or use gdb to step through the code and inspect the values to see if it's doing what you expect. Also try simplifying your program (e.g. to output just one scan line), or try it with different images to see how the program behaves. Once you can understand the problem, the solution is often not far away. – Luke Van In Aug 10 '14 at 13:42
  • 1
    Well I just discovered an important bug in my code. In the loops I'm iterating over the length and width of the resized image, when I should be iterating over the length and width of default image. That is why my code starts writing garbage value after one point. So, I corrected that, resized small.bmp by a factor of 4 and compared it using xxd with the implementation of the staff and it looks like my code is working, but, i'm having problems with the headers now. It seems like my calculations of bfSize and biSizeImage are wrong. – FranGoitia Aug 10 '14 at 15:00
1

necromancer here,

for people going forward, the bf.bfSize formula given by OP is missing sizeof(bi)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .