Chapter 17 -- Advanced Graphics: Multimedia
Chapter 17
Advanced Graphics: Multimedia
CONTENTS
There is no doubt that we are in the information age. Right now,
people are reading books, newspapers, magazines, journals, listening
to radios, and watching TV. Information is flowing like a million
rivers spreading their spider web tributaries across the world.
Information is a profitable commodity with large corollary industries.
There are many different distribution paths for information. An
example is a book, which is directly bought (and read). A more
complex distribution path is the use of advertisers to support
TV and radio stations' programs (the information). Stations attempt
to draw in the most viewers possible so that advertisers have
a large pool of viewers' time to buy.
One of the newest distribution paths for information is the Internet.
Information is put on the Internet to be read. Since there is
such an abundance of information on the net, the information needs
to be very impressive for people to want to read it. Advertisers
may wish to embed their advertisements in Internet information
with a high readership, just as they target radio and TV stations
that have high viewerships. But regardless of whether information
providers seek to sell time to advertisers with their information,
they are still likely to make their information as impressive
as possible to the readers.
The best way to create impressive presentations is to understand
the medium. For instance, an example of misuse of a medium is
a TV show that scrolls by the text of a Shakespearean play. This
is unsuccessful because it is not using the benefits of the TV
medium-moving picture and sound. For the text of a Shakespearean
play, this interface is much worse than a book as a medium because
the TV scroll does not account for the reader's speed, a reader
cannot refer back to earlier passages to check forgotten characters
or internal references, and such. Thus most viewers prefer to
watch a mindless sitcom on TV-which more effectively uses the
TV medium's potential benefits-than read a Shakespearean play
on TV, even though the play has considerably more quality content
than the sitcom.
What is the Internet as a medium about? As with TV, information
on the Internet can be displayed by pictures and sound. However,
on the Internet, viewers have more freedom in what they view,
they can interact with the information they view, and they can
interact with other viewers. The Internet can use most forms of
media (visual, auditory, textual, and so forth).
However, the Internet does have two serious drawbacks: bandwidth
limitations and accessibility. Radio shows and songs can be heard
over the Internet (with RealAudio http://www.realaudio.com),
but the bandwidth problems reduce the sound quality. Similarly,
TV has bandwidth problems that reduce picture quality. Books on
the other hand can be comfortably downloaded but the interface
is not as physically convenient as a hand-held novel. Even for
interactive computer games and educational tools, CDs are better
because all of the information is local. It is therefore much
quicker than the Internet-again, a bandwidth problem. On the other
hand, CDs are static, so there is no real interaction with the
information. Accessibility is another problem as there are still
many individuals and companies who lack Internet access. Once
accessed, the information is not always portable.
The best use of the Internet as a medium is one that maximizes
its potential benefits and minimizes the problems like bandwidth
limitations and accessibility. The Internet is therefore the best
medium for numerous uses, including the following:
- Information that changes frequently
- Information with a high degree of specificity
- Inexpensive worldwide distribution of
information
- User interaction with the information
How can the information be displayed effectively? This question
brings up the focus of this chapter. Computers are very good at
manipulating and massaging data. It is important to give the computer
the information in a format such that the computer can play with
it and output the data in many ways. For instance, text of many
Web pages can be configured by viewers. They can change the font,
font size, and page size and search for key words. If, instead
of using primitive text, the text is scanned in from a page in
a book (maybe in the format of a bitmap), then viewers can do
very little with the information. The format of the information
is thus of vital importance, and choosing a format that is appropriate
needs to be stressed.
When designing a format for graphics, you can learn from the flexibility
shown in the text format, as in the previous example. Finally,
you arrive at a strategy for making impressive graphics. Right
now, there are many pages that use ImageloopItem
written by James Gosling at Sun Systems or its descendants like
the Animator. ImageloopItem
takes a series of GIFs and flips through them, making a short
animation. The Java mascot Duke is a classic example. Duke waves
every once in a while-it is very impressively done. The animation
consists of ten images that are 55x68 pixels. In total, it consists
of approximately 20K of data and 3K of class files. I contrast
this example with my program Rotator,
which uses a very different technology. Rotator
uses more primitive data and constructs the images from the data.
The example I use has 1K of data and 16K of class files.
All the previous discussion leads to Rotator.
Rotator is a utility that
displays rotating 3-D shapes. The shapes are composed of colored
polygons that fit together snugly. The output (the picture) is
shown on a Canvas whose size
is determined at runtime. The main method of achieving a 3D effect
with the shape is to keep the picture rotating continuously, but
perspective, shading, and hidden face removal also help achieve
this effect. There is a simple interface that allows the user
to move the shape around. The original intent of the utility was
to make geometrical objects accessible, but this utility can also
be used to make 3-D logos spin, bounce, and move in fascinating
patterns. Much of the appeal of these 3-D logos compared with
many of the standard 3-D logos is that they are interactive, and
they have infinite variety, as opposed to a fixed collection of
frames.
Rotator is an extension of
Canvas, which is a "component."
This means Rotator inherits
a large number of methods that relate to input from the keyboard
and mouse, and graphics (these methods can be found in the API
User's Guide). Rotator is
thus in the same family as Label,
Button, List,
TextComponent and such, which
means that it is simple to place Rotator
within a complex applet like a data entry form or the game in
Chapter 18, "Serious Play: Game Applets."
For instance, Listing 17.1 is a section of code that adds Rotator
to a panel.
Listing 17.1. Excerpt from SimpleApplet.
Rotator rotator= new Rotator(this,solid,W,H);
new Thread(rotator).start();
rotator.background = new Color(255,200,200);
add(rotator);
show();
This code comes from SimpleApplet.java.
Rotator has its own thread, making the continuous rotating independent
and easy.
The classes that Rotator
uses are Solid (which later
gets extended to ncSolid-non-convex
solid) and Omatrix. The Solid
class holds all the information about the shape: faces, colors,
position, and how to draw the shape. The Omatrix
class holds all the information about orientations of shapes:
how the shape sits in space. With these general class descriptions
in mind, take a look at the code for Rotator
in Listing 17.2.
Listing 17.2. Rotator.
import java.io.*;
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.lang.*;
import Solid;
import Omatrix;
/*----------------------------------------------------------------------*/
public class Rotator extends Canvas implements Runnable {
public Color background;
private int W,H,delay,oldx,oldy;
private double angle,angle0=0,bounce=0;
private boolean keep_going;
private Solid solid;
private Omatrix tmp,tmp2,tmp3,M0;
private Image im;
private Graphics offscreen;
The only public variable is background;
all the other variables can only be accessed by Rotator's
methods. The purpose of the variables will become clear later,
as they are used.
Listing 17.3 shows the constructor (there is only one for now).
Listing 17.3. Constructor for Rotator.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
Rotator(Applet applet,Solid solid_,int width,int height)
{
solid = solid_;
W = width;
H = height;
M0 = new Omatrix();
tmp = new Omatrix();
tmp2 = new Omatrix();
tmp3 = new Omatrix();
angle0 = 0;
delay = 100;
resize(W,H);
im = applet.createImage(W,H);
background = Color.lightGray;
setBackground(background);
offscreen = im.getGraphics();
}
Now, look at two major aspects of the above code. First, the reference
solid is set to match the
reference passed in solid_
since solid_'s scope is only
the constructor; these references are now equivalent. The reference
refers to the object that knows everything about the shape to
be drawn. Similarly, W and
H are set to the values passed
in. Then four orientation matrices (Omatrix)
are instantiated. M0 stands
for the initial orientation, and all tmps
signify temporary matrices that are used to do calculations.
Second, regarding the display, the Canvas
is resized to the desired width and height, creating an image,
im, that is used for double
buffering. This means that instead of drawing directly to the
screen, you can create an image that you can take your time drawing
and then when you are finished drawing, you can throw the whole
image to the screen. This stops the distracting flicker that some
programs have. The image is basically a block of memory for which
the object, offscreen, can
manipulate with the graphics methods like fillRect,
setColor, drawPoly
and so forth.
Since this is the graphics chapter, I had better start drawing
things! The three essential methods in a Canvas
are paint, update,
and repaint. The first information
to know about these methods is how they fit in the hierarchy.
Paint and update
are methods called by the parent. The parent calls the Canvas'
paint method when the Canvas
is initialized. Also, if part or all of the Canvas
gets covered by another window, the parent can call the paint
command to fix its appearance. If the Canvas
is static, this is the only method required. If the Canvas
changes, as in the case of animation, then the Canvas
calls the 'repaint,' which notifies the parent that it should
update the Canvas; the parent
then calls the Canvas' update
method.
Often it is easier to redraw the whole picture than to change
only what needs to be changed. The following strategy achieves
this:
public void update(Graphics
g) {
paint(g);
}
Unfortunately, this solution introduces a flicker problem. The
paint method starts by clearing the Canvas,
and then numerous commands draw the picture. There can be a noticeable
time lag between the execution of the first and last commands
in the drawing sequence, which is distracting.
The way to solve this problem is by reversing the roles of update
and paint: have paint
call update. As well, you
may add a double buffer. The update
is likely called more frequently than the paint,
so it makes sense for it to be the dominant method of the two.
Listing 17.4 shows Rotator's
paint and update
methods.
Listing 17.4. Rotator's
paint and update
methods.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public void paint(Graphics g) {
update(g);
}
public void update(Graphics g) {
offscreen.setColor(background);
offscreen.fillRect(0, 0, W, H);
solid.Draw(offscreen,(int)(W/2),(int)(H/2),bounce);
g.drawImage(im, 0, 0, null);
}
This simple update draws
a filled rectangle to offscreen
(which effectively clears anything that is on the off screen)
and then draws the solid.
The offscreen image, im,
is then drawn to g. This
g is a graphics context like
offscreen, but it is beyond
the scope of this class; it must be supplied by the parent.
In the update method of Rotator,
there is little that can change except the variable bounce.
Another value that may change-although it is not readily apparent-is
the orientation matrix of the shape that is encapsulated in the
object, solid. In the run
method of Rotator, repaint
is called. The update then
draws the picture using the current bounce and shape orientation
values. If they have changed since the last update, the current
picture may be drastically different from the previous picture.
The run method of Rotator
is conceptually simple, as shown in Listing 17.5. It updates the
height and the orientation of the shape and then redraws the shape.
It then loops until it is told to stop.
Listing 17.5. Run
method for Rotator.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public void run(){
keep_going = true;
angle=0;
while(keep_going){
/* .. modify bounce
and solid.orient .. */
angle += 0.1;
tmp3.Rotation(1,2,angle);
tmp2.Rotation(1,0,angle*Math.sqrt(2)/2);
tmp.Rotation(0,2,angle*Math.PI/4);
solid.orient = tmp3.Times(tmp2.Times(tmp.Times(M0)));
bounce = Math.abs(Math.cos(0.5*(angle+angle0)))*2-1;
repaint();
try {Thread.sleep(delay);}
catch (InterruptedException e){}
}
}
Referring to the loop in the above code, solid.orient
(the shape's orientation) is a function of angle,
with M0 as the initial value.
Bounce (the shape's height),
however, is a function of angle,
with angle0 as its initial
value. The values of solid.orient
exhibit interesting behavior: theoretically, the orientation never
repeats. The Rotation and
Times methods are discussed
later in the chapter.
After the request to update via the repaint
command, the run thread sleeps for a while to give up the CPU
for other threads. This process continues as long as keep_going
is true.
A simple and logical interface is essential to any interactive
application. Now look at a simple interface for Rotator
that gives the user control of the shape's orientation, rather
than leaving the shape to tumble in accordance with the convoluted
function. The interface design is this: the tumbling stops when
users press a mouse button. They may then drag the mouse to rotate
the shape in chosen directions at will. Letting go of the mouse
button returns the shape to its tumbling.
The first step in designing this interface is to stop the run
thread when a mouse button is pressed, as shown in Listing 17.6.
Listing 17.6. Rotator:
mouseDown routine.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public boolean mouseDown(java.awt.Event evt, int x,
int y) {
keep_going = false;
try {Thread.sleep(delay*2);} catch (InterruptedException
e){}
M0 = solid.orient;
angle0 += angle;
angle = 0;
oldx = x;
oldy = y;
return true;
}
The first line ensures that the run process eventually finishes.
The sleep command waits to make sure this is so. The current value
of the shape's orientation is saved, as it becomes the new initial
value. When the tumbling resumes, it thus resumes from the current
orientation. The variables oldx
and oldy record the position
where the mouse has been pressed; this is used to determine how
much the mouse moves during a mouseDrag.
The "return true"
tells the Rotator's parent
that the mouseDown event
has been handled, so there is no need for another thread or process
to handle the mouseDown event.
The responsibility of returning the shape to the tumbling shape
falls on the mouseUp method,
as shown in Listing 17.7. But all it has to do is start a new
run thread.
Listing 17.7. Rotator: mouseUp
routine.
/* - - - - - - - - - - - - - - - - - - - &nb
sp;- - - */
public boolean mouseUp(java.awt.Event evt, int x,
int y) {
new Thread(this).start();
return true;
}
The mouseUp event starts
the shape tumbling again by starting the this
thread, that is, starting the run
method of this object (Rotator).
Finally, you arrive at the sensitive part of this interface. The
mouseDrag method allows the
user to manipulate the shape. You use the difference between the
old and new mouse position to calculate how you want the shape
moved.
Listing 17.8. Rotator: mouseDrag
routine.
/* - - - - - - - - - - - - - - - - - - &nbs
p;- - - - */
public boolean mouseDrag(java.awt.Event evt, int x,
int y) {
tmp2.Rotation(0,2,(x - oldx) * Math.PI/
solid.W);
tmp.Rotation(1,2,(oldy - y) * Math.PI/
solid.H);
M0 = tmp.Times(tmp2.Times(M0));
solid.orient = M0;
oldx = x;
oldy = y;
repaint();
return true;
}
The first line of this method causes the shape to rotate in the
XZ-plane by an angle that relates to the drag motion in the x
direction, the angle being equal to (x-oldx)*constant. Similarly,
the second line rotates in the YZ-plane. This case differs from
the previous one by a minus sign; this is because the origin is
in the upper left corner, and traveling down the screen corresponds
to an increase in the y value. Since M0
always refers to the current orientation during mouseDrag,
when the mouseUp is called
the shape begins tumbling again starting with the current orientation.
Although this finishes the brief discussion of this interface,
the next chapter looks more deeply into good interfaces.
In the following section, there are some difficult mathematical
concepts, so rather than going through every detail, a general
picture of what is happening is presented. For those who like
more details, you may check the CD and go over the code that is
there. Part of the appeal of object-oriented code is that the
programmer doesn't need to understand all of the code to effectively
use it. For this reason, big projects benefit from using object-oriented
programming languages.
The basis of the Omatrix
method used by Rotator is
that shapes are defined in terms of points, in the form (x, y,
z). For a simple visualization of this form, imagine x as a horizontal
axis across the width of the screen, y as a vertical axis along
the length or height of the screen, and z as an axis along a direction
perpendicular to the screen, with one end pointing in toward the
screen and the other end pointing out toward the user. Call this
the standard orientation.
The class Omatrix describes
an orientation. The way it does this is it replaces the standard
x-y-z axes with new axes. These new axes are 3 vectors; call them
x'-y'-z'. You store x'-y'-z' as the columns of a matrix. For x'-y'-z'
to make sense as an orientation, these vectors must each have
a unit length, and must also be perpendicular to each other-like
the standard orientation (1,0,0)-(0,1,0)-(0,0,1).
In Listing 17.9, you have the initial code for the Omatrix
class. It mainly concerns itself with the 3-by-3 matrix.
When an Omatrix is constructed,
it is set to the standard axis.
Listing 17.9. First section of the Omatrix
class.
public class Omatrix{
public double[][] M;
private int row,col,k,l;
Omatrix(){
M = new double[3][3];
Assign(1,0,0,0,1,0,0,0,1);
}
/* - - - - - - - - - - - - - - - */
public void Assign(double a, double b, double c,
double
d, double e, double f,
double
g, double h, double i ){
M[0][0] = a;
M[1][0] = b;
M[2][0] = c;
M[0][1] = d;
M[1][1] = e;
M[2][1] = f;
M[0][2] = g;
M[1][2] = h;
M[2][2] = i;
}
This matrix, M, is the data
part of the class. The other variables declared are row,
col, k,
and l. These dummy variables
are used often. In general, it is safer to have dummy variables
declared in the methods that require them, but this setup works
here because no two methods are called at the same time.
In the Omatrix constructor,
the space for M is allocated,
and the method Assign sets
the initial value of M equal
to the standard orientation. The following two methods are the
major tools used by Rotator
to change the orientation: Rotation
and Times.
Rotation
The method Rotation replaces
the matrix, M. It is replaced with a matrix that represents an
orientation different from the standard orientation by a rotation
in one of the coordinate planes by "angle"
(in radians). Here, i and
j are the numbers of the
axes that get changed. For instance, 0 and 1 represent that the
change occurs in the first and second columns-the x and y axis.
Rotation(0,1,0.5) sets the
orientation, M, to the following:
Orientation Matrix
cos(0.5) sin(0.5) 0
-sin(0.5) cos(0.5) 0
0 0 1
Figures 17.1 and 17.2 show the result.
Figure 17.1 : The same shapes viewed with the standard basis.
Figure 17.2 : The same shapes after a rotation of half a radian.
Listing 17.10 shows how "rotation" works.
Listing 17.10. Rotation
method in Omatrix.
/* - - - - - - - - - - - - - - - */
public void Rotation (int i, int j, double angle)
{
Assign(1,0,0,0,1,0,0,0,1);
M[i][i] = Math.cos(angle);
M[j][j] = M[i][i];
M[i][j] = Math.sin(angle);
M[j][i] = -M[i][j];
}
Times
The next method, shown in Listing 17.11, allows you to make changes
in the orientation relative to any given orientation. In matrix
notation, this operation is multiplication or Times.
Unlike the method Rotation,
Times returns a new orientation
object rather than changing M.
This turns out to be convenient, as seen in Rotator.
Notice that every instance Times
is called, a new Omatrix
object is created. Happily, however, garbage collection is automatic,
so the old Omatrix objects
are cleared away.
Listing 17.11. Times
method of Omatrix.
/* - - - - - - - - - - - - - - - */
public Omatrix Times(Omatrix N){
Omatrix temp = new Omatrix();
for (row=0; row < 3; row++) {
for (col=0; col <3; col++)
{
temp.M[row][col]
= 0.0;
for (k=0; k <
3; k++) {
temp.M[row][col]
+= M[row][k] * N.M[k][col];
}
}
}
return temp;
}
In Figure 17.3, you can see the result of two rotations composed
by using Times. Combining the methods Rotation
and Times together allows
you to make any orientation. This completes the explanation of
the Omatrix methods used
by Rotator. However, there
are several more methods to Omatrix,
some of which are quite interesting. They are used in the next
chapter.
Figure 17.3 : An orientation constructed by combining two simple notations.
Next is an explanation about the Solid
class. This class holds all of the shape's information. In general,
the shapes are made up of faces; these faces are all flat. The
faces are polygons that have only one side, or plane, that is
visible. All the faces put together form the outside of a convex
shape. In other words, the polygons are all joined along their
sides to form one shape, for which there is a perspective that
only sees the planes that face upwards. The upward-facing planes
vary with the shape's orientation. This description of a shape
is very narrow; it will be extended when ncSolid
is described, but for now, these restrictions help draw the shape
without a sorting routine, and without redrawing the same areas
of the picture.
The method CalcScrPts (calculate
screen coordinates) is a routine that takes the orientation of
the shape in space and computes all the screen coordinates. First,
it computes the points relative to the new basis, then it projects
each of the points to the screen. This means that you can draw
a wire frame diagram of the shape by connecting all the appropriate
screen coordinates. This is a bad idea, however, since the back
and front of the object often become hard to identify.
Instead, the Draw method
uses faceUp to decide which
faces it draws. The faceUp
method returns a true if the face is pointing upwards; this is
where the one-sidedness of the face is important. The faces have
a top that you want drawn, and a bottom that you don't want drawn
(as you never see the bottoms). Since the shape is convex, all
the hidden faces return a false value for faceUp,
and therefore don't need to be drawn. Thus all of the hidden face
removal is taken care of. The getColour
method then computes the shading, and the polygons are drawn with
DrawPoly.
Since this Draw method is
fairly specific, there is another Draw
method that is more general but slower. This Draw
method is part of the class ncSolid
in Listing 17.12, which extends Solid.
Listing 17.12. The class ncSolid.
import java.io.*;
import java.awt.*;
import Solid;
public class ncSolid extends Solid{
private int order[],min;
ncSolid (InputStream is, int wid, int heig, int ncolour_)
throws IOException{
super(is, wid, heig, ncolour_);
order = new int[nface];
}
/* - - - - - - - - - - - - - - - */
public void Draw(Graphics offscreen,int x0,int y0,double
z){
CalcScrPts((double)x0,(double)y0,z);
/* .. put in a list all the faces that
are point up.. */
i=0;
for (f=0;f<nface;f++)
if (faceUp(f)) {
order[i] = f;
i++;
}
/* .. find the face farthest away and
draw it 'i' times .. */
for (j=0;j<i;j++){
min = j;
for (k=j+1;k<i;k++)
if (rotPts[order[k]+nface][2]<rotPts[order[min]+nface][2])
min =k;
if (min !=j) {
f = order[min];
order[min] = order[j];
} else f = order[j];
for (k=1;k<faces[f][0]+1;k++)
DrawPoly(offscreen,faces[f][k],getColour(f,k));
}
}
}
The following line is a check to see which one of the faces is
closer to the screen:
if (rotPts[order[k]+nface][2]<rotPts[order[min]+nface][2])
min =k;
This line is used to sort the faces into an order that delineates
farthest to closest.
Here are some examples of Rotator
being used. Figure 17.4 uses Solid.
Figure 17.5 uses ncSolid.
Figure 17.4 : A sample of SimpleApplet (cubeR.hmt)
Figure 17.5 : A sample of SimpleApplet2 (tetraR.html).
How economical has the bandwidth been so far? Table 17.1 shows
the data for the cube.
Table 17.1. The breakdown of data needed to be transferred
with SimpleApplet.
Data | Size |
SimpleApplet.class
| 1885 bytes |
Rotator.class
| 3010 bytes |
Solid.class
| 6274 bytes |
Omatrix.class
| 4385 bytes |
cube.dat
| 947 bytes |
Total | 16501 bytes = 16.5K
|
Table 17.2 shows the data for the tetrahedron with the center
missing (the second image).
Table 17.2. The breakdown of data needed to be transferred
with SimpleApplet2.
Data | Size |
SimpleApplet2.class
| 1889 bytes |
Rotator.class
| 3010 bytes |
Solid.class
| 6274 bytes |
ncSolid.class
| 1197 bytes |
Omatrix.class
| 4385 bytes |
tetra.dat
| 1451 bytes |
Total | 18206 bytes = 18.2k
|
Since the bandwidth has been used sparingly so far (under 20K),
go ahead and make Rotator
more interesting by adding a background image, and call it ImageRotator
(see Listing 17.13). Following the ncSolid
example of sub-classing, add more data into the constructor, and
replace the Draw routine.
Just as the Solid is supplied
to Rotator from Rotator's
parent, the Image for Rotator's
background is also supplied from the parent.
Listing 17.13. The class ImageRotator.
/*----------------------------------------------------------------------*/
public class ImageRotator extends Rotator implements Runnable
{
Image gif;
int gifx,gify;
/* - - - - - - - - - - - - - - - - - - -&
nbsp; - - -
*/
ImageRotator(Applet applet,Solid solid_,Image gif_,int
width,int height) {
super(applet,solid_,width,height);
gif = gif_;
}
/* - - - - - - - - - - - - - - - - - - -&
nbsp; - - -
*/
public void update(Graphics g) {
offscreen.setColor(background);
offscreen.fillRect(0, 0, W, H);
gifx = (int)((W - gif.getWidth(null))/2);
gify = (int)(((H - gif.getHeight(null))/2)*(1-bounce));
offscreen.drawImage(gif,gifx, gify, null);
solid.Draw(offscreen,(int)(W/2),(int)(H/2),bounce);
g.drawImage(im, 0, 0, null);
}
}
The image that can be passed follows the general description of
an Image. In the example of SimpleApplet3
in Listing 17.14, you will pass in an Image
that is created from a GIF.
Listing 17.14. SimpleApplet3's
inclusion of Rotator.
URL here = this.getDocumentBase();
...
Image gif = getImage(new URL(here,image));
add(new Label(title,Label.CENTER));
ImageRotator rotator= new
ImageRotator(this,solid,gif,W,H);
...
Figure 17.6 shows the program's output.
Figure 17.6 : A sample of SimpleApplet3 (tetraRB.html).
Table 17.3 shows how much information is sent over the Net.
Table 17.3. The breakdown of data needed to be transferred
with SimpleApplet3.
Data | Size |
SimpleApplet3.class
| 2025 bytes |
Rotator.class
| 3010 bytes |
ImageRotator.class
| 1265 bytes |
Solid.class
| 6274 bytes |
ncSolid.class
| 1197 bytes |
Omatrix.class
| 4385 bytes |
tetra.dat
| 1451 bytes |
something.gif
| 92375 bytes |
Total | 111982 bytes = 112.0K
|
Even in this small example that uses only one GIF, the GIF is
a large portion of what has been sent. Notice as well, the pictures
are no longer scaleable: when the size of the Canvas
is changed, the shape is scaled, but the GIF has a fixed resolution.
This takes away some of the flexibility. Instead of using the
GIF, the graphics primitives can be used to create the background
Image. This, no doubt, uses
less information than needs to be sent, but the information is
in a format that allows the computer to manipulate it. Thus the
image also comes alive and dances and the viewer doesn't have
to wait for everything to download.
So the real goal of graphics programming is to find the happy
medium for bandwidth, flexibility, and really cool graphics. This
must be solved by carefully choosing the format of the graphics
information and pushing the format to its limit. Reflect on the
92K GIF and think of all the wonderful Java code and data that
can be downloaded in the same time.
Next
Previous
Contents
|