Note: a few bits of this note have been moved into the first and second sections dealing with graphics


Graphics III  Animation                     Peter Komisar
 

references: Animation, 'Java 1.2 Developer's Handbook, Heller&Roberts
Graphics 'Just Java' Peter v.d.Linden           JDK1.2.2 API documentation


Animation is showing several images or frames in quick succession. The human eye sees a series of
slightly different frames as movement, a quality known as persistence. Changing the entire frame is
called frame animation, while changing a small area is called sprite animation. The principle enemy
of animation quality (aside from bandwidth restrictions on downloads) is flicker. Flicker can be reduced
or eliminated by using the programming techniques such as 1) clipping, 2) double buffering. and in
the case of Java, 3) overridding update. Sprites are shapes that move across the screen, often tracking
the mouse. The general problems associated with sprites is repairing of an area of the screen from which
the sprite has been moved and the reduction of flicker. Graphics animation can be achieved by using
Graphics drawing methods in conjunction with loops, and changes to the (x, y) drawing  coordinates.
A variation on this is to draw an image to screen and have it's (x, y) coordinates be changed.
Conventional animation, the kind we see at the theatre, requires a series of images of the same rectangular
dimensions to be passed over a screen at a fixed rate. Something over 24 frames per second appears
continuous to the human eye. In this situation, where the whole screen is changed as each image is shown,
Java affords the optimization of overriding update. (The flicker producing process of resetting the
background color can be eliminated.)

Clipping Rectangles

Clipping is a standard graphics technique to restrict the amount of graphics area being updated so
unchanged areas of the screen do not have to be repainted. It is used extensively in animation. Graphics
class' clipRect( ) method is declared,

public void clipRect( int x, int y, int width, int height );

Because clipRect( ) only works by reductions, the method setClip( int x, int y, int width, int height )
was added to work on areas of unrestricted size.

Adjusting the Clip Region

clipRect( ) limits or 'clips' the area being repaired to that specified by the clip rectangle. All that needs
to be repaired is the union of the old and new sprite locations, described by the smallest rectangle that
contains both the old and new sprite positions. When clipping buffering to an offscreen location should
be used to eliminates flash.

The algorithm occurs in three steps.

1) set the clip region
2) draw the background image
3) drawing the sprite on the new location.

Setting the clip region

Imagine the following table represents an area of a screen that is being effected by a sprite animation.
The text Sprite position, (representing an image), is to be repositioned  from the lower-left panel to
the upper-right.The rectangular union of the two panels is the area that will need to be repainted. Here
the rectangular union is represented by the bounds of the table itself.

|------   rectangular union width        -------|
 A  Sprite position
 Sprite position  B

Java's Rectangle class has a union( ) method which can simplify this process.

example   If R1 and R2 are Rectangle objects, their union is described by a rectangle returned by
                 the union method
                                                 Rectangle U = R1.union(R2);

Rectangle class has methods such as getX( ),  getY( ), getWidth( ) & getHeight( ) which can be used
to provide parameters for the clipRect method.


Besides the union( ) method, there a quite a few interesting functions in the Rectangle class.


Rectangle Class Snapshot
                                                             +--java.awt.geom.Rectangle2D
                                                                           |
                                                                           +--java.awt.Rectangle
 Fields
 int height  The height of the Rectangle.
 int width  The width of the Rectangle.
 int x   The x coordinate of the Rectangle.
 int y   The y coordinate of the Rectangle.

 Constructors
  Rectangle
(int width, int height) 
Constructs a new Rectangle whose top-left corner is at (0, 0) in the coordinate space, and  whose width and height are specified by the arguments of the same name. // 1 of 6 variations

 Methods                                                                                                                     ( 6 of 37 methods)
void add (int newx, int newy)  Adds a point, specified by the integer arguments newx and newy, to this   Rectangle.                                                                                     // one of three
 boolean contains (int x, int y)   Checks whether or not this Rectangle contains the point at the specified
 location (x, y).                                                                               // one of four
 Rectangle2D createIntersection
(Rectangle2D r)
 Returns a new Rectangle2D object representing the intersection of this 
 Rectangle with the specified Rectangle2D. 
Rectangle2D createUnion
(Rectangle2D r) 
 Returns a new Rectangle2D object representing the union of this Rectangle 
 with the specified Rectangle2D. 
boolean intersects (Rectangle r)   Determines whether or not this Rectangle and the specified Rectangle intersect.
 void translate (int x, int y)   Translates this Rectangle the indicated distance, to the right along the 
 x coordinate axis, and downward along the y coordinate axis.
 union (Rectangle r)   Computes the union of this Rectangle with the specified Rectangle.
  inside move, reshape resize are deprecated



Review: Framework and methods of a standard applet
 
 public class zapplet extends java.applet.Applet{
    public void init( ) { . . . } 
    public void start( ) {. . .}
    public void stop( ) {. . .}
    public void paint(Graphics g){ . . .}
    }

 Framework and methods a Threaded Applet used in Animation
 
public class tv_applet extends java.applet.Applet
implements Runnable{
 applet implements the Runnable interface
  Thread t ;   declare a Thread class reference
  public void run( ){ // thread action }  Runnable's method requiring implementation
  public void init(  ) { . . . . }  same function as in conventional applet
  public void start( ) {
     t = new Thread(this); 
     t.start( ); 
  }
 A new thread is started with this, the containing
 applet, serving as the target. The target implements
 Runnable by a body for run( ). Thread's start calls run
  public void stop( ){
      t.interrupt( );
      t=null; 
  }
 Thread's stop( ) is no longer to be used inside applets  stop( ). Instead interrupt and assign to null or just assign  to null 
  public void paint(Graphics g) { . . . }
}
same role except adapted to do animations

The most common form of animation uses arrays of images. An integer variable is used to indicate the
index of the next frame to be displayed. The animation thread executes in an infinite loop in which it
1) sleeps, 2) increments the index and 3) calls repaint( ).



Code from page 359, 'Java 1.2 Developer's Handbook', Phillips&Heller

import java.awt.*;
import java.applet.Applet;

public class Bogus extends Applet implements Runnable{

     Image images[];
     int index;
     Thread animator;

public void init( ){
     images = initImages( );          // this method provides the images it builds and isn't listed here
     index=0;                               // if coming from file MediaTracker would be used
     animator = new Thread(this);
}
/*  The animation thread executes in an infinite loop in which it
       1) sleeps, 2) increments the index and 3) calls repaint( ).   */

public void run( ){
     while(true){
          try{
               Thread.sleep(100 );
               }catch(InterruptedException ex){ }
               repaint( );
               index = ++ index % images.length;
      }
}
public void paint(Graphics g){                             // paint is overridden to draw the images
     g.drawImage(images[index], 0,0,this);
     }
}


Design Problems in the typical animation example

1) When the browser is away from the page the animator thread should be paused so it doesn't
     waste CPU cycles. Code should also be prepared to cause the animation to resume.

2) The applet should offer the user the option to start or stop the animation.

3) update( ) should be overridden to reduce flicker. // double buffering could be used as well

4) ++Index is vulnerable to interrupts. The thread could be pre-empted after the index was incremented
    but before the modulo operator was called resulting in an out-of-bounds exception.



1) Heller & Roberts suggest using a boolean suspendRequest flag to cause the thread to
    wait( ) and then notify( ) to start in synch with applets stop start methods.
 . . .
public void stop( ){
suspendRequest = true;                        // just a boolean declared at head of code
}

public void start( ){
synchronized(this){                              // here's the synchronized block you don't see that often
     suspendRequest = false;
     notify( );                                         // wait( ), notify( ) and notifyAll( ) are defined in Object
     }                                                    // notify( ) tells the thread to run when ready
}
public void run( ){
  while(true){
    if(suspendRequest){
      sychronized(this){
         try{
             wait( );
             }
             catch(InterruptedException x) { }
             }
          }
     try
    {Thread.sleep(100);
     }catch(InterruptedException e) { }
     index = ++ index % images.length;
     repaint( );
     }
}



2) Support for user control of animation can be introduced with a component such as a
    Checkbox. The applet will need to implement the ItemListener interface and it's method,
    public void itemStateChanged( ) method.

public void itemStateChanged(ItemEvent ev){ // the method does the same job as start and stop
     if (theCheckbox.getState( ) = = true)
          suspendRequest = true;
     else {
          synchronized(this) {
          suspendRequest = false;
          notify( );
          }
     }
}
/*  the revision to start( ) makes sure the animation remains
     off if checked off and the page is left and then returned to */

public void start( ) {
      if (theCheckbox.getState( ) = = false){
          synchronized(this){
               suspendRequest = false;
               notify( );
          }
     }
}



3) The author of Animation in the Java 1.2 Developer's Handbook override update( )
     to reduce flicker. Double buffering could also have been used.

To review, update( ) by default is:

public void update(Graphics g) {
     g.setColor(getBackground( ));
     g.fillRect(0,0,width,height);
     g.setColor(getForeground( ));
     paint(g);
     }

// overridden to just painting the foreground, update( ) becomes
public void update(Graphics g){
     paint(g);
     }



4) The final issue is avoiding corruption of the index increment.

Two means can be used to solve this problem, the problem being, if at index max - 1, index is
incremented to max  but not modulo operated on, an array index out-of-bounds exception will occur.
Looking at the code,  index = ++ index % max;  the line is not atomic.In assembly programming, an
atomic operation is one which can't be interrupted. In the above line several assembly-like statements
occur between getting the value, incrementing the value then reassigning the new value to index. There
are many places where interrupts could occur.

Solution 1

The JVM spec states, 32 bit writes (i.e. index = temp) are atomic. //not 64's so not true for long
Therefore,
                                 int temp = index;
                                 temp = ++ temp % max ;
                                 index = temp;                        // this is an atomic, uninterruptable line,

fixes the problem.

Solution 2

Create a synchronized method called from run( ) which takes care of incrementing the index.
So in run( ), a method like the following would be called.
 // something to consider, synchronizing code has a performance penalty which may or may not be significant

synchronized void IndexUp( ){
index = ++ index % max:
}
The act of synchronizing makes sure the method returns without being interrupted.


Image Processing


Transparency

alpha   bits 31 to 23   |  red     bits 23 to 15    |    green  bis 15 to 7   |    blue bits 7 to 0    |

Alpha set to 0 is totally transparent of invisible. Alpha set to 255 is a solid color. Values between
vary in translucence or opacity.
 

ImageConsumer

The abstract class ImageConsumer converts an image into an array of pixels. PixelGrabber extends
ImageConsumer.
 

PixelGrabber

1) Instantiate PixelGrabber taking an image (or ImageProducer), dimensions, and an int array as arguments.

            PixelGrabber pg = new PixelGrabber( lots of arguments );

2) call grabPixel( ) to fill the int array with pixels from the image

              pg.grabPixels( );

3) check the status to see if bits were grabbed successfully
    if (pg.status( ) & ImageObserver.ALLBITS) !=0)

// all bits grabbed successfully

PixelGrabber constuctors
 

public PixelGrabber( Images img, int x, int y int w, int h, int[] pix, int off, int scansize );
public PixelGrabber( ImageProducer img, int x, int y int w, int h, int[] pix, int off, int scansize );

where 1) img is the image to retrieve pixels from
            2) x & y are upper left hand corner coordinates
            3) w & h are the width and height of the rectangle of pixels to retrieve
            4) pix is the array of integers used to hold the RGB values
            5) off is the offset into the array of where to store the first pixel
            6) scansize number of pixels associated with each array row
 

ImageProducer

MemoryImageSource is a concrete extension ofthe abstract ImageProducer class.
 

MemoryImageSource

MemoryImageSource serves the reverse function of PixelGrabber, reading an array of pixels
and returning an image. An instance of MemoryImageSource is created. The instance is supplied
to Component.createImage( ) which returns an Image object. Simplest of six constructors is,

public MemoryImageSource(int w, int h, int pix[], int off, int scan)

// a w x h MemoryImageSource starting from the pix[off] element in the array of pixels, with scan
pixels in each line // other constructors add Hashtables and ColorModels



Miscellaneous

RubberBanding        // really more of a graphics than animation technique

Rubber-band lines are outlines of regions on the screen created by clicking and dragging the mouse.
(mousePressed( ) creates the first point and mouseDragged( ) and mouseReleased( ) a second point which
defines the diagonal corner of a rectangular region. (the user sees the rectangle stretching and retracting
with the mouse movement hence the rubber analogy.)

Summary of the logic

     1) The x and y coordinates of of the mouse pressed set the anchor corner of the rectangle
     2) Dragging, new x and y's are created, call to repaint( ) during dragging represent tracking
     3) In paint( ) the whole background is redrawn and the final rectangle is drawn.
     4) Override update( ) to just paint( ) to reduce flicker. // Look at code if time in the lab

Why you should do your drawing in paint and not in action handlers

1) Easy maintenance and debugging as all drawing operations are centrally located in paint( )
2) All screen state data is located in one spot so the screen is rendered accurately and completely
3) Painting outside paint( ) won't neccessarily be repaired  in case of screen damage from an overlapping window.

setXORMode( ) vs setPaintMode( )

In regular paint mode pixel values are set independently of previous pixel colors. In XOR drawing the
new color is computed by XORing the previous pixel with the current pixel. setXORMode( ) sets
XOR drawing, and takes an instance of Color as an argument (the XOR color of the graphics object)
This color mixed with the current color of a draw plus the color being overwritten are XOR'd together
to create the new pixel.

new pixel = old pixel ^ current color ^ XOR color

There are many interesting relationships between the pixels after repeated drawings but the important
point is drawing twice is the same as not drawing at all. If a graphics object is in XOR mode, a box
drawn with g.drawRect(x,y,w,h) and then undrawn by the same call.

Disadvantages

1) Imprecision using drawRect over a photo may cause a visibly obscure multicolored box to appear.
    Also using XOR to erase may leave visible traces on screen.
2) Unpredictable if a pixels data refer to color tables rather than a  true color model
3) Programming overhead is associated with adding  logic to the paint method



Reference

PixelGrabber

public PixelGrabber(Image img,
                    int x,
                    int y,
                    int w,
                    int h,
                    int[] pix,
                    int off,
                    int scansize)

     Create a PixelGrabber object to grab the (x, y, w, h) rectangular section of pixels from the
     specified image into the given array. The pixels are stored into the array in the default RGB
     ColorModel. The RGB data for pixel (i, j) where (i, j) is inside the rectangle (x, y, w, h) is
     stored in the array at pix[(j - y) * scansize + (i - x) + off].
     Parameters:
          img - the image to retrieve pixels from
          x - the x coordinate of the upper left corner of the rectangle of pixels to retrieve from
          the image, relative to the default (unscaled) size of the image
          y - the y coordinate of the upper left corner of the rectangle of pixels to retrieve from
          the image
          w - the width of the rectangle of pixels to retrieve
          h - the height of the rectangle of pixels to retrieve
          pix - the array of integers which are to be used to hold the RGB pixels retrieved from
          the image
          off - the offset into the array of where to store the first pixel
          scansize - the distance from one row of pixels to the next in the array
 


MemoryImageSource

public MemoryImageSource(int w,
                         int h,
                         int[] pix,
                         int off,
                         int scan)

     Constructs an ImageProducer object which uses an array of integers in the default RGB
     ColorModel to produce data for an Image object.