Drawing Bitmaps

0
265

A bitmap is a 2D image, also known as a texture in the realm of 3D programming. Bitmap images can be created in memory, but they are more often loaded from an existing bitmap file. The most common file types are BMP, PNG, and JPG. For games, we want high-quality bitmaps without lossy compression (in which pixels are changed to shrink the file size). The BMP format does work well but it has fallen out of favor in recent years due to format incompatibilities (such as some BMP files supporting an alpha channel, and some not). A good compromise is the PNG format, which offers decent file size and 32-bit ARGB color. For the sake of discussion, images will just be referred to as “bitmaps” regardless of the file format. In this hour, we’ll learn to add bitmap files to the Content project, and then load and draw them. This is the basis for most games!

Adding a Bitmap File to an XNA Project

Bitmap programming is the precursor to sprite programming, which is where we really want to go in order to draw game characters, ships, or whatever the game calls for. We can’t draw a 3D shape with just a bitmap, although bitmaps are used as textures when rendering in 3D. A texture is an image wrapped around the 3D shape, such as a tree, a car, or a human figure. Texturing can get pretty complex! Fortunately, we’ll start with the basics of 3D in a later hour and learn how to draw with lights and special effects. But first we need to learn 2D or bitmap programming.

There’s so much we can do with bitmaps in XNA! I’m eager to jump right into the advanced rendering stuff, but we’ll start off at a steady pace and first learn the basics. In the next hour, we’ll do some really fascinating things with bitmaps, like scaling and rotation.

We need to create a new project for our explorations of bitmap programming. Let’s begin with a new project.

Adding a Bitmap Asset to an XNA Project

Follow these steps to create the Bitmap Demo project that we will use as an example while learning how to load and draw bitmaps in this hour:

  1. Create a new Windows Phone (4.0) project called Bitmap Demo.
  2. Create or find a bitmap file containing an image you want to draw. The example uses an image of a spaceship that you can borrow. The bitmap file is a 32-bit PNG with a transparent region (via alpha channel).
  3. Right-click the Content project in Solution Explorer (it is called Bitmap Demo- Content (Content)), as shown in Figure 5.1. From the context menu that comes up, choose Add, Existing Item.

    Adding an existing bitmap file to the project.
    FIGURE 5.1 Adding an existing bitmap file to the project.
  4. The Add Existing Item dialog should come up. Select the bitmap file you want to add to the project. The file is added to the project as shown in Figure 5.2.

    The ship.png file has been added.
    FIGURE 5.2 The ship.png file has been added.
  5. If the Properties window is not visible, open it with View, Properties Window. When you select an asset file in the Content project, the file’s properties will be shown. Note the properties in the case of ship.png. Both the Content Importer and the Content Processor properties are automatically set to Texture – XNA Framework. This means that XNA recognized the file type and will process it as a texture. If you look at the other items in the drop-down list, you should find the following:
    • Effect – XNA Framework
    • Autodesk FBX – XNA Framework
    • Sprite Font Description – XNA Framework
    • WAV Audio File – XNA Framework
    • WMA Audio File – XNA Framework
    • WMV Video File – XNA Framework
    • XACT Project – XNA Framework
    • X File – XNA Framework
    • XML Content – XNA Framework

    These are the types of asset files XNA recognizes automatically. You can also create your own custom content importer to convert data files of any type to work with XNA—although you still have to write the code to read the file.

Loading a Bitmap File as an Asset

The name of the bitmap file added to the project is important, but the extension is not. The ship.png file added in the preceding Try It Yourself tutorial will be converted into a file called ship.xnb when compiled, and will be recognized by the name “ship” in the content manager. Let’s see how to load the bitmap.

Writing Code to Load a Bitmap File

Follow these steps to write the code to load a bitmap file:

  1. Add two new variables near the top of the Game1 class definition (where other global variables are found) as shown. The first variable is of type Texture2D, which is a class for bitmaps. The second is a Vector2 to keep track of the position of the bitmap on the screen.
    [code]
    public class Game1 : Microsoft.Xna.Framework.Game
    {
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    //texture variable
    Texture2D shipImage;
    //position variable
    Vector2 shipPos;
    [/code]
  2. Load the “ship” bitmap in the LoadContent() method and set the position to (0,0).
    [code]
    protected override void LoadContent()
    {
    spriteBatch = new SpriteBatch(GraphicsDevice);
    //load the ship bitmap
    shipImage = Content.Load<Texture2D>(“ship”);
    shipPos = Vector2.Zero;
    }
    [/code]

If XNA cannot find an asset file that your code tries to load with Content.Load() in the program’s main LoadContent() method, an exception error will be generated!

Drawing a Bitmap with SpriteBatch

After adding the bitmap file as an asset item to the Content project, adding the variables, and loading the bitmap as a Texture2D, we can now draw the bitmap onto the screen. For that, we need to jump down to the Draw() method. Here is the easiest way to draw a bitmap:

[code]
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
//begin drawing
spriteBatch.Begin();
//draw the bitmap
spriteBatch.Draw(shipImage, shipPos, Color.White);
//finish drawing
spriteBatch.End();
base.Draw(gameTime);
}
[/code]

The output from this program is shown in Figure 5.3.

SpriteBatch is an XNA core class that can draw both bitmaps and text, with many variations of the Draw() and DrawString() methods. In the next hour, we will work with SpriteBatch.Draw() extensively, exploring all of its capabilities. Let’s look at all the overloaded variations of Draw() here to get a feel for these capabilities. We will study these in more detail in the next hour.

The first parameter is always a Texture2D. The second parameter is always the destination— either a Rectangle or a Vector2. Beyond those two are a variety of parameters that make it possible to perform the following actions:

  • Rotate
  • Scale
  • Animate
  • Flip horizontally
  • Flip vertically
The Bitmap Demo program.
FIGURE 5.3 The Bitmap Demo program.

We will learn to use these special effects in upcoming chapters, as well as take a peek under the hood of the SpriteBatch.Begin() and SpriteBatch.End() methods that have been so mysterious up to this point. We have used only the second version, although the first one is easy enough to follow as well. Overloads 3 to 7 will be examined in the next hour and others following in depth, so think of this as just a sneak peek! Some of this code might be hard to read, because these are the definitions of the methods. I’m only going into this much detail now for Draw() because we will use it a lot!

[code]
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Color color);
public void Draw(Texture2D texture, Vector2 position, Color color);
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Rectangle? sourceRectangle, Color color);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color);
public void Draw(Texture2D texture, Rectangle destinationRectangle,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, SpriteEffects effects, float layerDepth);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, float scale, SpriteEffects effects, float layerDepth);
public void Draw(Texture2D texture, Vector2 position,
Rectangle? sourceRectangle, Color color, float rotation,
Vector2 origin, Vector2 scale, SpriteEffects effects,
float layerDepth);
[/code]

Drawing Bitmaps with Transparency

The ship.png file used in the example in this hour has an alpha channel and draws without showing the background pixels around the shape of the actual ship inside the bitmap. Let’s learn how to create an alpha channel. I’ll use Gimp because it’s a free graphics editor and it has many features similar to the uber-powerful Adobe Photoshop (download Gimp from www.gimp.org).

XNA will draw an image transparently if it has an alpha channel, and we don’t need to do anything extra for this to happen. This really makes life easier for an XNA game programmer. If you supply XNA with a bitmap file containing transparent pixels, it will draw that image with transparency.

I have used many graphics editors, including Photoshop, PaintShop Pro, Corel- DRAW, and Gimp. Although each behaves somewhat differently, they all share a similar toolset, including the ability to work with alpha channels. The instructions here for Gimp will be basically similar for other graphics editors.

Creating an Alpha Channel

Let’s take a look at how to create an alpha channel. To add a transparency layer to an image, you need to locate the Magic Wand tool available in most graphics editors.

  1. After selecting the Magic Wand tool, click somewhere in the background (which should be a solid color). The Magic Wand locates the edges of the game object and highlights everything around it, as shown in Figure 5.4. Another, more precise way to select a background is with the Color Picker or the Select by Color tool.
  2. Now that you have a selection available, you can create a layer mask. In Gimp, open the Layer menu and choose Mask, Add Layer Mask, as shown in Figure 5.5.
  3. The Add Layer Mask dialog shown in Figure 5.6 comes up. Choose the Selection option and check the Invert Mask option; then click the Add button.
    Finding the edges of the game object with the Magic Wand tool.
    FIGURE 5.4 Finding the edges of the game object with the Magic Wand tool.
    Preparing to add a layer mask.
    FIGURE 5.5 Preparing to add a layer mask.

    The Add Layer Mask dialog is used to choose options for a layer mask.
    FIGURE 5.6 The Add Layer Mask dialog is used to choose options for a layer mask.
  4. The next step is to apply the layer mask. You can tell Gimp to apply the mask using the Layer menu, then Mask, Apply Layer Mask, as shown in Figure 5.7.

    Applying the new layer mask makes it permanent.
    FIGURE 5.7 Applying the new layer mask makes it permanent.
  5. In the final figure, shown in Figure 5.8, the alpha channel has been created based on the masked selection. The checkerboard background behind the asteroid image shows the transparent region. The result looks very nice! You can load this image into your XNA project, and it will draw with transparency so that the outer edges of the image (where the solid background is located) will not overwrite the background of the screen. Just be sure to save the file using a bitmap file format that supports transparency and works with XNA. The most common formats are PNG and TGA.

    The asteroid image now has a masked transparency layer.
    FIGURE 5.8 The asteroid image now has a masked transparency layer.

This hour began a journey that will last for essentially the rest of the book. Most of our attention will be on 2D graphics in upcoming chapters, with all the variations of the SpriteBatch.Draw() method and the special effects that are possible with it. Being able to add a bitmap file to the project (with transparency information), and then load and draw it, is essential for every XNA programmer.