package org.nzdl.gsdl.GsdlCollageApplet; import java.applet.*; import java.awt.*; import java.awt.image.*; import java.awt.geom.*; import java.util.*; /** * @author Katrina Edgar * @author David Bainbridge * * Retrieves images from the download thread, determines an appropriate position * for thes images onscreen ensuring maximum whitespace coverage and minimal overlap * when possible. Draws images using advanced image processing techniques when applicable, * such as fading the edges of an image and discolouring backgrounds. Also re-uses images * from those that have previously been displayed, or are aging onscreen, in order to * to create movement when no new images have been downloaded. */ public class DisplayImages extends Thread { /** Will fire a removal operation when 4 images in the collage overlap a single area */ static final int NO_IMAGES_OF_OVERLAP = 4; /** To determine if there are several images overlapped, can remove the * layers underneath to save time in repainting */ boolean overlap = false; /** Refers to download thread */ DownloadImages download_images_ = null; /** Refers to applet */ GsdlCollageApplet app_ = null; /** Applets width on screen */ public static int app_x_dim_ = 0; /** Applets height on screen */ public static int app_y_dim_ = 0; /** Background colour of the applet screen */ protected Color bgcolor_ = null; /** Indicates whether java2 functionality should be used
* If true will allow advanced image processing techniques to occur, * such as fading and colouring using pixel manipulation
* If false, images will maintain an alpha value of 1 (appear solid), * newer images will simply be pasted on top of existing images
*/ protected boolean is_java2_ = false; /** Holds images currently on display in the applet */ protected Vector inuse_ = null; /** Holds images ready to be used */ protected Vector inuse_ready_ = null; /** Holds previously displayed images for re-use */ protected Vector notinuse_ = null; protected Image screen_buffer_ = null; protected Graphics screen_graphic_ = null; protected Image finished_buffer_ = null; protected Graphics finished_graphic_ = null; Thread next_frame_ ; String dots[] = new String[]{" ",".","..","...","....",".....","......"}; int dots_nums=0; boolean start_paint = false; /** Used to determine where to place new images on the applet to optimize * whitespace coverage
* A co-ordinate in the applet has a value of zero if it is whitespace, * one if it is covered by one image, two if it is covered by two images, * and so on. New images will be placed such that they cover the largest * area of whitespace (zero valued co-ordinates) */ public static int [] [] used_space = null; /** * Starts the display images thread. Initialises variables using parameters and * static values. Creates the blank applet screen, initialises the used space * 2D array to indicate that there are no images onscreen * * @param app the applet to display the images on * @param download_images thread from which to retrieve downloaded images * @param is_java2 whether advanced image processing techniques should be used * @param bgcolor the background colour of the applet screen */ public DisplayImages(GsdlCollageApplet app, DownloadImages download_images, boolean is_java2, Color bgcolor) { super("DisplayImages"); // parameters saved locally app_ = app; is_java2_ = is_java2; bgcolor_ = bgcolor; inuse_ = new Vector(); notinuse_ = new Vector(); inuse_ready_ = new Vector(); download_images_ = download_images; // sets width and height if (is_java2_) { app_x_dim_ = app_.getWidth(); app_y_dim_ = app_.getHeight(); } else { app_x_dim_ = 645; app_y_dim_ = 780; } // creates initial screen screen_buffer_ = app_.createImage(app_x_dim_, app_y_dim_); screen_graphic_ = screen_buffer_.getGraphics(); screen_graphic_.setColor(bgcolor_); screen_graphic_.fillRect(0,0,app_x_dim_,app_y_dim_); finished_buffer_ = app_.createImage(app_x_dim_, app_y_dim_); finished_graphic_ = finished_buffer_.getGraphics(); // initialises used space array to indicate an empty screen used_space = new int [app_x_dim_][app_y_dim_]; for (int n=0; n < app_x_dim_; n++) for (int m=0; m < app_y_dim_; m++) used_space[n][m] = 0; next_frame_ = new Thread(new Runnable(){ public void run() { Thread curr_thread = Thread.currentThread(); while (curr_thread == next_frame_) { try { Runtime rt = Runtime.getRuntime(); //System.out.println("total: "+rt.totalMemory()); //System.out.println("free: "+rt.freeMemory()); rt.gc(); //System.out.println("**************next frame..."); next_frame(); Thread.sleep(app_. refreshDelay_); curr_thread = Thread.currentThread(); } catch (Exception e) { e.printStackTrace(); break; } } } }); next_frame_.start(); } /** Determines what the user has clicked on in the applet, either an image or whitespace * @param x the x co-ordinate of the mouse * @param y the y co-ordinate of the mouse * @return the image that the user has clicked on or null if the user has clicked whitespace */ public CollageImage clickedOnImage(int x, int y) { if (app_.verbosity() > 1) { System.err.print("Checking for clicked on image: ("+x+","+y+")"); } // checks from last image down, as last images place will be at the top of the collage for (int i= inuse_.size() - 1; i >= 0; i--) { CollageImage collage_image = (CollageImage)inuse_.elementAt(i); if (collage_image.inside(x,y)) { return collage_image; } } // no image at these co-ordinates, just whitespace return null; } /** Generates the images onscreen. * Loops through the vector of currently displayed images and draws each one in turn. * If the image is being drawn for the first time, its position will be determined * and its alpha channel set as solid prior to drawing.
* Drawing of the image will depend on whether or not java2 is enabled. If so then * the image will be processed so that fading occurs with age, so that the edges * fade faster than the rest of the image and so that white images are slightly * discoloured to allow distinction. If java2 is not enabled, new images will simply * be pasted on top of existing images without any special effects occuring
* This function also deletes old images. If java2 is enabled then images are deleted * once they have faded to a suitably low alpha value. If java2 is not enabled then * images will be removed once there are greater than 25 images onscreen, to ensure * the applet cannot be overloaded. */ public void display_collage() { // if there is nothing new to add if (inuse_.size()==0) return; // otherwise need to re-generate images if (app_.verbosity() >= 2) { //system.err.println("Regenerating images"); } // get each image currently onscreen inturn for (int i=0; i NO_IMAGES_OF_OVERLAP) overlap = true; } // sets the image as fresh and adds it to the vector of images previously seen removed_image.fresh = true; notinuse_.addElement(removed_image); } else{ i++; } } else if (inuse_.size() > 25 || overlap) { overlap = false; CollageImage removed_image = (CollageImage)inuse_.elementAt(i); inuse_.remove(i); // indicates space for this image is no longer used for (int n = removed_image.xl_; n < (removed_image.xl_ + removed_image.image_x_dim_); n++) for (int m = removed_image.yt_; m < (removed_image.yt_ + removed_image.image_y_dim_); m++) if(used_space[n][m] != 0) { used_space[n][m]--; if (used_space[n][m] > NO_IMAGES_OF_OVERLAP) overlap = true; } // sets the image as fresh and adds it to the vector of images previously seen removed_image.fresh = true; notinuse_.addElement(removed_image); } else{ i++; } } } /** Alters the images currently on display in the applet
* Changes are prioritised in the following way: * 1. If there are images that have been downloaded and are yet to be displayed * they will be added to the list of images in use.
* 2. Otherwise if there are images that have previously been displayed but * are not currently on the applet screen, re-use one of these images.
* 3. Otherwise if there are any images on screen at all, take the oldest and * repaint it as the newest.
* Then the collage is asked to display and repaint with the new image set. */ public void next_frame() { double random = Math.random(); if (inuse_ready_.size() > 0){ int pos = (int) ((inuse_ready_.size() - 1) * random); CollageImage collage_image = (CollageImage) inuse_ready_.remove(pos); inuse_.addElement(collage_image); } else if (notinuse_.size()>0) { int position = (int) ((notinuse_.size() - 1) * random); CollageImage collage_image = (CollageImage)notinuse_.elementAt(position); notinuse_.remove(position); inuse_.addElement(collage_image); if (app_.verbosity()>3) { System.err.println("Re-using an image not on screen"); } } else{ if (app_.verbosity()>=4) System.err.println("No images in download area"); } display_collage(); } /** Paints the applet on the screen */ public void paint(Graphics g) { screen_graphic_.setColor(bgcolor_); screen_graphic_.fillRect(0,0,app_x_dim_,app_y_dim_); // get each image currently onscreen inturn for (int i=0; i < inuse_.size(); i++) { CollageImage collage_image =null; try{ collage_image = (CollageImage)inuse_.elementAt(i); start_paint = true; } catch(Exception e){ System.out.println("exception in paint"); break; } // don't paint new images yet if (collage_image.fresh) { if (app_.verbosity()>=1) { System.err.println("doing nothing... "+ collage_image.name_ +" is fresh."); } } // advanced image processing may be used else if (is_java2_) { //the alphacomposite controls the blending of these two images. Graphics2D sg2d = (Graphics2D)screen_graphic_; sg2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); sg2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); collage_image.expand(); sg2d.drawImage(collage_image.image_, collage_image.af_, app_); } // simple image collage else { // add the image to the collage screen_graphic_.drawImage(collage_image.image_, collage_image.xl_, collage_image.yt_, collage_image.xr_, collage_image.yb_, 0,0,collage_image.image_x_dim_-1, collage_image.image_y_dim_-1, app_); } } finished_graphic_.drawImage(screen_buffer_,0,0,app_); if (finished_buffer_ != null) { g.drawImage(finished_buffer_, 0, 0, app_); if (!start_paint){ g.setColor(Color.white); g.setFont(new Font("font",Font.PLAIN+Font.BOLD,30)); g.drawString("Downloading" + dots[(dots_nums++)%dots.length],app_x_dim_/3-20,app_y_dim_/2-2); } } } /** Runs display thread by repeatedly calling the next frame, * waiting for a specified delay period between calls */ public void run() { try { Thread curr_thread = Thread.currentThread(); while (curr_thread == this) { if (inuse_ready_.size() >= 1){ Thread.sleep(1000); continue; } CollageImage collage_image = download_images_.getCollageImage(); if (collage_image !=null){ inuse_ready_.addElement(collage_image); } curr_thread = Thread.currentThread(); } } catch (Exception e) { e.printStackTrace(); System.out.println("Display images thread is interrupted"); } System.out.println("Display images thread stoped"); } }