http://GameProgrammer.Com

Programming

GP Mailing List
     Thread Index
     Date Index

ATXGPSIG List
     Thread Index
     Date Index

Google
>

Home

Wise2Food



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

More how-to: The Windows BMP File Format



=================================================================
To SUBSCRIBE or UNSUBSCRIBE please visit:
http://gameprogrammer.com/mailinglist.html
=================================================================
The Windows BMP File Format
I read lots of comments on the "one-dimensional" stuff I put in before. 
That's the reason why I put it in here. Thanks for all your comments. As you 
will see, future testing will show you when multiplication is faster and when 
bit shiting is faster. With today's processors, it's almost impossible to 
predict. I'll get to that later.

OK, this time I'm focusing on the Windows BMP File Format. It's a common 
enough format and relatively easy to program, in comparison to GIF and JPEG, 
which still give me nightmares. Many compilers can open up and read these 
files for you, but I'm more interested in what exactly the compiler does when 
it reads those files. And, if possible, I'd like to read it faster than the 
compiler can (I've beaten some BMP readers, but others I've only equalled. 
I've never surpassed the Windows 98 BMP reading routine. Of course, my code 
isn't that much optimized...).

This is again, easy step-by-step work. If you are familiar with the concepts 
I'm about to deal with, you probably don't need to read this e-mail. Besides, 
this e-mail is LONG. My advice to you if you want to read this: don't read it 
all at once. You'll go crazy that way.

Once you open a file, you must be familiar with it's format. Most files have 
headers, bodies, and some have footers. Headers store information about the 
file, and often times have identification (make sure that the BMP file you're 
reading is actually in BMP format). The body (or bodies, depending on the 
format) consists of the information we're most interested in: the picture we 
want to load. Some formats also have a footer, telling you that the file is 
done. Footers are most useful in multi-body files, and they also may store 
information not required to display the file (TGA comes to mind...).

That's all great and nice, but what is the BMP header composed of? Well, 
first of all, the BMP file format has two headers: BITMAPFILEHEADER, and 
BITMAPINFOHEADER. BITMAPFILEHEADER is the first thing encountered in the 
file, and it contains BMP identification and a pointer to where the 
information begins. BITMAPINFOHEADER immediately follows the first header and 
contains information about the image: resolution, type, compression type, 
etc. Here is what they look like:

Windows BITMAPFILEHEADER file header
Offset  Size    Name        Description
0       2       bfType      ASCII "BM"
2       4       bfSize      Size in bytes of the file
6       2       bfReserved1 Zero
8       2       bfReserved2 Zero
10      4       bfOffBits   Byte offset in files where image begins

*Quick note: Size of 2 refers to word (unsigned short) and size of 4 refers 
to double word (unsigned long).

This header starts out with BMP identification, bfType. These are just the 
ASCII letters B and M. This is useful if you want to load a graphics file, 
but you don't know what format it is. The "BM" marker can be used to tell you 
that it is a BMP/DIB file. I have learned through experience to not use 
bfSize. Many applications set this to some weird number or zero. I always set 
bfSize correctly whenever I write any BMPs using my applications, it's just 
that I don't use bfSize for loading purposes - it could be way off. 
bfReserved1 and bfReserved2 are reserved for future versions of the BMP file. 
Don't assume that it is zero - since it is not used for anything, many 
applications set bfReserved1 and bfReserved2 to a random number. I remember 
staying up late for weeks because my BMP identification routine didn't work - 
it seemed simple enough, it was composed of three tests: 1) Does it have a BM 
at the beginning? 2) Is it Windows or OS/2 Version (get to that later)? 3) 
Are bfReserved1 and bfReserved 2 zero? The last one was my error, for the 
reserved spots were just random numbers. Don't make the same mistake. Here's 
the next header:

Windows BITMAPINFOHEADER image header
Offset  Size    Name                Description
14      4       biSize              Size of this header, 40 bytes
18      4       biWidth             Image width in pixels
22      4       biHeight            Image height in pixels
26      2       biPlanes            Number of image planes (must be 1)
28      2       biBitCount          Bits per pixel: 1, 4, 8, 24
30      4       biCompression       Compression type (below)
34      4       biSizeImage     Size in bytes of compressed image, or zero
38      4       biXPelsPerMeter Horizontal resolution in pixels/meter
42      4       biYPelsPerMeter Vertical resolution in pixels/meter
46      4       biClrUsed           Number of colors used (below)
50      4       biClrImportant      Number of "important" colors
54      4*N     bmiColors           Color map

biSize is set to 40 when the BMP is the Windows version, and set to 12 when 
the BMP is the OS/2 version. I'll get to the differences between the Windows 
BMP and the OS/2 BMP later. The resolution of the header is defined in 
biWidth and biHeight. biPlanes requires some explanation. Back in the days of 
4-bit and 1-bit BMPs, this variable may have been set to something other than 
one. Basically, you can group pixels not only into width (x) and height (y), 
but also planes. Say you divided your bitmap into four planes. This way, you 
can divide your bitmap into four equally sized chunks. The only use of this 
that I have every used was to divide a large image into 64K chunks (planes) 
for faster loading into VESA. However, for our purposes, always set this to 
one. Another possible use of this would be to divide a 24-bit image into 
three planes: Red, Green, and Blue. I've never used it that way, and besides 
- BMP has its own kooky way of storing 24-bit images. biBitCount is set in 
the following manner: 1 = 2 color, 4 = 16 color, 8 = 256 color, and 24 = 16.7 
million color. The reason the numbers are so weird have to do with the bits 
(a better explanation will be given in a future e-mail). biCompression is 0 
for uncompressed images, and 1 for RLE compressed images (more on that 
later). biSizeImage is useful if you want to load the BMP into memory before 
processing it. Of course, like biSize, this number can be very inaccurate. I 
usually don't trust biSizeImage. biXPelsPerMeter and biYPelsPerMeter I never 
use. In fact, those would only be useful in printing. Many applications set 
these two to zero, as they are often neglected. biClrUsed tells how many 
colors are defined in the color map. This is 0 for 24-bit images, for they 
have no color map. Many applications will set this to 256 in 8-bit graphics 
even if less colors are used. biClrImportant was meant to be used  for 
adapters that couldn't display 8-bit graphics when an 8-bit BMP file was 
given. This tells how many of the colors are "important," or are required in 
order to view the image correctly. Of course, this assumes that the color map 
starts with the most important colors and ends with the least important - not 
an easy thing to do. I usually set this equal to biClrUsed. The color map 
I'll get into later.

Another thing to note is that OS/2's second header is slightly different. 
Here's its definition:

OS/2 BITMAPCOREINFO image header
Offset  Size    Name        Description
14      4       bcSize      Size of this header, 12 bytes.
18      2       bcWidth Image width in pixels.
20      2       bcHeight    Image height in pixels.
22      2       bcPlanes    Number of planes (must be 1)
24      2       bcBitCount  Bits per pixel (1, 4, 8, or 24)
26      3*N     bmciColors  Color map

It's pretty self-explanatory. As you can see, OS/2 headers (and color maps) 
are a lot more compact. However, there is a downside: OS/2 BMPs are always 
uncompressed. 

OK, that's all nice and good, but how do you load a header? Here's a code 
chunk that does exactly that. Don't worry, I'll go over it:

struct Picture {
    unsigned short x,y;
   unsigned char *picture;
    unsigned char *palette;
} Pict;
struct BMP1 {
/******FILE HEADER****************/
        unsigned short bfType;          // Must be set to 'BM'
        unsigned long  bfSize;          // The size of the file in bytes
        unsigned short bfReserved1; // Reserved for future expansion
        unsigned short bfReserved2; // Reserved for future expansion
        unsigned long  bfOffBits;       // The number of bytes before bitmap
};
struct BMP1 BMPFILEHEADER;
struct BMP2 {
/******BITMAP HEADER**************/
        unsigned long  biSize;              // The size of this header: 40
        unsigned long  biWidth;             // The width in pixels
        unsigned long  biHeight;            // The height in pixels
        unsigned short biPlanes;            // The number of planes (1)
        unsigned short biBitCount;          // The number of bits per pixel
        unsigned long  biCompression;   // The type of compression
        unsigned long  biSizeImage;     // Size of image in bytes when 
compressed
        unsigned long biXPelsPerMeter;  // The horizontal number of pix/meter
        unsigned long biYPelsPerMeter;  // The vertical number of pix/meter
        unsigned long  biClrUsed;           // The number of colors that are 
used
        unsigned long  biClrImportant;  // The number of important colors
} BMPINFOHEADER;
struct BMP3 {
/******OS/2 BITMAP HEADER*********/
        unsigned long   bcSize;             // The size of this header: 12
        unsigned short  bcWidth;            // The width in pixels
        unsigned short  bcHeight;       // The height in pixels
        unsigned short  bcPlanes;       // The number of planes (1)
        unsigned short  bcBitCount;     // The number of bits per pixel
} BMPCOREINFO;

int LoadBMP(char *file, Picture *P) {
    FILE *fp;
    long index;
   int x,type,cnt,c,d;  // type: 0=OS/2, 1=Windows
   double a,b;
    unsigned char reed;
   if((fp=fopen(file,"rb"))==NULL) {
      return 1; // Error opening file
   }
   fread(&BMPFILEHEADER,sizeof(BMPFILEHEADER),1,fp);        // <------note 1
   fread(&BMPINFOHEADER.biSize,sizeof(BMPINFOHEADER.biSize),1,fp);
   if(BMPINFOHEADER.biSize==40) { // Windows
   fread(&BMPINFOHEADER.biWidth,36,1,fp); // 36 = 40 - 4
       P->x=BMPINFOHEADER.biWidth;
       P->y=BMPINFOHEADER.biHeight;
       type=1;
   }
   if(BMPINFOHEADER.biSize==12) { // OS/2
       BMPCOREINFO.bcSize=BMPINFOHEADER.biSize; // 12
         fread(&BMPCOREINFO.bcWidth,8,1,fp); // 8 = 12 - 4
       P->x=BMPCOREINFO.bcWidth;
         P->y=BMPCOREINFO.bcHeight;
       type=0;
   }
   if((BMPINFOHEADER.biSize!=40)&&(BMPINFOHEADER.biSize!=12))
    return 5; // Unknown BMP type or corrupted header...

As you can see, I defined a few structs known as BMPFILEHEADER, 
BMPINFOHEADER, and BMPCOREINFO, which are the three various types of headers. 
At note 1, the first header, BMPFILEHEADER, is read directly from the file. 
Now, we have a dilemma: is it a Windows BMP or an OS/2 BMP? We can't tell 
until we read biSize (or bcSize, if it is OS/2). Therefore, before reading 
the entire second header, we read biSize. If it is 40, we proceed with 
reading the rest of the header. If it is 12, we copy biSize into bcSize, then 
copy the rest of the header. Note that when the rest of the second header is 
read, it starts with biWidth/bcWidth, since biSize/bcSize is already read. 
I've also declared a struct known as Picture, which I use to store the 
picture. The resolution is stored in P->x and P->y. If biSize/bcSize is NOT 
equal to 40 or 12, the procedure returns with an error condition of 5. OK, 
that was easy. Now for color-maps.

Color maps define the palette of the image. I will only be dealing with 
24-bit files (which have no color map) and 8-bit files (which do) to simplify 
things. Besides, why would you ever use a 4-bit image nowadays? Never mind. 
The color map directly follows the second header. For 8-bit images, since 
there are 256 colors, 256 entries are entered (or the number stored in 
biClrUsed). The Windows and OS/2 methods of describing color maps are 
slightly different. Here they are:

Windows RGBQUAD color map entry
Offset  Name            Description
0       rgbBlue         Blue value for color map entry.
1       rgbGreen        Green value for color map entry.
2       rgbRed          Red value for color map entry.
3       rgbReserved Zero

OS/2 RGBTRIPLE color map entry
Offset  Name        Description
0       rgbtBlue    Blue value for color map entry.
1       rgbtGreen   Green value for color map entry.
2       rgbtRed     Red value for color map entry.

As you can see, each variable is a byte (unsigned short) in length. However, 
there are a few problems. When writing palettes to the DAC (Digital to Analog 
Converter - to the screen), you have to write it in the order or Red, Green, 
and Blue. So, if you're going to write directly to the palette, you need to 
reverse the order (Blue, Green, Red to Red, Green, Blue). Another problem: 
BMP color intensities (the values stored in the BMP) range from 0 to 255. VGA 
color intensities range from 0 to 63 (unless you have a VESA that supports 0 
to 255), so you need to fix that as well. Another thing: when I made the 
code, I stored the palette into a 256*3 byte array. Since palettes can be 
considered two-dimensional arrays (R/G/B and color entry number), I converted 
it into a one-dimensional array. Essentially, this is what the array looks 
like:
[R1 G1 B1 R2 G2 B2... R255 G255 B255] <---- those ranging from 0 to 63. This 
is all done in the following code fragment:

        if((P->palette=new unsigned char[256*3])==NULL) {
            fclose(fp);
           return 4; // Not enough memory for palette
        }
        for(index=0;index<256;index++) {
            P->palette[(int)(index*3+2)]=fgetc(fp)>>2;
            P->palette[(int)(index*3+1)]=fgetc(fp)>>2;
        P->palette[(int)(index*3+0)]=fgetc(fp)>>2;
          if(type) x=fgetc(fp);
        }

The first four lines declare memory for the palette, or, failing that, 
returns an error code of 4. This is done for if the new command fails, it is 
equal to NULL, and the if command is true. The palette-reading routine 
assumes 256 colors. the ">>2" converts the range of the BMP palette (0 to 
255) to the allowable range on a normal VGA DAC (0 to 63). If you want to 
understand how the point definitions work, remember your one-dimensional 
arrays: index is y (the 2nd dimension that we're converting), and x is the 
number constant that is added (2, 1, or 0). This rather odd pointer system is 
required for we're converting the BMP palette entry (order: Blue, Green, Red) 
to the palette order (Red, Green, Blue). The last if command requires some 
explanation. type is a variable I use as a flag. If it is 0, then we are 
dealing with an OS/2 BMP. If it is 1, then we are dealing with a Windows BMP. 
This was defined in the header code from above. Remember that Windows palette 
entries have an extra, reserved byte. OS/2 does not. Therefore, if type is 
not 0 (it is 1), then we are dealing with a Windows palette entry, where we 
have an extra byte we can scrap. However, if type IS 0, then we are dealing 
with OS/2 palette entry, where we don't have an extra byte to scrap, so the 
command "x=fgetc(fp);" isn't executed.

Immediately following the color map (if there is one) is the image data. The 
following applies to both 8-bit and 24-bit images, compressed or 
non-compressed images: images are stored in the order left to right, bottom 
to top. When drawing directly to video memory, this poses a problem, for 
images on the screen are stored left to right, but top to bottom. BMP'S ARE 
SCREWED UP!!! (Sorry, that's just my personal opinion.) Therefore, if we are 
to load the image in the way that we would write an image to the screen, we 
would have to unscramble the image! This isn't THAT hard to deal with. Once 
again, with one-dimensional arrays, just have the counter of x go from 0 to 
P->x (maximum x resolution), and y go from P->y (maximum y resolution) to 0. 
Yes, this is one way to do it, but I have a much faster way to do it. Here's 
how you load 8-bit, uncompressed images:

                for(index=(P->y-1)*P->x;index>=0;index-=P->x)
                for(x=0;x<P->x;x++)
                    P->picture[(index+x)]=(unsigned char)fgetc(fp);

That wasn't THAT bad. The pointer may throw you off. Basically, I've 
simplified (y*P->x+x) into (index+x), if index=(y*P->x). Let's say you have a 
320x200 image. In this case, P->x=320, and P->y=200. The first y position you 
will calculate will be 199 (0 to 199 = 200 rows). 199*P->x, or 199*320 = the 
last row on the screen, with no x offset. That is added (index+x) later. 
However, the last row on the screen is the first one defined in the BMP file. 
This y value is decremented in order to fit the order in the BMP file. By the 
way, I skipped the definition of P->picture. It is defined as a 
one-dimensional array of size P->x*P->y earlier in the file.

Loading 24-bit images can be done in the same way, except that each pixel is 
now 3 bytes (24 bits). Like the palette, each pixel is stored backwards - in 
the order Blue, Green Red. Each pixel is side by side, and like 8-bit images, 
it goes left to right, top to bottom. This time, I did a straight conversion 
from two to one-dimensional arrays:

          for(index=P->y;index>=0;index--)
            for(x=0;x<P->x;x++) {
                    P->picture[(index*P->x+x)*3+2]=fgetc(fp);
                    P->picture[(index*P->x+x)*3+1]=fgetc(fp);
                    P->picture[(index*P->x+x)*3+0]=fgetc(fp);
            }

I didn't define a "y" variable, so I just used index. If it helps, substitute 
index with y. The offset to the memory location where the pixel definition 
starts is (index*P->x+x)*3. This position is the red position, a byte later 
is the blue position, and another byte later is the green position, hence the 
backward definitions. This is what the array ends up looking like:
[R1 G1 B1 R2 G2 B2... B(P->x*P->y)]
I guess if you wanted to be completely accurate, I converted a 
three-dimensional (color attribute, x, y) array into a one-dimensional. 
Strange.
For those of you who don't know: 24-bit images are where 3 bytes define each 
and every pixel. Each byte defines a certain attribute of the color. There 
are three attributes (one for each byte): red, green, and blue. Every color 
can be made by mixing and matching different amounts of these colors. That is 
how 24-bit images work. If you didn't know that before, this should be 
helpful in understanding the above.

Now, I did install support for 8-bit RLE compression, so I'll go over it. RLE 
compression is (as far as I know) the only compression supported by the BMP 
version, and is unavailable in the OS/2 version. RLE stands for Run Length 
Encoding and is perhaps the simplest (and least effective) compression 
available for graphics file formats. It takes advantages of repeated pixels 
in bitmaps. For example, let's say we needed to load the following numbers in 
this order:
57 69 17 49 49 49 49 49 10
For the first three pixels, there would be no compression. However, the next 
five numbers are exactly the same, so RLE compression takes advantage of 
this. The stream would end up looking like this:
57 69 17 <<05 49>> 10
Where the <<'d text is is the compression data. This is a significant 
improvement - what took 9 numbers to define fit in 5 numbers. The first 
number represents how many times the number is repeated, and the second 
number is the number to be repeated. However, it isn't quite this easy. How 
do you know when you're supposed to repeat a number and when you're not 
supposed to? In the above example, the << and >> point out the compression 
data. There is no such thing in the BMP language. You could easily see the 57 
and 69 and say that you're supposed to repeat the number 69 a total of 57 
times. Obviously, you need some sort of signal to tell you when you're 
dealing with literal (uncompressed) numbers, or RLE-compressed numbers.

Well, let's assume that the program always assumes that you have RLE 
compressed numbers. So the following stream:
5 18 8 12 3 73
decompresses to:
18 18 18 18 18 12 12 12 12 12 12 12 12 73 73 73
Now what if you wanted a few literal numbers tagged on? What if you wanted to 
add the numbers 48 29 17 to the above, but only once each? Well, if you use 0 
as a first number, this can be used as a signal for literal numbers. Why does 
this work? An RLE count number of 0 would be meaningless - you would be 
defining a number that is repeated zero times. So, such a thing would never 
be defined. Therefore, since it isn't used in the RLE compression itself, it 
can be used as a signal for a set of literal numbers. The following stream:
5 18 8 12 3 73 0 3 48 29 17
decompresses to:
18 18 18 18 18 12 12 12 12 12 12 12 12 73 73 73 48 29 17
This can be confusing, but look. You have your old familiar RLE stream: 5 18 
8 12 3 73. Then you have your literal marker (0). The number following it is 
the total number of literal numbers you want to define (3 of them). Following 
that, you have your three numbers. So, let's look at this again:

    * The first number tells how many times to repeat the second number, IF 
the first number isn't zero.
    * If the first number is zero, the second number tells how many literal 
numbers follow, then the literal numbers follow the second number.

This is essentially how RLE compression works in BMP files. There are a few 
special exceptions (I think Microsoft made them to confuse the programmer). 
There are 3 special codes. If the first AND second number are 0, this signals 
the end of the row. If the first number is 0 and the second number is 1, then 
this signals the end of the bitmap. If the first number is 0 and the second 
number is 2, then this signals what is referred to as a position delta. This 
special marker is followed by two more numbers: xx and yy. It means that the 
bitmap is to continue xx pixels to the right and yy pixels down.
This makes a number of requirements. You can only RLE compress one line at a 
time. For example, if you have a single color bitmap that has the resolution 
10x10, you cannot say 100 1, meaning that you would repeat the single color 
100 times. This would be useful, but it isn't supported in RLE compression. 
You have to make a declaration for each line (10 1 10 1 10 1 10 1 10 1...) 
with 00 00 (the end of row marker) in between each declaration. Also, since 
the special codes are 0, 1, and 2, you MUST declare that you have more than 2 
pixels to define as literal (correct me if I'm wrong). Look at the following 
compressed line:
00 02 47 53
This means that you're going to define two literal numbers, which are 47 and 
53, right? WRONG. 00 02 is a position delta marker, so you would actually 
continue the bitmap 47 pixels to the right and 53 pixels down. Don't make 
this mistake when you write your own BMP-making programs.
Here's a rather important note: all RLE compressed data is 8-bit (one byte, 
or unsigned short) in length. You cannot say 1024 74 (repeat the number 74 a 
total of 1,024 times) for 1,024 is not an 8-bit number. So, you are limited 
to numbers between 0 and 255. 
This is how RLE compression works in BMP files.
There's one more quirk in the literal mess. In the BMP file format, all 
definitions are even-byte padded. This means that each and every literal/RLE 
definition must be composed of a total of bytes that is divisible by 2. This 
is not a problem with RLE definitions (you have two bytes, the count and the 
number to be repeated, and two is an even number). However, take the 
following literal definition:
00 03 13 57 69
You have a total of five bytes, and five is NOT an even number. You need to 
stick in another byte. So you NEED to do the following:
00 03 13 57 69 00.
Now you have a six-byte definition, and six is an even number. The extra zero 
at the end does NOT signal another literal/special definition. It is just 
added so that the definition is even-byte padded. WHY did Microsoft do this? 
Well, I can make a guess. If you read the file in two-byte (word) chunks, it 
would be easier if the beginning of each definition was at the beginning of a 
chunk. Since I programmed this the way I did, it's just another obstacle.

 Here's a code snipit that handles the RLE mess:

        if((type)&&(BMPINFOHEADER.biCompression)) {
            index=(P->y-1)*P->x;
               while(index>=0) {
                x=0;
                        while(x<P->x) {
                        reed=fgetc(fp);
                         if(!reed)  { // Literal or special
                              c=fgetc(fp);
                              if(c==2) {
                                x+=fgetc(fp);
                                 index-=(P->x*fgetc(fp));
                              }
                              if((c!=0)&&(c!=1)&&(c!=2)) {
                                    for(cnt=0;cnt<c;cnt++) {
                                            P->picture[index+x]=(unsigned 
char)fgetc(fp);
                                        x++;
                                    }
                                a=(float)c;
                                    if(modf(a/2,&b)) {
                                        d=fgetc(fp);    // even byte padding
                                 }
                              }
                         } else {   // RLE
                              d=fgetc(fp); // what to store
                              for(cnt=0;cnt<reed;cnt++) {
                                P->picture[index+x]=(unsigned char)d;
                                   x++;
                              }
                         }
                    }
                    index-=P->x;
               }
The first line brings up an important point: compression is only available in 
Windows BMPs (type==1). Then, I made a few while loops that work much like 
the 8-bit for-loops that read the palette and uncompressed images. index 
again is the offset to the correct row, and x is the x offset into that row. 
A byte is read into reed (I named it that to avoid confusion with the command 
"read"). Now, if reed is not equal to zero, we have a RLE number (the number 
that says how many times to repeat the following byte). However, if it is 
zero, we have either a literal marker or a special marker. Let's look into 
the literal/special loop (!reed). The next byte is written into c. If c is 0, 
1, or 2, we have a special marker. No action is done if c is the special 
marker 0 or 1 (if you get a 1 before the end of the bitmap, then something is 
wrong with the bitmap - 1 signifies the end of the bitmap). If c is the 
special marker of 2, then we have a position delta situation. The next byte 
is the x offset, and the byte after that is the y offset. The x offset is 
added directly to x and the y offset is calculated for y. However, if c is 
NOT 0, 1, or 2, we have a literal situation. The number in c already has the 
number of literal pixels we have to define, so the pixels are read directly 
into P->picture. Since neither RLE compression nor literal definitions can go 
beyond a row, only the x position is added (in a correctly defined BMP, there 
is at least one RLE/literal definitions per row). The modf() test that 
follows tests to see if c (which is now stored in the float variable "a") is 
divisible by two. Now think: modf returns the decimal part of a number (.25 
in 1.25). So, if a number (like the number of bytes in the definition) is 
divisible by two, then it has no decimal part (6/2 is 3.00). Therefore, the 
if command is false - the definition is already even-byte padded, there is no 
need to read an extra byte so that it is even-byte padded. However, if that 
number is NOT divisble by two, then it has a decimal part of .5 (7/2 is 3.5) 
- the if command is true - the definition is NOT even-byte padded, there IS a 
need to read an extra byte. That's the end of the literal/special loop, and 
the RLE loop begins. The number to be repeated is stored in d, and the times 
to repeat the number is already stored in reed, so a simple for-loop 
uncompresses the RLE encoded data.

And voila! You have read a BMP! I have attached the full version of BMP.H. It 
has a number of limitations:
    * it only loads BMPs - I didn't include any way to write BMPs, but you 
should be able to figure out how to do that. I suppose if enough people 
wanted me to, I could send out a WriteBMP procedure.
    * it was designed for a DOS 16-bit compiler - This means that is was 
designed for files less than 64K in size. This doesn't mean that it can't be 
used on a 32-bit compiler. In fact, it can be easily transcribed for use with 
ANY C++ compiler.
    * it's not fast - I designed the routine NOT for speed or size. I 
designed it so that the concepts I pointed out would be clear (or as clear as 
they can be in source code). 
    * there is no 'BM' test - remember this? The first two bytes of a BMP 
always reads 'BM', and I didn't test for this. I just assumed that you would 
not feed into it any GIF files or anything else like that.


Other than that, have fun with the code. You probably don't need it with your 
automatic BMP readers, but it's good to know what those do. Until next time!
-Ender Wiggin
=================================================================
The GameProgrammer.Com mailing list is for the open discussion
of any topic related to the art, science, and business of
programming games. This list is especially tolerant of beginners.
We were all beginners once

To SUBSCRIBE or UNSUBSCRIBE please visit:
http://gameprogrammer.com/mailinglist.html