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.