Reading in bitmaps is always a difficult thing.
There are quite some points to consider.
fread(&im[i][j].res, sizeof(unsigned char), 1, filePtr);
With this line your read the reserved byte of an RGBQUAD
from the file. However, this member is not in the file. The image data in the file contains scanlines. See below.
After reading the Bitmap File Header, you open the file again. However, you didn't close it. This might be succesful or it fails because the file was already open. But you don't chek the return value of fopen
. Anyway, there is no need to do this because after having read the BFH the filepointer is positioned at the BITMAPINFOHEADER; you can just read it in. And you need to read it in, otherwise you won't know the dimensions of the bitmap.
From MSDN documentation:
The established bitmap file format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER [...] structure. An array of RGBQUAD structures (also called a color table) follows the bitmap information header structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).
For 24 bits-per-pixel bitmaps, there is no color table.
so the sequence is now:
//read the bitmap file header
fread(&bfh, sizeof(bfh), 1, filePtr);
//read the bitmap info header
fread(&bih, sizeof(bih), 1, filePtr);
int bpp= bih.biBitCount;
if (bpp != 24) return 0; // error: must be 24 bpp/ 3 bytes per pixel
Now we must calculate the amount of memory needed to store the image. An image consists of heighth rows of width pixels. The rows are called scanlines.
For some reason, a scanline is alligned on a 4 byte boundary. This means that the last bytes of a scanline may not be in use. So the amount of memory to read the bitmap data from the file is the heigth of the image, times the number of scanlines of the image:
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
int size= bih.biHeight * WIDTHBYTES(bih.biWidth*32));
unsigned char *im= malloc(size);
Finally you can read the data. If there was a color table, you should have skipped that with a seek but since there is none, you can now read the data:
fread(im, size, 1, filePtr);
Now, to address the pixels you cannot use a simple two-dimensional notation governed by width and heigth of the image...due to the scanlines. You can use the following:
int scanlineSize= WIDTHBYTES(bih.biWidth*bih.biBitCount);
unsigned char *p, *scanline= im;
for (int i=0; i<bih.biHeight; i++)
{
p= scanline;
for (int j=0; j<bih.biWidth; j++)
{
g= *p++;
b= *p++;
r= *p++;
}
scanline += scanlineSize;
}
So to address a 3-byte pixel (x,y)
use: im[y*scanlineSize + x*3]
.. except that I believe that scanlines are reversed, so the pixel would be at im[(bih.biHeight-y)*scanlinesize + x*3]
.
UPDATE: complete function
#include <winGDI.h>
// WIDTHBYTES takes # of bits in a scanline and rounds up to nearest word.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
unsigned char *readBitmap(char *szFilename)
{
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
int i, j, size, scanlineSize;
unsigned char r, g, b, *p, *img, *scanline;
FILE *filePtr;
if ((filePtr=fopen(szFilename, "rb"))==0) return 0;
//read the bitmap file header
if (fread(&bfh, sizeof(bfh), 1, filePtr)!=1
|| bfh.bfType != 'MB') {fclose(filePtr); return 0;}
//read the bitmap info header
if (fread(&bih, sizeof(bih), 1, filePtr)!=1
|| bih.biSize!=sizeof(bih)) {fclose(filePtr); return 0;}
if (bih.biBitCount != 24) {fclose(filePtr); return 0;} // error: must be 24 bpp/ 3 bytes per pixel
// allocate memory and read the image
scanlineSize= WIDTHBYTES(bih.biWidth * bih.biBitCount);
size= bih.biHeight * scanlineSize;
if ((img= malloc(size))==0) {fclose(filePtr); return 0;}
if (fread(img, size, 1, filePtr)!=1) {free (img); fclose(filePtr); return 0;}
fclose(filePtr);
scanline= img;
for (i=0; i<bih.biHeight; i++)
{
p= scanline;
for (j=0; j<bih.biWidth; j++)
{
g= *p++;
b= *p++;
r= *p++;
}
scanline += scanlineSize;
}
return img;
}