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 RGBTRIPLE
s in this scanline.)
After this loop ends execution, as shown earlier, a full scanline is read (excluding the padding).
Hope that helps!