by Kamran Husain
IN THIS CHAPTER
This chapter will introduce you to the following graphics tools and libraries available
under Linux:
The Portable Bitmap Toolkit (pbm toolkit), developed in 1991, is a very powerful toolkit for converting graphics images from one format to another. The toolkit consists of a set of programs that convert data images in a graphics format to or from a PBM format.
The pbm toolkit has no graphical user interface, and all the commands operate on images via the command-line interface. The toolkit programs all work by utilizing the standard input and output features of programs. Each program in the toolkit reads image data from its standard input and writes back to standard output.
The pbm toolkit is available from Linux FTP sites in both binary and source forms. At the ftp://sunsite.unc.edu site, the files can be found in the pub/Linux/apps/graphics/convert subdirectory. The file used for this chapter was the binary archive for Linux: pbmplus-10dec91-bin.tar. Untar the files in a directory, and you then see a README file and man and bin directories under the pbmplus10dec91 directory. Point your PATH to the bin directory. If you cannot get the binary files to work in your installation, you can download the source code in the file pbmplus-10dec91_tar.Z. Uncompress and untar the file to a subdirectory, and follow the instructions in the README file to build the pbmplus executables.
The man directory contains a well-documented set of man pages for the utilities in the toolkit. You can view the contents with the command
nroff -man filename
where filename is the name of the man file you want to look at. The complete list of binary files extracted from this tar archive is shown in Listing 69.1.
$ ls pbmplus10dec91/bin -rwxr-xr-x 1 root root 3568 Oct 21 1996 atktopbm -rwxr-xr-x 1 root root 2112 Oct 21 1996 brushtopbm -rwxr-xr-x 1 root root 2136 Oct 21 1996 cmuwmtopbm -rwxr-xr-x 1 root root 3864 Oct 21 1996 fitstopgm -rwxr-xr-x 1 root root 2628 Oct 21 1996 fstopgm -rwxr-xr-x 1 root root 6532 Oct 21 1996 g3topbm -rwxr-xr-x 1 root root 2844 Oct 21 1996 gemtopbm -rwxr-xr-x 1 root root 6184 Oct 21 1996 giftoppm -rwxr-xr-x 1 root root 2320 Oct 21 1996 gouldtoppm -rwxr-xr-x 1 root root 2320 Oct 21 1996 hipstopgm -rwxr-xr-x 1 root root 2504 Oct 21 1996 icontopbm -rwxr-xr-x 1 root root 6108 Oct 21 1996 ilbmtoppm -rwxr-xr-x 1 root root 2804 Oct 21 1996 imgtoppm -rwxr-xr-x 1 root root 2540 Oct 21 1996 lispmtopgm -rwxr-xr-x 1 root root 2248 Oct 21 1996 macptopbm -rwxr-xr-x 1 root root 2144 Oct 21 1996 mgrtopbm -rwxr-xr-x 1 root root 1884 Oct 21 1996 mtvtoppm -rwxr-xr-x 1 root root 1844 Oct 21 1996 pbmlife -rwxr-xr-x 1 root root 1836 Oct 21 1996 pbmmake -rwxr-xr-x 1 root root 3104 Oct 21 1996 pbmmask -rwxr-xr-x 1 root root 2996 Oct 21 1996 pbmreduce -rwxr-xr-x 1 root root 3116 Oct 21 1996 pbmtext -rwxr-xr-x 1 root root 2768 Oct 21 1996 pbmto10x -rwxr-xr-x 1 root root 1796 Oct 21 1996 pbmtoascii -rwxr-xr-x 1 root root 2424 Oct 21 1996 pbmtoatk -rwxr-xr-x 1 root root 1964 Oct 21 1996 pbmtobbnbg -rwxr-xr-x 1 root root 1988 Oct 21 1996 pbmtocmuwm -rwxr-xr-x 1 root root 1956 Oct 21 1996 pbmtoepson -rwxr-xr-x 1 root root 5552 Oct 21 1996 pbmtog3 -rwxr-xr-x 1 root root 2124 Oct 21 1996 pbmtogem -rwxr-xr-x 1 root root 2988 Oct 21 1996 pbmtogo -rwxr-xr-x 1 root root 2548 Oct 21 1996 pbmtoicon -rwxr-xr-x 1 root root 2164 Oct 21 1996 pbmtolj -rwxr-xr-x 1 root root 3020 Oct 21 1996 pbmtomacp -rwxr-xr-x 1 root root 1916 Oct 21 1996 pbmtomgr -rwxr-xr-x 1 root root 1884 Oct 21 1996 pbmtopi3 -rwxr-xr-x 1 root root 1876 Oct 21 1996 pbmtoplot -rwxr-xr-x 1 root root 1876 Oct 21 1996 pbmtoptx -rwxr-xr-x 1 root root 3596 Oct 21 1996 pbmtox10bm -rwxr-xr-x 1 root root 3180 Oct 21 1996 pbmtoxbm -rwxr-xr-x 1 root root 1836 Oct 21 1996 pbmtoybm -rwxr-xr-x 1 root root 3556 Oct 21 1996 pbmtozinc -rwxr-xr-x 1 root root 7288 Oct 21 1996 pbmupc -rwxr-xr-x 1 root root 3532 Oct 21 1996 pcxtoppm -rwxr-xr-x 1 root root 1572 Oct 21 1996 pgmbentley -rwxr-xr-x 1 root root 5644 Oct 21 1996 pgmcrater -rwxr-xr-x 1 root root 2140 Oct 21 1996 pgmedge -rwxr-xr-x 1 root root 2164 Oct 21 1996 pgmenhance -rwxr-xr-x 1 root root 1916 Oct 21 1996 pgmhist -rwxr-xr-x 1 root root 3556 Oct 21 1996 pgmnorm -rwxr-xr-x 1 root root 1868 Oct 21 1996 pgmoil -rwxr-xr-x 1 root root 2372 Oct 21 1996 pgmramp -rwxr-xr-x 1 root root 12404 Oct 21 1996 pgmtexture -rwxr-xr-x 1 root root 2992 Oct 21 1996 pgmtofits -rwxr-xr-x 1 root root 2468 Oct 21 1996 pgmtofs -rwxr-xr-x 1 root root 2204 Oct 21 1996 pgmtolispm -rwxr-xr-x 1 root root 6972 Oct 21 1996 pgmtopbm -rwxr-xr-x 1 root root 2412 Oct 21 1996 pgmtoppm -rwxr-xr-x 1 root root 1844 Oct 21 1996 pi1toppm -rwxr-xr-x 1 root root 1844 Oct 21 1996 pi3topbm -rwxr-xr-x 1 root root 16752 Oct 21 1996 picttoppm -rwxr-xr-x 1 root root 4556 Oct 21 1996 pjtoppm -rwxr-xr-x 1 root root 4316 Oct 21 1996 pnmarith -rwxr-xr-x 1 root root 4684 Oct 21 1996 pnmcat -rwxr-xr-x 1 root root 5524 Oct 21 1996 pnmconvol -rwxr-xr-x 1 root root 2556 Oct 21 1996 pnmcrop -rwxr-xr-x 1 root root 2268 Oct 21 1996 pnmcut -rwxr-xr-x 1 root root 2188 Oct 21 1996 pnmdepth -rwxr-xr-x 1 root root 1804 Oct 21 1996 pnmenlarge -rwxr-xr-x 1 root root 1892 Oct 21 1996 pnmfile -rwxr-xr-x 1 root root 3772 Oct 21 1996 pnmflip -rwxr-xr-x 1 root root 2712 Oct 21 1996 pnmgamma -rwxr-xr-x 1 root root 1604 Oct 21 1996 pnminvert -rwxr-xr-x 1 root root 1556 Oct 21 1996 pnmnoraw -rwxr-xr-x 1 root root 3308 Oct 21 1996 pnmpaste -rwxr-xr-x 1 root root 5916 Oct 21 1996 pnmrotate -rwxr-xr-x 1 root root 6508 Oct 21 1996 pnmscale -rwxr-xr-x 1 root root 3228 Oct 21 1996 pnmshear -rwxr-xr-x 1 root root 1804 Oct 21 1996 pnmtile -rwxr-xr-x 1 root root 8072 Oct 21 1996 pnmtops -rwxr-xr-x 1 root root 4224 Oct 21 1996 pnmtorast -rwxr-xr-x 1 root root 4836 Oct 21 1996 pnmtoxwd -rwxr-xr-x 1 root root 3404 Oct 21 1996 ppmdither -rwxr-xr-x 1 root root 13204 Oct 21 1996 ppmforge -rwxr-xr-x 1 root root 1836 Oct 21 1996 ppmhist -rwxr-xr-x 1 root root 1652 Oct 21 1996 ppmmake -rwxr-xr-x 1 root root 12620 Oct 21 1996 ppmpat -rwxr-xr-x 1 root root 6876 Oct 21 1996 ppmquant -rwxr-xr-x 1 root root 1972 Oct 21 1996 ppmrelief -rwxr-xr-x 1 root root 5956 Oct 21 1996 ppmtoacad -rwxr-xr-x 1 root root 4852 Oct 21 1996 ppmtogif -rwxr-xr-x 1 root root 4412 Oct 21 1996 ppmtoicr -rwxr-xr-x 1 root root 4136 Oct 21 1996 ppmtoilbm -rwxr-xr-x 1 root root 4116 Oct 21 1996 ppmtopcx -rwxr-xr-x 1 root root 4740 Oct 21 1996 ppmtopgm -rwxr-xr-x 1 root root 2444 Oct 21 1996 ppmtopi1 -rwxr-xr-x 1 root root 3680 Oct 21 1996 ppmtopict -rwxr-xr-x 1 root root 3652 Oct 21 1996 ppmtopj -rwxr-xr-x 1 root root 2228 Oct 21 1996 ppmtopuzz -rwxr-xr-x 1 root root 1956 Oct 21 1996 ppmtorgb3 -rwxr-xr-x 1 root root 2964 Oct 21 1996 ppmtosixel -rwxr-xr-x 1 root root 6312 Oct 21 1996 ppmtotga -rwxr-xr-x 1 root root 3644 Oct 21 1996 ppmtouil -rwxr-xr-x 1 root root 3160 Oct 21 1996 ppmtoxpm -rwxr-xr-x 1 root root 2148 Oct 21 1996 ppmtoyuv -rwxr-xr-x 1 root root 2276 Oct 21 1996 psidtopgm -rwxr-xr-x 1 root root 2004 Oct 21 1996 qrttoppm -rwxr-xr-x 1 root root 3484 Oct 21 1996 rasttopnm -rwxr-xr-x 1 root root 2244 Oct 21 1996 rawtopgm -rwxr-xr-x 1 root root 3764 Oct 21 1996 rawtoppm -rwxr-xr-x 1 root root 2132 Oct 21 1996 rgb3toppm -rwxr-xr-x 1 root root 7836 Oct 21 1996 sldtoppm -rwxr-xr-x 1 root root 2636 Oct 21 1996 spctoppm -rwxr-xr-x 1 root root 2052 Oct 21 1996 sputoppm -rwxr-xr-x 1 root root 4776 Oct 21 1996 tgatoppm -rwxr-xr-x 1 root root 3492 Oct 21 1996 xbmtopbm -rwxr-xr-x 1 root root 4588 Oct 21 1996 ximtoppm -rwxr-xr-x 1 root root 4084 Oct 21 1996 xpmtoppm -rwxr-xr-x 1 root root 6684 Oct 21 1996 xwdtopnm -rwxr-xr-x 1 root root 2120 Oct 21 1996 ybmtopbm -rwxr-xr-x 1 root root 2220 Oct 21 1996 yuvtoppm
Four types of base formats are supported in the pbm toolkit:
You usually convert from a format to either PPM or PNM. Programs that work with PPM files can write only PPM files. Programs that process PNM files can write PBM, PPM, or PGM files.
This powerful toolkit is surprisingly easy to use. Let's say you want to convert a GIF image into a PCX image. This is the command you would use on the file myfile.gif to get myfile.pcx:
$ giftoppm < myfile.gif | ppmtopcx > myfile.pcx
The pnm* functions let you manipulate the contents of images. You can rotate or scale an image with these functions. For example, to scale the original GIF image down by a factor of two, you would use the following command:
$ giftoppm < myfile.gif | pnmscale 3 | ppmtopcx > myfile.pcx
Particularly interesting is the pbmtext program, which converts text files into PPM files. You can therefore take text lines and convert them into images for use in World Wide Web server applications for generating non-editable messages using a fixed font. This would be a typical usage of pbmtext:
$ pbmtext < /etc/motd | pnmscale 4 | ppmtogif > motd.gif
The scale factor would have to be adjusted to fit the particular GIF image size you want. To get rotated text at a 45-degree angle (0 degrees is a horizontal line going from left to right), try the following command:
$ pbmtext < /etc/motd | pnmrotate 45 | ppmtogif > motd.gif
The angle parameter should be between -90 and 90 degrees. For angles greater than that, try flipping the image with pnmflip to get that extra 180-degree rotation.
The best use for the pbm toolkit is from within shell scripts where you do not want to have any user interaction, especially if you are doing only basic scalar operations on images or converting data from one graphical format to another.
Of course, the major thing lacking with the pbm toolkit is the capability to graphically show the data. Let's see how to display the data we create with the pbm toolkit by using the xv toolkit.
The xv program in Linux is written by John Bradley. The version number of the program on the CD-ROM is 3.10 and is dated 12/16/94. To see an image, issue the following command:
$ xv filename &
It's a good idea to run xv in the background to free up the terminal on which you are working. For example, to see the motd.gif image we just created with the pbm toolkit, we would issue this command:
$ xv motd.gif &
You can bring up the main menu for the screen by clicking the right mouse button
anywhere on the display window. You are presented with the menu shown in Figure 69.1.
FIGURE
69.1. The xv main menu screen.
The menu lets you load and save graphics files in several formats, and load and save
programs in different formats. Several files can be loaded at one time, but only
one can be viewed at a time. Using this graphical program is helpful if you want
to work within an interactive environment.
Several filtering algorithms are available. You can rotate images at an angle, as shown in Fig-ure 69.2. An image can also be embossed, as shown in Figure 69.3. An embossed image can be a bit hard to read at times, so you might want to edit the colors with the color editor in the 24/8 Bit menu option. Embossing looks better for graphical images, as shown in Figure 69.4, than for text.
FIGURE 69.2. An image rotated at a 12-degree angle.
FIGURE 69.3. An embossed image with text.
FIGURE
69.4. An embossed image without text.
The xv program provides you with the power of the pbm toolkit and the capability
to show the results right away. It is an interactive program, however, and if you
want to do something within a shell script, you would want to do it with the pbm
toolkit. It's an excellent package for manipulating 2D images.
The Geomview toolkit is another freeware tool that works great for displaying 3D images. You can get the software for Linux from the Web site at
http://www.geom.umn.edu/software/download/geomview.html
Geomview is available in source form or in binary form for Linux. (Unless you are the adventurous kind or are impatient, get the binary form. The source files took forever to build on my Linux box, and I wished I had downloaded the ELF binary image instead.) The Web site is from University of Minnesota's Geometry Center, where Geomview was developed.
You need to be aware of certain requirements for visualizing 3D images on Linux machines:
After you untar the file, change to the Geomview directory that tar creates for you. Then you can issue the following command in bash to set the environment variable GEOMVIEW to this current directory:
$ GEOMVIEW=`pwd`; export GEOMVIEW
The INSTALL file provides more details about how and where to store the executable if the current directory is not suitable for you. If you want to make Geomview available to you all the time, be sure to set GEOMVIEW in your .profile file. Then make sure that Geomview is in the PATH, and enter the following command at the prompt:
$ geomview
When you start Geomview, you are presented with the main screen, as shown in Figure
69.5. Use the File menu to go to the ./data/geom directory, and open a file
with the oogl extension. (See Figure 69.6.) These files are good demo files
to work with. You can load more than one file at a time.
FIGURE
69.5. The main menu for Geomview.
FIGURE 69.6. The object selection menu for Geomview.
The Geomview software is great for visualizing 3D images, and it includes the option to do "fly-bys" on images. You can literally walk around a 3D object you have created and can even "step into" it. Several tools are available with the buttons in the Tools menu, shown in Fig- ure 69.7. The buttons in the menu enable you to rotate and translate objects with the mouse button. Press the left mouse button and move the image by holding down the button and moving the mouse. The movement is based on the inertia of the object. Releasing the mouse does not stop the object from rotating! This is a bit hard to master at first because the speed of motion is somehow proportional to the speed your mouse is moving when the button is released. To get static movement, you must stop the mouse movement before you release the mouse button.
FIGURE
69.7. The Tools menu for Geomview.
The speed of the mouse is also a factor when you select the "fly" option.
Press the fly button in the menu. Hold down the left mouse button in the view area.
Move the mouse at the speed at which you want to do the fly-by, and then release
the mouse button. Remember that the rendering is slow on a 486 system and that you
might have to work slowly to get just the right effect. The Geomview application
uses a command language called the Graphical Command Language (GCL). Using GCL, you
can create your animation sequences by sending commands via pipes directly to Geomview.
The work of rendering and deciphering images is performed with the Object Oriented
Graphics Library (OOGL), also developed at the University of Minnesota. The full
documentation for the OOGL is available at the umn site at
http://www.geom.umn.edu/software/geomview/geomview_toc.html
The documentation includes a great tutorial with lots of examples and code to work with.
The OpenGL standard is a software interface and libraries designed for 3D graphics on high-end machines such as Iris, Silicon Graphics, IBM, and Sun workstations. Yes, OpenGL libraries also exist for Windows NT products. The OpenGL libraries are generally optimized for the hardware they are designed for, whereas the software interface to their functions remains constant.
The Mesa toolkit was written primarily by Brian Paul as a software-only solution running on top of X Window. The primary motivation for this design is to enable OpenGL programs to run on all platforms that run X Window. This means you can run Mesa on Linux too!
The cost of abstracting this software from the hardware via the layer of X Window is loss of performance. Programs running with Mesa will almost certainly run slower on X than on library code designed for OpenGL. The Mesa toolkit library can be called directly from within C and FORTRAN programs. The Mesa library offers an almost complete set of OpenGL functions. More functions are being added to the library as we go to print. The latest version of the software and documentation can be downloaded from the HTML page at
http://www.ssec.wisc.edu/~brianp/Mesa.html
The files are found in zipped, tar archives. The latest version is 2.0, and the source file is called Mesa-2.0.tar.gz. At the rate versions were changing while I was writing this chapter, you will see a later version of the library.
Untar and unzip the archive, and change directories into the Mesa source tree. Then run the following command:
$ make linux
The make program takes a very long time to complete. So be patient.
After the make program terminates, you are left with the library directory ./lib. As root, link these libraries to /usr/local/lib because the make program does not move these for you. The header files should be linked under /usr/local/include.
The makefile I used for my system and setup is shown in Listing 69.2.
INCPATH=/home/khusain/mesa/Mesa-2.0/include LIBPATH=/home/khusain/mesa/Mesa-2.0/lib LIBX= /usr/X11/lib LIBS= -lMesaaux -lMesatk -lMesaGL -lMesaGLU -lXext -lX11 -lm s1: s1.c gcc -o s1 s1.c -I$(INCPATH) -L$(LIBPATH) -L$(LIBX) $(LIBS)
Note that in Listing 69.2 I used the /home/khusain/mesa directory rather than /usr/local/lib. This is simply for my convenience and pure laziness while I write this book. You should really just create a link from the /usr/local/lib directory to the files in the Mesa package. I just have too many software packages on my Linux machine that are cluttering my /usr/local/lib source tree. It's ironic that even my /usr/local tree, which is intended for use with custom packages, is too cluttered up with weird links because I have installed and removed so many different versions of different packages. Oh, well; I will do a spring cleanup...real soon now. ;-)
Four libraries must be linked in with this program. All of these libraries are located in the lib subdirectory. If you are going to port this code to non-Linux machines, you might not have to link in the MesaGLU library because this provides the layered interface for the underlying graphics libraries.
The test program I tried was the one based on the samples provided in the Mesa package. I actually prefer to write simpler stuff because it's easier to explain some of the examples. See Listing 69.3 for a simple OpenGL program.
1 2 #include <stdlib.h> 3 #include <GL/gl.h> 4 #include <glaux.h> 5 6 float x,y; 7 8 void moveUp() { y += 10.0; } 9 void moveDown() { y -= 10.0; } 10 void moveRight() { x += 10.0; } 11 void moveLeft() { x -= 10.0; } 12 13 void forever() 14 { 15 glClearColor(1.0,1.0,1.0,0.0); 16 glClear(GL_COLOR_BUFFER_BIT); 17 glColor3f(0.0,0.3,0.0); 18 glMatrixMode(GL_PROJECTION); 19 glLoadIdentity(); 20 glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0); 21 glRotatef(x,1.0,0.0,0.0); 22 glRotatef(y,0.0,1.0,0.0); 23 auxWireCube(1.0); 24 auxWireCube(0.5); 25 glFlush(); 26 } 27 28 29 void main(int argc, char *argv[]) 30 { 31 x = 0.0; 32 y = 0.0; 33 auxInitDisplayMode(AUX_SINGLE | AUX_RGB); 34 auxInitPosition(0,0,400,400); 35 auxInitWindow(argv[0]); /* program name */ 36 auxKeyFunc(AUX_UP,moveUp); 37 auxKeyFunc(AUX_DOWN,moveDown); 38 auxKeyFunc(AUX_RIGHT,moveRight); 39 auxKeyFunc(AUX_LEFT,moveLeft); 40 auxMainLoop(forever); 41 }
Two include files are required for building an application using the Mesa-enabled program. Two global variables are declared at line 6 for the x and y angle of rotation. Lines 8 through 11 define four callback functions that increment and decrement the value of x and y by 10. Each of these functions is designed to be called when a key is pressed. We will be tying these functions to presses of the arrow keys on the keyboard. The values of the keys are defined in OpenGL as AUX_UP, AUX_DOWN, AUX_LEFT, and AUX_RIGHT. (Only the arrow keys worked with this program. The numeric keys with the NumLock key off did not work with these definitions.)
The forever() function is called whenever the window area for the application has to be refreshed. (See line 13.) There are several function calls to each of the libraries in Mesa. The gl functions call the Mesa libraries, whereas the aux functions call the Mesaaux (auxiliary) library functions. The glClearColor() function clears the buffer and color display memory. If you are going to use surfaces and lighting in your program, you must also clear the bit buffer with a call to glClear like this:
16 glClear(GL_COLOR_BUFFER_BIT | GL_COLOR_DEPTH_BUFFER);
All these constants are defined in the header files for Mesa. All functions that end in the letter f take floating-point numbers as arguments. (You should use the decimal point when specifying numbers as arguments to functions, or the gcc compiler will complain.) The function that takes integers as arguments is defined without the f prefix; that is, glColor(int, int, int). Instead, we used the floating format version in line 17 like this:
17 glColor3f(0.0,0.3,0.0);
The next lines (18-22) define the mode of display as GL_PROJECTION and set up the orthogonal projection for the viewing angle. The rotations are done from the origin (0,0,0) and are performed along the line drawn from the point specified in the arguments to the glRotate call. For example, glRotatef(x,1.0,0.0,0.0) rotates about the line from (0,0,0) to (1,0,0). The next two auxiliary functions (lines 23 and 24) are used to draw a wire cube of scale 0.5 and 1.0, respectively. After the drawing commands are sent, we simply flush the buffers and we are done.
The main program does the initialization of the (x,y) coordinates and the initial starting position for the viewing angle. It also initializes the color model (AUX_RGB) and the windows with the program name (lines 33-35). The numeric keys are assigned to handler functions in lines 36-39. Finally, the message loop to process events that in turn call the function to handle user input and render the graphics is done with the call to auxMainLoop in line 40. The function auxMainLoop never returns, and the program must be terminated with an exit() call in one of the handler functions.
This is a very simple introduction to OpenGL. A lot of the information simply cannot be covered in one chapter. Each function I have shown has many variants and options. The OpenGL library itself has many functions for coloring, shading, and three-dimensional graphics tricks that will certainly make programming a joy. Unfortunately, you need a fast Linux box to really take advantage of some the advanced features. Speaking of advanced features, forgive me for not discussing what you can do with OpenGL programming, but lack of time and space in a Linux book simply makes it impossible to deviate into this arena. A topic for this type of programming would take at least a book (or two) on its own.
For more information on the OpenGL standard libraries, check out the files at the Web site at
http://www.sgi.com/Technology/OpenGL
You'll find a FAQ and lots of sample code. The sample programs within the Mesa package run quite slowly with a 486 processor running at 100MHz. The capability to write portable OpenGL code on a Linux machine, however, makes the slow speed a minor inconvenience when you consider the price of a Sun or Iris workstation.
There you have it. We covered four programs and packages that are available on Linux for performing graphical image processing. The pbm toolkit is useful when you want to manipulate images from within programs. For interactive image editing, try using the xv program. Three-dimensional viewing and creating of models is possible with the Geomview program. Finally, you can use the Mesa libraries for writing OpenGL code that is portable for other UNIX and Windows NT systems with installed OpenGL libraries.