Exploring the Internal Structure of a 24-Bit Uncompressed Bitmap File

The Reason for This Article

I've recently had some experience dealing with image editing. Since 1997, I've wondered what a bitmap file format looks like inside. Because there are API functions that load and display bitmaps, I never actually got into the detail of the bitmap file's internal structure. Recently, I was able to get some new information on the internal structure of bitmap files. So, I am sharing my experience with everyone.

I am sure most people know this already. This article deals the simplest form of bitmap—a 24-bit, uncompressed bitmap file. There is no RGBQUAD structure or compression. It should effectively demonstrate the internal structure of a bitmap. For other related topics, such as color table and compressions, they can be easily understood based on the stuff in this article. First, let's begin with the data structures that describe the bitmap.

Bitmap File Internal Structure Overview

A bitmap file consists of four different parts. The first structure is the BITMAPFILEHEADER structure. You can check the Visual Studio's Help file for this structure:

"bfType": The type of this file usually should be two letters, "B" and "M," as two bytes (combined, it is one WORD with the value "B" as the upper 8 bits and "M" as the lower 8 bits).

"bfSize": The size of the file, in bytes. If you right-click a bitmap file, then select Property and check the size of the file (not the actual size on the disk), it should be the same as what this variable contains. This variable is extremely useful. I'll show you later.

"bfReserved1" and "bfReserved2": Useless; should be 0 at all times.

"bfOffBits": This variable indicates how many bytes are from the beginning of the file to the actual pixels. On my computer, it always returns at 54 (14 bytes for BITMAPFILEHEADER and 40 bytes for BITMAPINFO). Other rumors say this variable should be only 40. I guess it should be right too for some cases. But, on my computer, this is always 54.

After this structure, you will encounter another structure, called BITMAPINFO, at least on my computer. Every 24-bit, non-compressed bitmap is written to the disk like this: The first part of the bitmap is the BITMAPFILEHEADER and the next part is the BITMAPINFO. Visual Studio describes BITMAPINFO like this:

This is actually two structures put together with the name of BITMAPINFO. When you process this structure, you really don't want to process them all together using a single fread(). Instead, process them one at a time (I guess any programmer would know that).

First, let me explain simply what BITMAPINFOHEADER is. It is described like this in Visual Studio:

The size of the structure. Basically equal to size of(BITMAPINFOHEADER).

It should be 40.

biWidth

The width of the image

biHeight

The height of the image

biPlanes

The number of planes of the bitmap.

In our case it is 1; I guess in most cases it should be 1.

biBitCount

The number of bites per pixel: 1bit (two colors black and white), 4 bits (16 colors using a color lookup table stored in RGBQUAD), 8 bits (256 colors using a color lookup table stored in RGBQUAD), and finally 24 bits (2^24 colors using 3 bytes, each for red, green, and blue).

In our case it is 24.

biCompression

0 denotes no compression, 1 to 3 denote three different RLE compression methods: 1 for RLE 8 bits compression, 2 for RLE 4 bits compression, and 3 for bit fields. I don't know much about compressions (except they save spaces).

In our case it is 0.

biSizeImage

The total size of the image (the number of bytes from the first pixel of the file to the last pixel of the file). Note: This variable may not be equal to biWidth times biHeight.

biXPelsPerMeter

The number of pixels in one meter in x-axis.

This is 0 in our case.

biYPelsPerMeter

The number of pixels in one meter in y-axis.

This is 0 too in our case.

biClrUsed

The number of colors used in this bitmap.

In our case, it is 0.

biClrImportant

The number of colors that is important for this bitmap.

In our case, it is 0.

After the BITMAPINFOHEADER structure comes a RGBQUAD variable; this is a color table. It is commonly either 16 colors or 256 colors, depending on how the file is specified in BITMAPINFOHEADER's biBitCount. In our case, it does not exist in the file because we are using a 24-bit bitmap. This article gives you a simple idea what the bitmap looks like inside, without using any Win32 API functions or MFC class methods. Once the detail of this simple idea is known, other situation will be explained by extending this simple idea with some mathematical imaginations and some creativity.

After BITMAPINFO, the remainder of the file is used to store pixel points. It is a linear array of bytes occupying the rest of the file. For 24-bit uncompressed, this array of bytes seems to be nothing more than just RGB color values. That is not true at all; for certain situations, each scan line of the bitmap is separated by 1, 2, or even 3 zeroes. These are junk bytes. (I don't know what else to call them.) The bytes shouldn't be read and used as pixels. If you do use them as pixels, it will mess up the orders of pixels and generate a distorted image. Man, did I learn this lesson the hard way.

Just One Last Thing about Bitmaps

I don't know who set the standard or why, but the image is usually stored in inverted order. That is, the image is stored so that the top left corner is stored at the end of the file, and the bottom right corner is stored as the first pixel after BITMAPINFO. Thus, the starting pixel is the lower-right corner of the bitmap, and the order of all pixel RGB bytes are B-G-R. The following table illustrates this idea:

Image 1 is the actual image when you open a bitmap file with the Windows Paint program or any other image editor. Image 2 is a false image of Image 1, stored as a file. The reason for the word "false" is that the RGB bytes are not inverted. Image 3 is the actual image in the file if you read the file from the starting pixel to the end of file. It is like writing "I am an idiot!" backwards—"!toidi na ma I". So if a bitmap's pixels bits are like this:

(255, 0, 0) (0, 255, 0) (0, 0, 255)

in the real file, the pixels are stored like this:

(255, 0, 0) (0, 255, 0), (0, 0, 255) exactly backwards.

How the Pixels Should be Read from the File to Represent the Correct Image

This is where most people would fall. We now know the image is inversely stored as a file. But not many people know to make the total pixels equal an even number; junk bytes (useless bytes) are used to fill the number of pixels to be an even number. I don't know if this is the correct analogy. As far as I have observed, this is true. For example:

This is a simple 31 X 31 pixels^2 24-bit uncompressed bitmap. Its total size on disk is 3,030 bytes. Let's work out some mathematics: 31 * 31 * 3 = 2883 bytes (Note: times 3 in the end because we are counting bytes, and each pixel consists 3 bytes; each byte denotes R, G, and B values), plus two structures, BITMAPFILEHEADER and BITMAPINFOHEADER (14 + 40 bytes = 54 bytes). The total size of file ideally should be: 2883 + 54 = 2937 bytes. Compare to 3,030 bytes, there are 93 extra bytes. These are the junk bytes. They could be found at the end of each pixel line (I would refer these pixel lines as scan lines).

As you use various sizes of 24-bit, uncompressed bitmaps, you will see these junk bytes sometimes can be only one "00" in each scan line, or sometimes it is two "00"s in each scan line, and sometimes it is three "00"s in each scan line. I haven't seen four or a larger number of "00"s as junk bytes in each scan line. I guess it is not necessary to add such a large number of "00"s as junk bytes in each scan line. Anyway, no matter how many bytes are used as junk bytes, they must be ignored. And it is very easy to determine how many such bytes need to be ignored in each line. Again, simple math is used to determine the number of bytes that must be ignored:

(Total size of bitmap on disk - (width * height * 3 + 14 + 40)) / height = number of bytes needed to be ignored during each line scan.

How to Parse a 24-bit, Uncompressed Bitmap File—The Source Code

We've explored every trick used to create and possibly encrypt an image into a bitmap. It is very easy to read a 24-bit uncompressed bitmap without the help of any WIN32, MFC, or other APIs to parse such a bitmap file. The following code is how you can do it:

Final Note

When I finished writing this, I checked the Internet. There are articles that actually talk about how to load 24-bit, uncompressed bitmaps. Even though I couldn't find one that described the details as much as I have done, some of them actually give enough information for a programmer to explore and to achieve the details I have done here.

Anyway, I hope this article gives everyone some direction about how a bitmap is stored. In real life, the situation is a bit more complex because there are 4-bit, 8-bit, and 16-bit bitmaps, using compression. I hope this article can at least help people start to extend the idea to solve these complicated situations. For me, this is enough to use for designing some cool RPG games.

Good luck to everyone.

Downloads

Comments

answer to junk "00" byte(s) info. in bitmap

Posted by kid01
on 05/02/2005 02:27pm

each scan line(width)-bytes size should be multiple of 4, so
7x7 bitmap is stored as 8x7 ( 8 = 4*2)
--> there is "00" at each end of line(eol)
9x7 bitmap as 12x7 ( 8=4*2 <9 < 12=4*3)
--> there are "00 00 00" at each eol
base on this fact,
in the original program, sizing & reading method can be compacted.
I hope it will be help for you

Why RGB are stored BGR in the bitmap file

About the RGB stored in the file : its in intel format (read about Big & small indian format)
the number are stored in memory as LO-HI value

Sample RGB colrs are quad bytes 00 RR GG BB

the 00 RR will swap so they are RR 00 (named the 2 bytes it WORD1)
the GG BB will swap so they are BB GG (named the 2 bytes it WORD2)
and because it a int value then the word RR 00 & BB GG are awapes as HI LO
waht we got is WORD1 WORD2 now we need to perform a swap between them so we wll get WORD2 WORD1 and now if we display the context of WORD1 and WORD 2 we get

BB GG RR 00

and it will explain why it saved like that U can read the four bytes in each pixel and and the value with 0x00FFFFFF and U will get the correct color value (rememeber that the step will be 3 bytes step because the pixel color is saved in 3 bytes for each pixel)

EXCELLENT website!!

Wow this is really the best website in describing details of bitmap files that I have encountered so far!! and the comments added by you guys did save me a hell lot of trouble in the debugging process..

MJaybe wrong sizeof

Hi,
i just tried out to read the bitmap file header and got some strange values out when I read the BitmapInfo afterwards. Then i found out that the sizeof operator got 16 bytes for my BitmapFile Header although its just 14 big. I think this has to do how the compiler alignes the code and structures in memory. Wouldn't it be safer to hardcode the size 14 into the fread operation ?

Top White Papers and Webcasts

Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT
The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility.
Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

The mobile market is white hot. Building a well-crafted product development plan that addresses market research, strategy, design, and development will provide the greatest chance for success. Each phase of an app's lifecycle is critical to the its overall success and feeds into the next step of the process of product development for the app. This white paper examines the five key phases of mobile app creation, one by one, to understand how they work together to help create a successful mobile app.