I am working on an image-resizer with c. It supposed to enlarge a picture by an integer scale between 1 and 100, inclusively. The problem I am having is that the output image ends up being a distorted version of the original, and I am having some difficulty pinpointing what's causing it. I've tried using printf to trace all the functions and variables, and they all seem to be functioning properly. What is happening is that, for an input of the image of square with a different colored square in the middle, I get an output of an enlarge square (per scale) but the square in the middle has turned into a rectangle and moved near the top of the picture. My sense is that the problem is in the nested loops. Any help is appreciated. Please let me know if you'd like to view a snapshot of the distortion.
Thanks!
Here's the code:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bmp.h"
int main (int argc, char *argv[])
{
// ensure proper usage
if (argc != 4)
{
fprintf(stderr, "Usage: ./resize n infile outfile\n");
return 1;
}
// check for number in first argument
for (int i = 0 , n = strlen(argv[1]); i < n; i++)
{
int k = isdigit(argv[1][i]);
if (k == 0)
{
fprintf(stderr, "Cannot use %s for scale. Please enter a valid value\n", argv[1]);
return 1;
}
}
//remember scale
int scale = atoi(argv[1]);
// check for value of first argument
if (scale > 100)
{
fprintf(stderr, "%i is greater than 100\n", scale);
return 1;
}
// remember filenames
char *infile = argv[2];
char *outfile = argv[3];
// open input file
FILE *inptr = fopen(infile, "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", infile);
return 2;
}
// open output file
FILE *outptr = fopen(outfile, "w");
if (outptr == NULL)
{
fclose(inptr);
fprintf(stderr, "Could not create %s.\n", outfile);
return 3;
}
// 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 4;
}
// determine padding for scanlines
int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
// update header
BITMAPFILEHEADER outbf = bf;
BITMAPINFOHEADER outbi = bi;
//define new dimensions
outbi.biWidth = scale * bi.biWidth;
outbi.biHeight = scale * bi.biHeight;
//define new padding
int outpadding = (4 - (outbi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
//define new sizes
outbi.biSizeImage = ((sizeof(RGBTRIPLE) * outbi.biWidth) + outpadding) * abs(outbi.biHeight);
outbf.bfSize = outbi.biSizeImage + bf.bfOffBits;
// write outfile's BITMAPFILEHEADER
fwrite(&outbf, sizeof(BITMAPFILEHEADER), 1, outptr);
// write outfile's BITMAPINFOHEADER
fwrite(&outbi, sizeof(BITMAPINFOHEADER), 1, outptr);
// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
for (int h = 0; h < scale; h++)
{
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// temporary storage
RGBTRIPLE triple;
// read RGB triple from infile
fread(&triple, sizeof(RGBTRIPLE), 1, inptr);
// write pixel per scale
for (int w = 0; w < scale; w++)
{
// write RGB triple to outfile
fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}
}
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
// then add it back (to demonstrate how)
for (int k = 0; k < outpadding; k++)
{
fputc(0x00, outptr);
}
}
}
// close infile
fclose(inptr);
// close outfile
fclose(outptr);
// success
return 0;
}
You are reading input for each repeated horizontal line, which is why the square moved up.
It should be moved out of the vertical scale loop. I suggest you allocate memory for one scan line, read the whole line into a buffer, and then write it from the buffer.
// iterate over infile's scanlines
// TEMPORARY STORAGE
RGBTRIPLE *buff = malloc (bi.biWidth * sizeof(RGBTRIPLE));
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
// THIS MOVED UP OUT OF THE LOOP
// read RGB triple from infile
fread(buff, sizeof(RGBTRIPLE), bi.biWidth, inptr);
for (int h = 0; h < scale; h++)
{
// iterate over pixels in scanline
for (int j = 0; j < bi.biWidth; j++)
{
// write pixel per scale
for (int w = 0; w < scale; w++)
{
// write RGB triple to outfile - FROM BUFFER
fwrite(&buff[j], sizeof(RGBTRIPLE), 1, outptr);
}
}
// then add it back (to demonstrate how)
for (int k = 0; k < outpadding; k++)
{
fputc(0x00, outptr);
}
}
// MOVED DOWN OUT OF LOOP
// skip over padding, if any
fseek(inptr, padding, SEEK_CUR);
}
free(buff);
The code should be checking the return value from fread
, the number of items read. Because it does not, you failed to notice that no more data was being read, and your RGBTRIPLE triple
continued to hold its previously read values.