This article provides a quick lesson in creating new images from old in real time.
by Jimmy Ball
One of the best kept secrets on the World Wide Web is how GIF images are created in real time or ``on the fly''. Some of the reasons for creating GIF images on the fly include highlighting a map image, combining several images into one (similar to the layers feature in CAD packages) and even making virtual postcards.
My reason for developing programs to create GIF images on the fly was for a campus map that would highlight important locations. That is, when anyone visited our campus map web page and selected a certain building or site to locate, we wanted to highlight that particular site in some way.
As with most projects, all of the details had not been worked out in advance, so I chose a development route that would save me time--I decided to write a program to automate the task. Programming is more fun for me than editing 100 or more images, especially when people tend to change their minds. It turns out that the development time was not dramatic, and when modifications were requested, it was a trivial task.
In this article, I simplify my project and outline the steps to create a new GIF image by adding an arrow to an existing image. You need to be familiar with CGI programming in Perl as well as HTML. You do not have to be an expert.
First, download and install the items listed below.
Once you have all the packages installed, you are ready to create your first CGI script that uses GD.pm.
In the first example, we read in two GIF images: a main image and an arrow image. Based on input from the user, we ``place'' the arrow on the main image. Last, we display the final image. Listing 1 shows the source code for this example.
Line 2 requests the use of the GD interface and Line 3 verifies that we are using Perl 5.002 or higher. Line 5 includes the routines from cgi-lib.pl for reading form input. One of those routines, ReadParse, is used in Line 6 to get the form input data. The data from this form is stored in the associative array named FORM.
Next, Lines 8-11 set up variables which include the filenames for the main and arrow images. In addition, the variables for placement of the arrow on the main image are assigned default values, if they are not provided from a web form or other technique. I will show an example of passing variables to a script later.
Lines 13-15 and 17-19 do the same thing. The first set reads the main image into a variable called $main. The second set reads the arrow image and stores the image in the variable $arrow. In each set, you will notice a routine newFromGif GD::Image(GIF). This is the GD function that reads an image from the file and stores it into a variable. The nice thing about this routine is that it does not require image dimensions, although you probably want to know them.
Line 21 does most of the work in the script. It takes the arrow image and stores it at the x-pixel position $FORM{xp} and the y-pixel position $FORM{yp} on the main image. The last four parameters for the function define how much of the arrow image is copied. In our case, all of the arrow image was copied onto the main image.
Finally, Lines 23 and 24 output the image as if it were being called from an HTML file and Line 25 ends the script.
Now, we need to call the script to generate the image. You can do this in a couple of ways. First, just call the CGI script directly from a web browser like Netscape. An example URL would be http://bodock.vislab.olemiss.edu/cgi-bin/example1.cgi.
Calling the script would simply display the image on the screen. You can also put this URL in an IMG tag of an HTML file like the following:
<IMG SRC= "http://bodock.vislab.olemiss.edu/cgi-bin/example1.cgi" ALT="">Last, you can modify the URL slightly to specify an x,y position for the arrow image. An example URL within an IMG tag would be as follows:
<IMG SRC="/cgi-bin/example1.cgi?xp=10&yp=40" alt="">Notice I took off the full URL. Doing this assumes that the CGI script is on the same machine as the web page.
The next example uses a few more of the functions supplied with the GD interface. We draw the arrow rather than read it from a file. By drawing the arrow, we can control the angle of the arrow which is drawn on the main image. Listing 2 shows the source code for this example.
Lines 1-8 should look familiar from the previous example. We simply define variables for the main GIF image and the position/angle of the arrow. Just for simplicity, I did not include the ReadParse routine, so no user input is allowed. As in the first example, we read the main image into the variable $im in Lines 10-12.
Line 15 is new. This line simply allocates a color based on RGB values which range from 0 to 255. The color blue has a RGB value of (0,0,255). We can then use the color later when drawing the arrow.
Lines 17 calls the function for creating the arrow. This routine starts on Line 26. In this routine we create a new polygon (an arrow) and then spend most of the time finding the points for the polygon. After we find an x,y position for a corner of the polygon, we put it in the polygon object, $poly.
As mentioned earlier, our polygon is an arrow. Line 37 creates the polygon variable. Then Lines 38-44 find points for the polygon. Here is how that section works. Lines 33-34 define the x,y positions for an arrow that points north. Since we may want to rotate this arrow, Lines 38-44 come into play. These lines figure out the new x,y positions of each point taking the rotation angle into consideration. Line 43 adds the point to the polygon taking into consideration the size of the image (subtracting 75) and the x,y position where the arrow will be placed ($xpos and $ypos). Rather than spending all day trying to explain this section of the code, take a look at an old CGA graphics book. In fact, I got the formulas for this section from a CGA graphics manual.
At last, we have a polygon stored as the variable $poly which is returned to the main program. Line 19 is another GD interface call to fill the arrow with our color and place it on the main image. Finally, the image is displayed and the script exits.
Like the first example, you can call this CGI script both directly and via an IMG tag as shown here:
<IMG SRC= "http://bodock.vislab.olemiss.edu/cgi-bin/example2.cgi" ALT="">If you want to dynamically control the angle of the arrow, you would need to modify this example to include the cgi-lib.pl library, read in the data with ReadParse and then define $angle appropriately.
We now know how to create a GIF image on the fly and view it both directly and from an IMG tag within an HTML file. What if you need to save the image?
Of course, first of all you need a directory where the images will be created. Depending on your web-server configuration, the ownership and write permissions of that directory will need to be modified so that the CGI program can save the image. Discuss this matter with your webmaster.
Once that issue is resolved, your CGI script can output the image to a file using a unique, file-naming scheme. To recall this file name, you can use a database or simply inform the client of the name of the file. For example, if you develop a CGI program to create virtual postcards, the only person who needs the name of the image is the sender and/or the recipient of the postcard. This could be displayed within the web browser once the postcard has been created or e-mailed to the recipient. In other situations, several people may need to view the image. In this case, a database for storing the file name with the associated options is the best choice in my opinion.
In either case, storing the image can be done in addition to displaying it. That is, just use regular Perl I/O statements to print the image to a file. Pick up any Perl programming book and check out the File I/O section.
Last, you might have to keep a watch on the directory where the images are stored. If the file names are unique and the images are large, consider deleting old files periodically to avoid buying a new disk each month. Then again, you might want to use that as an excuse to obtain more disk space.
Of course, the documentation that comes with GD.pm can fill in any gaps that you might have. In addition, here are two URLs that show some of my programming work in creating GIF images on the fly. The first is a campus map for the University of Mississippi (UM). The second example highlights a few interesting sites about my home state.
For the UM campus map, the original GIF creation programs are written in C and use the gd C library. You can visit that page at the URL http://www.olemiss.edu/hospitality/maps/. The Find option at the top of the page will take you to other web pages. From there, you can select a site of interest. With a simple mouse click, an image is displayed of the campus with an arrow to highlight your selection. In addition, all the information on each building is listed in a database, so the CGI program has to first get this information before the image is created.
A more elaborate example of GIF images created on the fly can be found at the URL http://bodock.vislab.olemiss.edu/onthefly.html. This combines several options such as text, a choice of colors, a variety of pointers, etc. Be sure to follow the ``More Practical Example'' link at the bottom of the left-most frame. That section shows examples of adding several text and highlighters at the same time. In addition, the map is clickable thanks to some HTML code for a a client-side image map. The CGI programs for this application were developed with Perl and the other packages mentioned earlier.
As with any development project, everyone has their opinion of the best package to use. I have been very satisfied with the gd C library and the GD.pm interface for creating GIF images on the fly. You may find other packages or interfaces that suit your needs better depending on your experience and your web server platform.
My only advice comes originally from my Dad, but slightly modified for the nineties and GIF image creation: ``Never use a wrench (image editor) when an air ratchet (programming with GD.pm) can do the job.''
Jimmy Ball is a Supercomputer User Consultant with the Mississippi Center for Supercomputing Research located on the campus of the University of Mississippi. He also freelances as an Internet consultant focusing on web development, Unix administration and training. Jimmy can be reached by e-mail at ccjimmy@mcsr.olemiss.edu or jball@ebicom.net.