void putpixel(BITMAP *bmp, int x, int y, int color)

advertisement
Lesson 3
Basic 2D Graphics
Programming with Allegro
Doing 2D with Allegro


Because we are pushing the 2D envelope
to the limit in this course, it is fitting that
we should start at the beginning and
cover vector graphics.
The term vector describes the CRT
(Cathode Ray Tube) monitors of the past
and the vector graphics hardware built
into the computers that used this early
technology.
Doing 2D with Allegro


A graphics primitive is a function that
draws a simple geometric shape, such as a
point, line, rectangle, or circle.
I should point out also that these graphics
primitives form the basis of all 3D
graphics, past and present; after all, the
mantra of the 3D card is the holy polygon.
Video Cards


The fact of the matter is that video cards are not
designed to render games; they are designed to
render geometric primitives with special effects.
As far as the video card is concerned, there is
only one triangle on the screen.
It is the programmer who tells the video card to
move from one triangle to the next. The video
card does this so quickly (on the order of 100
million or more polygons per second) that it
fools the viewer into believing that the video
card is rendering an entire scene on its own.
Video Cards

The triangles are hidden away in the
matrix of the scene (so to speak), and it is
becoming more and more difficult to
discern reality from virtual reality due to
the advanced features built into the latest
graphics chips.
Xbox 360

The Xbox 360 can
crunch through 3
trillion operations
per second with its
triple-core, hexathreaded processor.
Vertices
Taken a step closer, each
triangle is made up of three
vertices, which is really all the
graphics chip cares about.
Filling pixels between the three
points and applying effects (such
as lighting) are tasks that the
graphics chip has been designed
to do quickly and efficiently.
2D Acceleration


The earliest Windows accelerators, as they were
known, produced for Windows 3.1 and Windows
95 provided hardware blitting. Blit is a term that
means bit-block transfer, a method of
transferring a chunk of memory from one place
to another.
In the case of a graphical blit, the process
involves copying a chunk of data from system
memory through the bus to the memory present
on the video card. In the early years of the PC,
video cards were lucky to have 1 MB of memory.
Video Memory


Contrast this with the latest 3D cards that have
256 MB of DDR (Double Data Rate) memory and
are enhanced with direct access to the AGP bus!
It simply must be as fast as possible to keep
feeding the ravenous graphics chip, which eats
textures in video memory and spews them out
into the frame buffer, which is sent directly to
the screen.
Parallel Processing

In a very real sense, the graphics card is a
small computer on its own. When you
consider that the typical high-end PC also
has a high-performance sound processing
card (such as the Sound Blaster Audigy 2
by Creative Labs) capable of Dolby DTS
and Dolby Digital 5.1 surround sound,
what we are really talking about here is a
multi-processor system.
Graphics Fundamentals



The basis of this entire chapter can be
summarized in a single word: pixel.
The word "pixel" is short for "picture
element," sort of the atomic element of
the screen.
The pixel is the smallest unit of
measurement in a video system.
Graphics Fundamentals



But like the atom you know from physics, even
the smallest building block is comprised of yet
smaller things.
In the case of a pixel, those quantum elements
of the pixel are red, green, and blue electron
streams that give each pixel a specific color.
This is not mere theory or analogy; each pixel is
comprised of three small streams of electrons of
varying shades of red, green, and blue
Allegro Graphics



Starting with this most basic building
block, you can construct an entire game
one pixel at a time.
Allegro creates a global screen pointer
when you call allegro_init.
This simple pointer is called screen, and
you can pass it to all of the drawing
functions.
Allegro Graphics


A technique called double-buffering (which
uses off-screen rendering for speed) works
like this: drawing routines must draw out
to a memory bitmap, which is then blitted
to the screen in a single function call.
Until you start using a double buffer, you'll
just work with the global screen object.
Setting Graphics Mode



The first function you'll learn about is
set_gfx_mode, which sets the graphics mode.
This function is really loaded, although you
would not know that just from calling it.
set_gfx_mode does a lot of work when called:





detecting the graphics card
identifying and initializing the graphics system
verifying or setting the color depth
entering full-screen or windowed mode
setting the resolution
Setting Graphics Mode


A comparable DirectX initialization is 20 to
30 lines of code.
This function has the following
declaration:

int set_gfx_mode(int card, int w, int h, int v_w, int v_h);
Setting Graphics Mode


If an error occurs setting a particular video
mode, set_gfx_mode will return a non-zero
value (0 is success) and store an error message
in allegro_error, which you can then print out.
For an example, try using an invalid resolution
for a full-screen display, like this:
ret = set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 645, 485, 0, 0);
Setting Graphics Mode


However, if you specify GFX_AUTODETECT and
send an invalid width and height to
set_gfx_mode, it will actually run in a window
with the resolution you wanted.
Running in windowed mode is a good idea when
you are testing a game and you don't want it to
jump into and out of full-screen mode every
time you run the program.
Setting Graphics Mode


The first parameter, int card, specifies the
display mode (or the video card in a dualcard configuration) and will usually be
GFX_AUTODETECT.
If you want a full-screen display, you can
use GFX_AUTODETECT_FULLSCREEN,
while you can invoke a windowed display
using GFX_AUTODETECT_WINDOWED.
Setting Graphics Mode


The next two parameters, int w and int h,
specify the desired resolution, such as
640x480, 800x600, or 1024x768.
The final two parameters, int v_w and int
v_h, specify the virtual resolution and are
used to create a large virtual screen for
hardware scrolling or page flipping.
Setting Graphics Mode

After you have called set_gfx_mode to
change the video mode, Allegro populates
the variables SCREEN_W, SCREEN_H,
VIRTUAL_W, and VIRTUAL_H with the
appropriate values, which come in handy
when you prefer not to hard-code the
screen resolution in your programs.
#include "allegro.h"
void main(void)
{
allegro_init(); //initialize Allegro
install_keyboard(); //initialize the keyboard
//initialize video mode to 640x480
int ret = set_gfx_mode(GFX_AUTODETECT_WINDOWED,
640, 480, 0, 0);
if (ret != 0) {
allegro_message(allegro_error);
return;
}
//display screen resolution
textprintf(screen, font, 0, 0, makecol(255, 255, 255),
"%dx%d", SCREEN_W, SCREEN_H);
while(!key[KEY_ESC]);
allegro_exit();
}
END_OF_MAIN();
textprintf


Another interesting function is textprintf,
which, as you might have guessed,
displays a message in any video mode.
The first parameter specifies the
destination, which can be the physical
display screen or a memory bitmap.
void textprintf(BITMAP *bmp, const FONT *f, int x, y, color,
const char *fmt, ...);
Colors


You might have noticed a function called
makecol. This function creates an RGB color
using the component colors passed to it.
If you want to define custom colors you can
create your own colors like this:
#define COLOR_BROWN makecol(174,123,0)
allegro_exit



The last function that you should be aware of is
allegro_exit, which shuts down the graphics
system and destroys the memory used by
Allegro.
In theory, the destructors will take care of
removing everything from memory, but it’s a
good idea to call this function explicitly. One very
important reason why is for the benefit of
restoring the video display.
(Failure to call allegro_exit might leave the
desktop in an altered resolution or color depth
depending on the graphics card being used.)
Drawing Bitmaps



Use the load_bitmap function to load an
image file (multiple formats supported).
Then use the blit function to draw the
bitmap.
I’ll go over bitmaps and sprites in more
detail in the next lesson.
#include "allegro.h"
void main(void)
{
char *filename = "allegro.pcx";
int colordepth = 32;
BITMAP *image;
int ret;
allegro_init();
install_keyboard();
set_color_depth(colordepth);
ret = set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);
if (ret != 0) {
allegro_message(allegro_error);
return;
}
//load the image file
image = load_bitmap(filename, NULL);
if (!image) {
allegro_message("Error loading %s", filename);
return;
}
//display the image
blit(image, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
//done drawing--delete bitmap from memory
destroy_bitmap(image);
//draw font with transparency
text_mode(-1);
//display video mode information
textprintf(screen, font, 0, 0, makecol(255, 255, 255),
"%dx%d %ibpp", SCREEN_W, SCREEN_H, colordepth);
//wait for keypress
while (!key[KEY_ESC]);
//exit program
allegro_exit();
}
END_OF_MAIN();
Loading A Bitmap

Obviously you'll need a PCX file to run this
program. However, you can just as easily
use a BMP, PCX, PNG, GIF, or JPG for the
graphics file if you want because Allegro
supports all of these formats!
Graphics Primitives


All of the graphics in a vector system are
comprised of lines (including circles,
rectangles, arcs, which are made up of
small lines).
Vector displays are contrasted with
bitmapped displays, in which the screen is
a bitmap array (the video buffer). On the
contrary, a vector system does not have a
linear video buffer.
Drawing Pixels

The simplest graphics primitive is
obviously the pixel-drawing function, and
Allegro provides one:
void putpixel(BITMAP *bmp, int x, int y, int color);
Drawing Pixels
while(!key[KEY_ESC])
{
//set a random location
x = 10 + rand() % (SCREEN_W-20);
y = 10 + rand() % (SCREEN_H-20);
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
}
//draw the pixel
putpixel(screen, x, y, color);
Drawing Lines

The horizontal line-drawing function is
called hline, while the vertical line function
is called vline:
void hline(BITMAP *bmp, int x1, int y, int x2, int color);
void vline(BITMAP *bmp, int x, int y1, int y2, int color);
Drawing Lines


The special-case lines functions for drawing
horizontal and vertical lines are not used often.
The following line function will simply call hline
or vline if the slope of the line is perfectly
horizontal or vertical:
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
Drawing Lines
while(!key[KEY_ESC])
{
//set a random location
x1 = 10 + rand() % (SCREEN_W-20);
y1 = 10 + rand() % (SCREEN_H-20);
x2 = 10 + rand() % (SCREEN_W-20);
y2 = 10 + rand() % (SCREEN_H-20);
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
}
//draw the line
line(screen, x1,y1,x2,y2,color);
Drawing Rectangles

The next logical step is a two-dimensional
object containing points in both the X-axis
and the Y-axis. Here is the rect function:
void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
Drawing Rectangles
while(!key[KEY_ESC])
{
//set a random location
x1 = 10 + rand() % (SCREEN_W-20);
y1 = 10 + rand() % (SCREEN_H-20);
x2 = 10 + rand() % (SCREEN_W-20);
y2 = 10 + rand() % (SCREEN_H-20);
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
}
//draw the rectangle
rect(screen,x1,y1,x2,y2,color);
Drawing Filled Rectangles

Outlined rectangles are boring, if you ask
me. They are almost too thin to be noticed
when drawn. On the other hand, a true
rectangle is filled in with a specific color!
That is where the rectfill function comes in
handy:
void rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
Line-Drawing Callback Feature


Allegro provides a really fascinating feature in
that it will draw an abstract line by firing off a
call to a callback function of your making (in
which, presumably, you would want to draw a
pixel at the specified (x,y) location.
To use the callback, you must call the do_line
function, which looks like this:

void do_line(BITMAP *bmp, int x1, y1, x2, y2, int d,
void (*proc))
Line-Drawing Callback Feature

The callback function has this format:


void doline_callback(BITMAP *bmp, int x, int y, int d)
To use the callback, you want to call the
do_line function like you would call the
normal line function, with the addition of
the callback pointer as the last parameter.
Line-Drawing Callback Feature



To fully demonstrate how useful this can be, I
wrote a short program that draws random lines
on the screen.
But before drawing each pixel of the line, a
check is performed on the new position to
determine whether a pixel is already present.
This indicates an intersection or collision. When
this occurs, the line is ended and a small circle is
drawn to indicate the intersection.
Line-Drawing Callback Feature
while(!key[KEY_ESC])
{
//set a random location
x1 = 10 + rand() % (SCREEN_W-20);
y1 = 10 + rand() % (SCREEN_H-20);
x2 = 10 + rand() % (SCREEN_W-20);
y2 = 10 + rand() % (SCREEN_H-20);
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
//draw the line using the callback function
stop = 0;
do_line(screen,x1,y1,x2,y2,color,*doline);
}
rest(200);
Line-Drawing Callback Feature
//doline is the callback function for do_line
void doline(BITMAP *bmp, int x, int y, int color)
{
if (!stop)
{
if (getpixel(bmp,x,y) == 0)
{
putpixel(bmp, x, y, color);
rest(5);
}
else
{
stop = 1;
circle(bmp, x, y, 5, 7);
}
}
}
Drawing Circles / Ellipses

The circle function has this declaration:
void circle(BITMAP *bmp, int x, int y, int radius, int
color);

The hollow circle function is interesting,
but really seeing the full effect of circles
requires the circlefill function:
void circlefill(BITMAP *bmp, int x, int y, int
radius, int color);
Drawing Circles / Ellipses

The ellipse function is similar to the circle
function, although the radius is divided
into two parameters, one for the
horizontal and another for the vertical:


void ellipse(BITMAP *bmp, int x, int y, int rx, int ry,
int color);
void ellipsefill(BITMAP *bmp, int x, int y, int rx, int ry,
int color);
Drawing Circles / Ellipses
while(!key[KEY_ESC])
{
//set a random location
x = 30 + rand() % (SCREEN_W-60);
y = 30 + rand() % (SCREEN_H-60);
radiusx = rand() % 30;
radiusy = rand() % 30;
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
//draw the ellipse
ellipsefill(screen, x, y, radiusx, radiusy, color);
}
rest(25);
Circle Callback Function

Allegro provides a circle-drawing callback
function just as it did with the line callback
function. To use do_circle, you must
declare a callback function and pass a
pointer to this function to do_circle.


void docircle(BITMAP *bmp, int x, int y, int d)
void do_circle(BITMAP *bmp, int x, int y, int
radius, int d);
Drawing Spline Curves


The spline function draws a set of curves
based on a set of four input points stored
in an array.
The function calculates a smooth curve
from the first set of points, through the
second and third, toward the fourth point:
void spline(BITMAP *bmp, const int points[8], int color);
int
int
int
int
int
points[8] = {0,240,300,0,200,0,639,240};
y1 = 0;
y2 = SCREEN_H;
dir1 = 10;
dir2 = -10;
while(!key[KEY_ESC])
{
//modify the first spline point
y1 += dir1;
if (y1 > SCREEN_H) dir1 = -10;
if (y1 < 0) dir1 = 10;
points[3] = y1;
//modify the second spline point
y2 += dir2;
if (y2++ > SCREEN_H) dir2 = -10;
if (y2 < 0) dir2 = 10;
points[5] = y2;
}
//draw the spline, pause, then erase it
spline(screen, points, 15);
rest(30);
spline(screen, points, 0);
Drawing Triangles

You can draw triangles using the triangle
function, which takes three (x,y) points
and a color parameter:
void triangle(BITMAP *bmp, int x1, y1, x2, y2, x3, y3, int color);
Drawing Triangles
while(!key[KEY_ESC])
{
//set a random location
x1 = 10 + rand() % (SCREEN_W-20);
y1 = 10 + rand() % (SCREEN_H-20);
x2 = 10 + rand() % (SCREEN_W-20);
y2 = 10 + rand() % (SCREEN_H-20);
x3 = 10 + rand() % (SCREEN_W-20);
y3 = 10 + rand() % (SCREEN_H-20);
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
//draw the triangle
triangle(screen,x1,y1,x2,y2,x3,y3,color);
}
rest(100);
Drawing Polygons

You have already seen polygons in action
with the Triangles program, because any
geometric shape with three or more points
comprises a polygon. To draw polygons in
Allegro, you use the polygon function with
a pointer to an array of points:
void polygon(BITMAP *bmp, int vertices, const int *points, int color);
while(!key[KEY_ESC])
{
//set a random location
vertices[0] = 10 + rand() % (SCREEN_W-20);
vertices[1] = 10 + rand() % (SCREEN_H-20);
vertices[2] = vertices[0] + (rand() % 30)+50;
vertices[3] = vertices[1] + (rand() % 30)+50;
vertices[4] = vertices[2] + (rand() % 30)-100;
vertices[5] = vertices[3] + (rand() % 30)+50;
vertices[6] = vertices[4] + (rand() % 30);
vertices[7] = vertices[5] + (rand() % 30)-100;
//set a random color
red = rand() % 255;
green = rand() % 255;
blue = rand() % 255;
color = makecol(red,green,blue);
//draw the polygon
polygon(screen,4,vertices,color);
}
rest(50);
Filling Regions

The last function I want to introduce to
you in this chapter is floodfill, which fills in
a region on the destination bitmap (which
can be the screen) with the color of your
choice:
void floodfill(BITMAP *bmp, int x, int y, int color);
Drawing Text


The text_mode function sets text output
to draw with an opaque or transparent
background.
Passing a value of -1 will set the
background to transparent, while passing
any other value will set the background to
a specific color.
int text_mode(int mode);
Drawing Text

The textout function is the basic text
output function for Allegro. There are
three alternate vesions that align the text
in different ways:
void textout(BITMAP *bmp, const FONT *f, const char *s, int x, y, int color);
void textout_centre(BITMAP *bmp, const FONT *f, const char *s, int x, y, color);
void textout_right(BITMAP *bmp, const FONT *f, const char *s, int x, y, color);
Drawing Text


A slightly different take on the matter of text
output is textout_justify, which includes two X
coordinates, one for the left edge of the text and
one for the right edge, along with the Y position.
In effect, this function tries to draw the text
between the two points. This really is more
useful when you are using custom fonts.
void textout_justify(BITMAP *bmp, const FONT *f,
const char *s, int x1, int x2, int y, int diff, int color);
Drawing Text

Allegro provides several very useful text
output functions that mimic the standard
C printf function, providing the capability
of formatting the text and displaying
variables. The base function is textprintf,
and it looks like this:
void textprintf(BITMAP *bmp, const FONT *f,
int x, y, color, const char *fmt, ...);
Download