0

Just spent 2 days for figuring out how fread(), fwrite() and fseek() works.

I tried to use "Rewrite" method from Zamyla's video for vertycal resizing:
- Remember pixels in array (first written row of outfile)
- Write array as many time as needed (n-1 times more)

Now I'm trying to copy a row of my out file in resize.c

fread() returns an error, please help me find the reason of it. Thanks!

/**
 * resize.c
 *
 * Computer Science 50
 * Problem Set 4
 * by Semen Pilyukov
 * [email protected]
 *
 * Copies a BMP piece by piece, just because.
 */

#include <stdio.h>
#include <stdlib.h>
#include "bmp.h"

int main(int argc, char* argv[])
{
    /**
     * Usage control and header stuff works correctly!
     * 
     */

    // ensure proper usage
     if (argc != 4)
    {
        printf("Usage: ./copy n infile outfile\n");
        printf("Usage: n - integer multiplier between 1 and 100 iclusivly\n");

        return 1;
    }

    // remember multiplier filenames
    int n = atoi(argv[1]);
    char* infile = argv[2];
    char* outfile = argv[3];

    // ensure proper multiplier
    if (n < 1 || n > 100)
    {
        printf("Usage: ./copy n infile outfile\n");
        printf("Usage: n - integer multiplier between 1 and 100 iclusivly\n");

        return -1;
    }

    // open input file 
    FILE* inptr = fopen(infile, "r");
    if (inptr == NULL)
    {
        printf("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 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;
    }

    // create headers for output image
    BITMAPFILEHEADER outbf = bf;
    BITMAPINFOHEADER outbi = bi;

    // determine biWidth and biHeight for output image
    outbi.biWidth *= n;
    outbi.biHeight *= n;

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

    // determine padding for scanlines and writing in outptr
    int outpadding =  (4 - (outbi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

    // determine biSizeImage and bfSize for output image
    outbi.biSizeImage = (outbi.biWidth * sizeof(RGBTRIPLE) + outpadding) * abs(outbi.biHeight);
    outbf.bfSize = outbi.biSizeImage + sizeof(outbf) + sizeof(outbi);

    // write outfile's BITMAPFILEHEADER and BITMAPINFOHEADER
    fwrite(&outbf, sizeof(BITMAPFILEHEADER), 1, outptr);
    fwrite(&outbi, sizeof(BITMAPINFOHEADER), 1, outptr);

    /**
     * Here starts the problem part of resizing
     * 
     * 1) Copy fist row with width multiply
     * 2) Step back to the start of row in outfile
     * 3) Read just written outfile's row (returns an error)
     * 4) Write this row (n-1) times more
     * Conptrol position in outfile at every step
     * 
     */

    // determine outfile's row size
    int outrowsize = outbi.biWidth * sizeof(RGBTRIPLE) + outpadding;

    // determine outfile's pixel row size (without padding)
    int pixelrowsize = outbi.biWidth * sizeof(RGBTRIPLE);

    // Position in out file - where we are now (position - ok)
    int pos = ftell(outptr);

    // Print parameters for control
    printf("outrowsize %i\noutpadding %i\npixelrowsize %i\npos before all %i\n\n", outrowsize, outpadding, pixelrowsize, pos);

    // Temporary storage of a whole row. !!! MAYBE HERE IS A PROBLEM
    RGBTRIPLE* outrow = malloc(pixelrowsize);

    // iterate over infile's scanlines
    for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
    {
        // 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 'n' times to outfile
            for (int f = 0; f < n; f++)
                fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
        }

        // Position in out file - where we are now (ok)
        pos = ftell(outptr);
        printf("before padding %i\n", pos);

        // skip over padding, if any
        fseek(inptr, padding, SEEK_CUR);

        // add outfile's padding
        for (int k = 0; k < outpadding; k++)
        {
            fputc(0x00, outptr);
        }

        // Check position - ok
        pos = ftell(outptr);
        printf("after padding %i\n", pos);

        // Move to the beginning of row
        fseek(outptr, - outrowsize, SEEK_CUR);

        // Check position - ok
        pos = ftell(outptr);
        printf("after lineback %i\n", pos);

        // HERE IS THE PROBLEM!
        // Read from "outptr" file stream "pixelrowsize" bytes 1 time
        // and place it into "outrow" storage
        // RETURNS AN ERROR :'-(
        if ((fread(outrow, pixelrowsize, 1, outptr) != 1))
            printf("read error!\n");

        // Check position - DID NOT CHANGED
        pos = ftell(outptr);
        printf("after pixelline read %i\n", pos);

        // Move to the end of row (skip padding)
        fseek(outptr, outpadding, SEEK_CUR);

        // then copy line of outfile 'n' times
        for (int m = 0; m < n - 1; m++)
        {
            // write copied row to outfile (WRITES SUCCESSFULLY)
            // But storage "outrow" is empty, thats way prints black
            if ((fwrite(outrow, pixelrowsize, 1, outptr) != 1))
                printf("write error!\n");  

            // Add padding
            for (int r = 0; r < outpadding; r++)
                {
                    fputc(0x00, outptr);
                }
        }

        // Check position - it changes, but it wrong because read error
        pos = ftell(outptr);
        printf("after pixelline write %i\n\n", pos);
    }

    // nobody likes memory leaks
    free(outrow);

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);

    // that's all folks
    return 0;
}

And results:

/home/ubuntu/workspace/pset4/bmp/ $ ./resize 2 small.bmp staff.bmp

outrowsize 20  
outpadding 2  
pixelrowsize 18  
pos before all 54  

before padding 72  
after padding 74  
after lineback 54  
read error!  
after pixelline read 54  
after pixelline write 76  

before padding 94  
after padding 96  
after lineback 76  
read error!  
after pixelline read 76  
after pixelline write 98  

before padding 116  
after padding 118  
after lineback 98  
read error!  
after pixelline read 98  
after pixelline write 120  

Out picture i predictable

5
  • Shouldn't you use the input row size when reading? You then have to repeat individual pixels.
    – Blauelf
    Commented Aug 11, 2016 at 14:54
  • Good idea, that is nice design improvement. I'll try after program will work correctly. Afraid that reading whole row including padding may be a problem: pixels are RGBTRIPLE type, then padding is raw bites. But will see. Commented Aug 11, 2016 at 15:03
  • From what I can see, the first time your loop runs, you have not written any pixel lines to your output file, but you fseek() backwards to it. Post the full code so we can debug your program. We can't guess all the missing lines. Please read How to create a Minimal, Complete, and Verifiable example
    – ChrisG
    Commented Aug 12, 2016 at 6:25
  • oki doki, I'll give all code and additional info, thanks! Commented Aug 12, 2016 at 13:38
  • Trying to address a similar issue with my code. I see where you create the temporary line storage in 'outrow' and where you try to use it for the vertical enlargement, but where are you putting the pixel data into the elements of 'outrow' so that it can be used to write n-1 additional lines?
    – covrebo
    Commented Aug 12, 2016 at 20:49

1 Answer 1

1

"Everyone's looking for a zebra. I see the injured horse."

All of the file reads and writes are using the outptr output file pointer. The only reference to the input file pointer inptr is the fclose statement at the end. Wouldn't it work better if the code read data from the input file instead of the output file?

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

5
  • Thanks for your reply. As I understood Zamayla, there are two ways to resize the image vertically: 1) read-and widely write infile's row n times - this one I successfully implemented in other version of resize.c 2) copy just written row of outfile (n-1) times - I guess this is more efficient way. In this code I tried to implement 2nd way, thats way I read from outfile and write to outfile, but smth wrong((( Commented Aug 12, 2016 at 13:32
  • You have to read from the infile first to get the data. Once you've created the first copy of a line from the infile to the outfile, then you can read it from the outfile to make copies. Remember to open the outfile for both read and write.
    – Cliff B
    Commented Aug 12, 2016 at 16:30
  • God bless you!!!!! FILE* outptr = fopen(outfile, "w"); - thats way I could not read from it! Just fixed with FILE* outptr = fopen(outfile, "r+") and all works!!!!! Commented Aug 13, 2016 at 11:08
  • Please post it as an answer below, I'll give a check! Commented Aug 13, 2016 at 11:11
  • Oh no, right answer is FILE* outptr = fopen(outfile, "w+") Commented Aug 13, 2016 at 11:58

You must log in to answer this question.

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