0

On resize.c, my code compiles and runs without any errors. However, the image produced is a broken image. At the top of the viewer is the error message 'Premature end-of-file encountered'. This is my code:

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

#include "bmp.h"

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

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

printf("%d\n",n);

// open input file 
FILE* inptr = fopen(infile, "r");
if (inptr == NULL)
{
    printf("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);

BITMAPFILEHEADER new_bf = bf;   
BITMAPINFOHEADER new_bi = bi; 

int oldwidth = bi.biWidth;
int oldheight = bi.biHeight;

new_bi.biWidth = oldwidth * n;
new_bi.biHeight = oldheight * n;


int newpadding =  (4 - (new_bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
int newwidthP = new_bi.biWidth + newpadding;

// bi size is img size w/out headers - bf size is with
new_bi.biSize = (newwidthP * sizeof(RGBTRIPLE)) * new_bi.biHeight;
new_bf.bfSize = new_bi.biSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPFILEHEADER);
printf("%i\n",new_bi.biSize);

// 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;
}

// write outfile's BITMAPFILEHEADER
fwrite(&new_bf, sizeof(BITMAPFILEHEADER), n, outptr);

// write outfile's BITMAPINFOHEADER
fwrite(&new_bi, sizeof(BITMAPINFOHEADER), n, outptr);

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


// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
    // 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 RGB triple to outfile
        fwrite(&triple, sizeof(RGBTRIPLE), n, outptr);
    }

    // skip over padding, if any
    fseek(inptr, padding, SEEK_CUR);

    // then add it back (to demonstrate how)
    for (int k = 0; k < newpadding; k++)
    {
        fputc(0x00, outptr);
    }
}

// close infile
fclose(inptr);

// close outfile
fclose(outptr);

// that's all folks
return 0;
}
2

First let's look at your headers...

int newwidthP = new_bi.biWidth + newpadding; //Incompatible units being mixed
                                             //biWidth is in pixels, newpadding in bytes
                                             //newwidthP is now a meaningless mix-up

// Exression below broken because of 1.) Unit mismatch above
//                                   2.) newpadding is not RGBTRIPLE, it's byte
//                                   3.) Is biHeight always positive?
new_bi.biSize = (newwidthP * sizeof(RGBTRIPLE)) * new_bi.biHeight;

Think about how you can get the new size of the image in BYTES. It's a certain number of rows, each a certain number of PIXELS wide, with a few BYTES of padding on the end of each row. Use sizeof(RGBTRIPLE) to convert between PIXELS & BYTES.

Now let's look at how you read and write the image...

The first thing to note is how you've called fwrite(). It seems logical by putting n as the third parameter that it would write &triple n times, but that's not quite how it works.

ptr − This is the pointer to the array of elements to be written.
size − This is the size in bytes of each element to be written.
nmemb − This is the number of elements, each one with a size of size bytes.
stream − This is the pointer to a FILE object that specifies an output stream.
http://www.tutorialspoint.com/c_standard_library/c_function_fwrite.htm

So, the way you've called fwrite(), it writes the first thing it finds at the memory location &triple, then it writes whatever memory is after that, thinking there are two more elements where really we don't know what is stored there. There are a few ways around this, one of which is to call fwrite() n times.

Once you finish reading a row of the input file, you skip the cursor over any padding in the input file and put padding into the output file. This is good.

Then you start reading the second line of the original image...But wait, have you scaled it vertically? Think about how to do this, sniff around at other questions that have been asked here with the tag "resize" and see if you can figure out how to implement it.

| improve this answer | |
1

Sam covered a number of issues, but there are more.

You have a number of issues in the header sections. Most importantly, you have mistaken one field for another. You changed new_bi.biSize for your output file. This field is the number of bytes required by the BITMAPINFOHEADER itself and should never, ever be changed! What you wanted to change was new_bi.biSizeImage.

Since new_bf.bfSize is dependent on biSizeImage, so it was wrong too, but yet, it will still be wrong because of what looks like a cut and paste error. If you read that line of code carefully, it should be immediately obvious. I'll let you find that one on your own. Can't take away all the fun! :-)

You also have an issue at the top. The usage spec doesn't match the assignment spec. The scaling factor is supposed to be the first parameter, not the third.

I recommend that you start by correcting these issues and testing your program with small.bmp and a scaling factor of 1. Verify that the headers are right by using the peek tool and/or the xxd tool to compare the output file to the input or to one produced by the staff solution. (If peek won't show the header info, at least xxd will show you the raw data. More info in the pset assignment and class material.)

Make sure you can successfully copy the source file with this test, and then move on to scaling up by 2 or more, or with larger files.

Sam is right that you need to work on how you process each line of pixels from the source file to the target. Remember that you have to multiply by the scaling factor both horizontally. You reprinted each pixel horizontally, (well, almost - you need to fix that with the fwrite), now you need to repeat the process vertically.

There may be more issues, but at leasty you can attack the bulk of the issues with all this info. Good luck!

If you feel that either of these answers will answer your question, please accept it by clicking on the check mark. Let's keep up on forum maintenance. ;-)

| improve this answer | |

You must log in to answer this question.

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