0

I have only written my code for the resize (more) problem up to the point where I rewrite the headers for the new outfile. At this point in my code, I only expect my code to work when I have an input rescaling float factor, f, of 1.

However, when I try to open "smiley2.bmp," the cs50 IDE displays an error reading "Invalid or Unsupported Image Format."

I assume this means something is funky in my BITMAPFILEHEADER out_bf or BITMAPINFOHEADER out_bi. However, when I debug/printf each updated value (debugging code not included to reduce clutter), all the values seem to be correct. I don't find any garbage values, and the file pointers are all where they should be for each step.

What am I missing?

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

    // n must be a float, otherwise return error
    char c;
    float f;
    if(sscanf(argv[1], "%f %c", &f, &c) != 1)
    {
        fprintf(stderr, "Usage: ./resize n infile outfile\n");
        return 1;
    } else if ((f <= 0) || (f >= 100))
    {
        fprintf(stderr, "Resize factor must be within bounts (0, 100)\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;
    }

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

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

    // ensure infile is (likely) a 24-bit uncompressed BMP 4.0
    if (in_bf.bfType != 0x4d42 || in_bf.bfOffBits != 54 || in_bi.biSize != 40 || 
        in_bi.biBitCount != 24 || in_bi.biCompression != 0)
    {
        fclose(outptr);
        fclose(inptr);
        fprintf(stderr, "Unsupported file format.\n");
        return 4;
    }

    // create BITMAPFILEHEADER and BITMAPINFOHEADER for the resized outfile
    BITMAPFILEHEADER out_bf = in_bf;
    BITMAPINFOHEADER out_bi = in_bi;

    // first, rescale BITMAPINFOHEADER's width and height
    out_bi.biWidth = in_bi.biWidth * f;
    out_bi.biHeight = in_bi.biHeight * f;

    // second, determine padding now that we have altered the outfile's height and width
    // we could have already determined in_padding, but easy to put together here next to outfile's
    int in_padding = (4 - (in_bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
    int out_padding = (4 - (out_bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

    // third, finish setting up out_bi by finalizing out_bi.biSizeImage
    out_bi.biSizeImage = ( out_bi.biWidth * sizeof(RGBTRIPLE) + out_padding ) * abs(out_bi.biHeight);

    // fourth, now that outfile's BITMAPINFOHEADER is set up, we can update BITMAPFILEHEADER's bfSize
    out_bf.bfSize = out_bi.biSizeImage - out_bf.bfOffBits;

    fwrite(&out_bi, sizeof(BITMAPINFOHEADER), 1, outptr);

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

    // iterate over infile's scanlines
    for (int i = 0, biHeight = abs(in_bi.biHeight); i < biHeight; i++)
    {
        // iterate over pixels in scanline
        for (int j = 0; j < in_bi.biWidth; j++)
        {
            // temporary storage
            RGBTRIPLE triple;

            // read RGB triple from infile
            fread(&triple, sizeof(RGBTRIPLE), 1, inptr);

            // this is where you need to FINESSE on that nae nae
            // write RGB triple to outfile
            fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
        }

        // skip inpointer over infile's padding, if any
        fseek(inptr, in_padding, SEEK_CUR);

        // then add new padding to outfile
        for (int k = 0; k < out_padding; k++)
        {
            fputc(0x00, outptr);
        }
    }

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);

    // success
    return 0;
}
0

Bugs are the best learning opportunities! When I first wrote this, I did not understand how fread() and fwrite() move the pointer's "cursor" (selector?--whatever ya wanna call it!). After retooling and analyzing the code, I had forgotten that I had rearranged some fwrite()s thinking it wouldn't matter.

Well I'm a dingus! After rewriting my code three times, I finally thought to check my output with xxd (ex: "xxd smileyCOPY.bmp" in the terminal"). Something was off--my header information was swapped. THEN IT HIT ME!

I had swapped the relative positions of the fwrite()s for the BITMAPFILEHEADER and the BITMAPINFOHEADER so that the BITMAPINFOHEADER was erroneously written first. D'oh!!

0

Working on resize less I learnt -the hard way, that there is no need to initialize two different bi and bf variables

BITMAPINFOHEADER bi and BITMAPFILEHEADER bf will create a buffer i.e a kind of special place where what you read from inptr will be stored temporarily and from that same buffer you can write to outptr

You only need create 2 different variables for padding that's inptr padding and outptr padding

You must log in to answer this question.

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