Right now I’m working on a program for my Visual Information Processing course. We’re given skeletal templates for all the homeworks since the focus of the class isn’t to learn MFC. I program on a Mac and don’t have access to the Window’s library which makes importing BMPs easy. Therefore I’ve used (and modified slightly) the code found from this website: paulbourke.net/dataformats/bmp/
I’ve actually been using this code for the past year, and it has worked fine for 24-bit BMPs (that is, ones with pixels represented as RGB). The major adjustment I needed to make to the code was adding a special routine which inverts the rows of the image if the height of the BMP is expressed as a negative number.
When I import a BMP into an array of type GLubyte, and the image has biBitCount = 24, using GLDrawPixels works perfectly:
https://i.stack.imgur.com/aL3JN.png
However, when I import a BMP with biBitCount = 8 and display it using GLDrawPixels, I get the following (notice the wrap around error highlighted in a red rectangle):
https://i.stack.imgur.com/PQv7i.png
I had to implement an automatic thresholding algorithm for my last assignment, to facilitate segmenting the image based on interpreted regions. I think the error for this wrap around stems from importing the BMP, and not from the GLDrawPixels call. This is because the region-izing algorithm I made identified more regions than it should have. Which seems to imply the part which wraps around is truly disjoint in the array representation of the BMP.
I’ve sifted through my code a bunch of times and can’t for the life of me figure out what’s causing the problem.
Here’s the code that displays the BMP after it is imported:
void drawBMP(BITMAPINFO *bitmapInfo, GLubyte *bitmapIn, GLfloat xOffset, GLfloat yOffset) {
if (bitmapInfo) {
glRasterPos2f(xOffset, yOffset);
if (bitmapInfo->bmiHeader.biBitCount == 24) {
glDrawPixels(bitmapInfo->bmiHeader.biWidth,
bitmapInfo->bmiHeader.biHeight,
GL_BGR, GL_UNSIGNED_BYTE, bitmapIn);
} else {
glDrawPixels(bitmapInfo->bmiHeader.biWidth,
bitmapInfo->bmiHeader.biHeight,
GL_LUMINANCE, GL_UNSIGNED_BYTE, bitmapIn);
}
}
glFinish();
}
Perhaps the GL_LUMINANCE setting is causing the problem?
Here’s the function which does the actual importing of the BMP:
GLubyte * /* O - Bitmap data */
LoadDIBitmap(const char *filename, /* I - File to load */
BITMAPINFO **info) /* O - Bitmap information */
{
FILE *fp; /* Open file pointer */
GLubyte *bits; /* Bitmap pixel bits */
GLubyte *ptr; /* Pointer into bitmap */
GLubyte temp; /* Temporary variable to swap red and blue */
int x, y; /* X and Y position in image */
int length; /* Line length */
int bitsize; /* Size of bitmap */
int infosize; /* Size of header information */
BITMAPFILEHEADER header; /* File header */
/* Try opening the file; use "rb" mode to read this *binary* file. */
if ((fp = fopen(filename, "rb")) == NULL)
return (NULL);
/* Read the file header and any following bitmap information... */
header.bfType = read_word(fp);
header.bfSize = read_dword(fp);
header.bfReserved1 = read_word(fp);
header.bfReserved2 = read_word(fp);
header.bfOffBits = read_dword(fp);
if (header.bfType != BF_TYPE) /* Check for BM reversed... */
{
/* Not a bitmap file - return NULL... */
fclose(fp);
return (NULL);
}
infosize = header.bfOffBits - 18;
if ((*info = (BITMAPINFO *)malloc(sizeof(BITMAPINFO))) == NULL)
{
/* Couldn't allocate memory for bitmap info - return NULL... */
fclose(fp);
return (NULL);
}
(*info)->bmiHeader.biSize = read_dword(fp);
(*info)->bmiHeader.biWidth = read_long(fp);
(*info)->bmiHeader.biHeight = read_long(fp);
(*info)->bmiHeader.biPlanes = read_word(fp);
(*info)->bmiHeader.biBitCount = read_word(fp);
(*info)->bmiHeader.biCompression = read_dword(fp);
(*info)->bmiHeader.biSizeImage = read_dword(fp);
(*info)->bmiHeader.biXPelsPerMeter = read_long(fp);
(*info)->bmiHeader.biYPelsPerMeter = read_long(fp);
(*info)->bmiHeader.biClrUsed = read_dword(fp);
(*info)->bmiHeader.biClrImportant = read_dword(fp);
if (infosize > 40)
if (fread((*info)->bmiColors, infosize - 40, 1, fp) < 1)
{
/* Couldn't read the bitmap header - return NULL... */
free(*info);
fclose(fp);
return (NULL);
}
/* Now that we have all the header info read in, allocate memory for *
* the bitmap and read *it* in... */
if ((bitsize = (*info)->bmiHeader.biSizeImage) == 0)
bitsize = ((*info)->bmiHeader.biWidth *
(*info)->bmiHeader.biBitCount+7) / 8 *
abs((*info)->bmiHeader.biHeight);
if ((bits = malloc(bitsize)) == NULL)
{
/* Couldn't allocate memory - return NULL! */
free(*info);
fclose(fp);
return (NULL);
}
if (fread(bits, 1, bitsize, fp) < bitsize)
{
/* Couldn't read bitmap - free memory and return NULL! */
free(*info);
free(bits);
fclose(fp);
return (NULL);
}
//This needs to be done when the height is negative
if ((*info)->bmiHeader.biHeight < 0) {
(*info)->bmiHeader.biHeight *= -1;
int bitsPerPixel = (*info)->bmiHeader.biBitCount;
int bytesPerPixel;
if (bitsPerPixel >= 8) {
bytesPerPixel = bitsPerPixel/8;
} else {
exit(1);
}
int i; //Row
int j; //Column
for (i = 0; i < floor((*info)->bmiHeader.biHeight/2); i++) {
int inlineRowValue = i * (*info)->bmiHeader.biWidth * bytesPerPixel;
int inlineInvRowValue = ((*info)->bmiHeader.biHeight - i) * (*info)->bmiHeader.biWidth * bytesPerPixel;
for (j = 0; j < (*info)->bmiHeader.biWidth; j++) {
int inlineColumnValue = j * bytesPerPixel;
int currentPos = inlineRowValue + inlineColumnValue;
int invCurrentPos = inlineInvRowValue + inlineColumnValue;
int k;
GLubyte *temp = malloc(sizeof(GLubyte)*bytesPerPixel);
for (k = 0; k < bytesPerPixel; k++) {
temp[k] = bits[currentPos+k];
}
for (k = 0; k < bytesPerPixel; k++) {
bits[currentPos+k] = bits[invCurrentPos+k];
bits[invCurrentPos+k] = temp[k];
}
free(temp);
}
}
}
/* OK, everything went fine - return the allocated bitmap... */
fclose(fp);
return (bits);
}
My intuition tells me that the file-pointer isn’t be properly incremented as things are read from the BMP file.
If you guys have any idea what the source of this bug is, please share. I’ll be incredibly appreciative. This is driving me nuts.
I think you are subtracting the wrong amount here:
This appears to be accounting for the size of the header information, but the header is 14 bytes (3 words and 2 dwords), not 18 bytes.
Instead of relying on reading exactly the right amount of header and info structures, why not directly use:
before reading in the image data?