1

The code I have written for resize checks out visually for all float values (0.0,100.0]. Also, check50 agrees that my code is functioning properly save for one specific case:

  • :) resize.c and bmp.h exist
  • :) resize.c compiles
  • :) doesn't resize 1x1-pixel BMP when f is 1.0
  • :) resizes 1x1-pixel BMP to 2x2 correctly when f is 2.0
  • :( resizes 1x1-pixel BMP to 3x3 correctly when f is 3.0
  • :) resizes 1x1-pixel BMP to 4x4 correctly when f is 4.0
  • :) resizes 1x1-pixel BMP to 5x5 correctly when f is 5.0
  • :) resizes 2x2-pixel BMP to 4x4 correctly when f is 2.0
  • :) resizes 2x2-pixel BMP to 1x1 correctly when f is 0.5
  • :) resizes 4x4-pixel BMP to 2x2 correctly when f is 0.5
  • :) resizes 6x6-pixel BMP to 3x3 correctly when f is 0.5

Any clue why my code, pasted below, does not function in this specific case? I have tried debugging, but do not have a 1x1 file to test on. Logically, I do not understand what's wrong with my code for this specific case. Thank you!

/**
 * Copies a BMP piece by piece, just because.
 */

#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;

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

    // determine infile and outfile padding
    int in_padding = (4 - (in_bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
    int out_padding = (4 - (out_bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

    // calculate out_bi.biSizeImage
    out_bi.biSizeImage = ( out_bi.biWidth * sizeof(RGBTRIPLE) + out_padding ) * abs(out_bi.biHeight);

    // update BITMAPFILEHEADER's bfSize
    out_bf.bfSize = out_bi.biSizeImage + out_bf.bfOffBits;

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

    // iterate over infile's scanlines

    float newHeightIndex = 0;
    for (int i = 0, biHeight = abs(in_bi.biHeight); i < biHeight; i++)
    {
        RGBTRIPLE* line = malloc(sizeof(RGBTRIPLE) * in_bi.biWidth);

        // copy infile's 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);

            line[j] = triple;
        }

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


        while (newHeightIndex < (i+1) )
        {
            for (int z = 0; z < out_bi.biWidth; z++)
            {
                int pixel = z/f;
                fwrite(&line[pixel], sizeof(RGBTRIPLE), 1, outptr);
            }

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

            newHeightIndex += (1/f);

        }

        free(line);

    }

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);

    // success
    return 0;
}
6
  • It should be fairly trivial to make a copy of resize that creates a 1x1 bmp. Read a (existing) bmp, set headers values, write headers, write 4 bytes: R G B padding. May 26, 2017 at 12:11
  • I can't answer your question sorry, but I am confused about the use of sscanf in your code. I read the cs50 reference page on the scanf() family of functions, but I am still confused as to what sscanf in particular does. Is it meant to compare data types or something or is that not what you were doing with %c %f with your sscanf function call? Jun 9, 2017 at 22:26
  • @dumbitdownjr, I copied this from cs50.c. This function takes an argument, argv[1], which must be a float and just a float (4 is correct, 4a would be incorrect). How to guarantee this? Well, if we just scanned for "%f", and the user typed "4foo", sscanf() would stop at 4 and return 1 (it found 1 float) though it wrongly contains 1 float and 3 chars. Including "%f %c" as the string allows sscanf to check for an extra char. If I type 4d while parsing for "%f, %c", sscanf returns 2. Since 2 != 1, our code returns an error because I said if sscanf(argv[1], "%f %c", &f, &c) != 1), then ERROR. Jun 16, 2017 at 19:34
  • @FinesseUpscale Ok, I think that makes sense now, can it only be used to check for floats and chars, or can it differentiate between other types? Thanks a lot by the way. Jun 16, 2017 at 19:46
  • @dumbitdownjr No problem. This is the first time I've actually ever helped anyone rather than ask a question, so it feels good to finally contribute. The man for sscanf probably contains better information, I honestly don't know the answer. I would assume you could use the %whatever's you're used to including in printf() formatted strings, though I'm not sure how it would handle a %s for string since that's not really a data type, rather just a char pointer (right?) Jun 19, 2017 at 21:18

1 Answer 1

1

Because 1/f when f = 3 is not exactly 1/3 but 0.33333.. with a lot of digits. So the loop iterate for some times, the offset accumulate making the variable newHeightIndex behave not as we want. For example, may be after 9 times, newHeightIndex = 2.9999.. < 3 so another iteration is bound to happen.

You must log in to answer this question.

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