/*
 * @(#)ImageTest.java	1.8 96/02/27
 *
 * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
 * without fee is hereby granted. 
 * Please refer to the file http://java.sun.com/copy_trademarks.html
 * for further important copyright and trademark information and to
 * http://java.sun.com/licensing.html for further important licensing
 * information for the Java (tm) Technology.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 * 
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  SUN
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.Applet;

public class ImageTest extends Applet {
    public void init() {
	setLayout(new BorderLayout());
	add("Center", new ImagePanel(this));
	add("North", new ImageHelp());
	//show();
    }
}

class ImageHelp extends Panel {
    public ImageHelp() {
	setLayout(new GridLayout(5, 1));
	add(new Label("Move the images with the arrow keys",
		      Label.CENTER));
	add(new Label("Resize the images with PgUp/PgDn",
		      Label.CENTER));
	add(new Label("Toggle a color filter with the Home key",
		      Label.CENTER));
	add(new Label("Change the alpha with shift-PgUp/PgDn",
		      Label.CENTER));
	add(new Label("Rotate the image with shift-Right/Left arrow",
		      Label.CENTER));
    }
}

class ImagePanel extends Panel {
    Applet applet;

    public ImagePanel(Applet app) {
	applet = app;
	setLayout(new BorderLayout());
	Panel grid = new Panel();
	grid.setLayout(new GridLayout(0, 2));
	add("Center", grid);
	grid.add(new ImageCanvas(applet, makeDitherImage(), 0.5));
	Image joe = applet.getImage(applet.getDocumentBase(),
				    "graphics/joe_surf_yellow_small.gif");
	grid.add(new ImageCanvas(applet, joe, 1.0));
	setSize(new Dimension(20, 20));
    }

    Image makeDitherImage() {
	int w = 100;
	int h = 100;
	int pix[] = new int[w * h];
	int index = 0;
	for (int y = 0; y < h; y++) {
	    int red = (y * 255) / (h - 1);
	    for (int x = 0; x < w; x++) {
		int blue = (x * 255) / (w - 1);
		pix[index++] = (255 << 24) | (red << 16) | blue;
	    }
	}
	return applet.createImage(new MemoryImageSource(w, h, pix, 0, w));
    }
}

class ImageCanvas extends Canvas
   implements ImageObserver, MouseListener, KeyListener, MouseMotionListener, FocusListener
{
    double 	hmult = 0;
    int		xadd = 0;
    int		yadd = 0;
    int		xprev = 0;
    int		yprev = 0;
    int		imgw = -1;
    int		imgh = -1;
    int		xoff = 0;
    int		yoff = 0;
    int		scalew = -1;
    int		scaleh = -1;
    boolean	focus = false;
    boolean	usefilter = false;
    static final int numalphas = 9;
    int		alpha = numalphas - 1;
    static final int numrotations = 8;
    int		rotation = 0;
    ImageFilter colorfilter;
    ImageFilter alphafilters[] = new ImageFilter[numalphas];
    RotateFilter rotfilters[] = new RotateFilter[numrotations];
    Image	origimage;
    Image	curimage;
    Applet	applet;

    public ImageCanvas(Applet app, Image img, double mult) {
	applet = app;
	origimage = img;
	hmult = mult;
	pickImage();
	setSize(new Dimension(100, 100));
	addMouseListener(this);
	addKeyListener(this);
	addMouseMotionListener(this);
	addFocusListener(this);
    }

	public void focusGained(FocusEvent e)
	{
		focus = true;
		repaint();
	}

	public void focusLost(FocusEvent e)
	{
		focus = false;
		repaint();
	}

	
    public void paint(Graphics g) {
	Rectangle r = getBounds();
	int hlines = r.height / 10;
	int vlines = r.width / 10;

	if (focus) {
	    g.setColor(Color.red);
	} else {
	    g.setColor(Color.darkGray);
	}
	g.drawRect(0, 0, r.width-1, r.height-1);
	g.drawLine(0, 0, r.width, r.height);
	g.drawLine(r.width, 0, 0, r.height);
	g.drawLine(0, r.height / 2, r.width, r.height / 2);
	g.drawLine(r.width / 2, 0, r.width / 2, r.height);
	if (imgw < 0) {
	    imgw = curimage.getWidth(this);
	    imgh = curimage.getHeight(this);
	    if (imgw < 0 || imgh < 0) {
		return;
	    }
	}
	if (scalew < 0) {
	    if (rotation == 0) {
		scalew = imgw;
		scaleh = imgh;
	    } else {
		Rectangle rect = new Rectangle(0, 0, imgw, imgh);
		rotfilters[rotation].transformBBox(rect);
		xoff = rect.x;
		yoff = rect.y;
		scalew = rect.width;
		scaleh = rect.height;
	    }
	    scalew = (int) (scalew * hmult);
	    scaleh = (int) (scaleh * hmult);
	    xoff = (imgw - scalew) / 2;
	    yoff = (imgh - scaleh) / 2;
	}
	if (imgw != scalew || imgh != scaleh) {
	    g.drawImage(curimage, xadd + xoff, yadd + yoff,
			scalew, scaleh, this);
	} else {
	    g.drawImage(curimage, xadd + xoff, yadd + yoff, this);
	}
    }

    static final long updateRate = 100;

    public synchronized boolean imageUpdate(Image img, int infoflags,
					    int x, int y, int w, int h) {
	if (img != curimage) {
	    return false;
	}
	boolean ret = true;
	boolean dopaint = false;
	long updatetime = 0;
	if ((infoflags & WIDTH) != 0) {
	    imgw = w;
	    dopaint = true;
	}
	if ((infoflags & HEIGHT) != 0) {
	    imgh = h;
	    dopaint = true;
	}
	if ((infoflags & (FRAMEBITS | ALLBITS)) != 0) {
	    dopaint = true;
	    ret = false;
	} else if ((infoflags & SOMEBITS) != 0) {
	    dopaint = true;
	    updatetime = updateRate;
	}
	if ((infoflags & ERROR) != 0) {
	    ret = false;
	}
	if (dopaint) {
	    repaint(updatetime);
	}
	return ret;
    }

    public synchronized Image pickImage() {
	ImageProducer src = origimage.getSource();
	if (alpha != numalphas - 1) {
	    ImageFilter imgf = alphafilters[alpha];
	    if (imgf == null) {
		int alphaval = (alpha * 255) / (numalphas - 1);
		imgf = new AlphaFilter(alphaval);
		alphafilters[alpha] = imgf;
	    }
	    src = new FilteredImageSource(src, imgf);
	}
	if (rotation != 0) {
	    RotateFilter imgf = rotfilters[rotation];
	    if (imgf == null) {
		double angle = (2 * Math.PI * rotation) / numrotations;
		imgf = new RotateFilter(angle);
		rotfilters[rotation] = imgf;
	    }
	    src = new FilteredImageSource(src, imgf);
	}
	if (usefilter) {
	    if (colorfilter == null) {
		colorfilter = new RedBlueSwapFilter();
	    }
	    src = new FilteredImageSource(src, colorfilter);
	}
	Image choice;
	if (src == origimage.getSource()) {
	    choice = origimage;
	} else {
	    choice = applet.createImage(src);
	}
	if (curimage != choice) {
	    if (curimage != null && curimage != origimage) {
		curimage.flush();
	    }
	    curimage = choice;
	}
	return choice;
    }

	/** Handle the key typed event from the text field. */
	public void keyTyped(KeyEvent e) {
	}

	public void keyPressed(KeyEvent e) {
		int key = e.getKeyCode();
		switch (key) {
			case KeyEvent.VK_HOME:
				usefilter = !usefilter;
				pickImage();
				repaint();
				break;
			case KeyEvent.VK_UP:
				yadd -= 5;
				repaint();
				return;
			case KeyEvent.VK_DOWN:
				yadd += 5;
				repaint();
				break;
			case KeyEvent.VK_RIGHT:
			case 'r':
				if (e.isShiftDown()) {
					rotation--;
					if (rotation < 0) {
						rotation = numrotations - 1;
					}
					pickImage();
					scalew = scaleh = -1;
				} else {
					xadd += 5;
				}
				repaint();
				break;
			case KeyEvent.VK_LEFT:
				if (e.isShiftDown()) {
					rotation++;
					if (rotation >= numrotations) {
						rotation = 0;
					}
					pickImage();
					scalew = scaleh = -1;
				} else {
					xadd -= 5;
				}
				repaint();
				break;
			case KeyEvent.VK_PAGE_UP:
				if (e.isShiftDown()) {
					if (++alpha > numalphas - 1) {
						alpha = numalphas - 1;
					}
					pickImage();
				} else {
					hmult *= 1.2;
					scalew = scaleh = -1;
				}
				repaint();
				break;
			case KeyEvent.VK_PAGE_DOWN:
				if (e.isShiftDown()) {
					if (--alpha < 0) {
						alpha = 0;
					}
					pickImage();
				} else {
					hmult /= 1.2;
					scalew = scaleh = -1;
				}
				repaint();
				break;
			}
	}

	public void keyReleased(KeyEvent e) {
	}

	/*
     * Mouse methods
     */
	public void mouseDragged(MouseEvent e) {
		int x = e.getX();
		int y = e.getY();
		xadd += x - xprev;
		yadd += y - yprev;
		xprev = x;
		yprev = y;
		repaint();
	}

	public void mouseMoved(MouseEvent e) {
	}

	public void mousePressed(MouseEvent e) {
		xprev = e.getX();
		yprev = e.getY();
	}

	public void mouseReleased(MouseEvent e) {
	}

	public void mouseEntered(MouseEvent e) {
		// get keyboard focus
		requestFocus();
	}

	public void mouseExited(MouseEvent e) {
	}

	public void mouseClicked(MouseEvent e) {
	}
}


class RedBlueSwapFilter extends RGBImageFilter {
    public RedBlueSwapFilter() {
	canFilterIndexColorModel = true;
    }

    public void setColorModel(ColorModel model) {
	if (model instanceof DirectColorModel) {
	    DirectColorModel dcm = (DirectColorModel) model;
	    int rm = dcm.getRedMask();
	    int gm = dcm.getGreenMask();
	    int bm = dcm.getBlueMask();
	    int am = dcm.getAlphaMask();
	    int bits = dcm.getPixelSize();
	    dcm = new DirectColorModel(bits, bm, gm, rm, am);
	    substituteColorModel(model, dcm);
	    consumer.setColorModel(dcm);
	} else {
	    super.setColorModel(model);
	}
    }

    public int filterRGB(int x, int y, int rgb) {
	return ((rgb & 0xff00ff00)
		| ((rgb & 0xff0000) >> 16)
		| ((rgb & 0xff) << 16));
    }
}

class AlphaFilter extends RGBImageFilter {
    ColorModel origmodel;
    ColorModel newmodel;

    int alphaval;

    public AlphaFilter(int alpha) {
	alphaval = alpha;
	canFilterIndexColorModel = true;
    }

    public int filterRGB(int x, int y, int rgb) {
	int alpha = (rgb >> 24) & 0xff;
	alpha = alpha * alphaval / 255;
	return ((rgb & 0x00ffffff) | (alpha << 24));
    }
}
