1

Well, it looks like I'm not the only one having issues with padding (I suspect that's my problem).

Specifically, if I test my version of resize with an enlargement factor of 2 (using small.bmp as input file), I can see (using xxd) that the first few pixels are "in phase" but then it gets out of whack: enter image description here

Given that output, I can't help but think I'm either not getting the padding right or I'm messing up my fseek statement. Can anyone tell me if I'm on the right track or does this have absolutely nothing to do with padding or fseek?

Code follows:

#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"

int main(int argc, char *argv[])
{
    // ensure proper usage
    if (argc != 4)
    {
        fprintf(stderr, "Usage: ./copy infile outfile\n");
        return 1;
    }

    // remember filenames
    int enlarge = atoi(argv[1]);
    char *infile = argv[2];
    char *outfile = argv[3];

    // open input file 
    if (enlarge < 1 || enlarge > 100)
    {
        fprintf(stderr, "Enlargement factor must be between 1 and 100\n");
        return 1;
    }

    FILE *inptr = fopen(infile, "r");
    if (inptr == NULL)
    {
        fprintf(stderr, "Could not open %s.\n", infile);
        return 1;
    }

    // open output file
    FILE *outptr = fopen(outfile, "w");
    if (outptr == NULL)
    {
        fclose(inptr);
        fprintf(stderr, "Could not create %s.\n", outfile);
        return 1;
    }

    // read infile's BITMAPFILEHEADER
    BITMAPFILEHEADER bf;
    fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);

    // read infile's BITMAPINFOHEADER
    BITMAPINFOHEADER bi;
    fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);

    // ensure infile is (likely) a 24-bit uncompressed BMP 4.0
    if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 || 
        bi.biBitCount != 24 || bi.biCompression != 0)
    {
        fclose(outptr);
        fclose(inptr);
        fprintf(stderr, "Unsupported file format.\n");
        return 1;
    }

    int inpadding =  (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;  // determine padding for input file

    bi.biWidth = bi.biWidth * enlarge;
    bi.biHeight = bi.biHeight * enlarge;

    int outpadding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;  // determine padding for output

    bi.biSizeImage = (sizeof(RGBTRIPLE) * bi.biWidth + outpadding) * abs(bi.biHeight);
    bf.bfSize = bi.biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    // write outfile's BITMAPFILEHEADER
    fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);

    // write outfile's BITMAPINFOHEADER
    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);

    for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)  //iterate for each scanline
    {
        for (int j = 0; j < enlarge; j++)  //iterate for "enlarge" number of times to make "enlarge" number of lines
        {
            for (int k = 0; k < bi.biWidth; k++)  //iterate for each pixel in line
            {
                RGBTRIPLE triple;
                fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
                for (int m = 0; m < enlarge; m++)  //iterate for "enlarge" number of times to make "enlarge" number of copies of pixels
                {
                    fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);   
                }
            }
            for (int n = 0; n < outpadding; n++)  //add padding to output file
            {
                fputc(0x00, outptr);
            }
            if (j < enlarge - 1) 
            {
                fseek (inptr, -bi.biWidth * 3, SEEK_CUR); //return to beginning of scanline
            }
            else
            {
                fseek (inptr, inpadding, SEEK_CUR);  //skip over padding in input file
            }
        }
    }

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);

    // success
    return 0;
}

I think I am just missing something about the nature of padding. As far as I understand, it adds 0x00 until the total number of bytes in the scanline is divisable by 4. This is just fine if we’re adding 3x 0x00 because it adds exactly 1 pixel’s worth of data. If, however, the image has 5 pixels (15 bytes) and we add 1 more 0x00 worth of padding or, if we have 6 pixels (18 bytes) and we add 2 more 0x00 worth of padding, then the pixels get out of phase.

For example, if we start with small.bmp:

00ff00 00ff00 00ff00 000000
00ff00 ffffff 00ff00 000000
00ff00 00ff00 00ff00 000000                            

And double it, our first line should be:

00ff00 00ff00 00ff00 00ff00 00ff00 00ff00….

…add 2 bytes worth of padding to it gives us:

00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 0000….

….and if I start adding the next line of data, I end up with

00ff00 00ff00 00ff00 00ff00 00ff00 00ff00 000000
ff0000 ff0000 ff0000 ff0000 ff0000 ff0000….

So, I guess my question is what the heck am I missing? It seems like whatever padding I add should be a multiple of 3 (in addition to making the total width equal a multiple of 4). Sorry for being so dense!

1

You change bi.biWidth and bi.biHeight to new scale using

    bi.biWidth = bi.biWidth * enlarge;
    bi.biHeight = bi.biHeight * enlarge;

and then use code that relies on the values to represent the input file

    for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)  //iterate for each scanline

and

            for (int k = 0; k < bi.biWidth; k++)  //iterate for each pixel in line

One way would be to write not bi itself, but a copy of bi with changed values for biWidth, another would be to store pre-scale size in two extra variables like inWidth and inHeight and use them instead, or you could use abs(bi.biHeight)/enlarge and bi.biWidth/enlarge to revert the scaling.

4
  • Oh, that’s just embarrassing! Yup, that was definitely creating major problems. I're really sorry for taking up your time on something I absolutely should have caught. I think I was so focused on the padding because there is something quite fundamental about it that I'm obviously not understanding. I don't have enough space to describe it here, so I'll edit my question to try to explain.
    – Rhinofish
    Mar 2 '17 at 20:10
  • Padding is measured in bytes, not pixels, and does not have to be a multiple of pixel length.
    – Blauelf
    Mar 3 '17 at 8:20
  • Thanks Blauelf. I know the padding is in bytes and that's precisely why I'm a bit confused: if we add 1 or 2 bytes to the image data, won't it throw the pixels out of synch since we're adding a fraction of one pixel's worth of data? In the example above, insertion of 2 bytes (0x00 twice) to 00ff00 00ff00... results in 00ff00 000000 ff0000... and I end up with black and blue pixels.
    – Rhinofish
    Mar 5 '17 at 0:47
  • The specification tells us to add 0 to 3 bytes of padding until the line length is divisible by 4, that might be meant to help address the start of single lines on systems where it is easier or faster to address memory addresses divisible by 4. Adding multiples of pixels would have been a waste of space, and there's no reason for doing so.
    – Blauelf
    Mar 6 '17 at 6:00

This site is temporarily in read only mode and not accepting new answers.

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