0

I'm able to resize a given .bmp horizontally, but the code for vertical resizing is not working. Here is my code for vertical expansion.

// resize vertically

//move pointer to start of scanline of outptr
fseek(outptr,  -3 * (bi2.biWidth + padding), SEEK_CUR);

fgetpos (outptr,&pos);

//an array of RGBTRIPLE to store one scanline
RGBTRIPLE triple[bi2.biWidth + padding]; 

//reading the already written first line    
for (int p = 0 ; p < bi2.biWidth + padding; p++)
{ 
    fread(&triple[p], sizeof(RGBTRIPLE), 1, outptr); 
}
fgetpos (outptr,&pos);           

//writing the copied array to outptr
for (int k = 0 ; k < n; k++)
{             
    for (int p = 0 ; p < bi2.biWidth + padding; p++)

        fwrite(&triple[p], sizeof(RGBTRIPLE), 1, outptr);   

}

This is executed after reading the first scanline of the original bmp. I used gdb to see the values of triple's elements, but I found that they're all garbage values.

Also when I check the position of the file position indicator in outptr using fgetpos(), I find it not changing!


Update 0: here's my code after modifications

// iterate over infile's scanlines
fpos_t pos;
for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
{   
    // iterate over pixels in scanline
    fgetpos (outptr,&pos);
    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 n times horizontally
        for (int k = 0 ; k < n; k++)
        {       
            fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);  

        }
    }

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

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

    // resize vertically  
    //move pointer to start of scanline of outptr
    fsetpos( outptr, &pos );

    //an array of RGBTRIPLE to store one scanline
    RGBTRIPLE triplex[bi2.biWidth]; 


    //reading the already written first line    
    for (int m = 0 ; m < bi2.biWidth ; m++)
    {             
        fread(&triplex[m], sizeof(RGBTRIPLE), 1 , outptr); 
    }

    //skip padding
    fseek(outptr, padding, SEEK_CUR);
    for (int mn = 0 ; mn < n ; mn++)
    {
        //writing the copied array to outptr
        for (int o = 0 ; o < bi2.biWidth ; o++)
        {
            fwrite(&triplex[o], sizeof(RGBTRIPLE), 1 , outptr);   
        }
        for (int p = 0; p < padding; p++)
        {
            fputc(0x00, outptr);
        }   
    }               
}

Update 1:

I used fflush(), but it doesn't work either. Here's my program

/**
* copy.c
*
* Computer Science 50
* Problem Set 5
*
* Copies a BMP piece by piece, just because.
*/

#include <stdio.h>
#include <stdlib.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>//for isdigit

#include "bmp.h"

int main(int argc, char* argv[])
{
    // ensure proper usage
    if (argc != 4)
    {
        printf("Usage: ./resize n infile outfile\n");
        return 1;
    }

    //checking if n is integer
    int i=0;
    string in=argv[1];
    while(in[i]!='\0')
    {
        if( isdigit(in[i]) )
        i++;

        else
        {
            printf("n, the resize factor, must be an integer.\n");
            return 5;
        }
    }

    int n=atoi(in);
    if(n<1 || n>100)
    {
        printf("n, the resize factor, must be a positive integer less than 100.\n");
        return 6;
    }


    // remember filenames
    char* infile = argv[2];
    char* outfile = argv[3];

    // 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;
    }

    // outfile's BITMAPFILEHEADER
    BITMAPFILEHEADER bf2=bf;

    // outfile's BITMAPINFOHEADER
    BITMAPINFOHEADER bi2=bi;    

    //multiplying width by n factor
    bi2.biWidth = n*(bi.biWidth);
    //multiplying height by n factor
    bi2.biHeight = n*(bi.biHeight);

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

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


    //size of the image
    bi2.biSizeImage = (bi2.biWidth + padding) * (abs(bi2.biHeight)) * 3;// positive bi2.biHeight for calculation of bi2.biSizeImage  

    //final size of bmp 
     bf2.bfSize = bi2.biSizeImage + 54;

    // write outfile's BITMAPFILEHEADER    

    fwrite(&bf2, sizeof(BITMAPFILEHEADER), 1, outptr);

    // write outfile's BITMAPINFOHEADER    

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


    // iterate over infile's scanlines
    fpos_t pos;
    for (int i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++)
    {   
        // iterate over pixels in scanline
        fgetpos (outptr,&pos);
        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 n times horizontally
            for (int k = 0 ; k < n; k++)
            {       
                fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);             
            }
        }



        // skip over padding in infile, if any
        fseek(inptr, padding_in, SEEK_CUR);//padding_in is padding in inptr

        // then add padding to outfile
        for (int l = 0; l < padding; l++)//padding is padding in outptr
        {
            fputc(0x00, outptr);
        }        

        //flushing the buffer to outptr
        fflush(outptr);

        // resize vertically  

        //move pointer to start of scanline of outptr
        fsetpos( outptr, &pos );

        //an array of RGBTRIPLE to store one scanline
        RGBTRIPLE triplex[bi2.biWidth]; 

        //reading the already written first line from outptr    
        for (int m = 0 ; m < bi2.biWidth ; m++)
        {             
            fread(&triplex[m], sizeof(RGBTRIPLE), 1 , outptr); 
        }

        //skip padding
        fseek(outptr, padding, SEEK_CUR);

        //writing the values in array to outptr n times
        for (int mn = 0 ; mn < n ; mn++)
        {
            //writing values from the array
            for (int o = 0 ; o < bi2.biWidth ; o++)
            {
                fwrite(&triplex[o], sizeof(RGBTRIPLE), 1 , outptr);   
            }
            //adding padding to outptr's scanline
            for (int p = 0; p < padding; p++)
            {
                fputc(0x00, outptr);
            }   
        }               
    }

    // close infile
    fclose(inptr);

    // close outfile
    fclose(outptr);

    // that's all folks
    return 0;
}
3

These are a number of points that you should consider:

  1. You shouldn't read the padding. Instead, you should calculate it for both the original image and the resized image, seek a number of bytes equal to the padding for the original image in case of reading and write a number of zeros equal the padding for the resized image (where each 0 is exactly a byte).

  2. You shouldn't be reading the padding in an RGBTRIPLE variable because a) the padding might be > or < 3 bytes (the size of RGBTRIPLE) and b) it doesn't represent red, green and blue values so this would be as bad as reading a char to use it in arithmetic operations. Regardless of the limits, yes a char is a number underneath the hood, but it's not meant to be represented as a number while an int is a perfect fit for that purpose.

  3. I don't know why you're calling fgetpos() many times.

  4. For the same reason as 2 you shouldn't be writing the padding as RGBTRIPLES. Instead you should do that separately. Maybe using a function like fgetc(). You may look at copy.c and make sure you understand that!

I brought a pencil and a paper to simulate the resizing process before I started coding. The idea behind vertical resizing is, as Zamyla suggested, either to store the current scanline (padding is excluded) in an array and write it as many times as needed (padding is included) or to seek back to the beginning of the current scanline, read it (padding is excluded) and write it back repeating this process as many times as needed (padding is included).


Update 0: I'm not able to see how you calculated the padding for both the original and the resized bmp images since you didn't include that in the modified code.

Assuming you calculated them the correct way, you seem to be reading the scanline, skipping the padding in the original image and putting the padding in the resized image correctly which resizes your image horizontally correctly in total.

Now, I get a better view of what you're trying to do to resize your image vertically. Actually, the process of writing data to a file using fwrite() is not that straight forward. You don't get a piece of data written in a file immediately after a call to fwrite() because, incidentally, the data is buffered first, then flushed to the stream (i.e., the file) at some point. And that's probably the main reason why you get garbage values read.

So it's not safe to read from outptr unless probably you force flushing the buffered data by a call to fflush(). However, probably two better ways to do that are

  1. to store the resized scanline in an RGBTRIPLE array and write it n times.
  2. to seek the file position indicator back in inptr to the beginning of the current scanline and repeat the resizing process, writing the scanline n times.

Note: do not forget about the padding!

I, personally, prefer (and used actually) the array method because it's probably faster and more efficient.


Update 1:

After spending really long time debugging your program, I found these issues

  1. Problem #1

    if (n < 1 || n > 100)
    {
        printf("n, the resize factor, must be a positive integer less than 100.\n");
        return 6;
    }
    

    The factor is in [1, 100] inclusive not exclusive.

  2. Problem #2

    FILE* outptr = fopen(outfile, "w");
    

    If you intend to read from outptr as well as writing to it, you should open it with the w+ mode (for reading and writing) not just w (for writing). And that's another main cause of not reading properly.

  3. Problem #3

    bi2.biSizeImage = (bi2.biWidth + padding) * (abs(bi2.biHeight)) * 3;// positive bi2.biHeight for calculation of bi2.biSizeImage 
    

    Think again about this! Unfortunately, this is calculated wrongly!

  4. Problem #4

    for (int m = 0 ; m < bi2.biWidth ; m++)
    {             
        fread(&triplex[m], sizeof(RGBTRIPLE), 1 , outptr); 
    }
    

    You're reading the scanline into triplex anyway no matter what n is. If n is 1, for example, you won't need to resize at all!

  5. Problem #5

    for (int mn = 0 ; mn < n ; mn++)
    {
        // some code  
    }
    

    You forgot that you already wrote the scanline once in a previous loop. You're writing the scanline one more time here!

6
  • Hey kareem! I corrected the code as per your suggestions Here is the modified version – sid Jul 9 '14 at 19:40
  • The problem is that its picking up garbage values when read from outptr's scanline. I also tried the "seek back to the beginning" method. It worked well except some cases of cheeck50. – sid Jul 9 '14 at 20:02
  • @user3425117 I just updated my answer! – Kareem Jul 10 '14 at 1:59
  • added fflush but it makes no difference. I am posting the complete program. Please see what else is wrong. – sid Jul 10 '14 at 8:29
  • @user3425117 updated my answer! :) – Kareem Jul 11 '14 at 1:05

You must log in to answer this question.

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