Size Matters
Let’s start by filling in some of the gaps that were left in Section 02.03 The Basic Windows Application. When discussing the process of registering a window class and creating a window for our application, I neglected to provide any details on what information Windows actually requires from us. You’ve surely noticed that there are two types of applications running under Windows – those that occupy a window on the desktop, and those that decide to take over the entire screen. As well, you have the ability to alter the resolution of your desktop and the number of displayable colors. Depending on how you want to display your game, you have some of these options available to you as well.
In case you weren’t aware, screen resolution describes the number of pixels in width and height for the display. Common choices are 640x480 (640 pixels across, 480 pixels down – also known as VGA), 800x600, 1024x768 and 1280x1024. Color depth determines the amount of simultaneous colors available on the display. Common values are 8, 16, 24 and 32-bit color (more on this later in the article).
Now, let’s look at one of your first decisions in creating a windows application – windowed or full-screen.
Windowed Applications
Applications choosing to run in windowed-mode supply the OS with their desired window’s width and height, background color and the desired position on the desktop for the new window (amongst other things). Some other options include the application’s icon, attributes such as the ability to resize the window, menu, scrollbars, etc.
What you don’t get to choose is the screen resolution or the color depth. You see, all windows on the desktop have to live with whatever settings the user has already configured for the OS. When you think about it though, the ‘screen resolution’ for a windowed application is not really applicable; choosing the width and height of your window accomplishes the same thing (provided the user’s setting are sufficient). Color depth is an entirely different matter. This becomes important because your game needs to know how many bits are currently being used to compose a pixel’s color, and use the correct calculation. Your game is going to either have to be able to handle all of the popular choices, or restrict the user from running your game unless they adhere to your color depth recommendation. As well, if you allow the user to resize your window, be prepared to handle a ‘resolution change on the fly’.
Face it, a windowed-game can be a real pain.
Full-Screen Applications
First, we let the OS know that we’re creating a special kind of window (a popup window) that has no extra frills, and then we direct the OS to have our window occupy the entire display surface. Since we’re the only thing visible at this point, we’re free to change the screen resolution and color depth to whatever the user’s hardware can support. If the focus leaves our application, that is to say, the user switches to another application, their resolution and color depth are restored until they rejoin us. Certainly you’ve seen this behavior in action when ‘ALT-TABbing’ in and out of a full-screen application.
Design Choices and Our Game
Regardless of what choices you make on how your game is going to look, you’re going to need to know the dimensions of your screen (or client area if windowed) and the color (bit) depth. These two parameters are important because we’re going to be manipulating memory – memory that represents a display surface. We need to be able to locate the piece of memory inside of the buffer that coincides with a particular pixel’s color. The next article will get you proficient in doing just that, but for now let’s look at the effect of resolution and color (bit) depth on display memory.
Display Memory
Think of the display surface as a two-dimensional array of pixels, with co-ordinates
(0,0) indicating the top-left corner of the screen, and
(x-1, y-1) being the bottom right corner. Therefore, if we have a screen resolution of 640x480, we have 307,200 pixels visible on the screen. Since the only attribute for a pixel is its color, what needs to be stored in memory is the current color of all 307,200 screen pixels. So, how much memory do we need to store an entire screen of pixel information? Well, that depends on how much memory each pixel requires. For example, if your pixels are two bytes wide (16-bit color), you’d require 307,200 x 2 bytes (= 614,400) of memory in order to store one full screen of pixel data.
The likely scenario is that you’ll have a pointer to the beginning of a memory buffer, and will use this pointer to access the actual memory:
BYTE *lpBuffer;
Assuming that each pixel occupies one byte of memory, we can perform simple operations such as altering a given pixel’s value. First though, it should be noted that pixels are mapped to memory row-by-row. So, for a 640x480 memory buffer, here’s some examples:
// Change the first pixel color to ‘1’
lpBuffer[0] = 1;
// Change the seventeenth pixel color to ‘1’
lpBuffer[16] = 1;
// Change the fourth pixel on the eighth row to ‘1’
int x = 3; // fourth column
int y = 7; // eighth row
lpBuffer[ (640 * y) + x] = 1;
As you can see by the last example, you can calculate the position of any pixel in the buffer so long as you know the width of a row. Before we look at other pixel sizes, let’s learn how to form proper color values.
Color Composition
As you are most likely aware, video cards are capable of storing images comprised of different amounts of colors. Here''s the common list:
- *16 colors (4-bits)
- *256 colors (8-bits)
- 32,768 colors (15-bit High Color)
- 65,536 colors (16-bit High Color)
- 16,777,216 colors (24-bit True Color)
- 16,777,216 colors (32-bit True Color - only 24 bits are used for the color!)
(NOTE: The first two are special...see section 4 below)
Of course, the number of colors you are capable of having depends on the type of video card you have. One deciding factor: video RAM -- the more colors available, the more RAM is needed to store screens of pixels. Also, not all cards support all color modes...some don''t have 15-bit and/or 24-bit color modes.
Using the list above, we can tell how many bytes are needed to store a pixel at each color-depth. Let''s work backwards from 32-bit color...
24 and 32-bit Color
We specify a color by choosing the amount of Red, Green and Blue in it. If you take 24-bit color for instance, 8 bits are used for each of the three components. The higher the intensity, the brighter the color component. Since we have 8 bits for each, that gives us 256 possible reds, greens and blues. The proper order to consider them in is RGB (Red, Green, Blue). In other words, black would be (0, 0, 0), white would be (255, 255, 255), green would be (0, 255, 0), and so on. By mixing together different combinations of colors, you can create any color in the spectrum -- using (255, 255, 0) would give you yellow, for instance (red+green). We put the values of each component together to form a single value;
0x000000(0) = black,
0xFFFFFF(16,777,215) = white,
0x00FF00(65,280) = green.
32-Bit color is kind of misleading because only 24 bits are used for the color (just like 24-bit color). The other 8 bits can be used for Alpha or transparency information, or not at all. Common notation for this mode is ARGB or XRGB; the first byte for alpha (or X - nothing). Here''s black (0, 0, 0, 0), white (0, 255, 255, 255) and green (0, 0, 255, 0) -- the last three bytes are the same as with 24-bit color. Throw them all together and you have:
0x00000000(0) = black,
0x00FFFFFF(16,777,215) = white,
0x0000FF00(65,280) = green (I''ve left the first unused byte zeroed out).
15 and 16-bit Color
15-Bit color isn''t all that popular due to the fact that the extra bit is wasted (a pixel here needs two bytes minus one of the bits; pixel sizes are always on byte-boundaries). Nonetheless, my video card actually supports it, so it does exist. Each color component (RGB) gets 5 bits, with the other bit (the first one) not used, giving us 32 possible red, green and blue values (intensities). Actually, you can use it for something like transparency or a flag of some kind if you want. The examples here would be:
0x0000(0) = black,
0x7FFF(32,767) = white,
0x03E0(992) = green.
16-Bit color takes that extra bit and includes it with the Green component, giving us 32 possible red and blue intensity values, and 64 green intensities. You should be able to put colors together for 16-bit by now...this time
0x07E0(2,016) would be green, and
0xFFFF(65,5535) would be white.
4 and 8-bit Color Modes
Oh boy, here we go. You may already be aware of the fact that these modes are funny. Let''s dismiss 4-bit color right away because it’s obsolete. If memory serves, 4 bits were used to denote one of 16 possible colors, and the other 4 bits in the byte were used for attributes such as reverse video, blink, etc. Don''t quote me on this though.
I''m not really sure if you can have a straight-up 8-bit color mode, but I can tell you what this mode usually stands for: Palletized Mode. In this mode, each pixel does in fact have an 8-bit value, but this value doesn''t actually describe the color -- it is an index to a table called the palette that holds the real color value for the pixel. Each entry in this palette holds a regular 24-bit color, and since there are 8-bits to a palette index, there can be a total of 256 different entries in the palette. What this means to you is that all of the pixels on the screen must use the same 256 possible colors, and although you can (in most cases) set these 256 colors to be whatever you want, all of the pixels that you need displayed at once have to share this palette.
This 8-bit palletized mode has its good points and bad points. On the good side of the coin, if you make any changes to the palette while pixels are on the screen, they will instantly change to the new color(s). You''ve probably seen this over and over again in classic games, where either everything purple changes to yellow or blinks, or areas of the display go wild with fast-cycling colors. A lot of simple animation tricks are also achieved by utilizing the palette... there are too many possibilities to even begin to mention. The other side of the coin is that this mode is more difficult to work with because you have a palette to manage, and if you''re writing a windowed-game you have to share some of these palette entries with everyone else that''s on the screen.
The bottom line here is that although 8-bit color might be going out of style, we might visit it nonetheless in our game projects, either to round-out our learning, or to take advantage of some of this mode''s capabilities in emulating classic games.
Simplify Things
If you find yourself needing to compose a color from each of it''s components, you are best off creating macros to simplify their creation. Here''s a couple:
#define RGB16((r << 11) | (g << 5) | b)
#define RGB32((a << 24) | (r << 16) | (g << 8) | b)
We shift colors over by a certain number of bits so that they represent the proper value. For instance, the 16-bit macro does this:
RRRRRGGGGGGBBBBB, and the 32-bit
AAAAAAAARRRRRRRRGGGGGGGGBBBBBBB (A doesn''t matter unless you are using it for something).
In Closing…
Wow, this has been quite the article! I realize that there’s a lot of information here to digest. The important thing to keep in mind is that there will be occasions where you need to manually alter pixel colors in a buffer of some kind, and you’ll need to ensure that you’re traversing the buffer’s memory properly, and that you’re assigning the proper type of color value.
Feel free to reply to this topic with your comments and questions.