// 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; } }