Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
64 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Introduction
 The Basic Blit
 Final Word

 Printable version
 Discuss this article

Summary

This article explores the inner workings of sprite drawing in DirectX 8. Using a low level approach, programmers can gain more control over their rendering code and address the shortcomings of any DirectX blitter.

With DirectDraw deprecated from DirectX 8, programmers are now forced to use 3D techniques to draw 2D graphics (or resort to using DirectDraw 7). Instead of "blitting a sprite", they must now "draw a textured quad". Of course, with wrapper functions there is no difference from an API point of view and this is what we'll be developing here: a set of routines to draw sprites in a 3D environment.

What about ID3DXSprite?

Microsoft provided the ID3DXSprite interface to simplify the task of "drawing a textured quad". So why are we reinventing the wheel? Because this wheel has a bug in it.

Sprites drawn with ID3DXSprite are not the same size than their original bitmap. This problem becomes glaringly obvious when you try to tile your bitmaps: tiles will not line up properly if they are bigger or smaller than their target rectangles.

Robert Dunlop (X-Zone) suggests that you expand your target rectangle "by 0.5 on all sides to allow proper mapping of texels to pixels." This will "compensate for the texel alignment rules of Direct3D" that are causing this problem [2, 3]

But it doesn't work.

Here's an example to illustrate. Compare figures 2, 3, and 4 with the original image (figure 1). Note the size differences and inaccuracies.

Fig 2. No extensions. Size: 31x39. Fig 3. Extend all sides by 0.5. Size: 32x41.
Fig 1. The original image. Size: 32x40. Fig 3. ID3DXSprite image. Size: 32x41 Fig 4. Extend right and bottom by 1. Size: 32x40.
Sprites created by Ari Feldman.

Figure 2 is smaller than the original. Figures 3 and 4 have noticeable inaccuracies at the top and left sides (and they are bigger). ID3DXSprite images are antialiased by default, hence the blurriness in figure 4. (I have no idea how to turn this off.)

Interestingly, there is a very simple solution to this problem: extend the right and bottom sides of your target rectangle by 1-pixel. Figure 4 is the only accurate reproduction of the original.

But why does it work? It all has to do with a simple documentation error.

The edges of figure 2 appear intact but it's definitely smaller. Figures 3 and 4 have noticeable inaccuracies at the top and left sides. ID3DXSprites are antialiased by default, hence the blurriness in figure 4. (I have no idea how to turn this off.)

Interestingly, there's a very simple solution to this problem: extend the right and bottom sides of your sprite by 1 pixel. Figure 4 is the only accurate reproduction of the original. But why does it work?

A Microsoft Bug

There's a pervasive bug throughout Windows and DirectX regarding rectangles. The pixel coordinates of a rectangle are

(x, y) to (x + width - 1, y + height - 1)

That "-1" part is really important. You can see this when you plot it out on a grid. (See A Simple Proof, next section.)

But in Windows and DirectX, this rectangle is defined as

(x, y) to (x + width, y + height)

This bug appears throughout Windows and DirectX. Try it with GetWindowRect and CopyRects (from Windows and DirectX respectively).

Maybe there's a logical explanation for it. Maybe they aren't referring to "pixel coordinates". The API documentation doesn't explicitly say this (but most people would interpret it that way).

Nevertheless, it does explain why extending the right and bottom of a sprite by 1 fixes the problem.

Bad Documentation

The DirectX 8 documentation for rectangles is wrong. You can find this page under DirectX Graphics:

  • Introduction to DirectX Graphics
    • Getting Started With DirectX Graphics
      • Rectangles

According to the documentation, the coordinates (right, bottom) refer to the bottom-right pixel of the rectangle. This is wrong. The coordinates (right, bottom) are actually 1-pixel outside the rectangle.

The documentation describes an inclusive-inclusive coordinate system. But DirectX uses inclusive-exclusive coordinates; the last pixel (right, bottom) is not part of the rectangle.

To most people, inclusive-inclusive coordinates make more sense. But inclusive-exclusive coordinates are easier to work with and most APIs favour them. For example, when calculating the width (and height):

width = right - left;     // inclusive-exclusive coordinates
width = right - left + 1; // inclusive-inclusive coordinates

The use of inclusive-exclusive coordinates also explains why extending the right and bottom by 1 fixes the problem.





Next : The Basic Blit