We’re back in session once more to discuss the fundamentals of Game Boy Advance SP-style graphics programming. Previously we discussed using offscreen buffers in the article, Basics of Offscreen Buffering. In this discussion we’re going to focus on copying sprites to the offscreen buffer. We’ll learn how to use a transparency pixel, and what it is used for in blitting <rendering> sprites.
Even on the smoothest of sailing trips one can encounter a little turbulence every now and then.. Well, today is no different. We’ve been sailing smoothly through Elementary Graphics topics without having to discuss the complex math involved. Today we have an exception. I need to cover some basic math in order to discuss computer pixels and color.
Computers use a special number system that will seem a little foreign to you at first. With time and experience, you’ll get used to it. When people learn to count, we’re accustomed to starting with 1 and counting to 10. This is called a base-10 decimal system. When a computer counts, it counts to 16. This is called a base-16 hexadecimal system. To make things a bit more interesting a computer uses letters after it counts to 9.
Let’s take a look, side by side, at a base-10 system and a base-16 system, starting from 0 and counting to 15.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -- base 10 counting |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | -- base 16 counting |
Why does a computer use base-16 numbering anyway? It all has to do with the computer’s hardware. A single base-16 digit can represent 4 bits. What is a bit? Now you’re making me work for my money, aren’t you?
A bit is the true language of a computer. A bit can have one of two values, 0 and 1. A bit is simply a placeholder for a 0 or a 1. Four bits are four placeholders for four zeros and ones. For example, 0000 is a four-bit representation of the number 0. The number 1 represented in 4-bit notation is 0001. A single-bit computer is pretty much useless. Early computers were 8-bit computers. This meant 4 bits used together could represent an 8-bit number.
To represent the number zero using 8-bit notation we would write, 0000 0000. To write the number 1 using 8-bit notation we would write, 0000 0001. The smallest number we can represent with 4 bits is 0 and the largest number is 15. Four bits, in the good ol’ days of computing, was called a nibble. Two nibbles, 4 bits + 4 bits = 8 bits, is called a byte. Let’s take a look at how bits are used to represent numbers.
|
Related Reading
Game Coding Complete |
| 4-Bits | Decimal | Hexadecimal |
| 0000 | 0 | 0 |
| 0001 | 1 | 1 |
| 0010 | 2 | 2 |
| 0011 | 3 | 3 |
| 0100 | 4 | 4 |
| 0101 | 5 | 5 |
| 0110 | 6 | 6 |
| 0111 | 7 | 7 |
| 1000 | 8 | 8 |
| 1001 | 9 | 9 |
| 1010 | 10 | A |
| 1011 | 11 | B |
| 1100 | 12 | C |
| 1101 | 13 | D |
| 1110 | 14 | E |
| 1111 | 15 | F |
The table shows side by side the binary numeric value with its decimal and hexadecimal counterpart. Before you get mad and jump out of a window, let me make this point. You don’t need to clearly understand hexadecimal just yet. Just know that it exists and WHY it exists. Here is WHY a programmer uses hexadecimal numbers in the first place. Look at the chart above. One hexadecimal digit can represent 4 bits. Take for example, the 4-bit value 1111. I know from a lookup chart that this value is F hexadecimal. This is easier for us to read, too. After some experience, you’ll get the feel for hexadecimal numbers.
This concludes today’s quick math lesson. Let’s go play with some video game graphics.
We’re going to pick up where we left off in the article, Elementary Computer Graphics: Basics of Offscreen Buffering. Our final programming example from the last article is shown in Figure 1.
![]() Figure 1. Sprite copied with no transparency pixel. |
We copied our sprite directly to the offscreen buffer. The sprite has a funky fuchsia background that covers the cool game background. It looks hokey, right? Well, we can fix this. What we need to do is draw all the pixels of the leaping monster sprite, except for the funky fuchsia pixel. This pixel is our transparency pixel, which means we won’t draw it.
The first task is to figure out the pixel values of the funky fuchsia color. Building on the code from the blitting sprites discussion in Elementary Computer Graphics: Basics of Offscreen Buffering, we know the sprite’s location in the Tk image sprite's buffer. The leaping monster sprite is located in the sprite's image buffer at location <rectangle> 265 737 328 800. The Tk image library will allow us to retrieve the pixel using the image photo library tools. It looks like this:
# retrieve the transparency pixel value
set transparency_pixel [$sprites get 265 737]
puts $transparency_pixel
% 198 0 107
The image photo procedure returns a list of three numeric values as shown in Figure 2. These are RGB values, for Red, Green, and Blue, the primary color pigments. The three values represent how bright each value should be for red, green, and blue to create a specific pixel color. This is just like mixing watercolors in art class. Only now we're using a computer to mix the paint for us. We need a red brightness of 198, a green brightness of 0, and a blue brightness of 107. These three Red-Green-Blue (RGB) values define our funky fuchsia transparency pixel. We have completed our first task. We have the RGB values for the transparency pixel.
![]() Figure 2. Fuchsia transparency pixel information. |
|
Uh-oh! I warned you this was coming. Whenever you deal with computer memory (buffers) you’re going to have to speak hexadecimal. The contents of our offscreen buffers are all stored using hexadecimal values. If we’re going to scan a buffer for RGB values, then we should be using RGB values in hexadecimal format. We can convert the RGB values we currently have to hexadecimal format using the following lines of code:
# convert to hexadecimal
set R_value [format "%02x" [lindex $transparency_pixel 0]]
set G_value [format "%02x" [lindex $transparency_pixel 1]]
set B_value [format "%02x" [lindex $transparency_pixel 2]]
I am using the tools provided in the Tcl scripting language to convert values to hexadecimal. So no complex math is involved.
set transparency_pixel {198 0 107}
198 0 107
() 2 % # convert to hexadecimal
() 3 % set R_value \
[format "%02x" [lindex $transparency_pixel 0]]
c6
() 4 % set G_value \
[format "%02x" [lindex $transparency_pixel 1]]
00
() 5 % set B_value \
[format "%02x" [lindex $transparency_pixel 2]]
6b
These values match the hexadecimal values shown in Figure 2. Now we define the transparency pixel value that we are going to scan for in the sprite's buffer.
# create the transparency value string
set RGB_value "#$R_value$G_value$B_value"
Testing the above code in the Wish Shell we see:
() 6 % # create the transparency value string
() 7 % set RGB_value "#$R_value$G_value$B_value"
#c6006b
The value #c6006b is the pattern we are going to scan for in the sprites buffer.
Wouldn’t it be nice if we had a library procedure we could call to magically
handle the RGB decimal-to-hexadecimal conversion for us? Well, the Tcl scripting
language allows us to add tools we need for our code. We’ll use the Tcl proc command to write a procedure.
proc RGB_convert_decimal_hexadeimal { transparency_pixel } {
# convert to hexadecimal
set R_value \
[format "%02x" [lindex $transparency_pixel 0]]
set G_value \
[format "%02x" [lindex $transparency_pixel 1]]
set B_value \
[format "%02x" [lindex $transparency_pixel 2]]
# create the transparency value string
set RGB_value "#$R_value$G_value$B_value"return $RGB_value
}
Let’s again try out our previous example of converting the transparency pixel RGB values from decimal to hexadecimal.
() 5 % RGB_convert_decimal_hexadeimal $transparency_pixel
#c6006b
() 6 %
Pretty slick, huh? The Tcl proc command allows us to add our own library calls.
I haven’t introduced anything new inside the body of the procedure. But check out what we can do now. Let’s take a close look at the procedure.
proc RGB_convert_decimal_hexadeimal { transparency_pixel } {
# convert to hexadecimal
:
return $RGB_value
}
We start with the Tcl proc command to write a procedure. The proc command is followed
by the name of the procedure. The name of the procedure is how we will call
the operation from our script. Next, in parenthesis, we have what is called
an argument. An argument is a Tcl variable from the script we want to use inside
the procedure. This is called passing in an argument. In this case, we pass
in the transparency_pixel RGB decimal value. When we convert the transparency_pixel values from decimal to hexadecimal we need to give the new value back to the calling script. We use the Tcl command return. This returns, or gives back,
the new hexadecimal value we were after.
Now we have our work cut out for us. We need to implement a more complex procedure than we wrote for converting between decimal and hexadecimal values. The Tk image library does not provide a library to blit using a transparency pixel. Not to worry, let’s roll up our sleeves and get to work. We’ll make our own!
The first task at hand is gaining access to the raw data pixels of our sprite. The Tk image library, fortunately, provides a procedure for us to do this. Otherwise, we would have had to close up shop and go home. The Tk image library procedure that does this is data.
Let’s grab the raw data pixels of our happy, jumping monster.
# get image data
set raw_monster_data [$sprites data -from 265 737 328 800]
Go ahead and follow along in the Wish Shell console. Experiment a bit. You’ll
see some interesting concepts we’ve been playing with thus far. Remember,
you will need the directory, /game_dev created on your hard drive. You will
need the art file downloaded to that directory as well.
The Script:
# load the art file
set filename "/game_dev/game_art.gif"
set sprites [image create photo -file $filename]
# load the raw pixel data into raw_monster_data
set raw_monster_data [$sprites data -from 265 737 328 800]
# raw_monster 63 lines long
llength $raw_monster_data
When the script executes you will see some familiar hexadecimal values, just
like the ones we have been playing with. I threw in the Tcl llength command
to demonstrate to you that the monster pixel data contains 63 lines or <rows>
of hexadecimal values. Do you wonder why that is? Let’s do a little math,
shall we?
The top pixel of the monster is at location y1=737. The bottom pixel is at location y2=800. Let’s subtract,
800 – 737 = 63 <height>
Let’s check our monster sprites.
() 9 % llength $raw_monster_data
63
Coincidence? Not exactly. Our monster sprite is 63 pixels tall. Now consider these to be 63 rows of pixel information. Each row of pixel information contains data for 63 pixels of information.
The row pixels across the monster are at location x1=265. The rightmost pixel is at location x2=328. Let’s subtract,
328 – 265 = 63 <width>
For each row we will call a scan row for this lesson. There are 63 scan rows of pixel information. Each individual scan row contains data for 63 pixels.
![]() Figure 3. There are 63 pixels per scan row. |
|
Remember blitting operations use bounding rectangles to perform the pixel copy operations. We will need to define two bounding rectangles. The first is the source rectangle. The source rectangle is the bounding rectangle of our monster sprite. We already know this to be 265 737 328 800. Top left is x1=265, y1= 737 and bottom right is x2=328, y2=800. We know the monster sprite is 63 pixels x 63 pixels. So we could describe the rectangle as being located with its top left edge at x1=265, y1=737 and the sprite is 63 pixels wide by 63 pixels tall.
Likewise, when we blit the sprite we are going to need to know a destination rectangle. This is where we are copying our sprite. What we do know is that the destination rectangle is the same size as the sprite's source rectangle. The destination rectangle is 63 pixels tall and 63 pixels wide. What we only really need to know is the top-left position of the destination rectangle when we execute the copy. Do you follow me here? Both the source sprite rectangle and the destination rectangle are 63 pixels high x 63 pixels wide.
Our blitter will need to know the source sprite's top left position, src_x and
src_y. We will need to know the sprite's height and width. And we will need to
know the destination rectangle's top-left position. Figure 4 shows how we’re
going to define the rectangular boundaries for our blitter operation. The source
rectangle, src, is the location of our monster sprite in the sprite's buffer.
The destination rectangle, dst, is where we are going to copy our monster sprite to.
![]() Figure 4. Defining the rectangular boundaries. |
Let’s play around with an example so you get an idea of what we’re
doing. Remember, our sprite’s rectangle is defined to be 265 737
328 800.
set src_x 265
set src_y 737
set width 63
set height 63
# calculate source bounding rectangle
set src_x2 [expr $src_x + $width]
set src_y2 [expr $src_y + $height]
And cutting and pasting the above code into the Wish Shell console, we get:
() 22 % # calculate source bounding rectangle
() 23 % set src_x2 [expr $src_x + $width]
328
() 24 % set src_y2 [expr $src_y + $height]
800
Using Figure 4 as our example, we have src_x =265, src_y=737, src_x2=328, and src_y2=800. This is the bounding rectangle of our happy, jumping monster sprite.
For the destination rectangle we’re going to copy the blit operation from the previous article, Basics of Offscreen Buffering. The example from that article didn’t use a transparency pixel because the Tk image library doesn’t support this method of blitting. We’re going to blit to the center of the offscreen buffer. The top-left corner of the destination rectangle is dst_x=320 and dst_y=303.
set dst_x 320
set dst_y 303
set width 63
set height 63
# calculate destination bounding rectangle
set dst_x2 [expr $dst_x + $width]
set dst_y2 [expr $dst_y + $height]
Plugging this code snippet into the Wish Shell console, we get:
() 29 % # calculate destination bounding rectangle
() 30 % set dst_x2 [expr $dst_x + $width]
383
() 31 % set dst_y2 [expr $dst_y + $height]
366
Time for a break? Talk about information overload. We covered a lot of ground
here from the hexadecimal number system to sprite-bounding rectangle-coordinate
systems. Give yourself a pat on the back. This was a lot of material. Next week
we’ll put this all to use and actually work with accessing and manipulating
raw pixel values. Finally, we’ll be able to see our little monster friend
magically appear with a background and no ugly fuchsia pixels.
Michael J. Norton is a software engineer at Cisco Systems.
Return to MacDevCenter.com.
Copyright © 2009 O'Reilly Media, Inc.