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:
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!