0

I'm struggling a bit with resize. I think my headers work correctly as I do get a correct size output, but after the first horizontal line everything goes wack.

My guess is that the fault lies in how I use the fseek function as I'm not sure I understand it completely yet. But there might also be some fault in my loops as well.

Here's the code:

// Copies a BMP file

#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 scale infile outfile\n");
    return 1;
}

// remember filenames
char *infile = argv[2];
char *outfile = argv[3];

// remember the scale
int scale = atoi(argv[1]);

if (scale < 0 || scale > 100)
{
    fprintf(stderr, "Scale should be between 0 and 100\n");
    return 2;
}

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

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

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

bfOut = bf;

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

biOut = bi;

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

// calculate resized height and width for the outfile header
biOut.biHeight = bi.biHeight * scale;
biOut.biWidth = bi.biWidth * scale;

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

// outfile padding
int outPadding = (4 - (biOut.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

// calculate new image size for the outfile's BITMAPINFOHEADER
biOut.biSizeImage = ((sizeof(RGBTRIPLE) * biOut.biWidth) + outPadding) * 
abs(biOut.biHeight);

// calculate new file size for the outfile's BITMAPFILEHEADER
bfOut.bfSize = biOut.biSizeImage + sizeof(BITMAPINFOHEADER) + 
sizeof(BITMAPFILEHEADER);

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

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

// iterate over infile's scanlines
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{
    // iterate over scanlines resize factor - 1 times
    for (int v = 0; v < scale - 1; v++)
    {
        // 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 multiplied by the resize factor
            for (int t = 0; t < scale; t++)
            {
                // write RGB triple to outfile
                fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
            }
        }

        // write padding to outfile
        for (int k = 0; k < outPadding; k++)
        {
            fputc(0x00, outptr);
        }

        // reset stream pointer for next scanline
        fseek(inptr, -((biOut.biWidth * sizeof(RGBTRIPLE)) + outPadding), 
SEEK_CUR);
    }
    // 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 multiplied by the resize factor
        for (int t = 0; t < scale; t++)
        {
            // write RGB triple to outfile
            fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
        }
    }

    // write padding to outfile
    for (int k = 0; k < outPadding; k++)
    {
        fputc(0x00, outptr);
    }
    // skip over padding, if any
    fseek(inptr, padding, SEEK_CUR);
}

// close infile
fclose(inptr);

// close outfile
fclose(outptr);

// success
return 0;
}

1 Answer 1

1

Your guess is pretty good. Look at the following line:

    // reset stream pointer for next scanline
    fseek(inptr, -((biOut.biWidth * sizeof(RGBTRIPLE)) + outPadding), SEEK_CUR);

First, this line is moving the pointer on the INPUT file, but is using values for the OUTPUT file.

Next, look carefully at where the pointer is when this line is executed. Is the pointer currently located at the end of the line or at the end of the pixels and before the padding?

================

As a side note, here's a very interesting question on efficiency. In this case, is it more important to have easily read code or is it more important to have code that executes as fast as possible?

It's interesting because it should always be a red flag when there are significant blocks of code duplicated. Usually, that means that the code can probably be rewritten so that there is only one block. The reason is that having the same code in multiple places can lead to errors if that code has to be changed in the future and one of the blocks isn't updated. In this case, the second block (the one that processes the last pass over a line) could be removed by using an if statement on the line above that needs to be fixed.

However, it's possible that having the extra block of code might make the program execute slightly faster. It would have to be weighed against the execution overhead of the for loops. Adding code to test execution speed would be the best way to determine whether there's an advantage to duplicate the code. (I suspect very little gain or possibly loss, but I could be wrong.)

It comes down to what type of efficiency is more important. Unless execution speed is really important, the more cleanly written code, without the duplicate block would be the way to go. (Unless, of course, there are other factors that are more important, like minimizing memory use, etc. ;-) )

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

You must log in to answer this question.

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