home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1997 October / PCO1097.ISO / FilesBBS / FREI / WIPER10.EXE / Wiper.java < prev    next >
Encoding:
Java Source  |  1996-09-20  |  13.8 KB  |  556 lines

  1. import java.io.*;
  2. import java.net.*;
  3. import java.util.*;
  4. import java.awt.*;
  5. import java.awt.image.*;
  6. import java.applet.*;
  7.  
  8. // Wiper v1.0 
  9. // 
  10. // Copyright (c) 1996 Bryan McNett
  11. //
  12. // This program is free software; you can redistribute it and/or modify
  13. // it under the terms of the GNU General Public License as published by
  14. // the Free Software Foundation; either version 2 of the License, or (at
  15. // your option) any later version.
  16. //
  17. // This program is distributed in the hope that it will be useful, but
  18. // WITHOUT ANY WARRANTY; without even the implied warranty of
  19. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20. // General Public License for more details.
  21. //
  22. // You should have received a copy of the GNU General Public License
  23. // along with this program (as the file COPYING in the main directory of
  24. // the distribution); if not, write to the Free Software Foundation,
  25. // Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26. //
  27. // Wiper is an advertising billboard that cycles through a list of images 
  28. // by applying user-defined special effects. When the user clicks
  29. // on an advertisement, Wiper takes her to the advertiser's home page.
  30. //
  31. // Here is a list of Wiper's parameters, all of which assume reasonable
  32. // default values if left unspecified:
  33. //
  34. // name                 type    description                             default
  35. // -----------------------------------------------------------------------------
  36. // src                  URL     location of play list                   play.txt
  37. // secondsBetweenSleeps double  seconds between sleeps                  0.1
  38. // secondsBetweenWipes  double  seconds between wipe effects            3.0
  39. // secondsBetweenFrames double  seconds between display frames          0.1
  40. // wipeBits             int     frame count equal to two to this power  5
  41. // 
  42. // The play list is text with one advertisement per line as follows:
  43. //
  44. // <url of advertiser's home page> <url of advertisement> [url of fade effect]
  45. //
  46. // for example:
  47. //
  48. // http://www.bigpanda.com/   bigpanda.gif   wackywipe.gif
  49. // http://www.intrasteve.com/ intrasteve.gif 
  50. //
  51. // Due to the security restrictions imposed upon applets, the applet code, 
  52. // advertisement, and fade effect URLs must refer to the same hostname.
  53. //
  54.  
  55. public class Wiper extends Applet implements Runnable {
  56.   String  pinfo[][]  = {
  57.     { "src"                 , "URL"   , "location of play list"                  },
  58.     { "secondsBetweenSleeps", "double", "seconds between sleeps"                 },
  59.     { "secondsBetweenWipes" , "double", "seconds between wipe effects"           },
  60.     { "secondsBetweenFrames", "double", "seconds between display frames"         },
  61.     { "wipeBits"            , "int"   , "frame count equal to two to this power" }
  62.   };
  63.  
  64.   public String[][] getParameterInfo() { return pinfo; }
  65.  
  66.   String ainfo = 
  67.   "Wiper v1.0 (c) 1996 Bryan McNett - distributed under the terms of the GNU General Public License";
  68.  
  69.   public String getAppletInfo() { return ainfo; }
  70.  
  71.   Thread thread = null;
  72.  
  73.   public void start() {
  74.     if( thread == null ) {
  75.       thread = new Thread( this );
  76.       thread.start();
  77.     }
  78.   }
  79.   
  80.   public void stop() {
  81.     if( thread != null ) {
  82.       thread.stop();
  83.       thread = null;
  84.     }
  85.   }
  86.  
  87.   String getParameterSafely( String name, String otherwise ) {
  88.     String value = null;
  89.     value = getParameter( name ); 
  90.     if( value == null ) return otherwise;
  91.     return value;
  92.   }
  93.  
  94.   int getIntParamSafely( String name, String otherwise ) {
  95.       String s = getParameterSafely( name, otherwise );
  96.       Integer i = Integer.valueOf( s );
  97.       return i.intValue();
  98.   }
  99.  
  100.   double getDoubleParamSafely( String name, String otherwise ) {
  101.       String s = getParameterSafely( name, otherwise );
  102.       Double d = Double.valueOf( s );
  103.       return d.doubleValue();
  104.   }
  105.  
  106.   // initialize the applet
  107.  
  108.   int secondsBetweenSleeps;
  109.   int secondsBetweenWipes;
  110.   int secondsBetweenFrames;
  111.  
  112.   Vector effectvect = null;
  113.   Hashtable bitmaphash = null;
  114.   Hashtable wipehash = null;
  115.   LineReader linereader = null;
  116.  
  117.   int width = 0;
  118.   int height = 0;
  119.  
  120.   Bitmap bitmap = null;
  121.  
  122.   public static int wipeBits   = 5;
  123.   public static int wipeFactor = 32;
  124.  
  125.   public void init() {
  126.     Rectangle r = bounds();
  127.     width       = r.width;
  128.     height      = r.height;
  129.  
  130.     bitmap = new Bitmap( width, height );
  131.     backdrop = createImage( bitmap.prepare() );
  132.  
  133.     effectvect = new Vector();
  134.     bitmaphash = new Hashtable();
  135.     wipehash   = new Hashtable();
  136.  
  137.     secondsBetweenSleeps = (int)(1000 * getDoubleParamSafely( "secondsBetweenSleeps", "0.100" ) );
  138.     secondsBetweenWipes  = (int)(1000 * getDoubleParamSafely( "secondsBetweenWipes",  "3.000" ) );
  139.     secondsBetweenFrames = (int)(1000 * getDoubleParamSafely( "secondsBetweenFrames", "0.100" ) );
  140.     wipeBits             =              getIntParamSafely   ( "wipeBits",                 "5"   );
  141.  
  142.     wipeFactor = 1 << wipeBits;
  143.     wipeBits   = 8 - wipeBits;
  144.  
  145.     String src        = getParameterSafely( "src", "play.txt" );
  146.  
  147.     URL u = null;
  148.     DataInputStream d = null;
  149.  
  150.     try { 
  151.       u = new URL( getDocumentBase(), src );
  152.       d = new DataInputStream( u.openStream() ); 
  153.     } catch( Exception e );
  154.  
  155.     linereader = new LineReader( d, this );
  156.   }
  157.  
  158.   // make sure to load each unique image URL exactly once
  159.   // to conserve bandwidth, cpu and memory
  160.  
  161.   Bitmap loadBitmap( String s ) {
  162.     URL u = null;
  163.     try { 
  164.       u = new URL( getDocumentBase(), s );
  165.     } catch( Exception e ) {;}
  166.     if( bitmaphash.containsKey( u ) ) {
  167.       return (Bitmap)bitmaphash.get( u );
  168.     } 
  169.     Bitmap b = new Bitmap( getImage( u ), width, height );
  170.     bitmaphash.put( u, b );
  171.     return b;
  172.   }
  173.  
  174.   // make sure to create wipe data at most once per Bitmap.
  175.  
  176.   Wipe loadWipe( Bitmap b ) {
  177.     if( wipehash.containsKey( b ) ) {
  178.       return (Wipe)wipehash.get( b );
  179.     }
  180.     Wipe w = new Wipe( b );
  181.     wipehash.put( b, w );
  182.     return w; 
  183.   }
  184.  
  185.   public void addLine( String line ) {
  186.     StringTokenizer token;
  187.     String u,b,w;
  188.     Bitmap bitmap, wipe;
  189.     URL url;
  190.  
  191.     token  = new StringTokenizer( line );
  192.  
  193.     u = token.nextToken();
  194.     b = token.nextToken();
  195.     w = b;
  196.     if( token.hasMoreElements() ) w = token.nextToken();
  197.  
  198.     url = getDocumentBase();
  199.     try {
  200.       url = new URL( getDocumentBase(), u );
  201.     } catch( Exception e );
  202.  
  203.     bitmap = loadBitmap( b );
  204.     wipe   = loadBitmap( w );
  205.  
  206.     effectvect.addElement( new Effect( url, bitmap, loadWipe( wipe ) ) );
  207.   }
  208.  
  209.   Image backdrop;
  210.  
  211.   public void paint( Graphics g ) {
  212.     synchronized( backdrop ) g.drawImage( backdrop, 0, 0, this );
  213.   }
  214.  
  215.   public void update( Graphics g ) {
  216.     paint( g );
  217.   }
  218.  
  219.   Effect lastEffect   = null;
  220.   Effect effect       = null;
  221.   URL url             = null;
  222.   boolean mouseInside = false;
  223.   int mouseX = 0;
  224.   int mouseY = 0;
  225.  
  226.   void mouseIt( int x, int y ) {
  227.     mouseX = x; mouseY = y;
  228.     if( effect == null ) return;
  229.     Effect e = lastEffect;
  230.     if( effect.get(x,y) ) e = effect;
  231.     if( e == null ) return;
  232.     URL u = e.getURL();
  233.     if( u == url ) return;
  234.     url = u;
  235.     showStatus( url.toString() );
  236.   }
  237.  
  238.   public boolean mouseMove( Event evt, int x, int y ) {
  239.     mouseIt( x, y );
  240.     return true;
  241.   }
  242.  
  243.   public boolean mouseUp( Event evt, int x, int y ) {
  244.     mouseIt( x, y );
  245.     if( url != null ) getAppletContext().showDocument( url );
  246.     return true;
  247.   }
  248.  
  249.   public boolean mouseEnter( Event evt, int x, int y ) {
  250.     mouseInside = true;
  251.     mouseIt( x, y );
  252.     return true;
  253.   }
  254.  
  255.   public boolean mouseExit( Event evt, int x, int y ) {
  256.     mouseInside = false;
  257.     return true;
  258.   }
  259.  
  260.   int index = 0;
  261.  
  262.   public void run() {
  263.     Waiter wipewaiter = new Waiter( secondsBetweenWipes );
  264.     Waiter framewaiter = new Waiter( secondsBetweenFrames );
  265.     while( effectvect.isEmpty() == true ) {
  266.       try { 
  267.         Thread.sleep( secondsBetweenSleeps );
  268.       } catch( Exception e ) {;}
  269.     }
  270.     while( true ) {
  271.       if( index >= effectvect.size() ) index = 0;
  272.       lastEffect = effect;
  273.       wipewaiter.go();
  274.       effect = (Effect)effectvect.elementAt( index++ );
  275.       effect.start();
  276.       boolean done;
  277.       do {
  278.         done = effect.go(bitmap);
  279.         if( mouseInside ) mouseIt( mouseX, mouseY );
  280.         framewaiter.go();
  281.         backdrop = createImage( bitmap.prepare() );
  282.         update( getGraphics() );
  283.       } while( done == false );
  284.       wipewaiter.go();
  285.     }
  286.   }
  287. }
  288.  
  289.  
  290. // A LineReader spawns a thread to read text lines from a DataInputStream.
  291. // It updates Wiper's play list as lines arrive.
  292.  
  293.  
  294. class LineReader extends Thread {
  295.   DataInputStream d;
  296.   Wiper w;
  297.   Thread thread;
  298.  
  299.   public LineReader( DataInputStream d, Wiper w ) {
  300.     super( "line reader" );
  301.     this.setPriority( 1 );
  302.     this.d  = d; 
  303.     this.w  = w;
  304.     this.start();
  305.   }
  306.  
  307.   public void run() {
  308.     String line;
  309.     while( true ) {
  310.       try {
  311.         line = d.readLine();
  312.       } catch( IOException e ) break;
  313.       if( line == null ) break;
  314.       synchronized ( w ) w.addLine( line );
  315.     }
  316.   }
  317.  
  318. }
  319.  
  320.  
  321. // A PixelMuncher spawns a thread to fill an array of ints with the pixels of 
  322. // an image.
  323.  
  324.  
  325. class PixelMuncher extends Thread {
  326.   static int muncher_number = 0;
  327.   int done=0;
  328.   PixelGrabber p = null;
  329.   Thread thread = null;
  330.  
  331.   public PixelMuncher( Image image, int width, int height, int[] data ) {
  332.     super( "pixel muncher number " + muncher_number++ );
  333.     this.setPriority( 1 );
  334.     p = new PixelGrabber( image.getSource(), 0, 0, width, height, data, 0, width );
  335.     this.start();
  336.   } 
  337.  
  338.   public void run() {
  339.     try {
  340.       p.grabPixels();
  341.       done = 1;
  342.     } catch( Exception e ) {} 
  343.   }
  344.  
  345.   public int ready() {
  346.     return done;
  347.   }
  348.  
  349. }
  350.  
  351.  
  352. // A Bitmap knows how to play with its pixels.
  353.  
  354.  
  355. class Bitmap {
  356.   int[] data = null;
  357.   int width=0,height=0;
  358.   PixelMuncher muncher = null;
  359.  
  360.   public Bitmap( Image image, int width, int height ) {
  361.     this.width  = width;
  362.     this.height = height;
  363.     data = new int[width*height];
  364.     clear();
  365.     muncher = new PixelMuncher( image, width, height, data );
  366.   }
  367.  
  368.   public Bitmap( int width, int height ) {
  369.     this.width  = width;
  370.     this.height = height;
  371.     data = new int[width*height];
  372.     clear();
  373.   }
  374.  
  375.   void clear( ) {
  376.     for( int i=0; i<pixels(); i++ ) {
  377.       data[i] = 0xff000000;
  378.     }   
  379.   }
  380.  
  381.   public int get( int index ) {
  382.     //   You've uncovered Wiper's secret weakness! Yes, when Wiper is working
  383.     //   with images as transition effects, it checks only the blue channel for 
  384.     //   brightness values. If you uncomment the following two lines of code, 
  385.     //   it will consider all three color channels (at a slightly higher cost).
  386.     // int i = data[index];
  387.     // return ((( i>>16 & 0xff )+( i>>8 & 0xff )+( i & 0xff )) / 3) >> Wiper.wipeBits;
  388.     return (data[index] & 0xff) >> Wiper.wipeBits; 
  389.   }
  390.  
  391.   public int get( int x, int y ) {
  392.     return get( y*width + x );
  393.   }
  394.  
  395.   public void paste( Bitmap src, int index, int count ) {
  396.     System.arraycopy( src.data, index, data, index, count );
  397.   }
  398.  
  399.   public int pixels() {
  400.     return width*height;
  401.   }
  402.  
  403.   public int ready() {
  404.     return muncher.ready();
  405.   }
  406.  
  407.   public MemoryImageSource prepare() {
  408.     return new MemoryImageSource( width, height, data, 0, width );  
  409.   }
  410. }
  411.  
  412.  
  413. // A Wipe knows how to decompose an image into 256 RLE compressed
  414. // representations of a single gray value.
  415.  
  416.  
  417. class Wipe {
  418.   Bitmap bitmap = null;
  419.   int[][] data = null;
  420.  
  421.   public Wipe( Bitmap bitmap ) {
  422.     this.bitmap  = bitmap;
  423.     data = new int[256][];
  424.     for( int i=0; i<256; i++ ) {
  425.       data[i] = null;
  426.     }
  427.   }
  428.  
  429.   public void go( Bitmap dest, Bitmap src, int value ) {
  430.     int j=0,index=0;
  431.     int r[] = rle(value);
  432.     while( j < r.length ) {      
  433.       index += r[j++];
  434.       dest.paste( src, index, r[j] );
  435.       index += r[j++];
  436.     }
  437.   }
  438.  
  439.   public int get( int x, int y ) { 
  440.     return bitmap.get( x, y );
  441.   }
  442.  
  443.   int[] rle( int value ) {
  444.     int ready = 0;
  445.     if( data[value] != null ) return data[value];
  446.     int   j=0, skip, run, index = 0, count;
  447.     int[] r = new int[ count = bitmap.pixels() ];
  448.  
  449.     // set this flag if the bitmap has completed loading
  450.  
  451.     if( bitmap.ready() != 0 ) ready=1; 
  452.  
  453.     // rle encode all pixels with a particular gray value
  454.  
  455.     while( count>0 ) {
  456.       skip = run = 0;
  457.       while( (count>0) && ( bitmap.get(index) != value ) ) {
  458.         skip++; index++; count--;
  459.       }
  460.       while( (count>0) && ( bitmap.get(index) == value ) ) {
  461.         run++; index++; count--;
  462.       }
  463.       if( j == r.length ) {
  464.         int temp[] = new int[ j + bitmap.pixels() ];
  465.         System.arraycopy( r, 0, temp, 0, j );
  466.         r = temp;
  467.       }
  468.       r[j++] = skip;
  469.       r[j++] = run;
  470.     }
  471.     if( j != r.length ) {
  472.       int temp[] = new int[j];
  473.       System.arraycopy( r, 0, temp, 0, j );
  474.       r = temp;
  475.     }
  476.  
  477.     // if we set this flag earlier, it means that we can cache the results of our encoding.
  478.  
  479.     if( ready != 0 ) data[value] = r;
  480.     return r;
  481.   }
  482.  
  483. }
  484.  
  485.  
  486. // An Effect knows how to run a Wipe.
  487.  
  488. class Effect {
  489.   Bitmap  src = null, map = null;
  490.   Wipe    wipe = null;
  491.   int     ticker = 0;
  492.   boolean complete = false;
  493.   URL     url;
  494.  
  495.   public Effect( URL url, Bitmap bitmap, Wipe wipe ) {
  496.     this.url = url;
  497.     this.src = bitmap; 
  498.     this.wipe = wipe;  
  499.     this.start();
  500.   }
  501.  
  502.   public URL getURL() {
  503.     return url;
  504.   }
  505.  
  506.   public void start() {
  507.     this.ticker=0;
  508.     this.complete=false;
  509.   }
  510.  
  511.   public boolean go( Bitmap dest ) {
  512.     if( this.complete == true ) return this.complete;
  513.     wipe.go( dest, this.src, this.ticker++ );
  514.     if( this.ticker >= Wiper.wipeFactor ) this.complete=true;
  515.     return this.complete;
  516.   }
  517.  
  518.   public boolean get( int x, int y ) {
  519.     if( this.wipe.get( x, y ) < this.ticker ) return true;
  520.     return false;
  521.   }
  522.  
  523. }
  524.  
  525.  
  526. // a waiter is something that knows how to wait.
  527.  
  528.  
  529. class Waiter {
  530.   int time=0, delay=0;
  531.  
  532.   int set() { 
  533.     int now = (int)System.currentTimeMillis();
  534.     time = now+delay;
  535.     return now;
  536.   }
  537.  
  538.   public Waiter( int delay ) {
  539.     this.delay = delay;
  540.     set();
  541.   }
  542.  
  543.   public void go() {
  544.     int ticks = time;
  545.     ticks -= set();
  546.     if( ticks > 0 ) 
  547.       try Thread.sleep( ticks ); 
  548.       catch( Exception e ) ;
  549.   }
  550.  
  551. }
  552.  
  553.  
  554.  
  555.  
  556.