If you think the interaction through the Web is limited to clicking hyperlinks and filling out forms, think again. While technologies like ActiveX, Java, and Shockwave have made the creation of interactive games and other products a popular feature of many Web sites, you can still create some interesting Web-based image manipulation applications with nothing more than Perl and JavaScript.
This chapter takes a break from the serious side of Web scripting and presents a simple, yet fun, way to waste hours of online time.
A Web-based coloring book is a heavy-duty data-transmission application involving:
Keeping track of current drawing color.
Responding to the user's paint requests.
Generating new images and incorporating new colors the user enters.
Transmitting the image back to the user.
Transmitting images alone makes the application "Internet intensive." If either your connection to your server or your server to the Internet is slow or bogs down due to massive activity, the coloring experience can be less than enjoyable. To try to offset this, the script set presented here attempts to minimize the amount of data transmitted between the client and the server by using JavaScript. This reduces the amount of data the server has to transmit to the minimum: a new graphic with additional painted colors.
Splitting the task of maintaining the coloring book between JavaScript and Perl requires setting up a two-frame interface through the browser, as shown in figure 25.1.
The top frame, the easel, runs as the user's canvas, displaying the changes as the user paints. The bottom frame, the palette, holds the available colors for painting, as well as identifies the current drawing color.
The color palette has three duties:
Displaying the table is relatively simple, given the advanced HTML table controls available under Navigator 3.0 and Explorer 3.0. Because you can specify the background color of individual table cells, you do not need to load a different "color swatch" from the server for each palette color you want to support. In essence, the color table code would look like the fragment in listing 25.1.
NOTE |
Because this technique relies on the ability to control the background color of individual table cells, this application only functions under Internet Explorer or Netscape Navigator (version 3.0 or later). |
Listing 25.1 Basic Color Table Structure
<TABLE BORDER=4 CELLPADDING=0 CELLSPACING=0> <TR> <TD WIDTH=32 HEIGHT=32 BGCOLOR=red> <A HREF="colors.htm" ONCLICK="SetColor('red')"><IMG SRC="blank.gif" BORDER=0></A> </TD> ... </TABLE>
NOTE |
The SetColor() function in the onClick handler is a user-defined JavaScript function that stores the current drawing color in a global variable. It's demonstrated in the coloring book code on the companion CD-ROM. |
The <TD> element shown in listing 25.1 repeats for each color in the table. This means that each palette cell is essentially the same-only the background color changes, so you can collapse all the repetitive HTML into one JavaScript function, like the one in listing 25.2.
TIP |
It's not technically necessary to load an image into the palette cells. However, loading a transparent GIF that is nothing but the transparent color is, unfortunately, necessary if you wish to support both Navigator and Explorer because each browser handles cell sizing differently. The only way to ensure a specific cell size is to use "placeholder" images. On the positive side, because the same image is loaded into each cell, the amount of additional load time imposed is negligible. |
Listing 25.2 JavaScript-Generated Colors
function DrawColors() { var tStr = ""; var argc = DrawColors.arguments.length; var argv = DrawColors.arguments; for(var i=0; i<argc; i++) { tStr += '<TD WIDTH=32 HEIGHT=32 BGCOLOR=' + argv[i] + '><A HREF="colors.htm" ' + 'ONCLICK="SetColor(' + "'" + argv[i] + "'" + ')"><IMG SRC="blank.gif" ' + 'BORDER=0></A></TD>'; } return tStr; }
Because DrawColors() creates as many table cells as it has parameters, you can replace the entire collection of table cells with the following code block:
<SCRIPT LANGUAGE="JavaScript"> document.write(DrawColors("red," "orange," "yellow," "green," "blue," "purple," "black," "white")); </SCRIPT>
The last cell to display is the one holding the current drawing color. As with the previous cells, JavaScript can be used to dynamically change the background color of this cell, based on a global variable stored in the parent document:
<SCRIPT LANGUAGE="JavaScript"> document.write(DrawColors(parent.currColor)); </SCRIPT>
The global variable used here changes via the SetColor() function associated with the individual palette cell's onClick event. Each cell contains a hyperlink:
<A HREF="colors.htm" ...>
which forces the palette document to reload itself. Each time
the palette document loads, it checks the global color variable
to determine what color to specify in the "current drawing
color" cell of the displayed table.
NOTE |
In order to prevent the browser from caching the color palette document, it's necessary to include the following line in the <HEAD> block: <META NAME="Pragma" CONTENT="no_cache"> This meta tag instructs the browser not to cache (either in memory or on disk) the document. When designing non-caching documents, keep in mind that you may not be able to test the pages entirely until you load them onto the server. While Navigator recognizes the Pragma directive when embedded within an HTML document, Explorer ignores it in favor of the file within its cache. |
While the palette keeps track of the current color, the easel's responsibility is to:
This is a job for a Perl script. Handling mouse clicks over the image and recording the location is relatively straightforward-define the image as an image map, demonstrated in listing 25.3.
Listing 25.3 Image Map Definition
<TABLE BORDER=3> <TR><TD> <A HREF="http://www.visi.com/cgi-bin/color.pl/00FF00"><IMG SRC="house.gif" ISMAP BORDER=0></A> </TD></TR> </TABLE>
The key to the magic is how image maps are handled. By the definition of an image map, the URL used in listing 25.3:
http://www.visi.com/cgi-bin/color.pl/00FF00
defines not only the program (color.pl) to use as the CGI binary, but the file (00FF00) to use as the map file. In this case, the map file is not really a map file, but the pen color to flood the image.
When an image map is clicked, the coordinates of the cursor are passed as part of a query string, as in:
http://www.visi.com/cgi-bin/color.pl/00FF00?75,25
making all the information necessary to redraw the image available. But, before diving into image drawing, it's necessary to pick up a couple of other Perl skills-namely, the ability to modify GIF images on-the-fly.
Perl 5 is object-oriented. Authors from around the world have created a variety of add-on packages that extend the language in the same way as libraries of functions extended the capabilities of Perl 4. One such package is gd.pm, an interface to the gd library written by Thomas Boutell. While gd permits you to create and manipulate GIF images on-the-fly, gd.pm lets you do all this through Perl and transmit the new information back to the server and, ultimately, the user.
NOTE |
gd.pm is available on the companion CD-ROM. |
Implementing gd.pm for the purpose of a coloring book is simple. You include the package in your Perl source and write a control block similar to listing 25.4.
Listing 25.4 Using GD.pm with Perl
#!/usr/local/bin/perl use GD; open (GIF, "house.gif") || die; $myImage = newFromGif GD::Image(GIF) || die; close GIF; ...
Once the GIF has been "imported" into the gd system, you can use the coordinates and pen color from the image map to repaint the image, as demonstrated in listing 25.5.
Listing 25.5 Modifying Images with Perl
... $xcoord = ARGV[1]; $ycoord = ARGV[2]; $white = $myImage->colorAllocate(0,0,0); #background color $black = $myImage->colorAllocate(255,255,255); $currColor = $myImage->colorAllocate($red, $green, $blue); $myImage->fill($xcoord, $ycoord,$currColor); ...
NOTE |
The additional lines in listing 25.5 that create $white and $black are necessary because of the way the gd library works. When using the colorAllocate() method, the first color allocated is the background color. Because the starting image is a line drawing (consisting only of black lines and a white background), allocating a white background prevents any possible image color problems when flooded with new colors. |
With the filling process complete, all that's left is to transmit the new graphic back to the user, as part of the new easel document.
Listing 25.6 The Easel
... print "Content-type: text/html\n\n"; print << __END_OF_EASEL__; <HTML> <HEAD> </HEAD> <BODY BGCOLOR=#ffffff> <CENTER> <H1>Coloring Book</H1> <HR WIDTH=50%> <TABLE BORDER=3> <TR><TD> <A HREF="http://www.visi.com/cgi-bin/color.pl/$penColor"><IMG SRC="$newImage.gif" ISMAP BORDER=0></A> </TD></TR> </TABLE> </CENTER> </BODY> </HTML> __END_OF_EASEL__
NOTE |
This Perl fragment takes advantage of the <Here-is> tag. This tag, identified by the << mark in a print statement, as in: print <<tag; instructs Perl to treat each line until the line: tag is encountered as though it had been formatted as: print "text"; This is another shortcut that makes creating large HTML documents easier to generate through Perl. As demonstrated in listing 25.6, you can still embed Perl variables within this block, which will be appropriately expanded. |
The only thing left requires the generation of a new GIF file that becomes the name of the new image-map graphic, which the updated image data is written to:
open(IMAGE, ">$newImage.gif"); print IMAGE $myImage->gif; close(IMAGE);
The coloring book interface detailed here treats drawing as though you were flooding a region with color. While the Web does not permit the kind of interaction necessary to enable line drawing and other fancy paint functions, you could try these three ideas:
For examples on how to implement these ideas, check out the source code on the companion CD-ROM.
This chapter presented a project that had JavaScript and Perl working in chorus to reduce the amount of data moving across the Internet, while creating an application that's fun for kids as well as adults.
For more information on this and other related techniques, check out: