fliplay.java

fliplay.java

fliplay.java — Java source code, 23 kB (24.082 bytes)

Dateiinhalt

// fliplay.java

// Copyright (C) 1998-2002 Klaus Ehrenfried
 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import java.awt.*;
import java.awt.image.IndexColorModel;
import java.awt.image.MemoryImageSource;
import java.net.*;
import java.applet.Applet;
import java.util.*;
import java.util.zip.*;
import java.io.*;

public class fliplay extends Applet implements Runnable {
   String file_name=null;
   Thread engine=null;
   flickframe frame[];
   Image bimage;
   Image klick_image=null;
   Graphics bgraphics;
   Color text_color;
   int image_width, image_height;
   int fli_magic;
   int fli_frames;
   int frame_number=0;
   int frame_delay;
   int max_delay;
   int min_delay;
   int frame_delay_mem;
   int row_start[], row_end[];
   int block_height, patch_blocks;
   int pixel_sum;
   int anim_loops, work_loops;
   int end_frame;
   int frame_data_len=-1;
   int klick_panel_xpos=0, klick_panel_ypos=0;
   int klick_dim=24;
   int mem_down_xpos=0, mem_down_ypos=0;
   byte pixels[];
   byte frame_data[];
   byte red[], green[], blue[];
   boolean loaded=false;
   boolean extracted=false;
   boolean load_failed=false;
   boolean display_frame=false;
   boolean userpause=false;
   boolean reverse_flag=false;
   boolean debug=false;
   boolean loop_flag=true;
   boolean box_flag=false;
   boolean auto_reverse=false;
   boolean advance_flag=false;
   boolean drag_flag=false;
   boolean klick_panel_flag=false;
   boolean klick_panel_mode=true;
   boolean keyboard_control=true;
   boolean busy_flag=false;
   boolean zip_flag=false;
   //Runtime rt;

   public void init () {
      loaded=false;
      extracted=false;
      display_frame=false;
      load_failed=false;
      text_color=Color.white;
      String param;

      System.out.println("fliplay v1.2b by Klaus Ehrenfried (27-Feb-2002)");

      file_name = getParameter("File");
      if ("yes".equals(getParameter("Reverse"))) reverse_flag = true;
      if ("yes".equals(getParameter("Auto"))) auto_reverse = true;
      if ("yes".equals(getParameter("Box"))) box_flag = true;
      if ("yes".equals(getParameter("Debug"))) debug = true;
      if ("yes".equals(getParameter("ZipFile"))) zip_flag = true;
      if ("no".equals(getParameter("ClickPanel"))) klick_panel_mode = false;
      if ("no".equals(getParameter("Keyboard"))) keyboard_control = false;

      String pval;
      pval = getParameter("TextColor");
      if (pval != null) {
	 if ("black".equals(pval)) text_color = Color.black;
	 if ("red".equals(pval)) text_color = Color.red;
      }

      Color border_color;
      Color bg_color;
      Color fg_color;
      border_color = Color.black;
      bg_color = Color.lightGray;
      fg_color = Color.black;

      pval = getParameter("PanelColor");
      if (pval != null) {
	 if ("1".equals(pval)) {
	    border_color = Color.lightGray;
	    bg_color = Color.black;
	    fg_color = Color.lightGray;
	 } else if ("2".equals(pval)) {
	    border_color = Color.red;
	    bg_color = Color.white;
	    fg_color = Color.blue;
	 } else if ("3".equals(pval)) {
	    border_color = Color.yellow;
	    bg_color = Color.blue;
	    fg_color = Color.yellow;
	 }
      }

      block_height = parse_param("Block",  64);
      frame_delay  = parse_param("Speed", 100);
      min_delay    = parse_param("MinDelay", 30);
      max_delay    = parse_param("MaxDelay", 10000);
      anim_loops   = parse_param("Loops",  -1);
      end_frame    = parse_param("End",     0);

      if (frame_delay < 0) frame_delay=100;
      if (block_height < 4) block_height=4;

      System.out.println("Block: " + block_height);
      System.out.println("Speed: " + frame_delay);
      System.out.println("Loops: " + anim_loops);
      System.out.println("End:   " + end_frame);

      frame_delay_mem = frame_delay;

      klick_image = createImage(3 * klick_dim, klick_dim);
      Graphics kgra = klick_image.getGraphics();

      if (kgra != null) {
	 //System.out.println("kgra!");
	 kgra.setColor(border_color);
	 kgra.fillRect(0,0,3 * klick_dim,klick_dim);
	 kgra.setColor(bg_color);
	 kgra.fillRect(2,2,(3*klick_dim)-4,klick_dim-4);
	 kgra.setColor(fg_color);
	 int hdim;
	 hdim = klick_dim/2;
	 int px[],py[];
	 px = new int[3];
	 py = new int[3];
	 px[0] = klick_dim-3;
	 px[1] = 3;
	 px[2] = klick_dim-3;
	 py[0] = 3;
	 py[1] = hdim;
	 py[2] = klick_dim-3;
	 kgra.fillPolygon(px,py,3);
	 int dd=3*klick_dim;
	 px[0] = dd - px[0];
	 px[1] = dd - px[1];
	 px[2] = dd - px[2];
	 kgra.fillPolygon(px,py,3);
	 kgra.fillRect(klick_dim,4,6,klick_dim-7);
	 kgra.fillRect(2*klick_dim-6,4,6,klick_dim-7);
      }
   }

   int parse_param(String name, int def_value) {
      int new_value;
      String param = getParameter(name);
      if (param == null) return def_value;
      try {
	 new_value = Integer.parseInt(param);
      }
      catch (NumberFormatException e) {
	 System.out.println(e);
	 return def_value;
      }
      return new_value;
   }

   int get_ubyte(byte bb[], int i) {
      int w;
      w = bb[i] &  0xff;
      return(w);
   }

   int get_ushort(byte bb[], int i) {
      int n;
      int w = 0;
      for (n=1; n >=0; n--) {
	 w <<= 8;
	 w |= (bb[i+n] & 0xff);
      }
      return(w);
   }

   int get_ulong(byte bb[], int i) {
      int n;
      int w = 0;
      for (n=3; n >=0; n--) {
	 w <<= 8;
	 w |= (bb[i+n] & 0xff);
      }
      return(w);
   }

   void read_loop(DataInputStream input, byte buff[], int len) throws IOException {
      int remaining=len;
      int sum=0;
      while (remaining > 0) {
	 int trans = input.read(buff, sum, remaining);
	 if (trans <= 0) break;
	 sum += trans;
	 remaining -= trans;
      }
      if (remaining > 0) {
	 System.out.println("Error reading data: got only " +
			    sum + " of " + len + " bytes");
	 throw (new IOException());
      }
   }

   void load_header(DataInputStream input) throws IOException {
      byte file_header[] = new byte[128];
      read_loop(input, file_header, 128);

      int file_size = get_ulong(file_header, 0);
      fli_magic = get_ushort(file_header, 4);
      fli_frames = get_ushort(file_header, 6);
      image_width = get_ushort(file_header, 8);
      image_height = get_ushort(file_header, 10);

      System.out.println("File Size:  " + file_size);
      System.out.println("Magic:      " + fli_magic);
      System.out.println("Frames:     " + fli_frames);
      System.out.println(image_width + "x" + image_height);

      if ((fli_magic != 0xAF12) && (fli_magic != 0xAF11)) {
	 System.out.println("Invalid Fli Magic!");
	 throw (new IOException());
      }
   }

   void process_color_256_chunk(byte data[], int pos, int shift) {
      int packets;
      if (debug) System.out.println("process_color_256_chunk");

      packets = get_ushort(data, pos);
      pos += 2;
      if (debug) System.out.println("Packets:  " + packets);

      int ic=0;
      int i,n;

      for (i=0; i < packets; i++) {
	 int skip = get_ubyte(data, pos);
	 pos++;
	 ic += skip;
	 ic = ic % 0x100;
	 int change = get_ubyte(data, pos);
	 pos++;
	 if (change == 0) change = 0x100;
	 for (n=0; n < change; n++) {
	    red[ic] = (byte) (data[pos++] << shift);
	    green[ic] = (byte) (data[pos++] << shift);
	    blue[ic] = (byte) (data[pos++] << shift);
	    ic++;
	 }
      }
      if (debug) System.out.println("O.K.");
   }

   void process_brun_chunk(byte data[], int pos) {
      int i,j,k,n;
      if (debug) System.out.println("process_brun_chunk");
      k=0;
      for (j=0; j < image_height; j++) {
	 int packets = get_ubyte(data, pos++);
	 for (i=0; i < packets; i++) {
	    int size_count = data[pos++];
	    if (size_count == 0) {
	       System.out.println("Warning: Error in Brun Chunk!");
	    } else if (size_count > 0) {
	       for (n=0; n < size_count; n++) pixels[k++] = data[pos];
	       pos++;
	    } else {
	       for (n=0; n < -size_count; n++) pixels[k++] = data[pos++];
	    }
	 }
      }
      if (debug) System.out.println("O.K.");
   }

   void process_delta_chunk(byte data[], int pos) {
      if (debug) System.out.println("process_delta_chunk");
      int lines = get_ushort(data, pos);
      pos += 2;
      int row_count = 0;
      int jnext = 0;
      int i,j,k,l,n,w;
      k=0;
      for (j=0; j < image_height; j++) {
	 //row_start[j] = image_width;
	 if (row_count >= lines) continue;
	 if (j >= jnext) {
	    int packets = get_ushort(data, pos);
	    pos += 2;
	    if (packets > 32768) {
	       packets -= 65536;
	       jnext = j - packets;
	    } else {
	       row_count++;
	       w=k;
	       row_start[j] = get_ubyte(data, pos);
	       for (i=0; i < packets; i++) {
		  w += get_ubyte(data, pos++);
		  int size = data[pos++];
		  if (size >= 0) {
		     for (n=0; n < size; n++) {
			pixels[w++] = data[pos++];
			pixels[w++] = data[pos++];
		     }
		  } else if (size < 0) {
		     for (n=0; n < -size; n++) {
			pixels[w++] = data[pos];
			pixels[w++] = data[pos+1];
		     }
		     pos += 2;
		  }
	       } /* packets */
	       row_end[j]=(w-k);
	    }
	 } /* j >= jnext */
	 k += image_width;
      }
      if (debug) System.out.println("O.K.");
   }

   void process_lc_chunk(byte data[], int pos) {
      if (debug) System.out.println("process_lc_chunk");
      int jnext = get_ushort(data, pos);
      pos += 2;
      int lines = get_ushort(data, pos);
      pos += 2;

      int line_count = 0;
      int i,j,k,l,n,w;
      k=0;

      for (j=0; j < image_height; j++) {
	 //row_start[j] = image_width;
	 if (line_count >= lines) continue;
	 if (j >= jnext) {
	    int packets = get_ubyte(data, pos++);
	    line_count++;
	    w=k;
	    row_start[j] = get_ubyte(data, pos);
	    for (i=0; i < packets; i++) {
	       w += get_ubyte(data, pos++);
	       int size = data[pos++];
	       if (size >= 0) {
		  for (n=0; n < size; n++) {
		     pixels[w++] = data[pos++];
		  }
	       } else if (size < 0) {
		  for (n=0; n < -size; n++) {
		     pixels[w++] = data[pos];
		  }
		  pos++;
	       }
	    } /* packets */
	    row_end[j]=(w-k);
	 } /* j >= jnext */
	 k += image_width;
      }
      if (debug) System.out.println("O.K.");
   }

   void load_frame(DataInputStream input, int nframe) throws IOException {
      int frame_size;
      int frame_magic;
      byte header[] = new byte[16];
      read_loop(input, header, 16);

      frame_size = get_ulong(header, 0);
      frame_magic = get_ushort(header, 4);
      frame[nframe].chunks = get_ushort(header, 6);

      if (debug) {
	 System.out.println("Frame Size: " + frame_size);
	 System.out.println("Magic:      " + frame_magic);
	 System.out.println("Chunks:     " + frame[nframe].chunks);
      }

      if (frame_magic != 0xF1FA) {
	 System.out.println("Invalid Frame Magic: " + frame_magic);
	 throw (new IOException());
      }

      int remaining = frame_size - 16;
      if (frame_data_len < remaining) {
	 frame_data_len = remaining + 10000;
	 frame_data = new byte[frame_data_len];
      }
      read_loop(input, frame_data, remaining);
   }

   void reset_row_start_end() {
      for (int j=0; j < image_height; j++) {
	 row_start[j] = image_width;
	 row_end[j] = -1;
      }
   }

   void extract_frame(int nframe) {
      byte data[];
      int chunk_size, chunk_type;
      int i,m;

      reset_row_start_end();
      data=frame_data;

      m=0;
      for (i=0; i < frame[nframe].chunks; i++) {
	 chunk_size = get_ulong(data, m);
	 chunk_type = get_ushort(data, m+4);

	 if (debug) {
	    System.out.println("Chunk Size: " + chunk_size);
	    System.out.println("Chunk Type: " + chunk_type);
	 }

	 switch (chunk_type) {
	  case 4:   process_color_256_chunk(data, m+6, 0); break;
	  case 7:   process_delta_chunk(data, m+6); break;
	  case 11:  process_color_256_chunk(data, m+6, 2); break;
	  case 12:  process_lc_chunk(data, m+6); break;
	  case 15:  process_brun_chunk(data, m+6); break;
	  default:
	    System.out.println("Unkown Chunk Type: " + chunk_type);
	    break;
	 }

	 m += chunk_size;
      }
   }

   void process_first_frame() {
      IndexColorModel cm;
      MemoryImageSource mis;

      cm = new IndexColorModel(8, 0x100, red, green, blue);
      mis = new MemoryImageSource(image_width,
				  image_height,
				  cm,
				  pixels,
				  0,
				  image_width);
      bgraphics.drawImage(createImage(mis), 0, 0, this);
   }

   void process_loop_frame(int nframe) {
      int line=0;
      int i,j;
      IndexColorModel cm;
      MemoryImageSource mis;

      cm = new IndexColorModel(8, 0x100, red, green, blue);

      frame[nframe].delete_patches();
      for (i=0; i < patch_blocks; i++) {
	 int left=image_width;
	 int right=-1;
	 int line_min=image_height;
	 int line_max=-1;

	 for (j=0; j < block_height; j++) {
	    if (line >= image_height) break;
	    if (row_start[line] < image_width) {
	       if (row_start[line] < left) left=row_start[line];
	       if (row_end[line] > right) right=row_end[line];
	       if (line < line_min) line_min=line;
	       line_max=line;
	    }
	    line++;
	 }
	 line_max++;
	 //if (debug) {
	 // System.out.println("Block " + i);
	 // System.out.println("  x: " + left + " to " + right);
	 // System.out.println("  y: " + line_min + " to " + line_max);
	 //}

	 if (line_min < line_max) {
	    int patch_width = right - left;
	    int patch_height = line_max - line_min;
	    int offset = (line_min * image_width) + left;
	    //int test1=image_width * image_height;
	    //int test2=offset + patch_width + (patch_height-1) * image_width;
	    //if (debug) System.out.println(test1 + " " + test2);
	    mis = new MemoryImageSource(patch_width,
					patch_height,
					cm,
					pixels,
					offset,
					image_width);
	    Image himage = createImage(patch_width, patch_height);
	    Graphics g = himage.getGraphics();
	    g.drawImage(createImage(mis), 0, 0, null);
	    if (box_flag) {
	       g.setColor(Color.red);
	       g.drawRect(0,0,patch_width-1,patch_height-1);
	    }
	    frame[nframe].add_patch(himage, left, line_min);
	    pixel_sum += (patch_width * patch_height);
	 }
      }
   }

   void load_fli() {
      red   = new byte[0x100];
      green = new byte[0x100];
      blue  = new byte[0x100];
      pixel_sum = 0;
      loaded=false;
      extracted=false;

      if (load_failed) return;

      if ((file_name == null) || ("".equals(file_name))) {
	 load_failed=true;
	 System.out.println("No file given!");
	 showStatus("Loading failed!");
	 return;
      }
      System.out.println("Loading: " + file_name);

      try {
	 URL fli_url = new URL(getCodeBase(), file_name);
	 DataInputStream input;
	 InputStream is = fli_url.openStream();
	 if (zip_flag) {
	    //GZIPInputStream zis = new GZIPInputStream(is);
	    ZipInputStream zis = new ZipInputStream(is);
	    zis.getNextEntry();
	    input = new DataInputStream(zis);
	 } else {
	    input = new DataInputStream(is);
	 }

	 load_header(input);
	 pixels = new byte[image_width * (image_height+1)];
	 frame = new flickframe[fli_frames+1];

	 row_start = new int[image_height];
	 row_end = new int[image_height];

	 bimage = createImage(image_width, image_height);
	 bgraphics = bimage.getGraphics();

	 patch_blocks = image_height/block_height;
	 if ((image_height % block_height) > 0) patch_blocks++;
	 System.out.println("patch_blocks: " + patch_blocks);

	 int i;
	 for (i=0; i <= fli_frames; i++) {
	    frame[i] = new flickframe(patch_blocks);
	    showStatus("Loading frame "+ i +" of "+ fli_frames);
	    load_frame(input, i);
	    extract_frame(i);
	    if (i == 0) {
	       process_first_frame();
               display_frame=true;
	    } else {
	       process_loop_frame(i);
	    }
	    frame_number = i;
	    System.gc();
	    repaint();
	 }

	 pixels = null;
	 frame_data_len = -1;
	 frame_data = null;

	 if (end_frame <= 0) {
	    end_frame += fli_frames;
	 }
	 if (end_frame <= 0) {
	    end_frame = fli_frames;
	 }
	 if (anim_loops < 0) end_frame = -1;
	 if (debug) System.out.println("end_frame: " + end_frame);

	 frame_number = fli_frames;
	 String s = new String((100.0 * pixel_sum/
				(fli_frames * image_width * image_height)) +
			       "000000000");
	 System.out.println("Update Pixels: " + pixel_sum + " (" +
			    s.substring(0,5) +" %)");

	 work_loops = anim_loops;
	 loaded=true;
	 extracted=true;
	 is.close();
	 System.gc();
      }
      catch (Exception e) {
	 load_failed=true;
	 display_frame=false;
	 showStatus("Loading failed!");
         System.out.println(e);
      }
   }

   void user_stop() {
      if (debug) System.out.println("Stop");
      loop_flag=false;
      userpause=true;
      work_loops=-1;
   }

   void user_start() {
      if (debug) System.out.println("Start");
      work_loops=anim_loops;
      start();
   }

   public boolean mouseDrag(Event evt, int x, int y) {
      //System.out.println(x + " " + y);
      if ((userpause) && (drag_flag)) {
	 klick_panel_xpos = x - mem_down_xpos;
	 klick_panel_ypos = y - mem_down_ypos;
	 if (klick_panel_xpos < 0) klick_panel_xpos=0;
	 if (klick_panel_ypos < 0) klick_panel_ypos=0;
	 int maxx=image_width - 3 * klick_dim;
	 int maxy=image_height - klick_dim;
	 if (klick_panel_xpos > maxx) klick_panel_xpos=maxx;
	 if (klick_panel_ypos > maxy) klick_panel_ypos=maxy;
	 repaint();
      }
      return true;
   }

   public boolean mouseDown(Event evt, int x, int y) {
      if (loaded) {
	 drag_flag = false;
	 x -= klick_panel_xpos;
	 y -= klick_panel_ypos;
	 mem_down_xpos=x;
	 mem_down_ypos=y;

	 if (userpause) {
	    if ((klick_panel_mode) && (x > 0) && (y > 0) && 
		(y < klick_dim) && (x < (3 * klick_dim))) {
	       if (x < klick_dim) {
		  reverse_flag=true;
		  advance_flag=true;
		  repaint();
	       } else if  (x > (2 * klick_dim)) {
		  reverse_flag=false;
		  advance_flag=true;
		  repaint();
	       } else {
		  drag_flag = true;
	       }
	    } else {
	       user_start();
	    }
	 } else {
	    user_stop();
	    if (klick_panel_mode) klick_panel_flag=true;
	    repaint();
	 }
      }
      return true;
   }


   public boolean handleEvent(Event evt) {
      if ((evt.id == Event.KEY_PRESS) && (keyboard_control) && loaded) {
	 if (debug) System.out.println("Key: " + evt.key);
	 if (evt.key == 32) {
	    //user_middle_button();
	    if (userpause) {
	       user_start();
	    } else {
	       user_stop();
	       repaint();
	    }
	 } else if (evt.key == 43) {
	    frame_delay = (int)(frame_delay*0.8);
	    if (frame_delay < min_delay) frame_delay=min_delay;
	    if (debug) System.out.println("Delay: " + frame_delay);
	    showStatus("Delay: " + frame_delay);
	 } else if (evt.key == 44) {
	    //user_left_button();
	    reverse_flag=true;
	    if (userpause) {
	       if (debug) System.out.println("Step backward");
	       advance_flag=true;
	       repaint();
	    } else {
	       user_stop();
	    }
	 } else if (evt.key == 45) {
	    frame_delay = (int)(frame_delay*1.25);
	    if (frame_delay > max_delay) frame_delay=max_delay;
	    if (debug) System.out.println("Delay: " + frame_delay);
	    showStatus("Delay: " + frame_delay);
	 } else if (evt.key == 46) {
	    //user_right_button();
	    reverse_flag=false;
	    if (userpause) {
	       if (debug) System.out.println("Step forward");
	       advance_flag=true;
	       repaint();
	    } else {
	       user_stop();
	    }
	 } else if (evt.key == 48) {
	    frame_delay = frame_delay_mem;
	    showStatus("Delay: " + frame_delay);
	 } else if ((evt.key == 82) || (evt.key == 114)) {
	    reverse_flag = !reverse_flag;
	 }
	 return true;
      }
      return super.handleEvent(evt);
   }
   
   synchronized void free_memory() {
      if (!loaded) return;
      extracted = false;
      loaded = false;
      display_frame = false;
      bimage.flush();
      bimage = null;
      for (int i=1; i < fli_frames; i++) {
	 frame[i].delete_patches();
	 frame[i] = null;
      }
      System.gc();
   }

   synchronized void next_frame() {
      if (extracted) {
	 //System.out.println("work_loops: " + work_loops +
	 //		    " frame_number: " + frame_number +
	 //		    " " + end_frame);
	 if ((work_loops == 0) && (frame_number == end_frame)) {
	    if (debug) showStatus("Ready!");
	    loop_flag=false;
	 } else {
	    if (reverse_flag) {
	       frame_number--;
	       if (frame_number < 1) {
		  if (auto_reverse) {
		     frame_number = 1;
		     reverse_flag = false;
		  } else {
		     frame_number = fli_frames;
		  }
		  work_loops--;
	       }
	    } else {
	       frame_number++;
	       if ((auto_reverse) && (frame_number == fli_frames)) {
		     frame_number = fli_frames - 1;
		     reverse_flag = true;
	       }
	       else if (frame_number > fli_frames) {
		  frame_number = 1;
		  work_loops--;
	       }
	    }
	    frame[frame_number].draw_patches(bgraphics);
	 }
	 if (userpause) {
	    showStatus("Frame "+ frame_number +" of "+ fli_frames);
	 }
      }
      advance_flag=false;
   }

   public void update (Graphics g) {
      if (advance_flag) next_frame();
      paint(g);
   }

   public void paint(Graphics g) {
      if (display_frame) {
	 g.drawImage(bimage, 0, 0, this);
	 if (!loaded) {
	    int h = size().height;
	    g.setColor(text_color);
	    g.drawString(frame_number + "/" + fli_frames, 10, h-10);
	 } else if (klick_panel_flag) {
	    g.drawImage(klick_image,
			klick_panel_xpos,
			klick_panel_ypos,
			this);
	 }
      } else {
	 int w = size().width;
	 int h = size().height;
	 g.setColor(Color.black);
	 g.fillRect(0,0,w,h);
	 g.setColor(Color.yellow);
	 if (load_failed) {
	    g.drawString("Loading failed!", 10, h-10);
	 } else {
	    g.drawString("Please wait....", 10, h-10);
	 }
      }
   }

   public void start() {
      if (engine != null) {
	 if (engine.isAlive()) {
	    engine.stop();
	 }
	 engine = null;
      }

      loop_flag = true;
      userpause = false;
      drag_flag=false;
      klick_panel_flag=false;

      engine = new Thread(this);
      engine.start();
   }

   public void stop() {
      if ((engine != null) && (engine.isAlive())) {
         engine.stop();
      }
      engine = null;
      loop_flag = false;
      free_memory();
   }

   synchronized void gen_repaint() {
      repaint();
   }

   public void run() {
      Thread me = Thread.currentThread();
      me.setPriority(Thread.MIN_PRIORITY);

      try {
	 me.sleep(250);
      }
      catch (InterruptedException e) {}

      if (!loaded) load_fli();
      if (load_failed) {
	repaint();
	return;
      }

      requestFocus();
      showStatus("Running");

      while ((engine == me) && (loop_flag) && (loaded)) {
	 advance_flag=true;
	 gen_repaint();
         try {
            engine.sleep(frame_delay);
         }
         catch (InterruptedException e) {}
      }
      if (userpause) {
	 showStatus("Frame "+ frame_number +" of "+ fli_frames);
      } else {
	 showStatus("Stop: " + frame_number);
	 userpause=true;
      }
   }
}

class flickframe {
   Image patch[];
   int xpos[], ypos[];
   int count;
   int chunks;

   flickframe(int max_len) {
      patch = new Image[max_len];
      xpos = new int[max_len];
      ypos = new int[max_len];
      count = 0;
   }

   public void add_patch(Image img, int x, int y) {
      patch[count]=img;
      xpos[count]=x;
      ypos[count]=y;
      count++;
   }

   public void draw_patches(Graphics g) {
      int i;
      for (i=0; i < count; i++) {
	 g.drawImage(patch[i], xpos[i], ypos[i], null);
      }
   }

   public void delete_patches() {
      int i;
      for (i=0; i < count; i++) {
	 patch[i].flush();
	 patch[i] = null;
      }
      count = 0;
   }
}