One of the most popular...and of course simplest file formats today, is the
bmp file format. It comes in a number of flavours...which include 1bit.. 4
bit...16 bit...32bit..etc There's is also a compressed version of the bmp
format, using run length encoding. But don't worry, we'll explain all that
as we go along.

Lets start by explaining a simple picture, and how its broken up....

If your reading this tutorial on your pc screen...well your well familiar
that your screen is made up of pixels....where each pixel has a Red, Green Blue
value (RGB) which makes up the colour of that pixel. You have a lot of
pixels on that screen!....lots and lots.... lets pick a nice number.... 640x480
pixels...

Once you have a grasp of what those things....I mean pixels are...its just a
matter of understanding how the bitmap file stores them.

The great thing about the bmp format is how it arranges these pixels....there
is a header part...which is the first few bytes...that tells us the width,
height etc of the image...then we would just have the raw data following
this....simple eh? Well its a little more complex than that if you use the
encoded version or the palette version.. but for the 32 and 24 bit versions its
exactly that.

Which type of bmp should we start with?... Well I think the best way is to
start simple....which is the simplest? Its the 24bit version of the bmp
file format...well I think it is.. But don't worry, we'll do the other
types...1 bit....8 bit etc.

// Simple Debug Feedback - writes to a simple text file located where the
exe is.

void
debug(char* str)

{

FILE* fp = fopen("info.txt", "a+");

fprintf(fp, "%s\n", str);

fclose(fp);

}//
End of debug(..)

char
buf[500]; // Temp buffer for text output

// Program Entry Point - Where we always start!

void
main()

{

FILE* fp = fopen("cross.bmp", "rb");

char readbuf[2];
// Temp Buffer

// +- Buffer to put the data in

// | +- Size of each value read in bytes
(1 byte each)

// | | +- How many times to read in
the value (e.g. twice read in 1 byte)

// | | | +- Stream souce

// | | | |

fread(readbuf, 1, 2, fp);

debug("First Two Bytes:");

sprintf(buf, "%c %c", readbuf[0], readbuf[1]);

debug(buf);

fclose(fp);

}//
End of main()

Output: info.txt

First Two Bytes:
B M

What on earth...well its simple...and it gives us a starting code from which
we can explore the bmp file format. All that I've done is read in the
first two bytes of any bmp file and write them to an output file...in the above
case called 'info.txt'. As all bitmap files have two bytes with 'B' and
'M' at the start of the file....so that where sure its a bmp file. You can
also open the bmp file in notepad and view it in there....it will be all
jibberish...but you'll notice that the first two bytes are B and M.

Starting from common ground is a good think....now rather than just use any
random bmp file...I think its best to create a custom one and then you know what
your looking for when you read in the data...that way if the bytes or bits arn't
aligned, then you'll now about it right away. So open up your image editor
and create a 24 bit bitmap that's 8x8 pixels wide as shown below:

Image: cross.bmp

Of course you can use your own demo image...but I think its best to start
small :) We can always improve and alter it as we go along.

Code: main.cpp

#include
<stdio.h> // So we can
use fopen and fread

// Simple Debug Feedback - writes to a simple text file located where the
exe is.

void
debug(char* str)

{

FILE* fp = fopen("info.txt", "a+");

fprintf(fp, "%s\n", str);

fclose(fp);

}//
End of debug(..)

char
buf[500]; // Temp buffer for text output

// Program Entry Point - Where we always start!

void
main()

{

FILE* fp = fopen("cross.bmp", "rb");

char typeBM[2]; //
Temp Buffer

fread(typeBM, 1, 2, fp);

sprintf(buf, "typeBM: %c %c", typeBM[0], typeBM[1]);

debug(buf);

unsignedint
iSize; // 4 Bytes

fread(&iSize, 4, 1, fp);

sprintf(buf, "iSize: %d", iSize);

debug(buf);

fclose(fp);

}//
End of main()

Output: info.txt

typeBM: B M
iSize: 246

Ooooo.....so if we read in the next two bytes from our file, as I've done
before....they represent our exact file size....if you right click on the
bitmap, and check its size in bytes...you'll see that its 246bytes. Where
getting there slowly...

Now bit by bit we read in the header information in...which tells us all the
information about the bitmap...things like what type of bitmap it is...filesize...image
width...offset to the image bits.... now rather than do it one by one... you can
put the data into a structure and read it in, in one go. Let me show you
the definitions of the header data, as its broken up into two parts.

BitmapFileHeader

Name

Size in Bytes

Small Description

bfType

2

Bitmap File Type Signature

bfSize

4

Whole size of the bitmap file on your pc

bfReserved1

2

Unused - just ignore

bfReserved2

2

Unused - just ignore

bfOffSetBits

4

Offset from the start of the file to our pixel data

BitmapInfoHeader

Name

Size in Bytes

Small Description

biSize

4

Size of this Header - 40 bytes

biWidth

4

Image Width

biHeight

4

Image Height

biPlanes

2

biBitCount

2

Bits per pixel - 1,4,8,16,24 or 32

biCompression

4

Compression type - 0=RGB(No Compression), 1=RLE8, 2=RLE4,
3=BITFIELDS

biSizeImage

4

Size of image

biXPelsPerMeter

4

Preferred resolution in pixels per meter

biYPelsPerMeter

4

Preferred resolution in pixels per meter

biClrUsed

4

Number of entries in the colour map that are actually used

biClrImportant

4

Number of significant colours

There you go.... a full description of the header information for a bitmap
file - so from the first 54 bytes we can determine the file size...image
format..width...height etc...which can be extremely useful. So lets create
a structure to represent all this data and read it in...and spit it out into our
text file and see what we have:

Well there you have it.... all the header information has been read in... its
not so bad when you use a structure and just read it in, in one big go :)
We can see from our output file that we have an image which is 8 pixels wide and
8 pixels height....its compression value is 0...which tells us that there is no
compression. Using this information we can determine what bmp file we have
on our hands. Of couse we created the custom 'cross.bmp' file here....so
this is just confirming what we already know...but try it with some other bitmap
files...it will give you its secret info of what it is.

On the coding front...those who are sort of new to coding will notice a few
lines which will scare you...those are the lines which contain
'#pragam pack(1)'...which makes sure our structure
data follows each other...so exactly after we do a character data, the following
data will be exactly after it.....aligning on 1 byte boundaries.

Let me give you an example:

struct
stNotPacked

{

char a;

int z;

};

// sizeof(stNotPacked) == 8 bytes

#pragmapack(1)

struct
stPacked

{

char a;

int z;

};

#pragmapack()

// sizeof(stPacked) == 5 bytes

As the default for structures in C is to align the data to the following data
type, so in the above example where we would have a char (1 byte) followed by a
int (4 bytes)... the char is padded with 3 empty spaces so the int is aligned on
a 4 byte boundary.

Note: The alignment of data is compiler and platform
dependent - so be sure to check if you compiling with OS or Linux etc for
example.

Hmmm...well all we have to do now is read in the pixel data....those red,
green, blue value things....you haven't forgot have you...hehe.. But how
do we find in the file where they are? Well the "iOffsetBits" value we
read in from our header data tells us exactly that. It tells us the
location of the colour bytes. In the example above note that its 54
bytes...and remember or header data is also 54 bytes...which tells us that our
image data comes exactly after or header data in this case.

Where should we put all this data though? Well I thought I'd create a
structure called stImage which we can use to hold or image data....and it will
keep hold of only the essential information. Storing an array of rgb
values.... but I'm going to store the colour information with 8 bits (or 1 byte)
per index colour (red, green and blue = 24 bytes). So if we read in a 16
bit bitmap later on, and we decide to store the image in here, we'll convert it.

Be careful with the above error, as it won't show in our example as its 8x8
pixels, so it aligns on the 4 byte boundary - but on occasion you'll open an
image which isn't aligned, and you'll wonder why! But don't worry I've
added 2 lines in which checks for it, using the % modulus operation - which
tells us how many remainders there are

Can you see the resemblance in our raw text output with our cross.bmp image?
Well its only to check that where making progress and that we haven't made any
drastic mistakes.

Whahooo...well we have our image more or less, there is one more detail about
flipping which I'll mention in a sec.... but with what we have here we could
create a nice re-usable function called Load24BitBitmap(..) ....

bool Load24BitBitmap(stImage* pImage, char* szFileName);

Where it would return bool true if all went okay, and false if an error
occured. Of course you'd add error checking to your image loading function
so you would check the bitcount and that the image starts with 'B' and 'M'
etc... but we'll get there.

Upside down? You can't really see it in the txt output that I've shown
above... but our image is in fact flipped upside down... so the very first pixel
we read in, is the bottom, and the last row pixel bytes we read in, is the top
of our image. So to fix this, all we have to do is flip our y value as we
read in our data and it will fix it, as show:

for(unsignedint h=0; h<ImageBits.iHeight; h++)

{

for(unsignedint w=0; w<ImageBits.iWidth; w++)

{

// So in this loop - if our data
isn't aligned to 4 bytes, then its been padded

// in the file so it aligns...so
we check for this and skip over the padded 0's

// Note here, that the data is
read in as b,g,r and not rgb as you'd think!

unsignedchar r,g,b;

fread(&b, 1, 1, fp);

fread(&g, 1, 1, fp);

fread(&r, 1, 1, fp);

//** Flips our data so its the
correct way around - note the off by one **//

So with that one change added we can throw all the code into a nice function
and give it a run.... Putting our bitmap reading code into bitmap24.h/.cpp we
find that our main.cpp function suddenly becomes really empty....with the
exception of our debug code it would only be a few lines long.

Its sweet isn't it.... we can now do a 1 bit....4 bit version....and work
towards creating a full library... But before we continue, I think there's
one thing we should do...we should make a GUI windows version where we can open
a 24 bit bitmap and render it to the screen in its full glory. Hopefully
it won't require to much overhead code, but it will help you believe how much
power this gives us.

By adding in some Win32 MFC code we able to create a simple Bitmap Loader
application, which we can use to test our 24 Bit Bitmap Loader Function we
created earlier. A screen shot of the application can be seen on the
right.

1 Bit Bitmap

Our next mission....if you haven't ran off..hehe....is to work on our 1 Bit
Loader code.....we've done most of the hard work now...its just a matter of
building from our previous errors. It would almost be a task of changing a
few lines from our previous code here...but for one little thing.... 1,4 and 8
bit bitmaps also store palette information, which is stored as an array of
RGBQUAD values.... the number of palette values is 2bitcount.

struct RGBQUAD // 4 bytes

{

unsigned char alpha, red, green,
blue;

};

This palette information, follows our header file data immediately.... For
our 1 bit file format here...we should have 2 palette entries...which will be
referenced by our image data. Remember our 1 bit image doesn't have to be
black and white...it can be green and blue for example....any two colours.

So the two starting points of our code are following on from before.... but
this time we'll create a 1 bit Loading function.

code: DownloadSourceCode

bool
Load1BitBitmap(stImage* pImage, char*
szFileName)

{

}//
Load1BitBitmap(..)

// Program Entry Point - Where we always start!

int__stdcall WinMain(HINSTANCE, HINSTANCE,
LPSTR, int)

{

stImage Image;

Load1BitBitmap(&Image, "cross.bmp");

}//
End of main()

In creating our new Load1BitBitmap(..) function....the two main
conciderations are the alignment of data so its width is on a 32 bit
boundary...and we skip any padded bytes....second, is the reading in of our
palette data, and using it for a reference.

Now I read the data in, and store it as a 32Bit ARGB value.....so after we've
read in each pixel value and discovered its colour, we can delete the palette
data in this case. The first part's the same...reading in this header
data... but then straight after this, we get our palette data. The tricky
bit of code to really step through...or maybe print out and look at for a while,
is the inner loop, which does a bit of bit shifting so that we take each bit and
get its value.... as we can't read in 1 bit at a time...we the smallest value we
can read in, is 1 byte (8 bits)...and hence for each 8 bits...we do bit wise
operations to get the data.

For the demo's...I did a simple square black and white demo image...and I
also tested the code with a 33x8 black and white image....just to make sure the
alignment code was okay. Again, because I'm using simple test images, it
means I can single step through the code, and compare with what values I
expect...and what values I have.

From 1 bit to 4 bit isn't so bad....and 8 bit should be easy....as we read in
1 byte at a time...so where in fact reading in a pixel at a time and finding its
palette value as we go along.

{Under Construction}

Visitor: 9534626 &nbsp{ 209.237.238.175 }

Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.