2

in copy.c i am seeing some weird code in the part of iterating the scanline and pixels. Since we are passing "inptr" to the (fread) and telling it to work just once. How does the code makes sure that it reaches directly to the pixles and ignore the metadata. Also isn't that for loop reading and writing the same pixel again and again till the loop ends??

 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
        fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
    }
13

Per the manual page for fread()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

The function fread() reads nmemb elements of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by ptr.

This line of code

fread(&triple, sizeof(RGBTRIPLE), 1, inptr);

reads 1 element of data, of size sizeof(RGBTRIPLE), frominptr (which is a stream), and stores that element in triple. In other words, we're reading a single RGBTRIPLE.

How does the code makes sure that it reaches directly to the pixles and ignore the metadata

Because we already read the headers (i.e., BITMAPFILEHEADER and BITMAPINFOHEADER) using 2 calls to fread() before we reach that loop.

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

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

There's something called the file position indicator. You can think of that like the blinking cursor in a text editor. As we read data using fread(), this indicator moves as many steps forward as the number of bytes we read.

So, for example, if we read 1 byte, this indicator will move 1 step forward. If we read 2 bytes, it will move 2 steps forward, and so on.

Since the size of the headers are 54 bytes, this indicator moved 54 steps. So the next thing to read is the bytes of the image itself. Here's a textual representation of a BMP file.

Think of the vertical bar (i.e., |) as the file position indicator

Before we read anything

|BITMAPFILEHEADER
 BITMAPINFOHEADER
 scanline padding
 scanline padding
 scanline padding
 EOF

After fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr)

 BITMAPFILEHEADER|
 BITMAPINFOHEADER
 scanline padding
 scanline padding
 scanline padding
 EOF

After fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr)

 BITMAPFILEHEADER
 BITMAPINFOHEADER|
 scanline padding
 scanline padding
 scanline padding
 EOF

After this loop ends execution

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
    fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}


 BITMAPFILEHEADER
 BITMAPINFOHEADER
 scanline | padding
 scanline   padding
 scanline   padding
 EOF

After we seek the padding fseek(inptr, padding, SEEK_CUR)

 BITMAPFILEHEADER
 BITMAPINFOHEADER
 scanline padding|
 scanline padding
 scanline padding
 EOF

and so on.

Also isn't that for loop reading and writing the same pixel again and again till the loop ends??

Let's break the loop step down and see what it does in more detail!

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
    fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr);
}

This loop basically does the following. Consider the following scanline

 |RGBTRIPLE RGBTRIPLE RGBTRIPLE padding

the first statement in this loop creates an RGBTRIPLE variable to read an RGBTRIPLE from the file in.

After the firs call to fread(&triple, sizeof(RGBTRIPLE), 1, inptr)

 RGBTRIPLE | RGBTRIPLE RGBTRIPLE padding

We then write the RGBTRIPLE we just read to the output file and we enter the next iteration of this loop. Notice that we didn't seek back to the beginning of the current scanline. Instead, we repeat the process and call fread() again.

After the second call to fread(&triple, sizeof(RGBTRIPLE), 1, inptr)

 RGBTRIPLE RGBTRIPLE | RGBTRIPLE padding

and so on. So we repeat the process bi.biWidth times which is the width of the current scanline (i.e, the number of RGBTRIPLEs in this scanline.)

After this loop ends execution, as shown earlier, a full scanline is read (excluding the padding).

Hope that helps!

2
  • Thanks mate. you just made all things clear. Thanks for your huge lond explanation. Was the file position indicator discussed somewhere??lectures, shorts, sections anywhere,did i missed that?
    – Milan
    Aug 18 '14 at 1:14
  • wow, awesome explanation!! thnx May 31 '16 at 14:15
1

What you've left out is the beginning of the program, where it first does

fread(&bf ...

and then

fread(&bi ...

So at this point in the program the inptr is sitting at the end of the header section (having read 54 bytes) and at the first byte in the pixels.

The for loop you've posted then starts and reads each pixel in turn based on the width value. So if the width is 3, that loop reads 3 "triples" or pixels.

3
  • I found that there is something called file position indicator,function named fseek keeps track the position. Was this topic discussed anywhere in cs50 -lectures,section, shorts,etc?? please tell me if a missed something.
    – Milan
    Aug 17 '14 at 4:25
  • section 6 discusses file i/o. cs50.tv/2013/fall/sections/6 (I've just noticed that for some reason, that section doesn't seem to be in the edX courseware.)
    – curiouskiwi
    Aug 17 '14 at 21:27
  • thanks mate, for the support.
    – Milan
    Aug 18 '14 at 1:19

You must log in to answer this question.

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