© 1998 Codex Software
The registered version of PETAL can be purchased for $15 US on the internet at Albert's Ambry. Registration at Albert's is immediate, and you can download the registered version right away without any shipping and handling costs. To register, please go to http://www.alberts.com and search for PETAL100 and click on "Buy It"
Remember, registering now entitles you to all updates up to and including the next major version (version 2.0). You also get the full source code. For more information, you can always look on the Codex homepage at http://www.codex.nu or email us at info@codex.nu.
Thank you for registering this program.
PETAL was originally designed to bridge the gap between Linux and DOS graphics programming. I wanted to be able to design my programs in DOS, and still be able to quickly and easily port them to Linux. Since then my intentions have changed a little, but PETAL remains the same. I now think of PETAL as a Linux graphics library which allows porting to DOS. This fundamental change occurred for two reasons:
DOS is dead, Linux isn't.
Unix (and therefore Linux) has one of, if not the, best development environments to date.
Simple enough; if given the choice of two environments, you pick the best. In keeping with that philosophy I decided to take full advantage of Linux. This meant external drivers, and full X11 support. The former was obtained easily enough, but presented a problem for DOS programs, which could not support these external drivers. Because of this, the DOS driver system is a bit different, and uses internal drivers. However, it is entirely possible to write code that will run using either driver system, without need to rewrite the code (no need for the pre-processor either). The latter took a bit more effort, but I feel it was completely worth it. Linux seems to be cluttered with programs that will work in only 8bpp colour. With PETAL there is no reason for this restriction. Not only that, but PETAL works equally well under any colour depth. This can't be said for many of the graphics libraries I've worked with.
My entire purpose in writing PETAL was to allow fast access to any graphics system, in any resolution, in any colour depth. This was no easy task, but I feel I have accomplished it. To compliment that, I feel I've provided a robust and easy to use API as well. The programmer need not even know what colour depth he or she is working in because PETAL can handle all the specifics without your knowledge.
Historically PETAL was built in C and C only. However, as it evolved I found myself wanting to use function overloading, which was provided by C++. PETAL then become a C++ library, but remained mostly procedural. The API has always been object oriented however. The main object utilized in PETAL is the Image object. This object is, in essence, the canvas that all PETAL programs will write to.
The future of PETAL looks very good. I have many ideas which I wish I could implement in version 1.0, but cannot due to time restraints. The major difference will be a switch back to C, or to provide a fully C++ API. One or the other. Right now I'm half way between both which doesn't seem right. The API will also be further matured. There seems no reason to have separate functions to work on the screen or an Image. I will provide one function which will handle each separately. Rest assured, however, that your code should recompile with little to no modifications.
One other major feature I would like to implement is a windowing and widget system. I plan on integrating my Smoke library with PETAL. This will further extend PETAL's abilities. PETAL will use the native windowing systems where possible, but use the specialized PETAL widget system at all times. A switch will also be provided as to wether PETAL should use the native window decorations, or overload them with the PETAL/PolyOS look.
Speaking of windowing systems, it was no mistake in my using a plural form. PETAL 2.0 will support Win95 and NT along with the currently support platforms; X11 and DOS. One change that'll effect the non-Linux crowd will be external drivers on every system. I have devised a system that will allow for a much more powerful and portable external driver system.
Intrigued? I invite you to purchase version 1.0 as soon as possible. With PETAL's licensing this entitles you to receive version 2.0 at no cost!
gcc program.cc -o program -Lpetal_directory -lpetal
Anyone who knows their compiler's command line syntax will understand this. You're compiling program.cc to the executable file, program. The -L switch just adds a directory to the compilers library search path. If you've copied libpetal.a in to your compiler's library path, then this will not be needed. The last switch, -lpetal, actually tells the compiler to link libpetal.a to your program.
Well, that's it for linking, but your programs will also have to do one more thing. They'll have to include the PETAL include files. The main petal include file is petal.h but you can also include pcx.h and sprite.h if you wish to use the PCX support or sprite system respectively.
Also, if you're going to be using the DOS drivers, you'll need to include the appropriate drivers. Currently the available DOS drivers are located in the following header files; lfb.h, pmode.h and banked.h. Include which ever ones you wish to use. You can also include these in Linux programs if you wish (for portability reasons) and they will simply be ignored.
Initializing PETAL is very simple. You simply use the init_petal() function. There are two versions of this function provided, however; one for DOS, one for Linux. They look like the following:
int init_petal(char *driver, char *name);
int init_petal(Driver *driver, char *name);
The first is the Linux version, and you provide it with the file name of the driver you wish to use (for example, "./drivers/xshm.driver"). The name variable, which is common to both versions, is the name of the window to open. This is actually only used in Linux, but is there for both functions if only for consistency. The last version is the DOS version. You pass a pointer to a driver object to this one. The driver objects are pre-defined by their respective driver source files. Currently the following exist:
lfb_driver |
For Vesa 2.0 linear frame buffer compatible cards |
pmode_driver |
For Vesa 2.0 protected mode banking compatible cards |
banked_driver |
For Vesa 1.0 compatible cards |
The other thing of interest here is the return value. Either function will return 0 if they are successful. You can use this to your advantage to create portable code which will choose the most appropriate driver for the task:
char *name[] = "My Window"; if(init_petal("./drivers/dga.driver", name) if(init_petal("./drivers/xshm.driver", name) if(init_petal(&lfb_driver, name) if(init_petal(&pmode_driver, name) if(init_petal(&banked_driver, name) { printf("Could not identify your graphics system\n"); exit(1); }
As you can see, if the function returns a non-zero return value, indicating an error, then the code continues on and tries other drivers. A much more elegant solution under Linux would be to load a general driver (for example, "./video.driver") which would be a link to a more specific driver. Not only does this simplify the code, but allows the user to change the video driver themselves.
Okay, with that out of the way, it now makes sense to open up a window (or set the graphics mode in DOS). This is handled by just one function this time:
int set_resolution(int width, int height, char bpp);
You provide this function with the width and height of the window you wish to open, and also the colour depth. The colour depth is specified as a bits per pixel value. In other words, you can specify 8, 15, 16, 24 or 32. In Linux, of course, you can specify anything to this function, but the colour depth will be ignored with the xshm driver. This cannot be helped; X11 is a windowing system and the user decides what colour depth to use, not the programmer. PETAL, however, will easily adapt to whatever the user is using. Also of interest is that the dga driver will ignore your width and height parameters. Again, this cannot be helped. DGA is designed to use the full screen, and so the full screen you get.
As for DOS, you are, of course, limited to certain resolution values, but can use any colour depth. PETAL will try anything you give it though, but it is highly unlikely that a video card will support an odd mode such as 100x100 :) For those of you who don't have much experience in DOS graphics programming I will now list some of the more commonly supported video modes:
320x200 is a pretty safe bet. All VGA cards will support it at the 8bpp colour depth.
640x480 is another safe bet. Most SVGA cards will support this in 8bpp, and usually 15 and 16bpp as well.
800x600 is pretty common now-a-days but some older cards may not support it.
1024x768 or 1280x1024 are supported by most of the newer cards coming out these days, but I wouldn't rely on them being there. They are still a little exotic.
That's about it for initialization. I would like to suggest a final step, however. Because different video systems have different limitations it makes sense to find out what resolution you're actually running in. You can do this with a simple call to get_resolution:
int get_resolution(int *width, int *height, char *bpp);
Provide this function with some pointers and it'll return the respective information in them. Very similar to set_resolution, just in reverse, in a sense.
After initializing PETAL you'll be able to make use of it by creating an Image object. The image object is the fundamental object of PETAL and the one where all drawing activities will take place. You will draw to this image, and then copy it to the screen when finished. The advantages to this are three-fold:
The user cannot see you composing your graphics because they are composed off-screen.
The actual image displaying is flicker-free. Writing directly to the screen will often cause flicker due to the vertical retrace of the video card. This is avoided by composing your graphics off-screen, and copying them, in one pass, when the vertical refresh is not active.
Speed. The memory PETAL uses for its Images is much faster than video memory and therefore your programs video performance will experience a substantial speed-up in this area.
With that said, how do you create an image? That's done with a simple call to new_image() with the proper resolution parameters:
Image *img; img = new_image(320, 200); if(img == NULL) { printf("Error creating image\n"); exit(1); }
It should be noted that the resolution provided to new_image() need not be the same as those provided to set_resolution(). In fact, if you wish to use the same resolution as provided to set_resolution() you can just call new_image() with no parameters.
You can now draw to your new image however you wish. PETAL provides the following graphics primitives for your use:
void putpixel(int x, int y, int c, Image *img);
int getpixel(int x, int y, Image *img);
void line(int x1, int y1, int x2, int y2, int c, Image *img);
void hline(int x1, int x2, int y, int c, Image *img);
void vline(int y1, int y2, int x, int c, Image *img);
void circle(int x, int y, int r, int c, Image *img);
void filled_circle(int x, int y, int r, int c, Image *img);
void rect(int x1, int y1, int x2, int y2, int c, Image *img);
void filled_rect(int x1, int y1, int x2, int y2, int c, Image *img);
void clear(int c, Image *img);
void poly(Point2d *points, int num_points, int c, Image *img);
If you want a brief explanation then I'll provide one right now. First of all getpixel() and putpixel(). These are pretty strait forward. First, putpixel() plots a pixel (a dot) at position (x,y) of colour c, while getpixel() returns the colour of the pixel at position (x,y).
The line() function simply draws a line for (x1,y1) to (x2,y2) of colour c. The hline() and vline() functions are specific versions for horizontal and vertical lines. The standard line() function will handle any type of line, but hline() and vline() are specially optimized for horizontal and vertical lines respectively.
The next two functions, circle() and filled_circle() draw circles at location (x,y) of radius r with colour c.
The rect() and filled_rect() functions draw rectangles given the co-ordinates they are given. Clear() will clear the image with the provided colour. This is somewhat analogous to drawing a filled rectangle to fill the entire screen.
The poly() function will draw a convex polygon from the array of points given to it. Point2d is a structure which contains only two elements, an x and a y variable. In other words, to draw a polygon you create an array of type Point2D and fill in all the x and y variables with the co-ordinates of your polygon vertices. You then pass these to the poly() function, making sure to tell it how many points are in the structure you're giving to it.
You've probably noticed that all the above functions take an image object as their last parameter. Perhaps you find this a bit tedious, especially if you only have one image to begin with. Well, then you'll probably want to do the following:
Image *img = new_image(); set_image(img);
The set_image() function is used to set up the default image. If you leave out the last parameter, the image, in any function the default will be used. This will cut down on some of your typing (therefore allowing you more time to add in useful comments :)
Colour manipulation in PETAL is somewhat unique. PETAL has a system for standardly defining colours, no matter what colour depth you're in. The system uses the standard rgb (red, green, blue) approach, but assumes they are in the range of 0 to 1, and are therefore floating point numbers. You can access this colour system like this:
pure_red = colour(1, 0, 0); light_blue = colour(0.4, 0.6, 1.0);
As you can see it's a very intuitive system. You can think of the floating point numbers as percents if you wish. The second order of business here is 8bpp colour. As some of you will know, on the PC you are able to redefine your colour set (also known as a palette) in 8bpp mode because of the limited number of colours in that mode. You can accomplish this in PETAL by using the set_pal() function:
rgb palette[256]; int i; /* set all your colours here, however you wish, this is just an example: */ for(i = 0; i < 256; i++) { palette[i].r = (i & 7) << 5; palette[i].g = (i & 56) << 3; palette[i].b = (i & 192); } set_pal(palette);
The above is, of course, just an example. It is, in fact, how PETAL sets up its default palette for the widest range of colours. What may not be totally obvious from the above example is that the r, g and b values for each colour are in the range of 0 to 255. This is the only exception to the PETAL colour system. Later, after you've set the palette, you may continue to refer to colours using the floating point technique and the colour() function.
PETAL provides a fairly rich set of routines to use and manipulate your images. The one that's probably of most importance to you right now is how to display them on the screen:
void show_image(Image *img, int tx, int ty);
void show_image(int x, int y, int width, int height, Image *img, int tx, int ty);
The first version will be the one used most often. It's fairly flexible in itself. You can, in fact, leave out all its parameters and it will display the default image (set using set_image()) at location (0,0). You can also leave out the tx and ty parameters and they will be assumed to be 0 and 0 respectively (therefore displaying the Image at location (0,0)).
One thing you cannot do, however, is leave out the image but provide the tx and ty variables. You must either provide nothing, in which case the defaults will be used, the image only, or all parameters.
Now on to the second form. This form is used when you only wish to copy a portion of the image. In such a case you must always provide at least the x, y, width and height parameters. These describe the area you wish to copy. Again, you can leave out the image, in which case the default image will be used. You can also leave out the tx and ty variables, which again default to 0 and 0.
This version follows the restrictions of the first almost equally. You must always provide x, y, width and height. After that you have some choices. You can provide nothing else, just the image, or everything else (the image, tx and ty).
Another related topic is filling in an image with the current contents of the display. You can do that with:
void get_image(Image *img, int x, int y);
This function follows the same rules as show_image with regards to restrictions. Most of the time you'll either provide no parameters, or just the image. The x and y variables are fairly specialized. If the image you provide has clipping enabled (see below) then you can specify where the clipped portion will be copied to withen the image. In other words, get_image will only copy the area defined by your clipping boundary (if clipping is disabled, it will copy the whole area)
Clipping images is the process of defining the images boundaries, so-to-speak. When you clip and image you provide a rectanglar area. Drawing will then be confined withen this rectangular area. If you try to draw outside of this area, your request will be ignored. If you draw partially in and partially out of the rectangular area, only the portion that is inside the area will be drawn.
Clipping is, by default, disabled on new images. In other words, you are able to draw anywhere withen the image's surface area. To enable clipping you can use either of the following functions:
void clip_image(int x, int y, int width, int height, Image *img);
void clip_image(bool yes_no, Image *img);
The first one not only turns clipping on, but also sets the clipping area. Again, you can leave out the image and the default one will be used. The second form simply turns clipping on and off depending on what you send it (1 or true turns clipping on, while 0 or false turns clipping off). It should be noted that by turning clipping off you do not erase the old clipping boundary, and so you can turn clipping on and off infinitely without loosing your clipping boundaries. Another thing worth mentioning is that, by default, the images clipping boundary is set to the whole image (but, as mentioned earlier, clipping is disabled by default).
It's worth noting that the second version of show_image will ignore your clipping area. It seems that this would be the most disired approach because there any many situations where you would wish to copy a portion of an image (for example, the scoreboard in a game) that wouldn't be in the clipping boundary (chances are, under this situation, the clipping boundary would be set to the main game window). Forcing the programmer to disable clipping, copy the portion, then enable clipping again seems very tedious, and so I have gone a different route.
This is a fairly touchy topic; I provided image offsetting more-or-less just because I could, and I wonder now about its uses. I would recommend not using it simply because it may not exist in version 2.0. However, there is an alternative approach which I feel is much more efficient and intuitive. This alternative will exist in version 2.0 for sure.
void offset_image(int x, int y, Image *img);
The above is the original method which I warn against using. You specify an x and a y parameter which will then be added to each drawing primitive, essentially making the co-ordinate (x,y) be the origin (0,0). As always, the image may be left out. My recommendation, however, is that you use the following:
Image *sub_image(int x, int y, int width, int height, Image *img);
Not only is the above more versitile but it is garaunteed to be in version 2.0. This function returns a new image which is a subset of the image provided (img). In other words, the image returned uses the same image memory as the image you provide, but is restrainted to the area you provide (as far as the image knows, it's only as large as you tell it to be). This function has the same effect as offset_image(), as it sets (x,y) to the origin, but it also has the added advantage of providing a width and a height (therefore providing a whole rectangular area). In other words, where as offset_image still used the remainder of the image, sub_image() can be told to only used the portion you give it.
One of the more interesting features of PETAL is the ability to make images transparent. You can set the background colour to actually be ignored, thereby making the image transparent. You do so by telling PETAL which colour to ignore:
void transparent(int colour, Image *img);
void transparent(bool yes_no, Image *img);
The first form should be fairly self explanatory. You simply specify the colour you wish PETAL to ignore and an optional image. The second form is just like when we talked about clipping an image. This form can be used to turn transparency on and off. The first form, of course, turns transparency on automatically.
No doubt in your programming you'll want to copy one image to another. PETAL provides to ways of doing so:
void copy_image(Image *dest, Image *src, int tx, int ty);
void copy_image(int x, int y, int width, int height, Image *dest, Image *src, int tx, int ty);
The first form simply copies the image src to the image dest. You can leave out tx and ty as they will default to 0 and 0. If you wish to provide them, however, they will allow you to specify exacty where to place the src image in relation to dest.
The second form allows you to specify only a portion of src that you wish to copy to dest. Again, tx and ty can be left out and will default to 0 and 0. Under both versions if you leave src out it will default to the default image.
It only stands to reason that once you've started PETAL up that you'll eventuall want to shut it down. There are basically two steps to this process. First you must delete all the images you've created with the following:
void delete_image(Image *img);
You actually must specify an image in the above. Make sure that you never use an image after you've deleted it, or else your program will probably crash. Next you must actually shut down PETAL. This is done with a simple call to the following:
void deinit_petal(void);
That's it!
Fonts are actually very easy to implement in PETAL, but I decided to describe them under their own section, rather than under basics. PETAL uses a predefined font format; the Borland stroked font format. I chose them because the font format is simple and is fully scalable, and also because there is a freely available editor available for them. This editor is bundled with the registered version, and both versions come with all the standard Borland fonts.
In the future PETAL may support other font formats, such as the new OpenType font format. Version 2.0 will probably also support each system's native font system, therefore allowing you to use whatever fonts exist on the development platform.
To use a font you must first load it. This is a very simple process of providing the file name. Here's a couple ways in which you might do so:
Font myfont("fontname.chr");
Font myfont;
myfont.load("fontname.chr");
Next you'll probably want to draw some text using this some. This, again, is a very easy task. You can use the outtextxy function to do so:
myfont.outtextxy(x, y, "Some text", img, len);
That should be fairly self explanatory. The x and y parameters tell outtextxy where to place the text, while the third parameter is the actual text. The image may be excluded if you wish, and the default image will be used. The last parameter may be a little confusing. It's there to set the maximum length to print. In other words, if you specify 100 for the length, outtextxy will draw characters for 100 pixels and then stop. It does not clip the characters, it simply stops printing them after that cut-off point. If you wish to clip a font, use clip_image() on the image you're writing to.
The length can, of course, be ignored if you wish, and the whole string will be displayed.
You can also customize how your fonts look. The most common method would probably be scaling your fonts. You can do this one of two ways:
void points(int size);
void scale(int numerator, int denominator);
These are both members of the font object, which means they are called like fontname.points(x) and fontname.scale(x,y). The first method will be the most commonly used one because it's simply easier to understand; you specify the point size of the font.
The second one is reminiscent of the old BGI graphics library. You see, Borland fonts are designed at a certain size, and so if you don't scale your fonts to the size you want, they will display at the default size. The numerator and denominator system is used to scale the fonts based upon the fraction your provide. For example, if you want the font to be half as large as the default then call myfont.scale(1,2).
Besides scaling you can also change the colour of the font, and make it bold if you wish.
void colour(int c);
void bold(bool yes_no);
Again, these are members of the font object so call them like all other font members.
Often you'll want to know about the geometry of a font. That is, you'll want to know how tall it is, or how long a certain string would be with a particular font. PETAL provides functions to do exactly that:
int length(char *string);
int height(void);
The length() function will return the length, in pixels, of the provided string, while height() will return the height of the tallest character. Both are member functions of the font object.
Another interesting feature of PETAL is the sprite system. Sprites are really just graphics, but are commonly used in games in situations where they move around quickly and often animate. To accomplish both these tasks PETAL provides a Sprite class.
The first step in using the sprite system is to define your sprites. Use whatever paint program you wish to draw them, but you must save them as PCX files because that is the only format that PETAL will currently understand. Remember, if you're planning on working in an 8bpp colour depth you only have 256 colours, so use them wisely.
After creating your sprites you must create a sprite definition file. This file contains all your sprites, and information as to how PETAL should animate them. Here's an example file:
5 chop0.spr chop1.spr chop2.spr chop3.spr missile.spr 2 4 1 2 3 4 1 5
The first line tells PETAL how many pictures you wish to use. As you can see, this example has five. The pictures are then listed, one after the other, on separate lines. The next line tells PETAL how many sprites you wish to define. In PETAL a sprite is actually a group of pictures allowing animation, this is why you must define your sprites separately from the individual pictures.
You then define your sprites, one at a time, on separate lines. The first number in the sprite definition is the number of pictures it uses. In the first sprite's case you can see it uses 4 pictures to make up its animation. Next you list which pictures these are. It would be very tedious to type in the file names again, and so instead, you type in the picture's number where 1 represents the first image, 2 represents the second and so on. In this example, the first sprite uses the first 4 pictures, and the second sprite uses only one picture, the last one. Sense the last sprite only uses one picture, it makes sence that this sprite cannot animate (you need more than one picture to animate, of course).
Next you must load the sprites into your program. You do this by defining a Sprite object and giving it your sprite definition file:
Sprite mysprites(640, 480); mysprites.load("sprites.dat");
You'll notice that when I create my sprite object I specify some parameters. These are the resolution parameters for the sprite's area. Your sprites don't have to take up the whole screen, an in fact probably rarely will simply because you'll probably want room for a scoreboard (or perhaps you could make the scoreboard a sprite!). You can leave these out and they will default to fill the whole screen.
Next you simply load the sprites. Easy as that. One thing you might want to do, however, is to set the palette now. If you're in the 8bpp colour depth then you'll probably want to give the sprite's the colours they want. You can do so by accesing the individual sprite's pictures' palette:
set_pal(mysprites.image[0].palette);
The above would set the palette to the one defined by your first sprite picture.
Okay, now that you've loaded the sprites, the next step is to actually use them. By default, every sprite is turned off. In other words, it is not visible. To turn a sprite on or off, you can use the following member functions:
void on(int num);
void off(int num);
That's easy enough. You simply pass the number of the sprite (in our example you would pass 1 for the first sprite, and 2 for the second). Remember, these are member functions of the object Sprite.
Before turning a sprite on or off, however, it makes sence to tell PETAL where you wish to place this sprite. You can also use the Sprite object to tell where any sprite currently is:
void set_pos(int num, int x, int y);
void get_pos(int num, int *x, int *y);
Easy enough, I would think. Again, num is the sprite number, and again, these are member functions of the Sprite object. You can, of course, set a sprite's position wether it is on or not. Now, for speed the Sprite object displays all these sprites to an image. If you wish to actually display this image to the screen then you must tell the Sprite object to do so:
void update(int x, int y);
A simple call to the above function will display the sprite's image at location (x,y). You can, of course, ignore those parameters and update will display the image at the origin, (0,0).
What good are sprites without a cool background behind them? I'm sure you'll want to create a background behind your sprites, and so you can do so by accessing the bg member of the Sprite object. It is an image and so you can use all of PETAL's functions to draw your background to this image. For example, to make the background red, you could do the following:
clear( colour(1,0,0), mysprites.bg); show_image(mysprites.bg); copy_image(mysprites.screen, mysprites.bg);
Because of the nature of my sprite system it is also a good idea to copy your altered background to the screen image as well (as shown above).
PETAL is currently able to read the PCX graphics file format. It is, however, limited to the 256 colour version only. This really shouldn't be a problem though. PETAL will convert all 256 colour PCX's to whatever colour depth you happen to be in, and each PCX can, of course, have different 256 colours.
There is really only one topic of interest here and that is how to load a PCX. This is really simple, and can be done in either of the following ways:
Pcx mypcx("filename.pcx");
Pcx mypcx;
mypcx.load("filename.pcx");
That's really about it. You will, of course, want to know how to display your PCX's though. You can do so like the following:
show_image(mypcx.img);
That's just a regular image object, so if you want, you can even draw to it. One last thing of interest here is setting the palette to the one contained in the PCX. This can be done like the following:
set_pal(mypcx.palette);
Again, very easy.
Lastly, I'll show you how you can use PETAL to get the position and button status of the mouse, as well as keyboard input portably across all PETAL supported platforms. First, you can use the following to get the currently depressed keyboard key:
char get_key(void);
This function will return 0 if no keys are depressed, so if you absolutely need a key press you'll have to wrap this function call withen a while loop.
To get the mouse position and button status you can use the following:
int get_pointer(int *x, int *y);
You pass it pointers to two integers which will contain the mouse's position after the call. The mouse's position will never be larger than your current resolution. The button status is returned by this function. You can simply test this to see what button is pressed. If it equals 1, the first button is pressed. If 2, the second button is pressed. Lastly, if it is 3, the third, or middle button, is pressed.