package org.nzdl.gsdl.GsdlCollageApplet;
import java.awt.*;
import java.awt.geom.*;
import java.io.*;
import java.net.*;
import java.awt.image.*;
/**
* @author Katrina Edgar
* @author David Bainbridge
*
* Data Structure to store an image once it is displayed in the applet.
* This structure remembers the images graphical representation, source url,
* name, width, height, position on the screen and whether or not it has
* previously been displayed. It provides methods to set the dimensions and
* position of the image. It also provides access methods to the translated
* x and y co-ordinates of the image */
public class CollageImage {
/** Decrements the alpha value between adjacent rows on the edge of an image
* Used to fade the edges faster than the rest of the image
* Only used when java2 parameter is true and advanced image processing
* techniques may be applied */
static final int FADEVALUE = 3;
/** Defines the shades of white used to colour the backgrounds of white images
* These colours are used because when several white images appear in the collage
* simultaneously it is difficult to distinguish their edges
* Only used when java2 parameter is true and advanced image processing
* techniques may be applied */
int colorMask [] = {0xF8FFFF, 0xFFF8FF, 0xFFFFF8, 0xF8F8FF, 0xFFF8F8, 0xF8FFF8, 0xFAFAFA};
/** Defines the number of shades of white used to colour the backgrounds of white images
* These colours are used because when several white images appear in the collage
* simultaneously it is difficult to distinguish their edges
* Only used when java2 parameter is true and advanced image processing
* techniques may be applied */
static final int NO_OF_COLORS = 7;
/** Will fire a removal operation when 4 images in the collage overlap a single area */
static final int NO_IMAGES_OF_OVERLAP = 4;
/** Source url of the image */
String from_url_ = null;
/** Name of the image */
String name_ = null;
/** Width of the image */
int image_x_dim_ = 0;
/** Height of the image */
int image_y_dim_ = 0;
/** Indicates whether or not the image has been drawn on the applet previously */
boolean fresh = true;
URL url_ = null;
/** Reflects the translation of the image from the origin to its position on the screen */
AffineTransform af_ = null;
/** Left x co-ordinate */
protected int xl_ = 0;
/** Top y co-ordinate */
protected int yt_ = 0;
/** Right x co-ordinate */
protected int xr_ = 0;
/** Bottom y co-ordinate */
protected int yb_ = 0;
/** Refers to applet */
GsdlCollageApplet app_ = null;
boolean isJava2_;
Image image_;
/** Constructs an CollageImage from the three specified parameters
*
* @param image The graphical representation of the image
* @param from_url The source url for the image
* @param name The file name of the image */
public CollageImage(GsdlCollageApplet app, boolean isJava2, DownloadImages.ImageUrlTriple iutriple)
{
image_ = iutriple.image();
from_url_ = iutriple.urlString();
name_ = iutriple.name();
url_ = iutriple.url();
isJava2_=isJava2;
app_=app;
process();
}
/** Sets the translation for the image from the origin
* And regenerate the image as the scaled version
*
* @param af The AffineTransform translation that has been calculated */
public void setAffineTransform(MyAffineTransform af, boolean is_java2_)
{
image_x_dim_ = (int) (image_x_dim_ * af.scaleX);
image_y_dim_ = (int) (image_y_dim_ * af.scaleY);
image_ = image_.getScaledInstance(image_x_dim_, image_y_dim_, Image.SCALE_DEFAULT);
if (is_java2_) {
af_ = new AffineTransform();
af_.translate(af.translateX, af.translateY);
af_.scale(1.0, 1.0);
calculate_rect();
}
else {
double trans_x = af.translateX;
double trans_y = af.translateY;
xl_ = Math.round((float)trans_x);
xr_ = Math.round((float)(trans_x + image_x_dim_)) -1;
yt_ = Math.round((float)trans_y);
yb_ = Math.round((float)(trans_y + image_y_dim_)) -1;
}
}
public void expand () {
// still expands a little too fast... how to fix this?
magnify(new Rectangle((int) (xl_ + (image_x_dim_ * 0.000001)),
(int) (yt_ + (image_y_dim_ * 0.000001)),
(int) (image_x_dim_ * 0.999998),
(int) (image_y_dim_ * 0.999998)));
}
public void magnify(Rectangle border) {
double magX = image_x_dim_/(double)border.width;
double magY = image_y_dim_/(double)border.height;
int x = border.x + border.width/2;
int y = border.y + border.height/2;
paintImage(x,y,magX, magY);
}
public void paintImage(int magCenterX, int magCenterY, double magX, double magY){
try {
//Point2D mgp = null;
//mgp = af_.inverseTransform((new Point(magCenterX, magCenterY)),(Point)mgp);
//double x = (mgp.getX()*magX)-mgp.getX();
//double y = (mgp.getY()*magY)-mgp.getY();
//scale(-x,-y, magX, magY);
}catch (Exception e) {System.out.println(e); }
}
public void scale(double magOffsetX, double magOffsetY, double magX, double magY){
af_.translate(magOffsetX,magOffsetY);
af_.scale(magX, magY);
}
/** Calculates the new image co-ordinates after translation has occurred */
protected void calculate_rect()
{
double trans_x = af_.getTranslateX();
double trans_y = af_.getTranslateY();
xl_ = Math.round((float)trans_x);
xr_ = Math.round((float)(trans_x + image_x_dim_)) -1;
yt_ = Math.round((float)trans_y);
yb_ = Math.round((float)(trans_y + image_y_dim_)) -1;
}
/** Determines whether a given co-ordinate is inside this image
*
* @param x The x co-ordinate
* @param y The y co-ordinate */
public boolean inside(int x, int y)
{
return ((x>=xl_) && (x<=xr_)) && ((y>=yt_) && (y<=yb_));
}
/** Gets the width of the translated image */
public double getX() {
return af_.getTranslateX();
}
/** Gets the height of the translated image */
public double getY() {
return af_.getTranslateY();
}
/** Fades and colours the image on a pixel-by-pixel basis
* First it grabs the pixels of the entire image and stores them in a 2D array.
* Then a bound is calculated to indicate the point from which edge
* fading should occur. The position of the bound will change in proportion
* to the age of the image so that older images have a larger faded edge.
* The area within the bounds, that forms the center of the image, is processed
* first and faded by a standard alpha value. Then each edge of the image
* is processed separately, creating a gradient fade from the true edge to the
* position of the bound.
* The faded pixel array is then turned into a new image a returned.
*
* @param img the image that requires processing
* @param x the x co-ordinate of the image
* @param y the y co-ordinate of the image
* @param w the width of the image
* @param h the height of the image
* @param p the position of the image in the applet (indicates age)
* @param fresh whether the image is being processed for the first time */
public int[] handlepixels(int p) {
int h = image_y_dim_;
int w = image_x_dim_;
// declare an array to hold the pixels
int[] pixels = new int[w * h];
// get the pixels of the image into the pixels array
PixelGrabber pg = new PixelGrabber(image_, 0, 0, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
System.err.println("interrupted waiting for pixels!");
}
// check for any failures
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
}
// calculates the bound from which fading should begin
double bound = p * 0.01;
if (w > h)
bound *= w;
else
bound *= h;
int upperboundheight = h - (int) bound;
int upperboundwidth = w - (int) bound;
int lowerbound = (int) bound;
// loop through every pixel in the picture and handle it
for (int j = lowerbound; j < upperboundheight; j++) {
for (int i = lowerbound; i < upperboundwidth; i++) {
// width and height: x+i y+j
pixels[j * w + i] = handlesinglepixel(pixels[j * w + i], p, fresh, false, 255);
}
}
int fade = 0;
int fader = 0;
int corealpha = (pixels[(h/2) * w + (w/2)] >> 24) & 0xff;
// top edge
for (int n = lowerbound; n >= 0; n--) {
fader = corealpha - fade;
if (fader < 0)
fader = 0;
for (int m = 0; m < w; m++){
int index = n* w + m;
if (index = 0; n--) {
fader = corealpha - fade;
if (fader < 0)
fader = 0;
for (int m = 0; m < h; m++) {
if ( m < lowerbound && n > m);
else if ( m > upperboundheight && n > (h - m));
else {
int index = m * w + n;
if (index m);
else if ( m > upperboundheight && (w - n) > (h - m));
else {
int index = m * w + n;
if (index
* If the image is being drawn for the first time, the RGB values are
* extracted. If the pixel is close to white (RGB > 250) then an offwhite
* colour is applied to this pixel.
* This is done because when several white images appear in the collage
* simultaneously it is difficult to distinguish their edges.
* This function also fades the alpha value of the pixel as the image ages.
* The alpha value is more heavily decremented as pixels get closer to the
* edge of the image
*
* @param pixel the pixel to manipulate
* @param p the position of the image in collage (representative of age)
* @param fresh indicates whether or not the image is being drawn for the first time
* @param edge indicates whether or not this pixel is near the edge of the image
* @param fade the amount by which to fade this pixel
* @return the adjusted pixel as an int */
public int handlesinglepixel(int pixel, int p, boolean fresh, boolean edge, int fade) {
int newpixel = 0;
//changes the colour of the picture, only when first drawn
//and only if the pixel is close to white
if (fresh) {
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel ) & 0xff;
if (red >= 250 && green >= 250 && blue >= 250) {
int c = colorMask[((int) (p%NO_OF_COLORS))];
red = (c >> 16) & 0xff;
green = (c >> 8) & 0xff;
blue = (c) & 0xff;
}
newpixel |= (red << 16) & 0x00ff0000;
newpixel |= (green << 8) & 0x0000ff00;
newpixel |= blue & 0x000000ff;
}
else {
newpixel |= pixel & 0x00ffffff;
}
int alpha = (pixel >> 24) & 0xff;
if (edge) {
// fade the edges more...
alpha = fade;
}
else if (alpha > 10 && !fresh) {
alpha -= 10;
}
newpixel |= (alpha << 24) & 0xff000000;
return (newpixel);
}
/** Resets the alpha channel of an image so that it appears solid
*
* @param img the image to restore
* @param x the x co-ordinate of the image
* @param y the y co-ordinate of the image
* @param w the width of the image
* @param h the height of the image */
public void restoreAlpha() {
int h = image_y_dim_;
int w = image_x_dim_;
// declare an array to hold the pixels
int[] pixels = new int[w * h];
// get the pixels of the image into the pixels array
PixelGrabber pg = new PixelGrabber(image_, 0, 0, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
System.err.println("interrupted waiting for pixels!");
}
// check for any failures
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
}
// loop through every pixel in the picture and handle it
for (int j = 0; j < h; j++) {
for (int i = 0; i < w; i++) {
pixels[j * w + i] |= (255 << 24) & 0xff000000;
}
}
// set the pixels of the whole picture to the pixels array
pg.setPixels(0, 0, w, h, pg.getColorModel(), pixels, 0, w);
image_ = app_.createImage(new MemoryImageSource(w, h, pixels, 0, w));
}
/** Checks whether an image has faded to the point where it must be removed
* from the collage.
*
* @param img the image to restore
* @param x the x co-ordinate of the image
* @param y the y co-ordinate of the image
* @param w the width of the image
* @param h the height of the image */
public boolean checkFaded (int[] pixels) {
int h = image_y_dim_;
int w = image_x_dim_;
// get the alpha value of the middle pixel of the image
int corealpha = (pixels[(h/2) * w + (w/2)] >> 24) & 0xff;
if (corealpha < 50)
return true;
return false;
}
public boolean isValid(){
image_x_dim_ = image_.getWidth(app_);
image_y_dim_ = image_.getHeight(app_);
return (image_x_dim_) >0 && ( image_x_dim_ >0);
}
public void process(){
// images x and y dimensions
image_x_dim_ = image_.getWidth(app_);
image_y_dim_ = image_.getHeight(app_);
if (( image_x_dim_ >0) && ( image_x_dim_ >0))
{
// places and sizes the image
MyAffineTransform af = new MyAffineTransform(image_x_dim_,image_y_dim_);
// sets location & size of collage image
setAffineTransform(af, isJava2_);
fresh = false;
if (isJava2_ ) {
restoreAlpha();
}
}
}
}