Search code examples
cdebuggingimage-resizingbmpcs50

CS50 pset4 resize less comfortable. Header seems to be updated correctly, until I write on the file


I wonder if someone could point me in the right direction on this problem (no answers, just hints on what the issue is). The idea of the problem is to resize a BMP image in accordance to an integer provided as a command argument. If the number is 2, the file will be twice the size in pixels.

I tested the code I created to update the headers, and the size of the file was correct when I runned it. However, after attempting to resize the pixels, the size fo the file increased dramaticaly or reduced to about 50 bytes in some occasion. Not sure what the mistake is.

This is the code:

// resizes 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: resize integer (1-100) infile outfile\n");
        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;
    }

    int n= atoi(argv[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 4;
    }

    // modify bitmapinfoheader for height an width

    bi.biWidth = bi.biWidth*n;
    bi.biHeight = bi.biHeight*n;

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

    // modify bisizeimage
    bi.biSizeImage = (((bi.biWidth*(sizeof(RGBTRIPLE)))+padding)*abs(bi.biHeight));

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

    // modify bfsize before writing it
    bf.bfSize = (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(bi.biSizeImage));

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

// iterate over infile's scanlines
    for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
    {

        // temporary storage for a triple
        RGBTRIPLE triple;
        // temporary storage for scanline
        RGBTRIPLE scanline[bi.biWidth];
        //array position counter
        int arraypos = 0;

        // iterate over pixels in scanline
        for (int j = 0; j < bi.biWidth; j++)
        {
            // read RGB triple from infile
            fread(&triple, sizeof(RGBTRIPLE), 1, inptr);


            //write to array n times
            for (int l = 0; l < n; arraypos++, l++)
            {
                scanline[arraypos] = triple;

            }
        }
        for (int m = 0; m < n; m++)
        {
             // write array to outfile
            fwrite(&scanline, sizeof(scanline), 1, outptr);
            // Add padding)
            for (int k = 0; k < padding; 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; }

Solution

  • bf.bfSize = (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
        sizeof(bi.biSizeImage));
    

    sizeof(bi.biSizeImage) is just sizeof(int). This is supposed to be the file size, so you need bi.biSizeImage itself.

    Another problem is that you change the width and height, but you don't save the old width and height. If the new width and height are larger then you hit end of file.

    Change as follows, also add error checks for fread

    int width_save = bi.biWidth;
    int height_save = bi.biHeight;
    
    bi.biWidth *= n;
    bi.biHeight *= n;
    
    //calculate width in bytes
    int wb_in = ((width_save * 24 + 31) / 32) * 4;
    int wb_out = ((bi.biWidth * 24 + 31) / 32) * 4;
    
    bi.biSizeImage = wb_out * abs(bi.biHeight);
    bf.bfSize = 54 + bi.biSizeImage;
    
    fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);
    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);
    
    RGBTRIPLE *line_in = malloc(wb_in); 
    RGBTRIPLE *line_out = malloc(wb_out);
    
    for(int i = 0; i < abs(height_save); i++)
    {
        fread(line_in, 1, wb_in, inptr);
    
        for(int a = 0; a < width_save; a++)
            for (int b = 0; b < n; b++)
                line_out[a * n + b] = line_in[a];
    
        for(int a = 0; a < n; a++)
            fwrite(line_out, 1, wb_out, outptr);
    }