/////////////////////////////////////////////////////////////////////
// SlideViewer.java           -- SlideViewer Applet v2.1           //
//                                                                 //
// This applet displays a set of images, performing different      //
// styles of transitions between the images.  Transitions include  //
// slides, roll-ins, wipes, pixelins, and rotates.  For more       //
// information, check the Java link off of my homepage,            //
//       http://www.cs.hope.edu/~crider/                           //
//                                                                 //
//*****************************************************************//
// Revisions:                                                      //
//    v1.0:     Written July 18 - August 7, 1995                   //
//    v2.0beta: Released January 2, 1996                           //
//    v2.0:     Released February 18, 1996                         //
//    v2.1:     Released March 18, 1996                            //
//              Added ability to target frames.                    //
//              Improved fast-forward control.                     //
//                                                                 //
//*****************************************************************//
// By Mike Crider                                                  //
//       crider@cs.hope.edu                                        //
//       http://www.cs.hope.edu/~crider/                           //
//                                                                 //
// © Copyright 1995 by Mike Crider                                 //
/////////////////////////////////////////////////////////////////////

import java.net.*;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.image.*;

import SlideScript;

/////////////////////////////////////////////////////////////////////
// The SlideInfo class specifies which is the new image as well as //
// all of the information relevant to the transition type to tell  //
// it how to perform the transition.                               //
/////////////////////////////////////////////////////////////////////
class SlideInfo
{
   int type;    // Type of transition
   int dir;     // Direction for the transition
   int pic;     // Number of the pic for this transition
   int delay;   // Delay time to use
   int size;    // jumpSize or pixelSize, depending on type
   int speed;   // the speed of the transition (only some transitions obey)
   int amp;     // amplitude -- used by wavy transitions
   int num;     // some value number--meaning depends on type
}

/////////////////////////////////////////////////////////////////////
// The SlidePic class contains the actual image of the picture,    //
// and the url of the page, if any, which is attached to the image //
/////////////////////////////////////////////////////////////////////
class SlidePic
{
   Image gif;           // The image of the picture
   String url;          // URL to go to, if any, when the image is clicked
   String target;       // The frame to target the url to, if any
}

/////////////////////////////////////////////////////////////////////
// The main SlideViewer class -- The core of the Applet.           //
/////////////////////////////////////////////////////////////////////
// Needs to be declared "abstract" to have AppletContest for showDocument()
public class SlideViewer extends java.applet.Applet implements Runnable
{
   //////////////////////////////////////////////////////////////////
   // The script containing the Vector of transitions with all of  //
   // their information, and the pics (images/bitmaps) needed for  //
   // those transitions.  The size of the images is also important.//
   //////////////////////////////////////////////////////////////////
   SlideScript script;          // The parsed script file
   SlidePic pics[];             // The images and their bitmaps
   boolean scriptinited;        // Have finished parsing the script?
   int picwidth, picheight;     // The size of the images

   //////////////////////////////////////////////////////////////////
   // Temporary images, and graphics contexts for drawing purposes //
   // in the transition functions.                                 //
   //////////////////////////////////////////////////////////////////
   Image tmpimg, picstrip[];
   Graphics tmpimggr;

   //////////////////////////////////////////////////////////////////
   // Variables relevent to the currently occuring transition      //
   //////////////////////////////////////////////////////////////////
   int curpt;                   // This is the current point in the trans.
   int points[];                // Used for randomization without repeats
   int curstrip;                // The current strip we are changing (for WavyMorph)
   float ang;                   // The current angle (for Wavy stuff)
   int fastForward;             // fast forward speed (1 = normal)

   //////////////////////////////////////////////////////////////////
   // Variables relevent to the current state of the SlideViewer   //
   //////////////////////////////////////////////////////////////////
   Thread killme = null;        // The controlling Thread
   int delay;                   // The delay used in the run() procedure
   boolean inWin;               // Is the mouse in the applet window?
   int state;                   // 0 = Done, 1 = Init Trans., 2 = Trans-ing
   int curtrans;                // The current transition being performed
   SlidePic curpic;             // Image/Bitmap of the current display
   Graphics curpicgr;           // Graphics context for the curpic
   String statusmessage;        // The status message to display.

   //////////////////////////////////////////////////////////////////
   // Variables relevent to the paused state of the SlideViewer    //
   //////////////////////////////////////////////////////////////////
   boolean paused;              // Specifies if the user has paused the show
   boolean pausedShown;         // Is the paused image currently displayed?
   Image pausedimg = null;      // The paused image to indicate we are paused

   //////////////////////////////////////////////////////////////////
   //**************************************************************//
   //****************** End of Global Variables *******************//
   //**************************************************************//
   //////////////////////////////////////////////////////////////////

   //////////////////////////////////////////////////////////////////
   public void init()
   {
      delay = 100;

      curpic = null;
      script = null;
      paused = false;
      state = 0;
      curtrans = 0;
      fastForward = 1;
      picwidth = -1;
      picheight = -1;
      inWin = false;
      scriptinited = false;

      statusmessage = getAppletInfo();
   }

   //////////////////////////////////////////////////////////////////
   // Applet info, in case it is ever used.  (appletviewer uses it)//
   //////////////////////////////////////////////////////////////////
   public String getAppletInfo()
   {
      return "SlideViewer v2.1 was written and copyright 1995 by Mike Crider";
   }

   //////////////////////////////////////////////////////////////////
   // Initialize the points array to the length specified, filling //
   // it with the integers 0 - len, then randomizes the contents.  //
   //////////////////////////////////////////////////////////////////
   void initPoints(int len)
   {
      int tp;
      int i, w;
      
      points = new int[len];

      for (i = 0; i < len; i++)
         points[i] = i;

      for (i = 0; i < len; i++)
      {
         w = (int)(Math.random()*points.length);
         tp = points[i];
         points[i] = points[w];
         points[w] = tp;
      }
   }

   //////////////////////////////////////////////////////////////////
   // Initialize the curpic variable so we can use the gif         //
   // in it, and copy the new stuff to it when we want to          //
   //////////////////////////////////////////////////////////////////
   void initCurpic(Graphics g)
   {
      if (picwidth < 0 || picheight < 0)
         return;                // Wait until we have read in images

      curpic = new SlidePic();
      curpic.gif = createImage(picwidth,picheight);
      if (curpic.gif == null)
      {
         curpic = null;
         return;
      }

      curpicgr = curpic.gif.getGraphics();
      curpicgr.setColor(Color.black);
      curpicgr.fillRect(0,0,picwidth,picheight);
   }

   //////////////////////////////////////////////////////////////////
   // Set the status bar, being careful that it only does the      //
   // actual display if the user's mouse is in the applet.         //
   //////////////////////////////////////////////////////////////////
   void setStatus(String msg)
   {
      statusmessage = msg;
      if (inWin)
         getAppletContext().showStatus(statusmessage);
   }

   //////////////////////////////////////////////////////////////////
   // Set the 'state' variable to indicate we are performing a     //
   // transition.  Also, clear the url setting.                    //
   //////////////////////////////////////////////////////////////////
   void transStarted()
   {
      state = 2;
      curpic.url = null;
      curpic.target = null;
      setStatus("");
   }

   //////////////////////////////////////////////////////////////////
   // Set the curpic image to the specified gif                    //
   //////////////////////////////////////////////////////////////////
   void setCurpic(Image gif)
   {
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      curpicgr.clipRect(0,0,picwidth,picheight);
      curpicgr.drawImage(gif,0,0,this);
   }

   //////////////////////////////////////////////////////////////////
   // Set the curpic image and URL to those of a SlidePic          //
   // specified by the index 'picnum'                              //
   //////////////////////////////////////////////////////////////////
   void setCurpic(int picnum)
   {
      if (curpic == null || (picnum < 0 || picnum >= pics.length))
         return;
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      curpicgr.clipRect(0,0,picwidth,picheight);
      curpicgr.drawImage(pics[picnum].gif,0,0,this);
      curpic.url = pics[picnum].url;
      curpic.target = pics[picnum].target;

      // Set the clickable url, if any
      if (curpic.url != null)
         setStatus("Go to " + curpic.url);
      else
         setStatus("");
   }

   //////////////////////////////////////////////////////////////////
   // DoneFunc is called after a transition has just completed.    //
   // Depending on the transition type, the curpic image will be   //
   // set to contain the current image on the display; other       //
   // transitions have already set the curpic image.  DoneFunc     //
   // then moves to the next transition and either sets state      //
   // to 1, telling the transition to initialize and run, or       //
   // to 0, saying that we have finished the display and should    //
   // stop.                                                        //
   //////////////////////////////////////////////////////////////////
   void doneFunc()
   {
      SlideInfo t;

      // If the pixmap stuff has been used, dispose of it now.
      if (tmpimg != null)
      {
         tmpimg = null;
      }
      if (tmpimggr != null)
      {
         tmpimggr.dispose();
         tmpimggr = null;
      }

      // Set the curpic variable if we are not a delay or fadeOut
      t = script.elementAt(curtrans);
      if (t.type < 90 && t.type != 10)
         setCurpic(t.pic);

      // Go to the next transition; set the state appropriately
      curtrans++;
      if (curtrans < script.numtrans)
         state = 1;
      else
         state = 0;
   }

   //////////////////////////////////////////////////////////////////
   // Fade In or Out an image.  FadeOut's always fade the current  //
   // image to black, and FadeIn's always start from black and     //
   // fade in the new image.  If an image is on the screen when    //
   // there is a FadeIn transition to occur, the image will flash  //
   // to black so that the new image can fade in.                  //
   //////////////////////////////////////////////////////////////////
   void fade(Graphics g, SlideInfo t)
   {
      SlidePic whichpic;
      FadeFilter filt;
      Image tmp;
      int i;

      if (t.type == 9)          // fadeIn
         whichpic = pics[t.pic];
      else                      // fadeOut
         whichpic = curpic;

      if (state == 1)           // Initialize
      {
         if (t.type == 9)       // fadeIn
            curpt = 0;
         else                   // fadeOut
            curpt = 99;
         transStarted();
      }

      if ((t.type == 9 && curpt >= 100) || (t.type == 10 && curpt <= 0))
      {
         if (t.type == 10)      // fadeOut
         {
            // Black out curpic
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            curpicgr.setColor(Color.black);
            curpicgr.fillRect(0,0,picwidth,picheight);
         }
         doneFunc();
         return;
      }
      
      // Do the fade!
      filt = new FadeFilter(curpt);
      tmpimg = createImage(new FilteredImageSource(whichpic.gif.getSource(),filt));
      g.drawImage(tmpimg,1,1,this);

      // Advance the percentage
      if (t.type == 9)  // fadeIn
      {
         curpt += t.speed * fastForward;
         if (curpt > 100)
            curpt = 100;
      }
      else
      {
         curpt -= t.speed * fastForward;
         if (curpt < 0)
            curpt = 0;
      }
   }

   //////////////////////////////////////////////////////////////////
   // PixelIn a new picture.  This version works via copyArea      //
   // calls, copying chunks of the new image over the old one.     //
   //////////////////////////////////////////////////////////////////
   void pixelIn(Graphics g, SlideInfo t)
   {
      int w, h, i, howmany;

      if (state == 1)   // Initialize transition pixmap
      {
         tmpimg = createImage(picwidth*2, picheight);
         tmpimggr = tmpimg.getGraphics();
         tmpimggr.drawImage(curpic.gif,0,0,this);
         tmpimggr.drawImage(pics[t.pic].gif,picwidth,0,this);
         initPoints((picwidth*picheight)/(t.size*t.size)+picheight/t.size);
         curpt = 0;
         transStarted();
      }

      if (curpt >= points.length)
      {
         doneFunc();
         return;
      }

      howmany = (800/(t.size*t.size)) * fastForward;
      if (howmany < 1)
         howmany = 1;
      for (i = 0; i < howmany; i++)
      {
         if (i+curpt < points.length)
         {
            w = points[i+curpt] % (picwidth/t.size+1);
            h = (points[i+curpt] - w) / (picwidth/t.size+1);
            w *= t.size;
            h *= t.size;
            tmpimggr.copyArea(w+picwidth,h,t.size,t.size,-picwidth,0);
         }
      }
      curpt += howmany;

      curpicgr.drawImage(tmpimg,0,0,this);
      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // Wipe will wipe on an image as if it were being wiped on      //
   // behind a windshield wiper.                                   //
   //////////////////////////////////////////////////////////////////
   void wipe(Graphics g, SlideInfo t)
   {
      float theta;
      int i;
      int picdiagnal, orgx, orgy, start, end;
      float dirx, diry, dirlen;
      boolean pastHalf;              // Have we done half of the transition?
      int clipType;                  // What side do we clip to/from?

      // These are the two angles for each pivot point, specifying the
      // starting and ending points for the wipe.
      // Clockwise (t.num == 1):   ang1 is start, ang2 is end
      // Counter-C (t.num == -1):  ang2 is start, ang1 is end
      //             LEFT, RIGHT, TOP, BOTTOM, unused, unused, TOPLEFT, TOPRIGHT, BOTLEFT, BOTRIGHT, CENTER
      int ang1[] = {   90,   270, 360,    180,      0,      0,     360,      270,      90,      180,    180 };
      int ang2[] = {  -90,    90, 180,      0,      0,      0,     270,      180,       0,       90,   -180 };

      // FirstHalf specifies the clip type to use during the first half
      // of the transition.  These are for the clockwise direction.
      // SecndHalf is for the second half of the transition (same for some)
      // Type:  0 is clip left to x position
      //        1 is clip x position to right
      //                  LEFT, RIGHT, TOP, BOTTOM, unused, unused, TOPLEFT, TOPRIGHT, BOTLEFT, BOTRIGHT, CENTER
      int firstHalf[] = {    0,     1,   1,      0,      0,      0,       1,        1,       0,        0,      0 };
      int secndHalf[] = {    1,     0,   1,      0,      0,      0,       1,        1,       0,        0,      1 };

      // Find the starting and ending angles for this direction
      if (t.num == 1)
      {
         start = ang1[t.dir];      // Clockwise
         end = ang2[t.dir];        // Clockwise
      }
      else
      {
         start = ang2[t.dir];      // Counter-Clockwise
         end = ang1[t.dir];        // Counter-Clockwise
      }

      if (state == 1)   // Prepare the image strips
      {
         // If the direction is invalid, default it to bottom
         if (t.dir < 0 || t.dir >= ang1.length)
            t.dir = 3;

         makeImgs(g,0,t.size,pics[t.pic].gif);

         curpt = start;         // Set to the starting angle
         transStarted();
         ang = 0;
      }

      orgx = 0;
      orgy = 0;
      // Set orgx
      switch (t.dir)
      {
         case 0:        // Left
         case 6:        // TopLeft
         case 8:        // BottomLeft
                 orgx = 0;
                 break;
         case 2:        // Top
         case 3:        // Bottom
         case 10:       // Center
                 orgx = picwidth/2;
                 break;
         case 1:        // Right
         case 7:        // TopRight
         case 9:        // BottomRight
                 orgx = picwidth;
                 break;
      }
      // Set orgy
      switch (t.dir)
      {
         case 2:        // Top
         case 6:        // TopLeft
         case 7:        // TopRight
                 orgy = 0;
                 break;
         case 0:        // Left
         case 1:        // Right
         case 10:       // Center
                 orgy = picheight/2;
                 break;
         case 3:        // Bottom
         case 8:        // BottomLeft
         case 9:        // BottomRight
                 orgy = picheight;
                 break;
      }

      curpt += -t.num * t.speed * fastForward;

      if ((t.num == 1 && curpt <= end) || (t.num == -1 && curpt >= end))
      {
         // Be sure to draw the full picture as the final step
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
         curpicgr.clipRect(0,0,picwidth,picheight);
         curpicgr.drawImage(pics[t.pic].gif,0,0,this);
         g.drawImage(curpic.gif,1,1,this);

         doneFunc();
         return;
      }


      theta = (float)(curpt/180.0f*Math.PI);

      picdiagnal = (int) Math.sqrt(picwidth*picwidth + picheight*picheight);
      dirx = (float)(Math.cos(theta) * picdiagnal);
      diry = -(float)(Math.sin(theta) * picdiagnal);

      // Normalize the direction vector
      dirlen = (float) Math.sqrt(dirx*dirx+diry*diry);
      dirx = dirx / dirlen;
      diry = diry / dirlen;

      // Determine if we have done half of the transition yet
      if (t.num == 1)        // clockwise
         pastHalf = ((start+end)/2 >= curpt);
      else                   // counter-clockwise
         pastHalf = ((start+end)/2 <= curpt);

      // Determine the clip type to use for this pivot and current angle
      clipType = 0;
      if (t.num == 1)
      {
         if (!pastHalf)
            clipType = firstHalf[t.dir];
         else
            clipType = secndHalf[t.dir];
      }
      else
      {
         if (!pastHalf)
            clipType = secndHalf[t.dir];
         else
            clipType = firstHalf[t.dir];

         // Need to invert clip types for counter-clockwise
         if (clipType == 0)
            clipType = 1;
         else // clipType == 1
            clipType = 0;
      }

      // Actually draw on the strips now
      for (i = 0; i < picstrip.length; i++)
      {
         boolean onLeft, inFirstHalf;
         float f;
         int nx, ny;
         float STRIPX, STRIPY, SDIRX, SDIRY;

         STRIPX = 0; STRIPY = t.size*i; SDIRX = 1; SDIRY = 0;   // For horizontals from left
         f = ((float)(orgx-STRIPX)*SDIRY-(float)(orgy-STRIPY)*(SDIRX))/((float)(SDIRX*diry-dirx*SDIRY));

         // If we are past half and This strip is in the first half,
         // we will go ahead and draw the full length of those strips.
         // This only effects Left, Center, and Right pivots.
         if (pastHalf && (t.dir == 0 || t.dir == 1 || t.dir == 10))
         {
            inFirstHalf = false;
            if (t.num == 1)        // Clockwise direction
            {
               if (firstHalf[t.dir] == 0 && i*t.size <= orgy)
                  inFirstHalf = true;
               if (firstHalf[t.dir] == 1 && i*t.size >= orgy)
                  inFirstHalf = true;
            }
            else                   // Counter-Clockwise direction
            {
               if (firstHalf[t.dir] == 1 && i*t.size >= orgy)
                  inFirstHalf = true;
               if (firstHalf[t.dir] == 0 && i*t.size <= orgy)
                  inFirstHalf = true;
            }

            if (inFirstHalf)
            {
               // Draw it
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
               curpicgr.clipRect(0,0,picwidth,picheight);
               curpicgr.drawImage(picstrip[i],0,i*t.size,this);
               continue;
            }
         }

         // If the intersection is behind us, don't try to draw it.
         if (f < 0)
            continue;

         nx = (int) (f*dirx + orgx);
         ny = (int) (f*diry + orgy);

         if (nx < 0)
            nx = 0;
         if (nx > picwidth)
            nx = picwidth;
         if (ny < 0)
            ny = 0;
         if (ny > picheight)
            ny = picheight;

curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
         if (clipType == 0)
            curpicgr.clipRect(0,0,nx,picheight);
         else
            curpicgr.clipRect(nx,0,picwidth,picheight);

         curpicgr.drawImage(picstrip[i],0,i*t.size,this);
      }
      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // StripsOver will slide strips of the new image over the old   //
   // one in a staircase fashion (each strip is ahead of the next) //
   // t.dir:        0 = stripsOver Left      (from Right)          //
   //               1 = stripsOver Right     (from Left)           //
   //               2 = stripsOver Up        (from Bottom)         //
   //               3 = stripsOver Down      (from Top)            //
   //////////////////////////////////////////////////////////////////
   void stripsOver(Graphics g, SlideInfo t)
   {
      int i, idx, max, pnew, pold, num;

      if (state == 1)   // Prepare the image strips
      {
         switch (t.dir)
         {
            case 0:     // Left
                  tmpimg = createImage(picwidth*2, picheight);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(curpic.gif,0,0,this);
                  tmpimggr.drawImage(pics[t.pic].gif,picwidth,0,this);
                  break;
            case 1:     // Right
                  tmpimg = createImage(picwidth*2, picheight);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(pics[t.pic].gif,0,0,this);
                  tmpimggr.drawImage(curpic.gif,picwidth,0,this);
                  break;
            case 2:     // Up
                  tmpimg = createImage(picwidth, picheight*2);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(curpic.gif,0,0,this);
                  tmpimggr.drawImage(pics[t.pic].gif,0,picheight,this);
                  break;
            case 3:     // Down
                  tmpimg = createImage(picwidth, picheight*2);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(pics[t.pic].gif,0,0,this);
                  tmpimggr.drawImage(curpic.gif,0,picheight,this);
                  break;
         }

         switch (t.dir)
         {
            case 0:        // Left
            case 1:        // Right
                  curpt = picwidth + t.amp*(picheight/t.size);
                  break;
            case 2:        // Up
            case 3:        // Down
                  curpt = picheight + t.amp*(picwidth/t.size);
                  break;
         }

         transStarted();
         ang = 0;
      }

      if (curpt < 0)
      {
         doneFunc();
         return;
      }

      curpt -= t.speed * fastForward;

      // Actually draw on the strips now
      if (t.dir == 0 || t.dir == 1)     // Left or Right
      {
         num = (int)Math.ceil((double)picheight/(double)t.size);
         for (i = 0; i < num; i++)
         {
            if (t.num == 0)
               idx = (num-1-i);
            else
               idx = i;

            if (t.dir == 0)     // Left
            {
               pnew = curpt - idx*t.amp;
               pold = pnew+t.speed * fastForward;

               if (pold > picwidth)
                  pold = picwidth;
               if (pnew < 0 && pold > 0)
                  pnew = 0;
                  
               if (pnew >= 0 && pnew < picwidth)
                  tmpimggr.copyArea(pold,i*t.size,picwidth,t.size,-(pold-pnew),0);
            }
            else                // Right
            {
               pnew = picwidth - (curpt - idx*t.amp);
               pold = pnew-t.speed * fastForward;

               if (pold < 0)
                  pold = 0;
               if (pnew > picwidth && pold < picwidth)
                  pnew = picwidth;
                  
               if (pnew > 0 && pnew <= picwidth)
                  tmpimggr.copyArea(pold,i*t.size,picwidth,t.size,-(pold-pnew),0);
            }
         }
      }
      else if (t.dir == 2 || t.dir == 3)
      {
         num = (int)Math.ceil((double)picwidth/(double)t.size);
         for (i = 0; i < num; i++)
         {
            if (t.num == 0)
               idx = (num-1-i);
            else
               idx = i;
            
            if (t.dir == 2)     // Up
            {
               pnew = curpt - idx*t.amp;
               pold = pnew+t.speed * fastForward;

               if (pold > picheight)
                  pold = picheight;
               if (pnew < 0 && pold > 0)
                  pnew = 0;

               if (pnew >= 0 && pnew < picheight)
                  tmpimggr.copyArea(i*t.size,pold,t.size,picheight,0,-(pold-pnew));
            }
            else                // Down
            {
               pnew = picheight - (curpt - idx*t.amp);
               pold = pnew-t.speed * fastForward;

               if (pold < 0)
                  pold = 0;
               if (pnew > picheight && pold < picheight)
                  pnew = picheight;

               if (pnew > 0 && pnew <= picheight)
                  tmpimggr.copyArea(i*t.size,pold,t.size,picheight,0,-(pold-pnew));
            }
         }
      }

      if (t.dir == 0 || t.dir == 2)     // Left and Up, new pic is at origin
         curpicgr.drawImage(tmpimg,0,0,this);
      else if (t.dir == 1)              // Right, new pic is in right half
         curpicgr.drawImage(tmpimg,-picwidth,0,this);
      else if (t.dir == 3)              // Down, new pic is in bottom half
         curpicgr.drawImage(tmpimg,0,-picheight,this);

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // StripsSlide will slide strips of the new image on, sliding   //
   // the old image off.  The strips are arranged in a staircase   //
   // fasion (each strip is a little ahead of the next)            //
   // t.dir:        0 = stripsSlide Left      (from Right)         //
   //               1 = stripsSlide Right     (from Left)          //
   //               2 = stripsSlide Up        (from Bottom)        //
   //               3 = stripsSlide Down      (from Top)           //
   //                                                              //
   // This is nearly identical to stripsOver, except that the      //
   // copyArea's do twice as much area, and the left and up ones   //
   // have an offset of the start.  Since these are so similar,    //
   // PERHAPS THEY CAN BE COMBINED INTO ONE!!!!!!!!!!!!!!!!!!!!!!  //
   //////////////////////////////////////////////////////////////////
   void stripsSlide(Graphics g, SlideInfo t)
   {
      int i, idx, max, pnew, pold, num;

      if (state == 1)   // Prepare the image strips
      {
         switch (t.dir)
         {
            case 0:     // Left
                  tmpimg = createImage(picwidth*2, picheight);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(curpic.gif,0,0,this);
                  tmpimggr.drawImage(pics[t.pic].gif,picwidth,0,this);
                  break;
            case 1:     // Right
                  tmpimg = createImage(picwidth*2, picheight);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(pics[t.pic].gif,0,0,this);
                  tmpimggr.drawImage(curpic.gif,picwidth,0,this);
                  break;
            case 2:     // Up
                  tmpimg = createImage(picwidth, picheight*2);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(curpic.gif,0,0,this);
                  tmpimggr.drawImage(pics[t.pic].gif,0,picheight,this);
                  break;
            case 3:     // Down
                  tmpimg = createImage(picwidth, picheight*2);
                  tmpimggr = tmpimg.getGraphics();
                  tmpimggr.drawImage(pics[t.pic].gif,0,0,this);
                  tmpimggr.drawImage(curpic.gif,0,picheight,this);
                  break;
         }

         switch (t.dir)
         {
            case 0:        // Left
            case 1:        // Right
                  curpt = picwidth + t.amp*(picheight/t.size);
                  break;
            case 2:        // Up
            case 3:        // Down
                  curpt = picheight + t.amp*(picwidth/t.size);
                  break;
         }

         transStarted();
         ang = 0;
      }

      if (curpt < 0)
      {
         doneFunc();
         return;
      }

      curpt -= t.speed * fastForward;

      // Actually draw on the strips now
      if (t.dir == 0 || t.dir == 1)     // Left or Right
      {
         num = (int)Math.ceil((double)picheight/(double)t.size);
         for (i = 0; i < num; i++)
         {
            if (t.num == 0)
               idx = (num-1-i);
            else
               idx = i;

            if (t.dir == 0)     // Left
            {
               pnew = curpt - idx*t.amp;
               pold = pnew+t.speed * fastForward;

               if (pold > picwidth)
                  pold = picwidth;
               if (pnew < 0 && pold > 0)
                  pnew = 0;
                  
               if (pnew >= 0 && pnew < picwidth)
                  tmpimggr.copyArea(pold-picwidth,i*t.size,picwidth*2,t.size,-(pold-pnew),0);
            }
            else                // Right
            {
               pnew = picwidth - (curpt - idx*t.amp);
               pold = pnew-t.speed * fastForward;

               if (pold < 0)
                  pold = 0;
               if (pnew > picwidth && pold < picwidth)
                  pnew = picwidth;
                  
               if (pnew > 0 && pnew <= picwidth)
                  tmpimggr.copyArea(pold,i*t.size,picwidth*2,t.size,-(pold-pnew),0);
            }
         }
      }
      else if (t.dir == 2 || t.dir == 3)
      {
         num = (int)Math.ceil((double)picwidth/(double)t.size);
         for (i = 0; i < num; i++)
         {
            if (t.num == 0)
               idx = (num-1-i);
            else
               idx = i;
            
            if (t.dir == 2)     // Up
            {
               pnew = curpt - idx*t.amp;
               pold = pnew+t.speed * fastForward;

               if (pold > picheight)
                  pold = picheight;
               if (pnew < 0 && pold > 0)
                  pnew = 0;

               if (pnew >= 0 && pnew < picheight)
                  tmpimggr.copyArea(i*t.size,pold-picheight,t.size,picheight*2,0,-(pold-pnew));
            }
            else                // Down
            {
               pnew = picheight - (curpt - idx*t.amp);
               pold = pnew-t.speed * fastForward;

               if (pold < 0)
                  pold = 0;
               if (pnew > picheight && pold < picheight)
                  pnew = picheight;

               if (pnew > 0 && pnew <= picheight)
                  tmpimggr.copyArea(i*t.size,pold,t.size,picheight*2,0,-(pold-pnew));
            }
         }
      }

      if (t.dir == 0 || t.dir == 2)     // Left and Up, new pic is at origin
         curpicgr.drawImage(tmpimg,0,0,this);
      else if (t.dir == 1)              // Right, new pic is in right half
         curpicgr.drawImage(tmpimg,-picwidth,0,this);
      else if (t.dir == 3)              // Down, new pic is in bottom half
         curpicgr.drawImage(tmpimg,0,-picheight,this);

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // WavyDelay will make the current image wavy around during a   //
   // delay until the length of time specified by t.num has passed //
   // dir =     0 Waves going left and right (horizontal strips)   //
   //           1 Waves going up and down (vertical strips)        //
   //////////////////////////////////////////////////////////////////
   void wavyDelay(Graphics g, SlideInfo t)
   {
      float theta;
      float cang;
      int i, offset;

      if (state == 1)   // Prepare the image strips
      {
         if (t.dir == 0)
            makeImgs(g,0,t.size,curpic.gif);
         else
            makeImgs(g,1,t.size,curpic.gif);

         curpt = (int) System.currentTimeMillis();   // Current time, so we can check delay
         state = 2;
         ang = 0;
      }

      // Have we passed our total delay time?
      if (((int) System.currentTimeMillis() - curpt) * fastForward >= t.num)
      {
         doneFunc();
         return;
      }

      curpicgr.setColor(Color.black);
      curpicgr.fillRect(0,0,picwidth,picheight);

      theta = (float)t.speed/314.0f;

      for (i = 0; i < picstrip.length; i++)
      {
         cang = ang+(i*theta);
         offset = (int) (Math.sin(cang) * t.amp);
         if (t.dir == 0)
            curpicgr.drawImage(picstrip[i],offset,i*t.size,this);
         else
            curpicgr.drawImage(picstrip[i],i*t.size,offset,this);
      }

      g.drawImage(curpic.gif,1,1,this);

      ang += .1 * fastForward;
   }

   //////////////////////////////////////////////////////////////////
   // Delay for the length of time specified by t.num, having the  //
   // current picture wiggle and decay during the delay            //
   //////////////////////////////////////////////////////////////////
   void decayDelay(Graphics g, SlideInfo t)
   {
      int x, y, nx, ny, i;

      if (state == 1)   // Initialize transition pixmap
      {
         curpt = (int) System.currentTimeMillis();   // Current time, so we can check delay
         state = 2;
      }

      // Exit when total delayed time is greater than or equal to desired delay
      if (((int) System.currentTimeMillis() - curpt) * fastForward >= t.num)
      {
         // Make sure the URL is set for curpic
         setCurpic(curpic.gif);

         doneFunc();
         return;
      }

      for (i = 0; i < t.speed; i++)
      {
         x = (int)(Math.random()*picwidth);
         y = (int)(Math.random()*picheight);
         nx = (int)(Math.random()*t.amp)-t.amp/2;
         ny = (int)(Math.random()*t.amp)-1;
         curpicgr.copyArea(x,y,t.size,t.size,nx,ny);
      }

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // Delay for the length of time specified by t.delay            //
   //////////////////////////////////////////////////////////////////
   void delay(Graphics g, SlideInfo t)
   {
      if (state == 1)   // Initialize transition pixmap
      {
         curpt = (int) System.currentTimeMillis();   // Current time, so we can check delay
         state = 2;
         return;
      }

      // We have supposedly passed the time, so make the delay time
      // small so the next thing will happen soon
      delay = 5;

      // Exit when total delayed time is greater than or equal to desired delay
      if (((int) System.currentTimeMillis() - curpt) * fastForward >= t.delay)
      {
         doneFunc();
         return;
      }
   }

   //////////////////////////////////////////////////////////////////
   // Make all the strips for the specified picture                //
   // type =    0 for horizontal strips                            //
   //           1 for vertical strips (get it?  vertical = | = 1 ?)//
   // chunk = # of pixels tall/wide for each strip                 //
   //////////////////////////////////////////////////////////////////
   void makeImgs(Graphics g, int type, int chunk, Image pic)
   {
      int i, numstrips;

      if (type == 0)
      {
         numstrips = pic.getHeight(this)/chunk;
         if (pic.getHeight(this) % chunk > 0)
            numstrips++;
         picstrip = new Image[numstrips];

         for (i = 0; i < numstrips; i++)
         {
            picstrip[i] = createImage(pic.getWidth(this), chunk);
            Graphics pixmap = picstrip[i].getGraphics();

            if (i == numstrips-1)
            {
               pixmap.setColor(Color.black);
               pixmap.fillRect(0,0,pic.getWidth(this),chunk);
            }

            pixmap.drawImage(pic,0,-i*chunk,this);
            pixmap.dispose();
         }
      }
      else
      {
         numstrips = pic.getWidth(this)/chunk;
         if (pic.getWidth(this) % chunk > 0)
            numstrips++;
         picstrip = new Image[numstrips];

         for (i = 0; i < numstrips; i++)
         {
            picstrip[i] = createImage(chunk, pic.getHeight(this));
            Graphics pixmap = picstrip[i].getGraphics(); 
            if (i == numstrips-1)
            {
               pixmap.setColor(Color.black);
               pixmap.fillRect(0,0,chunk,pic.getHeight(this));
            }

            pixmap.drawImage(pic,-i*chunk,0,this);
            pixmap.dispose();
         }
      }
   }

   //////////////////////////////////////////////////////////////////
   // Make a single strip, the one specified by stripnum           //
   // type =    0 for horizontal strips                            //
   //           1 for vertical strips (get it?  vertical = | = 1 ?)//
   // chunk = # of pixels tall/wide for each strip                 //
   //////////////////////////////////////////////////////////////////
   void makeStrip(Graphics g, int type, int chunk, Image pic, int stripnum)
   {
      float cang;
      int i, val;

      if (stripnum >= picstrip.length)
         return;

      if (type == 0)
      {
         val = picwidth;
         try
         {
            val = picstrip[stripnum].getWidth(this);
         } catch (Exception e);
         picstrip[stripnum] = createImage(val, chunk);
         Graphics pixmap = picstrip[stripnum].getGraphics();
         pixmap.drawImage(pic,0,-stripnum*chunk,this);
         pixmap.dispose();
      }
      else
      {
         val = picheight;
         try
         {
            val = picstrip[stripnum].getHeight(this);
         } catch (Exception e);
         picstrip[stripnum] = createImage(chunk, val);
         Graphics pixmap = picstrip[stripnum].getGraphics();
         pixmap.drawImage(pic,-stripnum*chunk,0,this);
         pixmap.dispose();
      }
   }

   //////////////////////////////////////////////////////////////////
   // WavyMorph will make the current image shake get wavier and   //
   // wavier until it is wavy enough that it will switch in the    //
   // new image and flatten out the waves                          //
   // dir =     0 Waves going left and right (horizontal strips)   //
   //           1 Waves going up and down (vertical strips)        //
   //////////////////////////////////////////////////////////////////
   void wavyMorph(Graphics g, SlideInfo t)
   {
      float theta;
      float cang;
      int i, offset;

      if (state == 1)   // Prepare the image strips
      {
         if (t.dir == 0)
         {
            makeImgs(g,0,t.size,curpic.gif);
            initPoints(picheight/t.size);
         }
         else
         {
            makeImgs(g,1,t.size,curpic.gif);
            initPoints(picwidth/t.size);
         }

         curpt = 0;
         transStarted();
         curstrip = 0;
         ang = 1.0f;
      }

      if (ang > 70)
      {
         for (i = 0; i < t.num * fastForward; i++)
         {
            if (curstrip < points.length)
            {
               state = 3;                  // The wait state
               if (t.dir == 0)
                  makeStrip(g,0,t.size,pics[t.pic].gif,points[curstrip]);
               else
                  makeStrip(g,1,t.size,pics[t.pic].gif,points[curstrip]);
               curstrip++;
            }
            else
            {
               state = 4;                  // The work-to-norm state
               ang = 70;
               break;
            }
         }
      }

      if (state == 5)
      {
         doneFunc();
         return;
      }

      if (ang < 1.0)
      {
         state = 5;     // The center the pic state
         ang = 1.0f;       // Center the pic for the last shot
         curpt = 0;
      }

      theta = (float)((ang-1.0f)/360.0f*2.0f*Math.PI);

      curpicgr.setColor(Color.black);
      curpicgr.fillRect(0,0,picwidth,picheight);

      for (i = 0; i < picstrip.length; i++)
      {
         cang = (i*theta);
         offset = (int) (Math.sin(cang) * t.amp);
         if (t.dir == 0)
            curpicgr.drawImage(picstrip[i],offset,i*t.size,this);
         else
            curpicgr.drawImage(picstrip[i],i*t.size,offset,this);
      }

      g.drawImage(curpic.gif,1,1,this);

      switch (state)
      {
         case 2: ang *= (1.05 + (float)t.speed/100.0) * fastForward;
                 break;
         case 3: ang *= 1.001;
                 break;
         case 4: ang /= (1.05 + (float)t.speed/100.0) * fastForward;
                 break;
      }
   }

   //////////////////////////////////////////////////////////////////
   // Rotate has the effect of having two pictures back to back on //
   // a board.  It rotates the board around to display the new     //
   // picture on the back.  't.dir' indicates the direction of     //
   // rotation:         0 = horizontal rotation (around vertical)  //
   //                   1 = vertical rotation (around horizontal)  //
   //////////////////////////////////////////////////////////////////
   void rotate(Graphics g, SlideInfo t)
   {
      float theta, costheta, stripcenter;
      int i, x, y;

      if (state == 1)   // Prepare the image we will slide
      {
         if (t.dir == 0)
            makeImgs(g,1,t.size,curpic.gif);
         else
            makeImgs(g,0,t.size,curpic.gif);
         curpt = 0;
         transStarted();
      }

      if (curpt >= 360)
      {
         // Make sure that the last view is of the actual image as it should look
         g.drawImage(pics[t.pic].gif,1,1,this);

         doneFunc();
         return;
      }

      if (curpt >= 90 && state == 2)
      {
         // Make the new image strips
         if (t.dir == 0)
            makeImgs(g,1,t.size,pics[t.pic].gif);
         else
            makeImgs(g,0,t.size,pics[t.pic].gif);

         curpt = 270;
         state = 3;
      }

      curpicgr.setColor(Color.black);
      curpicgr.fillRect(0,0,picwidth,picheight);

      curpt += t.speed * fastForward;

      // Make sure we don't spin too far before changing images
      if (curpt > 90 && curpt < 180)
         curpt = 90;

      theta = (float)(curpt/180.0f*Math.PI);
      costheta = (float)Math.cos(theta);

      if (curpt == 90)
         costheta = 0.0f;

      if (t.dir == 0)
         stripcenter = (float)picwidth/2.0f;
      else
         stripcenter = (float)picheight/2.0f;

      for (i = 0; i < picstrip.length; i++)
      {
         if (t.dir == 0)
         {
            x = (int) (costheta*((float)i*t.size-stripcenter)+stripcenter);
            curpicgr.drawImage(picstrip[i],x,0,this);
         }
         else
         {
            y = (int) (costheta*((float)i*t.size-stripcenter)+stripcenter);
            curpicgr.drawImage(picstrip[i],0,y,this);
         }
      }

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // rollIn will roll out an image on top of the current image    //
   // The direction of the rolling is determined by the t.dir      //
   // parameter:        0 = rollIn Left      (from Right)          //
   //                   1 = rollIn Right     (from Left)           //
   //                   2 = rollIn Up        (from Bottom)         //
   //                   3 = rollIn Down      (from Top)            //
   //                   4 = rollIn LeftRight (from Left & Right)   //
   //                   5 = rollIn UpDown    (from Top & Bottom)   //
   //////////////////////////////////////////////////////////////////
   void rollIn(Graphics g, SlideInfo t)
   {
      int max, i;

      if (state == 1)   // Prepare the image we will slide
      {
         curpt = 0;
         transStarted();
      }

      max = 0;
      switch (t.dir)
      {
         case 0:        // Left
         case 1:        // Right
                max = picwidth;
                break;
         case 2:        // Up
         case 3:        // Down
                max = picheight;
                break;
         case 4:        // LeftRight
                max = picwidth/2;
                break;
         case 5:        // UpDown
                max = picheight/2;
                break;
      }

      if (curpt >= max)
      {
         doneFunc();
         return;
      }

      curpt += t.speed * fastForward;
      if (curpt >= max)
         curpt = max;

curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      // Apply the new image in the correct location
      switch (t.dir)
      {
         case 0:        // Left
                  curpicgr.clipRect(picwidth-curpt,0,picwidth,picheight); break;
         case 1:        // Right
                  curpicgr.clipRect(0,0,curpt,picheight); break;
         case 2:        // Up
                  curpicgr.clipRect(0,picheight-curpt,picwidth,picheight); break;
         case 3:        // Down
                  curpicgr.clipRect(0,0,picwidth,curpt); break;
         case 4:        // LeftRight
                  curpicgr.clipRect(0,0,curpt,picheight);
                  curpicgr.drawImage(pics[t.pic].gif,0,0,this);  // Draw Left side
                                                            // Right side drawn below
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                  curpicgr.clipRect(picwidth-curpt-1,0,curpt+1,picheight);
                  break;
         case 5:        // UpDown
                  curpicgr.clipRect(0,0,picwidth,curpt);
                  curpicgr.drawImage(pics[t.pic].gif,0,0,this);  // Draw top part
                                                            // Bottom part drawn below
curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                  curpicgr.clipRect(0,picheight-curpt-1,picwidth,curpt+1);
//Example of Failr//System.out.println("updown, cliprect is x=" + curpicgr.getClipRect().x
                  //                                    + " y=" + curpicgr.getClipRect().y
                  //                                    + " width=" + curpicgr.getClipRect().width
                  //                                    + " height=" + curpicgr.getClipRect().height
                  //                                    + " while curpt+1 is " + (curpt+1));
                  break;
      }
      curpicgr.drawImage(pics[t.pic].gif,0,0,this);

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // rollOut will roll out an image on top of the current image   //
   // The direction of the rolling is determined by the t.dir      //
   // parameter:        0 = rollOut Left      (to Left)            //
   //                   1 = rollOut Right     (to Right)           //
   //                   2 = rollOut Up        (to Top)             //
   //                   3 = rollOut Down      (to Bottom)          //
   //                   4 = rollOut LeftRight (to Left & Right)    //
   //                   5 = rollOut UpDown    (to Top & Bottom)    //
   //////////////////////////////////////////////////////////////////
   void rollOut(Graphics g, SlideInfo t)
   {
      int max, i;

      switch (t.dir)
      {
                 // Until I implement a roller of some kind, rollOuts
                 // for these are identical to their rollIns
         case 0:
         case 1:
         case 2:
         case 3:
                  rollIn(g,t);
                  return;
      }

      if (state == 1)   // Prepare the image we will slide
      {
         curpt = 0;
         transStarted();
      }

      max = 0;
      switch (t.dir)
      {
         case 4:        // LeftRight
                max = picwidth/2;
                break;
         case 5:        // UpDown
                max = picheight/2;
                break;
      }

      if (curpt >= max)
      {
         doneFunc();
         return;
      }

      curpt += t.speed * fastForward;
      if (curpt >= max)
         curpt = max;

curpicgr = curpic.gif.getGraphics(); // TO WORK AROUND BUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      // Apply the new image in the correct location
      switch (t.dir)
      {
         case 4:        // LeftRight
                curpicgr.clipRect(picwidth/2-curpt,0,curpt*2+1,picheight); break;
         case 5:        // UpDown
                curpicgr.clipRect(0,picheight/2-curpt,picwidth,curpt*2+1); break;
      }
      curpicgr.drawImage(pics[t.pic].gif,0,0,this);

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // Explode will expand out an image on top of the current image //
   // from the center out in all directions                        //
   //////////////////////////////////////////////////////////////////
   void explode(Graphics g, SlideInfo t)
   {
      int wamm, hamm;

      if (state == 1)   // Prepare the image we will use
      {
         tmpimg = createImage(picwidth*2, picheight);
         tmpimggr = tmpimg.getGraphics();
         tmpimggr.drawImage(curpic.gif,0,0,this);
         tmpimggr.drawImage(pics[t.pic].gif,picwidth,0,this);
         curpt = 0;
         transStarted();
      }

      if (curpt >= 100)
      {
         doneFunc();
         return;
      }

      curpt += t.speed * fastForward;
      if (curpt >= 100)
         curpt = 100;

      wamm = (int)((picwidth/2)*(float)curpt/100.0);
      hamm = (int)((picheight/2)*(float)curpt/100.0);

      // Apply more of the new image in the correct location
      tmpimggr.copyArea(picwidth+(picwidth/2-wamm),picheight/2-hamm,wamm*2+1,hamm*2+1,-picwidth,0);

      curpicgr.drawImage(tmpimg,0,0,this);
      g.drawImage(curpic.gif,1,1,this);
      g.drawImage(tmpimg,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // Implode will implode in an image on top of the current image //
   // from outside in to the center                                //
   //////////////////////////////////////////////////////////////////
   void implode(Graphics g, SlideInfo t)
   {
      int wamm, hamm;

      if (state == 1)   // Prepare the image we will use
      {
         tmpimg = createImage(picwidth, picheight);
         tmpimggr = tmpimg.getGraphics();
         curpt = 100;
         transStarted();
      }

      if (curpt <= 0)
      {
         doneFunc();
         return;
      }

      curpt -= t.speed * fastForward;
      if (curpt <= 0)
         curpt = 0;

      wamm = (int)((picwidth/2)*(float)curpt/100.0);
      hamm = (int)((picheight/2)*(float)curpt/100.0);

      // Put on the new image
      tmpimggr.drawImage(pics[t.pic].gif,0,0,this);

      // Apply less and less of the old image in the middle
      tmpimggr.clipRect(picwidth/2-wamm,picheight/2-hamm,wamm*2+1,hamm*2+1);
      tmpimggr.drawImage(curpic.gif,0,0,this);

      curpicgr.drawImage(tmpimg,0,0,this);
      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // SlideOver will slide an image onto the display, overwriting  //
   // the unmoved old image.  The direction of slide is determined //
   // by t.dir:         0 = slideOver Left      (from Right)       //
   //                   1 = slideOver Right     (from Left)        //
   //                   2 = slideOver Up        (from Bottom)      //
   //                   3 = slideOver Down      (from Top)         //
   //////////////////////////////////////////////////////////////////
   void slideOver(Graphics g, SlideInfo t)
   {
      int max, i, x, y;

      if (state == 1)   // Prepare the image we will slide
      {
         curpt = 0;
         transStarted();
      }

      max = 0;
      switch (t.dir)
      {
         case 0:        // Left
         case 1:        // Right
                max = picwidth;
                break;
         case 2:        // Up
         case 3:        // Down
                max = picheight;
                break;
         case 6:        // TopLeft
         case 7:        // TopRight
         case 8:        // BotLeft
         case 9:        // BotRight
                max = 100;      // As in 100% done
                break;
      }

      if (curpt >= max)
      {
         doneFunc();
         return;
      }

      curpt += t.speed * fastForward;
      if (curpt >= max)
         curpt = max;

      // If this is a topleft/topright/botleft/botright direction,
      // then find the x and y offsets for the picture
      x = 0;
      y = 0;
      if (t.dir >= 6 && t.dir <= 9)
      {
         x = (int) ((float)picwidth*((float)curpt/100.0));
         y = (int) ((float)picheight*((float)curpt/100.0));
      }

      // Apply the new image in the correct location
      i = t.pic;
      switch (t.dir)
      {
         case 0:        // Left
                curpicgr.drawImage(pics[i].gif,0+picwidth-curpt,0,this); break;
         case 1:        // Right
                curpicgr.drawImage(pics[i].gif,0-picwidth+curpt,0,this); break;
         case 2:        // Up
                curpicgr.drawImage(pics[i].gif,0,0+picheight-curpt,this); break;
         case 3:        // Down
                curpicgr.drawImage(pics[i].gif,0,0-picheight+curpt,this); break;
         case 6:        // TopLeft
                curpicgr.drawImage(pics[i].gif,-picwidth+x,-picheight+y,this); break;
         case 7:        // TopRight
                curpicgr.drawImage(pics[i].gif,+picwidth-x,-picheight+y,this); break;
         case 8:        // BotLeft
                curpicgr.drawImage(pics[i].gif,-picwidth+x,+picheight-y,this); break;
         case 9:        // BotRight
                curpicgr.drawImage(pics[i].gif,+picwidth-x,+picheight-y,this); break;
      }

      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // Slide will slide an image onto the display, pushing the old  //
   // image off.  The type of slide is determined by the t.dir     //
   // parameter:        0 = slide Left      (from Right)           //
   //                   1 = slide Right     (from Left)            //
   //                   2 = slide Up        (from Bottom)          //
   //                   3 = slide Down      (from Top)             //
   //////////////////////////////////////////////////////////////////
   void slide(Graphics g, SlideInfo t)
   {
      int max;

      if (state == 1)   // Prepare the image we will slide
      {
         switch (t.dir)
         {
            case 0:     // Left
                     tmpimg = createImage(picwidth*2, picheight);
                     tmpimggr = tmpimg.getGraphics();
                     tmpimggr.drawImage(curpic.gif,0,0,this);
                     tmpimggr.drawImage(pics[t.pic].gif,picwidth,0,this);
                     break;
            case 1:     // Right
                     tmpimg = createImage(picwidth*2, picheight);
                     tmpimggr = tmpimg.getGraphics();
                     tmpimggr.drawImage(curpic.gif,picwidth,0,this);
                     tmpimggr.drawImage(pics[t.pic].gif,0,0,this);
                     break;
            case 2:     // Up
                     tmpimg = createImage(picwidth, picheight*2);
                     tmpimggr = tmpimg.getGraphics();
                     tmpimggr.drawImage(curpic.gif,0,0,this);
                     tmpimggr.drawImage(pics[t.pic].gif,0,picheight,this);
                     break;
            case 3:     // Down
                     tmpimg = createImage(picwidth, picheight*2);
                     tmpimggr = tmpimg.getGraphics();
                     tmpimggr.drawImage(curpic.gif,0,picheight,this);
                     tmpimggr.drawImage(pics[t.pic].gif,0,0,this);
                     break;
         }
         curpt = 0;
         transStarted();
      }

      max = 0;
      switch (t.dir)
      {
         case 0:        // Left
         case 1:        // Right
                max = picwidth;
                break;
         case 2:        // Up
         case 3:        // Down
                max = picheight;
                break;
      }

      if (curpt >= max)
      {
         doneFunc();
         return;
      }

      curpt += t.speed * fastForward;
      if (curpt >= max)
         curpt = max;

      switch (t.dir)
      {
         case 0:        // Left
                curpicgr.drawImage(tmpimg,0-curpt,0,this); break;
         case 1:        // Right
                curpicgr.drawImage(tmpimg,0-picwidth+curpt,0,this); break;
         case 2:        // Up
                curpicgr.drawImage(tmpimg,0,0-curpt,this); break;
         case 3:        // Down
                curpicgr.drawImage(tmpimg,0,0-picheight+curpt,this); break;
      }
      g.drawImage(curpic.gif,1,1,this);
   }

   //////////////////////////////////////////////////////////////////
   // Displays an image telling the user that the script file was  //
   // either not specified or not found.                           //
   //////////////////////////////////////////////////////////////////
   void noTransitions(Graphics g)
   {
      resize(153,27);
      tmpimg = createImage(size().width, size().height);
      tmpimggr = tmpimg.getGraphics();

      tmpimggr.setColor(Color.black);
      tmpimggr.fillRect(0,0,size().width,size().height);

      tmpimggr.setColor(Color.white);
      tmpimggr.setFont(new java.awt.Font("TimesRoman",Font.BOLD,24));
      tmpimggr.drawString("No Script File",5,size().height-8);

      g.drawImage(tmpimg,0,0,this);

      delay = 5000;
   }

   //////////////////////////////////////////////////////////////////
   // Creates the paused image                                     //
   //////////////////////////////////////////////////////////////////
   void createPaused(Graphics g)
   {
      // Bar width  height  separation
      int    W = 6, H = 15, S = 4;
      int i;

      if (pausedimg != null)
         return;

      int d[] = {1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,2,2,2,2,1,0,0,0,0,1,2,2,2,2,1,
                  1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1 };

      for (i = 0; i < (W*2+S)*H; i++)
      {
         if (d[i] == 0)
            d[i] = 0;
         else if (d[i] == 1)
            d[i] = 255 << 24;                   /* Make these black (0 color, full alpha) */
         else if (d[i] == 2)
            d[i] = 255 << 24 | 255 << 16;       /* Make these red (full alpha, full red) */
      }

      pausedimg = createImage(new MemoryImageSource(W*2+S,H,d,0,W*2+S));
   }

   //////////////////////////////////////////////////////////////////
   // Show the paused image                                        //
   //////////////////////////////////////////////////////////////////
   void showPaused(Graphics g)
   {
      if (pausedimg == null)
         createPaused(g);

      if (pausedShown)
      {
         pausedShown = false;
         g.drawImage(curpic.gif,1,1,this);
      }
      else
      {
         pausedShown = true;
         g.drawImage(pausedimg,size().width-pausedimg.getWidth(this)-5,5,this);
      }
   }

   //////////////////////////////////////////////////////////////////
   // The all important Update() procedure that calls the actual   //
   // transition functions appropriately.                          //
   //////////////////////////////////////////////////////////////////
   public void update(Graphics g)
   {
      Graphics gtmp;
      SlideInfo t;

      if (!scriptinited)
      {
         if (curpic != null && curpic.gif != null)
         {
            g.drawImage(curpic.gif,1,1,this);
         }
         return;
      }

      if (paused)
      {
         showPaused(g);
         return;
      }

      if (script.numtrans == 0)
      {
         g.drawString("no transitions",20,20);
         noTransitions(g);
         return;
      }

      // If we are just sitting, just leave
      if (state == 0)
         return;

      if (curtrans < 0 || curtrans >= script.numtrans)
         return;

      // Must be transitioning
      t = script.elementAt(curtrans);
      delay = t.delay;

      switch (t.type)
      {
         case 1: // appear
            gtmp = getGraphics();
            gtmp.clipRect(1,1,size().width-2,size().height-2);
            gtmp.drawImage(pics[t.pic].gif,1,1,this);
            gtmp.clipRect(0,0,size().width,size().height);
            gtmp.draw3DRect(0,0,size().width-1,size().height-1,true);
            doneFunc();
            break;

         case 2: // pixelin
            g.clipRect(1,1,size().width-2,size().height-2);

            pixelIn(g,t);

            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 3: // scroll
            g.clipRect(1,1,size().width-2,size().height-2);
            slide(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 4: // scrollOver
            g.clipRect(1,1,size().width-2,size().height-2);
            slideOver(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 5: // rollIn
            g.clipRect(1,1,size().width-2,size().height-2);
            rollIn(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 6: // rollOut
            g.clipRect(1,1,size().width-2,size().height-2);
            rollOut(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 7: // rotate
            g.clipRect(1,1,size().width-2,size().height-2);
            rotate(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 8: // wavyMorph
            g.clipRect(1,1,size().width-2,size().height-2);
            wavyMorph(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 9:  // fadeIn
         case 10: // fadeOut
            g.clipRect(1,1,size().width-2,size().height-2);
            fade(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 11: // explode
            g.clipRect(1,1,size().width-2,size().height-2);
            explode(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 12: // implode
            g.clipRect(1,1,size().width-2,size().height-2);
            implode(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 13: // wipe
            g.clipRect(1,1,size().width-2,size().height-2);
            wipe(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 14: // stripsOver
            g.clipRect(1,1,size().width-2,size().height-2);
            stripsOver(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 15: // stripsSlide
            g.clipRect(1,1,size().width-2,size().height-2);
            stripsSlide(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 97: // wavyDelay
            g.clipRect(1,1,size().width-2,size().height-2);
            wavyDelay(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 98: // decayDelay
            g.clipRect(1,1,size().width-2,size().height-2);
            decayDelay(g,t);
            g.clipRect(0,0,size().width,size().height);
            g.draw3DRect(0,0,size().width-1,size().height-1,true);
            break;

         case 99: // delay
            delay(g,t);
            break;

         case 100: // repeat
            // Have we already repeated the full number of times?
            if (t.size <= 0 && t.amp >= 0)
            {
               t.size = t.amp;     // Reset it for next time
               doneFunc();
            }
            else
            {
               // If this isn't an infinite repeat, decrease the counter
               if (t.amp > 0)
                  t.size--;
               curtrans = t.num;
               repaint();
            }
            break;

         // This should never be called.  If it is, it is probably
         // somehow getting called on a parse-use-only function,
         // which shouldn't be in the transitions.
         default:
            System.out.println("Error in SlideViewer.");
            state = 0;
            break;

      }

   }

   //////////////////////////////////////////////////////////////////
   // Da Paint() function                                          //
   //////////////////////////////////////////////////////////////////
   public void paint(Graphics g)
   {
      // Just draw the current screen image and leave
      if (curpic != null && curpic.gif != null)
      {
         g.drawImage(curpic.gif,1,1,this);
      }
      g.draw3DRect(0,0,size().width-1,size().height-1,true);
   }

   //////////////////////////////////////////////////////////////////
   // InitScript() gets the name of the script to use, defaulting  //
   // to "transition.script", then has the Script class read in    //
   // the script and the images used in it.  The readScript() call //
   // sets picwidth and picheight, so we can resize() to the       //
   // proper size afterwards.                                      //
   //////////////////////////////////////////////////////////////////
   void initScript()
      throws MalformedURLException
   {
      String scriptName;

      scriptName = getParameter("script");
      if (scriptName == null)
         scriptName = "transition.script";

      script = new SlideScript(this);
      System.out.println("Reading script: " + scriptName + " at docbase: " + getDocumentBase());
      script.readScript(new URL(getDocumentBase(),scriptName));
      System.out.println("Done Reading script");
/*      resize(picwidth+2,picheight+2);*/

      scriptinited = true;

      // Request a repaint, so we will start executing the new
      // script now
      //repaint();
   }

   //////////////////////////////////////////////////////////////////
   public void run()
   {
      int delaytime;

      if (script == null)
      {
         try
         {
            System.out.println("SlideViewer is trying to init script");

            initScript();
         }
         catch (Exception e)
         {
            System.out.println("SlideViewer is stopping due to failure to init script");
            setStatus("SlideViewer stopped due to errors.");
            killme = null;
         }
         state = 1;             // Start at the init for the first transition
         curtrans = 0;
         paused = false;
      }

      while (killme != null)
      {
         repaint();
         try
         {
            delaytime = delay / fastForward;
            if (delaytime < 5)
               delaytime = 5;
            Thread.sleep(delaytime);
         }
         catch (Exception e)
         {
         }
      }
   }

   //////////////////////////////////////////////////////////////////
   public void start()
   {
      picwidth = size().width;
      picheight = size().height;

      if (curpic == null)
         initCurpic(getGraphics());

      if (killme == null)
      {
         killme = new Thread(this);
         killme.start();
      }
   }

   //////////////////////////////////////////////////////////////////
   public void stop()
   {
      killme = null;
   }

   //////////////////////////////////////////////////////////////////
   // If there is a url attached to the current picture, this      //
   // mouse click should take us to it.                            //
   //////////////////////////////////////////////////////////////////
   public boolean mouseUp(java.awt.Event evt, int x, int y)
   {
      if (curpic != null && curpic.url != null)
      {
         try
         {
            getAppletContext().showDocument(new URL(getDocumentBase(),curpic.url),curpic.target);
         }
         catch (Exception e)
         {
            System.out.println("SlideViewer:  Error - could not show document");
         }
      }
      return true;
   }

   //////////////////////////////////////////////////////////////////
   // Get the focus when the mouse is in the applet, so that the   //
   // special keys defined in the keyDown() procedure will be in   //
   // effect.                                                      //
   //////////////////////////////////////////////////////////////////
   public boolean mouseEnter(java.awt.Event evt, int x, int y)
   {
      inWin = true;

      requestFocus();
      
      // Set the clickable url, if any
      getAppletContext().showStatus(statusmessage);
      return true;
   }

   //////////////////////////////////////////////////////////////////
   // Clear the statement at the bottom of the window, if any      //
   //////////////////////////////////////////////////////////////////
   public boolean mouseExit(java.awt.Event evt, int x, int y)
   {
      inWin = false;
      getAppletContext().showStatus("");
      return true;
   }

   //////////////////////////////////////////////////////////////////
   // Have some special keys for the applet when the mouse is in   //
   // in the applet space.                                         //
   //////////////////////////////////////////////////////////////////
   public boolean keyDown(java.awt.Event evt, int ch)
   {
      int val;

      switch ((char)ch)
      {
         case 'p': if (state != 0)
                      paused = !paused;         // Toggle the paused state
                   break;
         case 's': // Stop
                   if (state != 0)
                     paused = true;
                   break;
         case 'c': // continue where you left off (from a stop/pause)
                   paused = false;
                   break;
         case 'n': // Skip to the 'N'ext transition
                   doneFunc();
                   repaint();
                   break;
         case ' ': // Start over
                   paused = false;
                   state = 1;
                   curtrans = 0;
                   break;
         case 'v': // Version info
                   setStatus("SlideViewer Version 2.1");
                   break;
         case 'f': // FastForward -- speed up the transition some
                   paused = false;
                   if (fastForward == 1)
                     repaint();
                   fastForward = 2;
                   break;
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
                   val = (int)ch - (int)'0';
                   paused = false;
                   if (fastForward != val)
                     repaint();
                   fastForward = val;
                   break;
      }

      // If paused is on, it was just turned on, so show the paused image
      // Also set the delay time to a pleasant blinking speed
      if (paused)
      {
         pausedShown = false;
         delay = 500;
      }
      return true;
   }

   //////////////////////////////////////////////////////////////////
   // Need to turn off fast-forward when the user lets go of the   //
   // 'f' key.                                                     //
   //////////////////////////////////////////////////////////////////
   public boolean keyUp(java.awt.Event evt, int ch)
   {
      switch ((char)ch)
      {
         case 'f': // Turn off FastForward
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
                   fastForward = 1;
                   break;
      }

      return true;
   }
}

/////////////////////////////////////////////////////////////////////
// A filter to do the fadeIn and fadeOut transitions with.         //
/////////////////////////////////////////////////////////////////////
class FadeFilter extends RGBImageFilter
{
   float percent;
   int r, g, b;

   public FadeFilter(int pcnt)
   {
      canFilterIndexColorModel = true;
      percent = (float)pcnt/100.0f;
   }

   public int filterRGB(int x, int y, int rgb)
   {
      r = (int)(((rgb >> 16) & 0xff) * percent);
      g = (int)(((rgb >>  8) & 0xff) * percent);
      b = (int)(((rgb >>  0) & 0xff) * percent);
      return ((rgb & 0xff000000) | r << 16 | g << 8 | b);
/*      return ((rgb & 0x00ffffff) | ((int)(255 * percent) << 24));*/
   }

}
