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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.