//****************************************************************************** // Lake.java: Applet // // (c) David Griffiths, 1997 // This source code may not be reproduced without the express permission of the // author. //****************************************************************************** import java.applet.*; import java.net.URL; import java.net.MalformedURLException; import java.awt.*; //============================================================================== // Main Class for applet Lake // //============================================================================== public class Lake extends Applet implements Runnable { // THREAD SUPPORT: // m_Lake is the Thread object for the applet //-------------------------------------------------------------------------- Thread m_Lake = null; // ANIMATION SUPPORT: // m_Graphics used for storing the applet's Graphics context // m_WaveGraphics used for storing the animation's Graphics context // m_Image the original image // m_WaveImage the image containing the wave animations // m_nCurrImage the index of the next image to be displayed // m_ImgWidth width of each image // m_ImgHeight height of each image // m_OvlWidth width of each overlay // m_OvlHeight height of each overlay // m_fAllLoaded indicates whether all images have been loaded // m_tAnimate indicates that OK to do animation (changed by mouse // click) // NUM_FRAMES number of NUM_FRAMES used in the animation //-------------------------------------------------------------------------- private Graphics m_Graphics, m_WaveGraphics; private Image m_Image, m_Overlay, m_WaveImage; private int m_nCurrImage; private int m_nImgWidth = 0; private int m_nImgHeight = 0; private int m_nOvlWidth = 0; private int m_nOvlHeight = 0; private boolean m_fAllLoaded = false, m_tAnimate = true; private final int NUM_FRAMES = 12; // PARAMETER SUPPORT: // Parameters allow an HTML author to pass information to the applet; // the HTML author specifies them using the tag within the // tag. The following variables are used to store the values of the // parameters. //-------------------------------------------------------------------------- // Members for applet parameters // = //-------------------------------------------------------------------------- private String m_ImageName = ""; private String m_OverlayName = ""; private URL m_HRef; private String m_Frame = "_self"; // Parameter names. To change a name of a parameter, you need only make // a single change. Simply modify the value of the parameter string below. //-------------------------------------------------------------------------- private final String PARAM_image = "image"; private final String PARAM_overlay = "overlay"; private final String PARAM_href = "href"; private final String PARAM_target = "target"; // Lake Class Constructor //-------------------------------------------------------------------------- public Lake() { // TODO: Add constructor code here } // APPLET INFO SUPPORT: // The getAppletInfo() method returns a string describing the applet's // author, copyright date, or miscellaneous information. //-------------------------------------------------------------------------- public String getAppletInfo() { return "Name: Lake v3.0\r\n" + "Author: David Griffiths\r\n" + "Created with Microsoft Visual J++ Version 1.0"; } // PARAMETER SUPPORT // The getParameterInfo() method returns an array of strings describing // the parameters understood by this applet. // // Lake Parameter Information: // { "Name", "Type", "Description" }, //-------------------------------------------------------------------------- public String[][] getParameterInfo() { String[][] info = { { PARAM_image, "String", "JPG or GIF file to reflect" }, { PARAM_overlay, "String", "JPG or GIF file to use as overlay" }, { PARAM_href, "URL", "URL to link to" }, { PARAM_target, "String", "Target frame" }, }; return info; } // The init() method is called by the AWT when an applet is first loaded or // reloaded. Override this method to perform whatever initialization your // applet needs, such as initializing data structures, loading images or // fonts, creating frame windows, setting the layout manager, or adding UI // components. //-------------------------------------------------------------------------- public void init() { // PARAMETER SUPPORT // The following code retrieves the value of each parameter // specified with the tag and stores it in a member // variable. //---------------------------------------------------------------------- String param; // image: JPG of GIF file to reflect //---------------------------------------------------------------------- param = getParameter(PARAM_image); if (param != null) m_ImageName = param; // overlay: JPG of GIF file to use as overlay //---------------------------------------------------------------------- param = getParameter(PARAM_overlay); if (param != null) m_OverlayName = param; // href: URL to link to //---------------------------------------------------------------------- param = getParameter(PARAM_href); if (param != null) try { m_HRef = new URL(getDocumentBase(), param); } catch (MalformedURLException e) { getAppletContext().showStatus("Bad URL: " + param); return; } // target: Target frame //---------------------------------------------------------------------- param = getParameter(PARAM_target); if (param != null) m_Frame = param; } // Place additional applet clean up code here. destroy() is called when // when you applet is terminating and being unloaded. //------------------------------------------------------------------------- public void destroy() { // TODO: Place applet cleanup code here } // ANIMATION SUPPORT: // Draws the next image, if all images are currently loaded //-------------------------------------------------------------------------- private void displayImage(Graphics g) { if (!m_fAllLoaded) return; // Draw frame of rippled lower half //---------------------------------------------------------------------- if (m_WaveImage != null) { g.drawImage (m_WaveImage, (-m_nCurrImage * m_nImgWidth), m_nImgHeight, this); g.drawImage (m_WaveImage, ((NUM_FRAMES-m_nCurrImage) * m_nImgWidth), m_nImgHeight, this); } // Draw the original in the tophalf. //---------------------------------------------------------------------- g.drawImage (m_Image, 0, -1, this); } // Lake Paint Handler //-------------------------------------------------------------------------- public void paint(Graphics g) { // ANIMATION SUPPORT: // The following code displays a status message until all the // images are loaded. Then it calls displayImage to display the current // image. //---------------------------------------------------------------------- if (m_fAllLoaded) displayImage(g); else g.drawString("Loading images...", 10, 20); // TODO: Place additional applet Paint code here } // The start() method is called when the page containing the applet // first appears on the screen. The AppletWizard's initial implementation // of this method starts execution of the applet's thread. //-------------------------------------------------------------------------- public void start() { if (m_Lake == null) { m_Lake = new Thread(this); m_Lake.start(); } } // The stop() method is called when the page containing the applet is // no longer on the screen. The AppletWizard's initial implementation of // this method stops execution of the applet's thread. //-------------------------------------------------------------------------- public void stop() { if (m_Lake != null) { m_Lake.stop(); m_Lake = null; } } // THREAD SUPPORT // The run() method is called when the applet's thread is started. If // your applet performs any ongoing activities without waiting for user // input, the code for implementing that behavior typically goes here. For // example, for an applet that performs animation, the run() method controls // the display of images. //-------------------------------------------------------------------------- public void run() { m_nCurrImage = 0; // If re-entering the page, then the images have already been loaded. // m_fAllLoaded == TRUE. //---------------------------------------------------------------------- if (!m_fAllLoaded) { repaint(); m_Graphics = getGraphics(); // Now load up the image to be used in the animation. Rather than do // this asynchronously with imageUpdate (which is a pain in the bum to // use) we'll do it synchronously with a MediaTracker. This hangs // around until the image is loaded. Using the waitForAll method, just // in case we ever add other images to the applet. //------------------------------------------------------------------ MediaTracker tracker = new MediaTracker(this); String strImage; m_Image = getImage(getDocumentBase(), m_ImageName); if (!"".equals(m_OverlayName)) m_Overlay = getImage(getDocumentBase(), m_OverlayName); tracker.addImage(m_Image, 0); if (!"".equals(m_OverlayName)) tracker.addImage(m_Overlay, 1); // Wait until all images are fully loaded //------------------------------------------------------------------ try { tracker.waitForAll(); m_fAllLoaded = !tracker.isErrorAny(); } catch (InterruptedException e) {} if (!m_fAllLoaded) { stop(); m_Graphics.drawString("Error loading images!", 10, 40); return; } // Can now set width and height straight away because the // MediaTracker object has ensured this information is now available. //-------------------------------------------------------------- m_nImgWidth = m_Image.getWidth(this); m_nImgHeight = m_Image.getHeight(this); if (!"".equals(m_OverlayName)) { m_nOvlWidth = m_Overlay.getWidth(this); m_nOvlHeight = m_Overlay.getHeight(this); } // Now create the animation of the rippling waves. //-------------------------------------------------------------- createAnimation(); } repaint(); while (true) { try // Draw next image in animation //-------------------------------------------------------------- if (m_tAnimate) { displayImage(m_Graphics); if (++m_nCurrImage == NUM_FRAMES) m_nCurrImage = 0; Thread.sleep(50); } else Thread.sleep(500); catch (InterruptedException e) stop(); } } // MOUSE SUPPORT: // Clicking on the applet starts/stops it. // Doesn't call 'stop()' directly because otherwise an InterruptedException // is thrown. .. //-------------------------------------------------------------------------- public boolean mouseUp(Event event, int i, int j) { boolean flag = super.mouseUp(event, i, j); if (m_HRef == null) m_tAnimate = !m_tAnimate; // Toggle m_tAnimate to start/stop animation. else { showStatus("" + m_HRef); getAppletContext().showDocument(m_HRef, m_Frame); } return true; } // ANIMATION // Create the animation within a single background image. We use a single // image rather than the default multiple images because it's quicker. //--------------------------------------------------------------------------- public void createAnimation () { // Create inverted image of original loaded image. // We create a background image (backImg) 1 pixel higher // than the original because we'll need an extra line of // pixels to play with when we flip the image upside down. //-------------------------------------------------------- Image backImg = createImage (m_nImgWidth, m_nImgHeight + 1); Graphics backG = backImg.getGraphics(); // Copy the original image (m_Image) onto the background // version. //-------------------------------------------------------- backG.drawImage (m_Image, 0, 1, this); // Now flip the image upside down. //-------------------------------------------------------- for (int i = 0; i < (m_nImgHeight >> 1); i++) { backG.copyArea (0, i, m_nImgWidth, 1, 0, m_nImgHeight - i); backG.copyArea (0, m_nImgHeight - 1 - i, m_nImgWidth, 1, 0, -m_nImgHeight + 1 + (i << 1)); backG.copyArea (0, m_nImgHeight, m_nImgWidth, 1, 0, -1 - i); } // Now create the large (NUM_FRAMES + 1 times the width) image // that will store dithered copies of the inverted original. //-------------------------------------------------------- m_WaveImage = createImage ((NUM_FRAMES + 1) * m_nImgWidth, m_nImgHeight); m_WaveGraphics = m_WaveImage.getGraphics(); m_WaveGraphics.drawImage (backImg, NUM_FRAMES * m_nImgWidth, 0, this); // Now create dithered copies (sine displacement up or down) of the // inverted original. //-------------------------------------------------------- for (int phase = 0; phase < NUM_FRAMES; phase++) makeWaves (m_WaveGraphics, phase); // Now, if there is an overlay image, draw the top half of it // over the frame. (The bottom half of the overlay will be drawn over // the rippled image) //------------------------------------------------------------ backG.drawImage (m_Image, 0, 1, this); if (!"".equals(m_OverlayName)) backG.drawImage (m_Overlay, (m_nImgWidth - m_nOvlWidth) >> 1, m_nImgHeight - (m_nOvlHeight >> 1), this); m_Image = backImg; } // ANIMATION // Take the initial (unwaved) image from the left-hand-side of the graphics // and make NUM_FRAMES copies of it - the pixels rows of each one dithered // up or down depending upon the dispy sine function. //--------------------------------------------------------------------------- public void makeWaves (Graphics g, int phase) { double p1; int dispx, dispy; // Convert the phase into radians (by splitting 2*PI into // NUM_FRAMES segments). //-------------------------------------------------------- p1 = 2 * Math.PI * (double)phase / (double)NUM_FRAMES; // dispx defines how far across the image has to be copied // from the original LHS frame. //-------------------------------------------------------- dispx = (NUM_FRAMES - phase) * m_nImgWidth; // Now process each horizontal line of pixels. Copy across // from original frame on the left-had-side and displacing // up or down WRT the dispy sine function. //-------------------------------------------------------- for (int i = 0; i < m_nImgHeight; i++) { // dispy defines the vertical sine displacement. It // attenuates higher up the image, for perspective. //-------------------------------------------------------- dispy = (int)((m_nImgHeight/14) * ((double) i + 28.0) * Math.sin ((double)((m_nImgHeight/14)*(m_nImgHeight - i)) /(double)(i + 1) + p1) / (double) m_nImgHeight); // If no line dithers here then copy original. //-------------------------------------------------------- if (i < -dispy) g.copyArea (NUM_FRAMES * m_nImgWidth, i, m_nImgWidth, 1, -dispx, 0); else // Else copy dithered line. //-------------------------------------------------------- g.copyArea (NUM_FRAMES * m_nImgWidth, i + dispy, m_nImgWidth, 1, -dispx, -dispy); } // Now, if there is an overlay image, draw the bottom half of it // over the frame. (The top half of the overlay will be drawn over // the original image) //------------------------------------------------------------ if (!"".equals(m_OverlayName)) g.drawImage (m_Overlay, (phase * m_nImgWidth) + ((m_nImgWidth - m_nOvlWidth) >> 1), -m_nOvlHeight >> 1, this); } }