Today’s graphics chips vary widely in performance and visual quality. As a result, computer users come into contact with varying graphics formats, resolutions, and color spaces. If developers want their applications to succeed in this situation, they must prepare these applications to work on a wide variety of hardware. Although high-level technologies such as DirectX simplify the process of writing portable software, the programmer still faces the problem of writing for different hardware. DirectX can indicate the capabilities of the underlying hardware, but in certain cases, the API doesn’t perform emulation (such as when an application blits among different surface formats). Programmers need to know how to handle the graphics formats that are available for the hardware on which their programs are supposed to run. In this article, we’ll examine how to prepare graphics data in various formats so that it will display properly in a game.
Often, the application needs to convert graphics data from a storage format on the hard disc to another format that’s usable by the game itself. To make matters worse, background pictures, sprites, and textures in 3D games may contain an alpha channel, which doesn’t simplify the problem. In order to display all graphics properly, a game must be ready to handle conversions to whatever pixel format the current hardware is using. How can we achieve these conversions? Many programmers beginning to work on the PC platform tend to use Windows GDI functions for pixel format conversions. Unfortunately, this strategy has a few limitations. GDI functions don’t support formats with an alpha channel, they’re typically slow, and they simply can’t perform certain conversions.
Many programmers also think that the 5:6:5 ratio (corresponding to the number of bits for red, green, and blue components) is standard for high-color (16-bit) modes. However, these same programmers could be using a high-color mode with a 5:5:5 ratio, any one of several modes with an alpha channel, or some other mode with some other ratio that is specific to a certain hardware vendor.
The key is to avoid the assumption that the order or width of a pixel’s RGBA components is fixed. We also need to determine what color formats the game can handle. The proper programming method is to read the capabilities of underlying hardware and perform tasks that make use of the hardware appropriately.
Looking for a Solution
So what could we do to avoid graphics hardware problems? We could create a C++ class that would satisfy the requirements of those graphics cards currently on the market or due out in the near future. We would need to determine the tasks required during conversion among pixel formats and then write an interface that would handle tasks and hide the implementation. Here are the minimum requirements:
• Perform format conversion of the most common pixel formats.
• Perform the conversion as quickly as possible, because a game can spend significant time loading all the textures and bitmaps for a scene.
• Ensure that the conversion works safely.
Pixel formats differ in many ways, notably the pixel depth. Today’s hardware uses 1-, 2-, 4-, 8-, 16-, 24-, and 32-bit wide pixels. Until games run in 32-bit environment, we’ll be satisfied with a maximum of 32 bits per pixel. Different pixel formats also offer palletized and RGB modes, as well as modes with an alpha channel.
We may want our format conversion class to be able to convert data from any format into any other format. However, it doesn’t need to be that flexible, at least on the source side. Because high-color data and 8-bit rgb formats typically aren’t the formats in which data is stored, things are a little simpler for us. When it comes to destination formats, the converter should be more flexible and able to handle most possible graphics formats. For the purpose of this article, however, we’ll be happy with a limited set of output formats.
At first, our task may seem complicated. How big must the library be? How will it handle all possible graphics formats, including palletized modes and alpha channels, as well as pixels that can be anywhere from 1-bit to 32-bits wide? Well, let’s see how we can simplify things.
First, we don’t need to handle all possible formats separately. Hardware must adhere to certain rules. Only formats that are 8 or fewer bits wide can be palletized. Furthermore, RGBA components always occupy contiguous bits within pixels.
Second, we can limit our converter to handle only RGB components of a maximum width of 8 bits per single component. When hardware offering higher resolutions becomes standard, we can add more features to the class that will accommodate higher pixel depths.
Finally, we can decide not to support graphics formats of fewer than 8 bits because they’re increasingly rare. If we must support these outdated formats, however, we can do so via less efficient general algorithms.
Let’s summarize the information that we need in order to perform pixel format conversion:
• We need to know the pixel format of our source data, including bit depth, RGBA bit masks, and palette (if any).
• We need to know which pixel input and output formats must be supported.
• We need some source data, usually loaded from disk. Because data is usually supplied as a bitmap, we can choose the rectangular memory scheme that is used by most graphics drivers and APIs. We need a pointer to the data, as well as to the width, height, and pitch of the source surface. Using a rectangular memory system isn’t required - it’s possible to convert contiguous memory blocks, RLE-packed sprites, and so on, to our needs. Working with rectangular memory is just another simplification and will be all we need in most cases.
As bitmaps are loaded from disk, the source format is available via the bitmap header. The destination format depends on the data’s final purpose. Usually, the data should match the format of the selected video mode or the chosen texture format. The conversion takes the source file and goes through it pixel by pixel, performing the conversion on each one and storing the results in the destination format.