Hi, I am trying to load a .bmp file. My intention is to convert the pixels through some colour manipulation (average of bgr components probably) into greyscale and save them into a custom format which I will load into my OpenGL app as a heightmap to render terrain.
I made a thread over at gamedev.net (site might not respond, it seems to get DDOSd all the time or something
) but it seems to have gone stale so I need some help from the good people here 
Heres the full c++ code (interesting bit extracted below):
This bit (highlighted as php
) is doing the work, and I think may contain the problem:
My reply in that thread on gamedev.net, extracted (this is where things get strange):
-------------------------------------------------
ok, I changed the sample image and now it gets so far through then thinks every pixel after then is just 0,0,0
with an image of only one colour (i dunno, 200,10,44) it works. But if there are a bunch of different colours present in the image it starts picking up everything as 0,0,0 after a certain point :S What...
This image makes it happen (programmer art yay):
Whereas if the image had only the yellow background and none of the coloured bars, it goes through the whole thing without a fuss - picking up the yellow all the way. Considering I am doing nothing with palettes or anything like that, how is this even possible?
edit:
Look at this:
http://www.bozebo.com/images/ubbuh.png (changed to link because it might be too wide for OCUK)
Always at the same pixel...
And look, the following image works fine without it ever reporting 0,0,0
Image dimensions: 240x175
If I make the 240x175 image look like this instead, it starts always reporting 0,0,0 at pixel 874.
I am confused.
I made a thread over at gamedev.net (site might not respond, it seems to get DDOSd all the time or something


Heres the full c++ code (interesting bit extracted below):
Code:
#include <stdint.h>
#include <iostream>
#define DEBUG
#ifdef DEBUG
#define DEBUG_INPIXELS
#endif
typedef struct fileHeaderMagic{
unsigned char magic[2];
} fileHeaderMagic;
typedef struct fileHeader{
uint32_t filesz;
uint16_t creator1;
uint16_t creator2;
uint32_t bmp_offset;
} fileHeader;
typedef struct fileBmpHeader{
uint32_t header_sz;
uint32_t width;
uint32_t height;
uint16_t nplanes;
uint16_t bitspp;
uint32_t compress_type;
uint32_t bmp_bytesz;
uint32_t hres;
uint32_t vres;
uint32_t ncolors;
uint32_t nimpcolors;
} fileBmpHeader;
//24 bpp colour format
typedef struct col24{
unsigned char b;
unsigned char g;
unsigned char r;
} col24;
using namespace std;
bool convert(char* file){
//make the image object
FILE * image;
//open the image file for reading
image = fopen(file,"r");
//check that it was opened
if(image == NULL){
cout << "could not open file";
return false;
}
//make the magic, file header and bitmap header
fileHeaderMagic magic;
fileHeader header;
fileBmpHeader bmpHeader;
//read the magic
fread(&magic,sizeof(magic),1,image);
//check the magic
if(magic.magic[0] != char(66) || magic.magic[1] != char(77)){
cout << "file is not a Windows bitmap";
return false;
}
//read the header
fread(&header,sizeof(header),1,image);
//read the bitmap header
fread(&bmpHeader,sizeof(bmpHeader),1,image);
//check header attributes
if(bmpHeader.header_sz != 40){
cout << "unexpected header size";
return false;
}
if(bmpHeader.nplanes != 1){
cout << "unexpected colour planes";
return false;
}
if(bmpHeader.width < 1 || bmpHeader.height < 1){
cout << "bitmap has an invalid dimension";
return false;
}
if(bmpHeader.bitspp != 24){
cout << "bitmap must be 24 bits per pixel";
return false;
}
if(bmpHeader.compress_type != 0){
cout << "bitmap must be uncompressed";
return false;
}
if(bmpHeader.ncolors != 0){
cout << "bitmap has an invalid number of colours (" <<
int(bmpHeader.ncolors) << ")";
return false;
}
//calculate bytes per row
int bytesPerRow = bmpHeader.bmp_bytesz/bmpHeader.height;
#ifdef DEBUG
cout << "bytesPerRow: " << bytesPerRow << endl;
#endif
//calculate padding size
int padding = bytesPerRow - bmpHeader.width*3; //byte per row - byte of pixels
#ifdef DEBUG
cout << "padding: " << padding << endl;
#endif
char scrap[padding]; //bytes of padding scrap
//make an array of 24 bit pixels
col24 inPixels[bmpHeader.width*bmpHeader.height];
#ifdef DEBUG
cout << bmpHeader.width*bmpHeader.height << " pixels" << endl;
cout << sizeof(col24) << " bytes per pixel" << endl;
cout << sizeof(inPixels) << " bytes in inPixels array" << endl;
cout << bmpHeader.width*bmpHeader.height*sizeof(col24) <<
" bytes (expected)" << endl;
system("pause");
#endif
//current pixel
int currentPixel = 0;
//loop through each row bottom to top
for(int row = bmpHeader.height - 1;row >= 0;row --){
#ifdef DEBUG
cout << "row " << row << endl;;
#endif
//loop through each column
for(int col = 0;col < bmpHeader.width;col ++){
#ifdef DEBUG_INPIXELS
cout << "currentPixel: " << currentPixel << endl;
#endif
fread(&inPixels[currentPixel],3,1,image); //read 3 byte as a colour
#ifdef DEBUG_INPIXELS
if(inPixels[currentPixel].b == 0 && inPixels[currentPixel].g == 0 &&
inPixels[currentPixel].r == 0) system("pause");
cout << int(inPixels[currentPixel].b) << ",";
cout << int(inPixels[currentPixel].g) << ",";
cout << int(inPixels[currentPixel].r) << " ";
#endif
currentPixel ++; //move to the next pixel
}
fread(&scrap,padding,1,image); //read the padding as scrap
#ifdef DEBUG
cout << endl;
#endif
}
#ifdef DEBUG
system("pause");
#endif
return true;
}
//application entry point
int main(int argc, char **argv, char **envp){
char * open; //path to file to convert
bool err; //if an error has occurred
if(argc >= 2){
open = argv[1];
err = !convert(open);
} else {
#ifdef DEBUG
err = !convert("image.bmp");
#endif
#ifndef DEBUG
cout << "please specify a file";
err = true;
#endif
}
if(err){
cout << endl;
system("pause");
}
/*
//bits per pixel
uint16_t bpp = 24;
//file dimensions
uint32_t width = 64,height = 64,
//calculate some required information
pixels = width*height,dataSize;
//make the "magic", file header and bitmap header
fileHeaderMagic file_headerMagic;
fileHeader file_header;
bmpHeader file_bmpHeader;
//make a colour in rbg 24 bit format
colour bmpCol;
bmpCol.r = 0xff;
bmpCol.g = 0;
bmpCol.b = 0xff;
int16_t padding;
//calculate the size in bytes of the pixels in each row
int16_t pixelRowBytes = width*bpp/8;
cout << "pixelRowBytes: " << pixelRowBytes << endl;
//calculate the padding at the end of a row
if(pixelRowBytes%4 == 0){ //if no padding is required
cout << "no padding required" << endl;
padding = 0;
} else {
cout << "needs padding" << endl;
padding = 0; //bleh
}
//calculate the width of a row in bytes
uint16_t rowBytes = pixelRowBytes + padding;
cout << "rowBytes: " << rowBytes << endl;
//which part of the colour is being written (bgr)
uint16_t colourPart = 0;
//calculate the data size
dataSize = height*rowBytes;
cout << "dataSize: " << dataSize << endl;
//make the bitmap data array
unsigned char bmpData[dataSize];
//fill the data
for(uint16_t row = height;row > 0;row --){ //loop rows, bottom to top order
for(uint16_t col = 0;col < pixelRowBytes;col ++){ //each column with pixels
switch(colourPart){
case 0:
bmpData[row*pixelRowBytes-pixelRowBytes+col] = bmpCol.b; //fill the blue byte
colourPart = 1;
break;
case 1:
bmpData[row*pixelRowBytes-pixelRowBytes+col] = bmpCol.g; //fill the green byte
colourPart = 2;
break;
case 2:
bmpData[row*pixelRowBytes-pixelRowBytes+col] = bmpCol.r; //fill the red byte
colourPart = 0;
break;
}
}
}
//populate the "magic"
file_headerMagic.magic[0] = 0x42; //B
file_headerMagic.magic[1] = 0x4D; //M
//populate the file header
file_header.filesz = sizeof(fileHeaderMagic)+sizeof(fileHeader)+
sizeof(bmpHeader)+dataSize;
file_header.creator1 = 0;
file_header.creator2 = 0;
file_header.bmp_offset = sizeof(fileHeaderMagic)+
sizeof(fileHeader)+sizeof(bmpHeader);
//populate the bitmap header
file_bmpHeader.header_sz = sizeof(bmpHeader);
file_bmpHeader.width = width;
file_bmpHeader.height = height;
file_bmpHeader.nplanes = 1;
file_bmpHeader.bitspp = bpp;
file_bmpHeader.compress_type = 0; //uncompressed
file_bmpHeader.bmp_bytesz = dataSize; //size in bytes of the data
file_bmpHeader.hres = 2835; //pixels per meter, wut
file_bmpHeader.vres = 2835;
file_bmpHeader.ncolors = 0; //no colours in palette
file_bmpHeader.nimpcolors = 0;
//handle for the image file
FILE * image;
//path for the image file
image = fopen("image.bmp","w");
//write the "magic"
fwrite(&file_headerMagic,1,sizeof(fileHeaderMagic),image);
//write the file header
fwrite(&file_header,1,sizeof(fileHeader),image);
//write the bitmap header
fwrite(&file_bmpHeader,1,sizeof(bmpHeader),image);
//write the bitmap data
fwrite(bmpData,1,dataSize,image);
//done with the image
fclose(image);
*/
//exit application
return 0;
}
This bit (highlighted as php

PHP:
int bytesPerRow = bmpHeader.bmp_bytesz/bmpHeader.height;
#ifdef DEBUG
cout << "bytesPerRow: " << bytesPerRow << endl;
#endif
//calculate padding size
int padding = bytesPerRow - bmpHeader.width*3; //byte per row - byte of pixels
#ifdef DEBUG
cout << "padding: " << padding << endl;
#endif
char scrap[padding]; //bytes of padding scrap
//make an array of 24 bit pixels
col24 inPixels[bmpHeader.width*bmpHeader.height];
#ifdef DEBUG
cout << bmpHeader.width*bmpHeader.height << " pixels" << endl;
cout << sizeof(col24) << " bytes per pixel" << endl;
cout << sizeof(inPixels) << " bytes in inPixels array" << endl;
cout << bmpHeader.width*bmpHeader.height*sizeof(col24) <<
" bytes (expected)" << endl;
#endif
//current pixel
int currentPixel = 0;
//loop through each row bottom to top
for(int row = bmpHeader.height - 1;row >= 0;row --){
#ifdef DEBUG
cout << "row " << row << endl;;
#endif
//loop through each column
for(int col = 0;col < bmpHeader.width;col ++){
#ifdef DEBUG_INPIXELS
cout << "currentPixel: " << currentPixel << endl;
#endif
fread(&inPixels[currentPixel],3,1,image); //read 3 byte as a colour
#ifdef DEBUG_INPIXELS
cout << int(inPixels[currentPixel].b) << ",";
cout << int(inPixels[currentPixel].g) << ",";
cout << int(inPixels[currentPixel].r) << " ";
#endif
currentPixel ++; //move to the next pixel
}
fread(&scrap,padding,1,image); //read the padding as scrap
#ifdef DEBUG
cout << endl;
#endif
}
My reply in that thread on gamedev.net, extracted (this is where things get strange):
-------------------------------------------------
ok, I changed the sample image and now it gets so far through then thinks every pixel after then is just 0,0,0
with an image of only one colour (i dunno, 200,10,44) it works. But if there are a bunch of different colours present in the image it starts picking up everything as 0,0,0 after a certain point :S What...
This image makes it happen (programmer art yay):

Whereas if the image had only the yellow background and none of the coloured bars, it goes through the whole thing without a fuss - picking up the yellow all the way. Considering I am doing nothing with palettes or anything like that, how is this even possible?
edit:
Look at this:
http://www.bozebo.com/images/ubbuh.png (changed to link because it might be too wide for OCUK)
Always at the same pixel...
And look, the following image works fine without it ever reporting 0,0,0

Image dimensions: 240x175
If I make the 240x175 image look like this instead, it starts always reporting 0,0,0 at pixel 874.
I am confused.
Last edited: