Reading 32-bit Bitmaps

Having trouble reading in 32-bit Bitmaps

Page 1 of 1

4 Replies - 3189 Views - Last Post: 13 August 2010 - 12:47 PM Rate Topic: -----

#1 Mike007  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 7
  • View blog
  • Posts: 332
  • Joined: 30-August 07

Reading 32-bit Bitmaps

Posted 12 August 2010 - 09:12 AM

Hi everyone,

I am trying to read in some RGBA data to use in an OpenGL application. Since it seemed like bitmaps were the easiest to read, I decided to save my graphics as 32-bit Bitmaps in photoshop.

I read this tutorial here and a few other sources, but I cannot seem to get the right data in my struct. In praticular I am looking at the BITMAPFILEHEADER struct (the file header) which is 14 bytes long. I made sure the structs look like they do in the wingdi.h header, but for some reason when the struct is filled 2 bytes seem to be skipped.

Memory looks like:
0x80583a0 : 0x80583A0 <Hex>
Address 0 - 3 4 - 7 8 - B C - F
080583A0 424D6884 00000000 00003600 00002800

And I expect to get something like in my struct:
bmfh BITMAPFILEHEADER {...}
bfType WORD 19778
bfSize DWORD 33896
bfReserved1 WORD 0
bfReserved2 WORD 0
bfOffBits DWORD 54

but instead I get this:
bmfh BITMAPFILEHEADER {...}
bfType WORD 19778
bfSize DWORD 0
bfReserved1 WORD 0
bfReserved2 WORD 54
bfOffBits DWORD 2621440

My question is why is it skipping bytes 2 and 3? If I change the type of bfSize in the struct it seems to WORD (in that specific case at least). But the correct type is DWORD.

I am using a 32-bit Intel processor under linux with a standard g++ compiler (the eclipse CDT IDE).

Here is my source code:

header file
/*
 * ImageLoader.h
 *
 *  Created on: 2010-08-11
 *      Author: mike
 */

#ifndef IMAGELOADER_H_
#define IMAGELOADER_H_

#include <cstdio>

typedef unsigned char BYTE;
typedef long LONG;
typedef unsigned long DWORD;
typedef unsigned short WORD;

//File information header
//provides general information about the file
typedef struct tagBITMAPFILEHEADER
{
  WORD    bfType;
  DWORD   bfSize;
  WORD    bfReserved1;
  WORD    bfReserved2;
  DWORD   bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

//Bitmap information header
//provides information specific to the image data
typedef struct tagBITMAPINFOHEADER
{
  DWORD  biSize;
  unsigned int   biWidth;
  unsigned int   biHeight;
  WORD   biPlanes;
  WORD   biBitCount;
  DWORD  biCompression;
  DWORD  biSizeImage;
  DWORD   biXPelsPerMeter;
  DWORD   biYPelsPerMeter;
  DWORD  biClrUsed;
  DWORD  biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;

//Colour palette
typedef struct tagRGBQUAD
{
  BYTE    rgbBlue;
  BYTE    rgbGreen;
  BYTE    rgbRed;
  BYTE    rgbReserved;
} RGBQUAD;


class ImageLoader
{
public:
    //variables

    //methods

    /**
     * Initializes an empty image
     */
    ImageLoader(void);

    /**
     * Initializes an image with the given image loaded from disk
     */
    ImageLoader(char *fileName);

    /**
     * Destructor...
     */
    ~ImageLoader();

    /**
     * Loads the given image
     */
    bool loadBMP(const char *fileName);

    RGBQUAD *getColors() const
    {
        return colors;
    }

    bool getLoaded() const
    {
        return loaded;
    }

    BYTE *getPixelData() const
    {
        return pixelData;
    }

    LONG getWidth() const
    {
        return width;
    }

private:
    //variables
    BITMAPFILEHEADER bmfh;
    BITMAPINFOHEADER bmih;
    RGBQUAD *colors;
    BYTE *pixelData;
    bool loaded;
    LONG width,height;
    WORD bpp;

    //methods
    void reset(void);
    bool fixPadding(BYTE *tempPixelData, FILE *in, DWORD size);
};
#endif /* IMAGELOADER_H_ */




source file
/*
 * ImageLoader.cpp
 *
 *  Created on: 2010-08-11
 *      Author: mike
 */
#include "ImageLoader.h"
#include <cstdio>
#include <cstring>
#define BITMAP_TYPE 19778

ImageLoader::ImageLoader()
{
	reset();
}

ImageLoader::ImageLoader(char *fileName)
{
	ImageLoader();
	loadBMP(fileName);
}

ImageLoader::~ImageLoader()
{
    if(colors != NULL)
    {
        delete[] colors;
    }

    if(pixelData != NULL)
    {
        delete[] pixelData;
    }
}



bool ImageLoader::loadBMP(const char * fileName)
{
	FILE *in = NULL;
	bool result = false;

	//open the file for reading in binary mode
	in = fopen(fileName, "rb");
	if(in == NULL)
	{
		return false;
	}

	fread(&bmfh, sizeof(BITMAPFILEHEADER), 1, in);

	// check if this is even the right type of file
	if(bmfh.bfType != BITMAP_TYPE)
	{
		return false;
	}

	fread(&bmih,sizeof(BITMAPINFOHEADER),1,in);
	width = bmih.biWidth;
	height = bmih.biHeight;

	//set the number of colours
	LONG numColors = 1 << bmih.biBitCount;

    //bitmap is not loaded yet
    loaded = false;
    //make sure memory is not lost
    if(colors != NULL)
    {
        delete[] colors;
    }

    if(pixelData != NULL)
    {
        delete[] pixelData;
    }

	//load the palette for 8 bits per pixel
	if(bmih.biBitCount < 24)
	{
	    colors = new RGBQUAD[numColors];
	    fread(colors, sizeof(RGBQUAD), numColors, in);
	}

	DWORD size = bmfh.bfSize - bmfh.bfOffBits;

	BYTE *tempPixelData;

	tempPixelData = new BYTE[size];

	if(tempPixelData == NULL)
	{
	    fclose(in);
	    return false;
	}

	fread(tempPixelData, sizeof(BYTE), size, in);
	fclose(in);

	result = fixPadding(tempPixelData, in, size);
	loaded = result;

	delete[] tempPixelData;

	return result;
}

bool ImageLoader::fixPadding(BYTE *tempPixelData, FILE *in, DWORD size)
{
	//byteWidth is the width of the actual image in bytes
	//padWidth is the width of the image plus the extra padding
	LONG byteWidth, padWidth;

	//initially set both to the width of the image
	byteWidth = padWidth = (LONG)((float)width * (float)bpp / 8.0);

	//add any extra space to bring each line to a DWORD boundary
	while(padWidth % 4 != 0)
	{
	   padWidth++;
	}

	DWORD diff;
	int offset;

	height = bmih.biHeight;
	//set diff to the actual image size(no padding)
	diff = height * byteWidth;
	//allocate memory for the image
	pixelData = new BYTE[diff];

	if(pixelData == NULL)
	{
	    fclose(in);
	    return false;
	}

	//bitmap is inverted, so the padding needs to be removed
	//and the image reversed
	//Here you can start from the back of the file or the front,
	//after the header.  The only problem is that some programs
	//will pad not only the data, but also the file size to
	//be divisible by 4 bytes.
	if(height > 0)
	{
	    int j = size - 3;
	    offset = padWidth - byteWidth;

	    for(unsigned int i = 0; i < size; i += 3)
	    {
	        if( (i + 1) % padWidth == 0)
	        {
	            i += offset;
	        }

	        *(pixelData + j + 2) =* (tempPixelData + i);
	        *(pixelData + j + 1 ) =* (tempPixelData + i + 1);
	        *(pixelData + j) =* (tempPixelData + i + 2);
	        j++;
	    }
	}
	//the image is not reversed.  Only the padding needs to be removed.
	else
	{
	    height = height * -1;
	    offset = 0;

	    do
	    {
	    	memcpy((pixelData + (offset * byteWidth)), (tempPixelData + (offset * padWidth)), byteWidth);
	        offset++;
	    } while(offset < height);
	}

	return true;
}

void ImageLoader::reset(void)
{
	width = 0;
	height = 0;
	pixelData = NULL;
	colors = NULL;
	loaded = false;
}



I am sure it is something simple that I missed, due to my lack of experience reading binary files. But I am quite fresh out of ideas, so I would appreciate any help.

This post has been edited by Mike007: 12 August 2010 - 09:14 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Reading 32-bit Bitmaps

#2 Salem_c  Icon User is offline

  • void main'ers are DOOMED
  • member icon

Reputation: 1689
  • View blog
  • Posts: 3,209
  • Joined: 30-May 10

Re: Reading 32-bit Bitmaps

Posted 12 August 2010 - 09:31 AM

http://gcc.gnu.org/o...Type-Attributes
Look up the "packed" attribute.

typedef struct tagBITMAPFILEHEADER
{
  WORD    bfType;
  WORD    thereIsAHoleInYourStructure;  // Not really here, but there is a 2-byte hole.
  DWORD   bfSize;
  WORD    bfReserved1;
  WORD    bfReserved2;
  DWORD   bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;


By default, memory is arranged for the most efficient access of the data types.
The problem comes when you assume that that is the same as your file format.

Note that you will encounter at some point something called "endianness".
For that, there is nothing you can easily set in the compiler (any compiler) which will fix that little problem.

For truly portable and robust code, you need to read the file one byte at a time and assign it to the correct place of whatever structure you have.
Was This Post Helpful? 2
  • +
  • -

#3 Mike007  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 7
  • View blog
  • Posts: 332
  • Joined: 30-August 07

Re: Reading 32-bit Bitmaps

Posted 12 August 2010 - 11:58 PM

Oh wow, thanks a lot! I would have never guessed this to be the problem. So it tries to optimize access for speed, thus using larger types to read faster (when hardware support allows it). Makes sense.

About the endians, I am actually already somewhat aware of this, although it has been abstracted from me so far. How do you tell which endianness a file is using? I know for TCP/IP there is a standard endian type used (big-endian if I recall correctly). Is there a similar standard for files? Or maybe each file format specifies a standard like that?

Quote

For truly portable and robust code, you need to read the file one byte at a time and assign it to the correct place of whatever structure you have.


So something like:
void readFileHeader()
{
	fread(&bmfh.bfType, sizeof(WORD), in);
        fread(&bmfh.bfSize, sizeof(DWORD), in);
	.
	.
	.
	if(machine_endians != file_endians)
	{
		if(machine_endians == BIG_ENDIAN)
		{
			convertToBigEndian(&bmfh, BITMAPFILEHEADER);
		}
		else
		{
			convertToSmallEndian(&bmfh, BITMAPFILEHEADER);
		}
	}   
}



I am not sure if you can pass a type like that to a function, but I suppose if we can pass a function, why not be able to pass a type somehow? But is that more or less what you had in mind?

In any case, I was going to abstract this further away and let a library handle the job of reading a file. But thought it might be a good idea to try it myself first, because I only need a very simple reader. I want to keep on this track for now to learn more about handling binary files.

Again, I do really apperciate the quick reply, I am glad I asked. I spent the rest of the day going over all of it to make it work and get rid of memory leaks (the logic in the article had a lot of problems). I will not know for certain if I got all cases right until I write unit tests for it.

This brings me to my next question, is there a standard unit testing for c++ like there is for other languages, a derivative of the jUnit family maybe? (I know Microsoft has their own, but I am not interested in that one).
Was This Post Helpful? 0
  • +
  • -

#4 Salem_c  Icon User is offline

  • void main'ers are DOOMED
  • member icon

Reputation: 1689
  • View blog
  • Posts: 3,209
  • Joined: 30-May 10

Re: Reading 32-bit Bitmaps

Posted 13 August 2010 - 11:47 AM

> How do you tell which endianness a file is using?
Read the documentation for the file format.

> I know for TCP/IP there is a standard endian type used (big-endian if I recall correctly).
Yes, and you also get functions like htons() to convert from native to network format.

You could do the same here, say BigEndianLongToNative() etc. On a BE machine, it does nothing, and on a SE machine, it does what it needs to.
99% of your code doesn't care, and the bit that does can be compiled appropriately.

> Is there a similar standard for files? Or maybe each file format specifies a standard like that?
Total variation. Like I said, read the spec.
Was This Post Helpful? 1
  • +
  • -

#5 Mike007  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 7
  • View blog
  • Posts: 332
  • Joined: 30-August 07

Re: Reading 32-bit Bitmaps

Posted 13 August 2010 - 12:47 PM

Yeah there is no standard for this file. It all depends on how many colors you have, in my case 32-bit is little-endian. It usually is little-endian, but for 16-bit images it is big-endian for example.

I could not find an official microsoft specification on their site, but this here is the next best thing:
http://www.fileforma...at/bmp/egff.htm

Thank again, you have given me some good ideas here. I better go work on it some more :).
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1