// osama_says.java // 12/31/01, 'Osama bin Laden Videotape' Electromic import java.applet.Applet; import java.awt.*; // import java.net.URL; // file read import java.io.*; import java.net.*; // classes // applet // main program class, 'osama_says' public class osama_says extends Applet implements Runnable { // constants // x/y, drawings canvas static final int CANVAS_W = 348; static final int CANVAS_H = 430; static final int TOTAL_IMGS = 3; // total # of images // base buff idx references for image parts static final int IMG_IDX_HEAD_BODY = 0; //head and body static final int IMG_IDX_EYES = 1; // eyes static final int IMG_IDX_MOUTH = 2; // mouth // image dimensions // osama's head and body static final int HEAD_BODY_W = 348; static final int HEAD_BODY_H = 348; // osama's head and body, offset from canvas top/left static final int HEAD_BODY_OFFSET_X = 0; static final int HEAD_BODY_OFFSET_Y = 5; // osama's eyes static final int EYES_W = 90; static final int EYES_H = 28; // osama's eyes, offset from head and body static final int EYES_OFFSET_X = 132; static final int EYES_OFFSET_Y = 85; // osama's mouth static final int MOUTH_W = 90; static final int MOUTH_H = 59; // osama's mouth, offset from head and body static final int MOUTH_OFFSET_X = 132; static final int MOUTH_OFFSET_Y = 125; // anim frames-per-second static final long OSAMA_FPS = 100; // osama's head and body, 10 fps static final long MOUTH_OPEN_CLOSED_FPS = 150; // osama's mouth open/closed, ~6.5 fps // text scroll frames-per-second static final long TXTSCROLL_FPS = 100; // advance scrolling text, 10 fps // text strings static final int TXTSTR_CNT = 10; // we're loading 10 strings // image load leader dots fps update time static final long DOTS_FPS = 125; // update 8x per second // vars // composite buffer and media tracker static Image composite_image; static Graphics composite_graphics; static MediaTracker img_tracker; // images static Image[] imgs = new Image[TOTAL_IMGS]; // graphics component of this applet, used to force immediate repaints static Graphics applet_graphics; // animation thread static Thread anim_thread; // squiggle and mouth open/closed anim timer(s) static long osama_squiggle_delay, mouth_open_closed_delay; // squiggle flag(s) static int osama_squiggle_flg, mouth_open_closed_flg; // e.g. 0-1, 2 frame anim // image locations // osama's head and body static int head_body_left, head_body_top; // osama's eyes static int eyes_left, eyes_top; // osama's mouth static int mouth_left, mouth_top; // eye flags // eyes status flg (0= closed/1= open) static int eyesstatus_flg; // eyes blink delay static long eyesblink_delay; // offscreen text scroll buffer static int txtscroll_width, txtscroll_height; static int txtscroll_text_height; static Image txtscroll_image; static Graphics txtscroll_graphics; static int txtscroll_start_y; // starting position for vertical y scroll static int txtscroll_y; // traveling vertical y scroll static long txtscroll_delay; // fps timer // text strings static String txtscroll_string[]; // we're loading 10 strings static int txtstr_ttl; // total text strings loaded (we may have less than 10) static int txtstr_idx; // text string index, the currently displayed text string // image load leader dots (animate 1 to 5 dots) static int img_load_anm_dots; static long dots_update_time; // leader dots fps update time // init flag static boolean init_flg = false; // false until initialization in 'main()' is complete // methods: // init // start // stop // destroy // update // run // mouseDown // mouseUp // keyDown // keyUp // paint // private methods: // my_paint // draw_osama_head_body // draw_text_scroll_area // load_text_scroll_area // load_txtscroll_strs() // 'init()' public void init() { int idx; // composite buffer composite_image = createImage(CANVAS_W, CANVAS_H); composite_graphics = composite_image.getGraphics(); // graphics component of this applet, used to force immediate repaints applet_graphics = this.getGraphics(); // misc init // anim flags osama_squiggle_flg = 0; mouth_open_closed_flg = 0; // e.g. 0-1, 2 frame anim // anim timers osama_squiggle_delay = System.currentTimeMillis() + OSAMA_FPS; // osama squiggle anm, 10 fps mouth_open_closed_delay = System.currentTimeMillis() + MOUTH_OPEN_CLOSED_FPS; // mouth open/closed anm, 5 fps // load images and media tracker img_tracker = new MediaTracker(this); // osama's head imgs[IMG_IDX_HEAD_BODY] = getImage(getCodeBase(), "images/osama_head_body.gif"); img_tracker.addImage(imgs[IMG_IDX_HEAD_BODY], 1); // osama's eyes imgs[IMG_IDX_EYES] = getImage(getCodeBase(), "images/osama_eyes.gif"); img_tracker.addImage(imgs[IMG_IDX_EYES], 2); // osama's mouth imgs[IMG_IDX_MOUTH] = getImage(getCodeBase(), "images/osama_mouth.gif"); img_tracker.addImage(imgs[IMG_IDX_MOUTH], 3); // image locations // osama's head and body head_body_left = HEAD_BODY_OFFSET_X; head_body_top = HEAD_BODY_OFFSET_Y; // osama's eyes eyes_left = head_body_left + EYES_OFFSET_X; eyes_top = head_body_top + EYES_OFFSET_Y; // osama's mouth mouth_left = head_body_left + MOUTH_OFFSET_X; mouth_top = head_body_top + MOUTH_OFFSET_Y; // init eyes status flg (0= closed/1= open) eyesstatus_flg = 1; // initially open // init eyes blink delay (blink every .50 to 1.5 seconds) eyesblink_delay = System.currentTimeMillis() + ( (long) (Math.round(Math.random() * 1000) + 500) ); // begin: init offscreen text scroll buffer // width and height of buffer txtscroll_width = CANVAS_W - 8; txtscroll_height = (CANVAS_H - (HEAD_BODY_OFFSET_Y + HEAD_BODY_H)) - 8; // create text scroll buffer // text height (based on # of lines) in buffer txtscroll_text_height = 0; // scroll height assigned as buffers are drawn into txtscroll_image = createImage(txtscroll_width, txtscroll_height); txtscroll_graphics = txtscroll_image.getGraphics(); // load text strings txtscroll_string = new String[TXTSTR_CNT]; // we're loading TXTSTR_CNT strings txtstr_ttl = 0; // total text strings loaded (we may have less than 10) // load text scroll strings txtstr_ttl = load_txtscroll_strs(); // System.out.println("lines loaded: " + txtstr_ttl); // debug //String tmp_str = "I am the Boogey Man. I am the Boogey Man. I am the Boogey Man. I am the Boogey Man. I am the Boogey Man. I am the Boogey Man. I am the Boogey Man. I am the Boogey Man. I am the Boogey Man."; //for (idx = 0; idx < TXTSTR_CNT; idx++) { // txtscroll_string[idx] = "OSM" + (idx + 1) + ": " + tmp_str; //} txtstr_idx = 0; // text strong index, the currently displayed text string // load wrapped text in specified buffer at specified width txtscroll_text_height = load_text_scroll_area(txtscroll_string[txtstr_idx], txtscroll_graphics); // init starting position for vertical y scroll txtscroll_start_y = (HEAD_BODY_OFFSET_Y + HEAD_BODY_H) + 4; txtscroll_y = txtscroll_start_y + txtscroll_height; // traveling vertical y scroll // fps timer txtscroll_delay = System.currentTimeMillis() + TXTSCROLL_FPS; // end: init offscreen text scroll buffer(s) // image load leader dots (animate 1 to 5 dots) img_load_anm_dots = 1; dots_update_time = System.currentTimeMillis() + DOTS_FPS; // leader dots fps update time // init flag init_flg = true; // false until initialization is complete } // end method 'init()' // 'start()' // called when the applet becomes visible on screen public void start() { // create animation thread anim_thread = new Thread(this); // start animation thread anim_thread.start(); } // end method 'start()' // 'stop()' // called when the applet is no longer visible on screen public void stop() { // stop and destroy animation thread if (anim_thread != null) anim_thread = null; } // end method 'stop()' // 'destroy()' // called just before the browser exits public void destroy() { int idx; // stop and destroy animation thread if (anim_thread != null) anim_thread = null; // dispose applet graphics and offscreen graphics context/ composite buffer applet_graphics.dispose(); applet_graphics = null; composite_graphics.dispose(); composite_graphics = null; // dispose offscreen text scroll buffer txtscroll_graphics.dispose(); txtscroll_graphics = null; } // end method 'destroy()' // 'update()' // update the screen public void update(Graphics g) { // intercepting 'update()' avoids background erase if (composite_graphics != null) paint(g); // call 'paint()' } // end method 'update()' // 'run()', thread runner public void run() { int paint_flg = 0; while ( anim_thread == Thread.currentThread() ) { // anim timers // osama squiggle if (System.currentTimeMillis() >= osama_squiggle_delay) { // note: moved squiggle update code to synchronized 'my_paint()' // flag paint paint_flg = 1; } // mouth open/closed if (System.currentTimeMillis() >= mouth_open_closed_delay) { if (mouth_open_closed_flg == 1) mouth_open_closed_flg = 0; // 0/1, 2 frame anim else mouth_open_closed_flg = 1; mouth_open_closed_delay = System.currentTimeMillis() + MOUTH_OPEN_CLOSED_FPS; // flag paint paint_flg = 1; } // eyes blink update // blink if ( (eyesstatus_flg == 1) && (System.currentTimeMillis() >= eyesblink_delay) ) { // mark eyes status flag eyesstatus_flg = 0; // flag eyes closed // keep blinking eyes closed 125 milisecs eyesblink_delay = System.currentTimeMillis() + 125; // flag paint paint_flg = 1; // debug: report blink // System.out.println("BLINK"); } // open else if ( (eyesstatus_flg == 0) && (System.currentTimeMillis() >= eyesblink_delay) ) { // mark eyes status flag eyesstatus_flg = 1; // flag eyes open // re-init eyes blink delay (blink every .50 to 1.5 seconds) eyesblink_delay = System.currentTimeMillis() + ( (long) (Math.round(Math.random() * 1000) + 500) ); // flag paint paint_flg = 1; } // text scroll if ( img_tracker.checkAll(true) && (System.currentTimeMillis() >= txtscroll_delay) ) { // ^ don't begin scroll unless we're painting (all images loaded) // decrement/reset traveling vertical y scroll if (txtscroll_y > (txtscroll_start_y - txtscroll_text_height)) txtscroll_y = txtscroll_y - 2; else { txtscroll_y = txtscroll_start_y + txtscroll_height; // load another text string if (txtstr_idx < (txtstr_ttl - 1)) txtstr_idx++; else txtstr_idx = 0; txtscroll_text_height = load_text_scroll_area(txtscroll_string[txtstr_idx], txtscroll_graphics); } // reset timer txtscroll_delay = System.currentTimeMillis() + TXTSCROLL_FPS; // flag paint paint_flg = 1; } // paint (??) if (paint_flg == 1) repaint(); // force repaint // sleep try { Thread.sleep(50); } catch (InterruptedException e) {} } // end "while (Thread.currentThread() == anim_thread)" } // end method 'run()' // 'mouseDown()' // called when the mouse button is pressed down public boolean mouseDown(Event evt, int x, int y) { if ( !init_flg ) return true; // return immediately if we're not done initializing return true; } // end method 'mouseDown()' // 'mouseUp()' // called when the mouse button comes back up public boolean mouseUp(Event evt, int x, int y) { if ( !init_flg ) return true; // return immediately if we're not done initializing return true; } // end method 'mouseUp()' // 'keyDown()' public boolean keyDown(Event evt, int key) { // debug: report key code // System.out.println("key: " + key); return true; } // end method 'keyDown()' // 'keyUp()' public boolean keyUp(Event evt, int key) { // debug: report key code // System.out.println("keyUp: " + key); return true; } // end method 'keyUp()' // 'paint()' public void paint(Graphics g) { // if init's complete, draw images in composite buffer and blast to screen if (init_flg) my_paint(g); } // end method 'paint()' // private methods // draw images in composite buffer and blast to screen // synchronized method, don't initiate another "paint" session until one's complete synchronized void my_paint(Graphics g) { int idx; String dots_str; boolean tmp_filmstrips_loaded_flg; // if composite buffer's non-existent, return immediately if (composite_graphics == null) return; // fill composite buffer with white paint composite_graphics.setColor(Color.white); composite_graphics.fillRect(0, 0, CANVAS_W, CANVAS_H); // have images loaded? if ( ! img_tracker.checkAll(true) ) { // System.out.println("loading...."); // debug // text font Font txt_Font = new Font("Helvetica", Font.PLAIN, 9); // set text string font composite_graphics.setFont(txt_Font); // set text string color composite_graphics.setColor(Color.black); // build str and write to screen dots_str = "Loading Images"; for (idx = 1; idx <= img_load_anm_dots; idx++) dots_str = dots_str + "."; composite_graphics.drawString(dots_str, 25, 25); // image load leader dots (animate 1 to 5 dots) if ( System.currentTimeMillis() >= dots_update_time ) { if (img_load_anm_dots < 5) img_load_anm_dots++; else img_load_anm_dots = 1; dots_update_time = System.currentTimeMillis() + DOTS_FPS; } } else { // osama squiggle if (System.currentTimeMillis() >= osama_squiggle_delay) { if (osama_squiggle_flg == 1) osama_squiggle_flg = 0; // 0/1, 2 frame squiggle anim else osama_squiggle_flg = 1; osama_squiggle_delay = System.currentTimeMillis() + OSAMA_FPS; // squiggle anim, 10 fps } // draw osama's head and body draw_osama_head_body(); // draw text scroll area draw_text_scroll_area(); } // black border composite_graphics.setColor(Color.black); composite_graphics.drawRect(0, 0, (CANVAS_W - 1), (CANVAS_H - 1)); // blast composite buffer to screen g.drawImage(composite_image, 0, 0, null); } // end method 'my_paint()' // draw osama's head and body void draw_osama_head_body() { int y_offset; Graphics clip_g = null; // clip/crop // head and body clip_g = composite_graphics.create(); // create clip clip_g.clipRect(head_body_left, head_body_top, HEAD_BODY_W, HEAD_BODY_H); // clip // squiggle if (osama_squiggle_flg == 0) y_offset = 0; else y_offset = HEAD_BODY_H; clip_g.drawImage(imgs[IMG_IDX_HEAD_BODY], head_body_left, head_body_top - y_offset, this); // draw clip_g.dispose(); // dispose clip // eyes clip_g = composite_graphics.create(); // create clip clip_g.clipRect( eyes_left, eyes_top, EYES_W, EYES_H ); // clip // squiggle if (osama_squiggle_flg == 0) y_offset = 0; else y_offset = (EYES_H * 2); // blink/eyes closed? if (eyesstatus_flg == 0) y_offset = y_offset + EYES_H; clip_g.drawImage(imgs[IMG_IDX_EYES], eyes_left, eyes_top - y_offset, this); // draw clip_g.dispose(); // dispose clip // mouth clip_g = composite_graphics.create(); // create clip clip_g.clipRect( mouth_left, mouth_top, MOUTH_W, MOUTH_H ); // clip // squiggle if (osama_squiggle_flg == 0) y_offset = 0; else y_offset = (MOUTH_H * 2); // mouth open or closed closed? if (mouth_open_closed_flg == 0) y_offset = y_offset + MOUTH_H; clip_g.drawImage(imgs[IMG_IDX_MOUTH], mouth_left, mouth_top - y_offset, this); // draw clip_g.dispose(); // dispose clip } // end method 'draw_osama_head_body()' // draw text scroll area void draw_text_scroll_area() { int tmp_h = CANVAS_H - (HEAD_BODY_OFFSET_Y + HEAD_BODY_H); Graphics clip_g = null; // clip/crop // black text box composite_graphics.setColor(Color.black); composite_graphics.fillRect(0, (HEAD_BODY_OFFSET_Y + HEAD_BODY_H), CANVAS_W, tmp_h); // white dividing box composite_graphics.setColor(Color.white); composite_graphics.drawRect(1, (HEAD_BODY_OFFSET_Y + HEAD_BODY_H) + 1, CANVAS_W - 3, tmp_h - 3); // blast text clip_g = composite_graphics.create(); // create clip clip_g.clipRect(4, txtscroll_start_y, txtscroll_width, txtscroll_height); // clip clip_g.drawImage(txtscroll_image, 4, txtscroll_y, this); // draw clip_g.dispose(); // dispose clip } // end method 'draw_text_scroll_area()' /* 'load_text_scroll_area()' load wrapped text in specified buffer at specified width, only break on space characters. parameter(s): (1) font (2) the buffer we're drawing into returns: txtscroll_text_height int value of max height of text (calculated as # of lines X [leading + ascent]) */ public int load_text_scroll_area( String txt_string, Graphics local_txtscroll_graphics ) { // font Font txt_font = new Font("Helvetica", Font.BOLD, 12); // calc string width FontMetrics txt_fontmetrics; // draw one line of text at a time from 'txt_string' String ln_str = ""; // count through chars in 'txt_string' int idx; // break from draw line/check width loop boolean break_flg; // total # of chars in txt_string int total_chars; // offset from y=0 to draw text int draw_y; // space between lines of text int space_y; // track total height, we're returning 'txtscroll_text_height' on exit int local_txtscroll_text_height; // used to trim width back to last space character int trim_length; // fill entire buffer with background_color local_txtscroll_graphics.setColor(Color.black); local_txtscroll_graphics.fillRect(0, 0, txtscroll_width, txtscroll_height); // set text string font local_txtscroll_graphics.setFont(txt_font); // font matrix (used to calc string width) txt_fontmetrics = local_txtscroll_graphics.getFontMetrics(txt_font); // offset from y=0 to draw text draw_y = txt_fontmetrics.getLeading() + txt_fontmetrics.getAscent(); // space between lines of text space_y = txt_fontmetrics.getHeight(); // track total height, we're returning 'txtscroll_text_height' on exit local_txtscroll_text_height = 0; // total chars in text string total_chars = txt_string.length(); // loop through all chars in description_String idx = 0; // first char while (idx < total_chars) { // begin: draw a line at a time break_flg = false; // true= break from line loop ln_str = ""; // init to empty string // enter line loop while( !break_flg ) { try { // build line string a character at a time ln_str = ln_str + txt_string.substring(idx, idx + 1); } catch(Exception e) { // at the last char in txt_string? then break break_flg=true; } // make sure we haven't exceeded the line width limit trim_length = ln_str.length(); // save the length of the line string if (txt_fontmetrics.stringWidth(ln_str) > txtscroll_width) { // keep trimming back (?) until we've found a space character while( !ln_str.endsWith(" ") ) { try { ln_str = ln_str.substring(0,trim_length); } catch(Exception e) { ; } trim_length--; // move back through line string idx--; // reset (decrement) index into description_String } // end 'while( !ln_str.endsWith(" ") )' break_flg = true; } // end 'if (txt_fontMetrics.stringWidth(ln_str) > txtscroll_width)' // advance to next character in 'txt_string' idx++; } // end 'while(!break_flg)' // end: draw a line at a time // draw text (line) local_txtscroll_graphics.setColor(Color.white); local_txtscroll_graphics.drawString(ln_str.trim(), 2, draw_y); // move draw down to next line draw_y = draw_y + space_y; // track total height, we're returning 'txtscroll_text_height' on exit local_txtscroll_text_height = local_txtscroll_text_height + space_y; } // end "while (idx < total_chars)" // track total height, returning 'txtscroll_text_height' on exit return local_txtscroll_text_height; } // end method 'load_text_scroll_area' // load text scroll strings // returns # of lines loaded public int load_txtscroll_strs() { int cnt = 0; // count # of lines loaded String tmp_str; // open 'videotext.txt', read and load text strings try { URL url = new URL(getCodeBase( ), "videotext.txt"); BufferedReader in = new BufferedReader( new InputStreamReader(url.openStream()) ); while (cnt < TXTSTR_CNT) { // load a max of TXTSTR_CNT lines tmp_str = in.readLine(); if (tmp_str == null) break; txtscroll_string[cnt] = "OBL: " + tmp_str; // debug // System.out.println("(line " + (cnt + 1) + ") " + txtscroll_string[cnt]); cnt++; } in.close( ); } catch (Exception exception) { System.out.println(exception); } return cnt; // return # of lines loaded } // end method 'load_txtscroll_strs()' } // end class "osama_says"