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");
}
}