Skip to main content
GameDev.net gamedev.net
🔒 Locked

Bitmap Loader Problem - Corrupt Colors and Skewed

Started by aevanthony Oct 15, 2007 at 11:38 AM 2 replies 1.4k views
Original Post
aevanthony
aevanthony
*** read my reply below for a small update, managed to fix the colors, it's an awkward 'hack' i think... i'll try to come up with better, i think i can just come up with an ugly hack to fix the skewing, i don't like either solutions. *** I'll keep this simple and clean, this is the result of my bitmap loader, and below that is all of my source code implementing the bitmap loader. The bitmap loader source originated from a tutorial on this site I think... but what I did is I tried to figure out how to get the pixel color at any given (x, y) coordinate since that wasn't implemented. It came out buggy, or at least not as I expected. So maybe someone can take a look. Original: http://www.threeam.info/tmp/664170_136_full.bmp Rendered: http://www.threeam.info/tmp/664170_136_full_bug.bmp ** Fixed color results: http://www.threeam.info/tmp/664170_136_full_bug_kindof.bmp ** The code I added to the bitmap loader:

void Bitmap::getRGB(int x, int y)
{
	if (rgb == NULL)
	    { rgb = new int[3]; }

	int i = ((y * width) + x) * RGB_BYTE_SIZE;

	rgb[0] = *(data + i + 2);
	rgb[1] = *(data + i + 1);
	rgb[2] = *(data + i);
}

Bitmap Loader class:

const short BITMAP_MAGIC_NUMBER	= 19778;
const int	RGB_BYTE_SIZE		= 3;

#pragma pack(push,bitmap_data,1)

typedef struct tagRGBQuad
{
	char rgbBlue;
	char rgbGreen;
	char rgbRed;
	char rgbReserved;
} RGBQuad;

typedef struct tagBitmapFileHeader
{
	unsigned short	bfType;
	unsigned int	bfSize;
	unsigned short	bfReserved1;
	unsigned short	bfReserved2;
	unsigned int	bfOffBits;
} BitmapFileHeader;

typedef struct tagBitmapInfoHeader
{
	unsigned int	biSize;
	int				biWidth;
	int				biHeight;
	unsigned short	biPlanes;
	unsigned short	biBitCount;
	unsigned int	biCompression;
	unsigned int	biSizeImage;
	int				biXPelsPerMeter;
	int				biYPelsPerMeter;
	unsigned int	biClrUsed;
	unsigned int	biClrImportant;
} BitmapInfoHeader;

#pragma pack(pop,bitmap_data)

class Bitmap
{
	public:
	    RGBQuad		*colours;
	    char		*data;
	    bool		loaded;
	    int			width, height;
	    unsigned short bpp;
	    int			*rgb;

	    Bitmap (void);
	    Bitmap (char *);
	    ~Bitmap ();
	    bool loadBMP (char *);
	    void getRGB (int x, int y);

	private:
	    BitmapFileHeader	bmfh;
	    BitmapInfoHeader	bmih;
	    int					byteWidth;	//the width in bytes of the image
	    int					padWidth;	//the width in bytes of the added image
	    unsigned int		dataSize;	//size of the data in the file

	    void reset(void);
	    bool convert24 (char *);	//convert to 24bit RGB bottom up data
	    bool convert8 (char *);	//convert to 8bit RGB bottom up data
};

void Bitmap::getRGB(int x, int y)
{
	rgb = new int[3];

	int offset = padWidth - byteWidth,
		i = (width * (y * RGB_BYTE_SIZE)) + (x * RGB_BYTE_SIZE);

	if ((i+1)%padWidth == 0)
		{ i += offset; }

	rgb[0] = *(data + i + 2);
	rgb[1] = *(data + i + 1);
	rgb[2] = *(data + i);
}

Bitmap::Bitmap()
{
    reset();
}

Bitmap::Bitmap(char *file)
{
    reset();
    loadBMP(file);
}

Bitmap::~Bitmap()
{
	if (colours!=0)
		{ delete[] colours; }

	if (data!=0)
		{ delete[] data; }
}

bool Bitmap::loadBMP(char *file)
{
	FILE	*in;		//file stream for reading
	char	*tempData;	//temp storage for image data
	int		numColours;	//total available colours

	loaded = false;

	if (colours != 0)
		{ delete[] colours; }

	if (data!=0)
		{ delete[] data; }

	in = fopen(file,"rb");
	if (in == NULL)
	{
		fclose(in);
		return false;
	}

	fread(&bmfh, sizeof(BitmapFileHeader), 1, in);
	if (bmfh.bfType != BITMAP_MAGIC_NUMBER)
	{
		fclose(in);
		return false;
	}

	fread(&bmih, sizeof(BitmapInfoHeader), 1, in);
	width		= bmih.biWidth;
	height		= bmih.biHeight;
	bpp			= bmih.biBitCount;
	dataSize	= (width * height * (unsigned int)(bmih.biBitCount/8.0));
	numColours	= (1 << bmih.biBitCount);

    if (bpp < 8)
	{
		fclose(in);
		return false;
	}

	if (bpp == 8)
	{
		colours = new RGBQuad[numColours];
		fread(colours, sizeof(RGBQuad), numColours, in);
	}

    tempData = new char[dataSize];
    if (tempData == NULL)
	{
		fclose(in);
		return false;
	}

	fread(tempData, sizeof(char), dataSize, in);
	fclose(in);
    byteWidth = padWidth = (int)((float)width * (float)bpp / 8.0);

    while (padWidth%4 != 0)
		{ ++padWidth; }

	if (bpp == 8)
		{ loaded = convert8(tempData); }
	else if (bpp == 24)
		{ loaded = convert24(tempData); }

	delete[] tempData;
	return loaded;
}

void Bitmap::reset(void)
{
	loaded	= false;
	colours	= 0;
	data	= 0;
}

bool Bitmap::convert24(char* tempData)
{
	int offset, diff;

	diff = width * height * RGB_BYTE_SIZE;
	data = new char[diff];

	if (data == NULL)
	{
		delete[] data;
		return false;
	}

	if (height > 0)
	{
		offset = padWidth - byteWidth;
		for (int i=0; i<dataSize; i+=RGB_BYTE_SIZE)
		{
			if ((i+1)%padWidth == 0)
				{ i += offset; }

			*(data+i+2)	= *(tempData+i);
			*(data+i+1)	= *(tempData+i+1);
			*(data+i)	= *(tempData+i+2);
		}
	}
    else
	{
		offset = padWidth - byteWidth;
		for (int i=0, j=dataSize-RGB_BYTE_SIZE; i<dataSize; i+=RGB_BYTE_SIZE, j-=3)
		{
			if ((i+1)%padWidth == 0)
				{ i += offset; }

			*(data+j+2)	= *(tempData+i);
			*(data+j+1)	= *(tempData+i+1);
			*(data+j)	= *(tempData+i+2);
		}
	}

	return true;
}

bool Bitmap::convert8(char* tempData)
{
	int offset, diff;

	diff = width * height * RGB_BYTE_SIZE;
    data = new char[diff];

    if (data == NULL)
	{
		delete[] data;
		return false;
	}

	if (height>0)
	{
		offset = padWidth - byteWidth;

		for (int i=0, j=0; i<dataSize*RGB_BYTE_SIZE; i+=RGB_BYTE_SIZE, ++j)
		{
			if ((i+1)%padWidth==0)
				{ i += offset; }

			*(data+i)	= colours[*(tempData+j)].rgbRed;
			*(data+i+1)	= colours[*(tempData+j)].rgbGreen;
			*(data+i+2)	= colours[*(tempData+j)].rgbBlue;
		}
	}
    else
	{
		offset = padWidth - byteWidth;
		for (int i=0, j=dataSize-1; i<dataSize*RGB_BYTE_SIZE; i+=RGB_BYTE_SIZE, --j)
		{
			if ((i+1)%padWidth==0)
				 { i += offset; }

            *(data+i)	= colours[*(tempData+j)].rgbRed;
            *(data+i+1)	= colours[*(tempData+j)].rgbGreen;
            *(data+i+2)	= colours[*(tempData+j)].rgbBlue;
        }
    }

    return true;
}

App using the loader class (cut it down a bit from original): (opengl screen coords are -1 -> +1 for both x, and y axis.)

Bitmap *lbmp;

static void display(void)
{
	if (keypress)
	{
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glLoadIdentity();
		glPushMatrix();
		glBegin(GL_POINTS);

		static int		x, y;
		static float	r, g, b, cx, cy;

		for (x=0; x<lbmp->width; ++x)
		for (y=0; y<lbmp->height; ++y)
		{
			lbmp->getRGB(x, y);

			r = lbmp->rgb[0] / 255.0;
			g = lbmp->rgb[1] / 255.0;
			b = lbmp->rgb[2] / 255.0;

			cx = (x / float(lbmp->width / 2.0)) - 1.0;
			cy = (y / float(lbmp->height / 2.0)) - 1.0;

			glColor3f (r, g, b);
			glVertex3f (cx, cy, 0.0);
		}

		glEnd();
		glPopMatrix();
		keypress = false;
	}

	glutSwapBuffers();
	Sleep(1);
}

int main(int argc, char *argv[])
{
	lbmp = new Bitmap("664170_136_full.bmp");
//...
}

[Edited by - aevanthony on October 15, 2007 3:54:29 PM]
aevanthony
aevanthony
after analyzing this code carefully and lots of paper and penning and thinking *smashes head on desk* i fixed the color, here's how:

http://www.threeam.info/tmp/664170_136_full_bug_kindof.bmp

for (x=0; x<lbmp->width; ++x)for (y=0; y<lbmp->height; ++y){	lbmp->getPixel(x, y);	if ((lbmp->rgb[0] < 0)||(lbmp->rgb[0] > 255))	r = (lbmp->rgb[0]+255) / 255.0;	else	r = (lbmp->rgb[0]) / 255.0;	if ((lbmp->rgb[1] < 0)||(lbmp->rgb[1] > 255))	g = (lbmp->rgb[1]+255) / 255.0;	else	g = (lbmp->rgb[1]) / 255.0;	if ((lbmp->rgb[2] < 0)||(lbmp->rgb[2] > 255))	b = (lbmp->rgb[2]+255) / 255.0;	else	b = (lbmp->rgb[2]) / 255.0;	cx = (x / float(lbmp->width / 2.0)) - 1.0;	cy = (y / float(lbmp->height / 2.0)) - 1.0;	glColor3f (r, g, b);	glVertex3f (cx, cy, 0.0);}
songho
songho
Is the image width divisible by 4? If not, the skew is caused by the paddings in BMP image data.

For example, if the image width is 103, then BMP adds 1 byte padding at the end of each scaneline (103 + 1 = 104), so each line can be evenly divisible by 4.

Please check this BMP class to handle the paddings:
imageBmp.zip
aevanthony
aevanthony
and this fixed the skewing:

lbmp->getPixel(x-(lbmp->width-y), y);



I still don't like these fixes.


edit: thanks for that information songho.
i bet you're correct, i'll re-write the appropriate piece of code to incorporate that. thank you. i'll post back soon.

and thanks for the code but i'd prefer to try and figure out how to incorporate the fix myself, your suggestion was enough. ;-)


edi2: oh you were definitely correct, sweet!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.