/*
* Copyright (C) Jerry Huxtable 1998
*/
package com.alkacon.simapi.filter;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.image.FilteredImageSource;
import java.awt.image.MemoryImageSource;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Morph extends JPanel implements PropertyChangeListener, ChangeListener, ActionListener, ItemListener {
private int rows = 7, cols = 7;
private WarpGridEditor editor1;
private WarpGridEditor editor2;
private WarpGridEditor editor3;
private WarpGrid warpGrid1;
private WarpGrid warpGrid2;
private WarpGrid warpGrid3;
private WarpFilter filter = new WarpFilter();
private Image sourceImage;
private Image destImage;
private JSlider tSlider;
private JCheckBox dissolveCheck;
private JCheckBox showGridCheck;
private JComboBox gridCombo;
private JButton animateButton;
private float t = 1.0f;
private boolean dissolveEm = true;
public Morph(String imageName1, String imageName2) {
JPanel panel = new JPanel();
setLayout(new BorderLayout());
panel.setLayout(new GridLayout(1, 3));
editor1 = new WarpGridEditor();
editor2 = new WarpGridEditor();
editor3 = new WarpGridEditor();
sourceImage = getToolkit().getImage(imageName1);
destImage = getToolkit().getImage(imageName2);
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(sourceImage, 0);
tracker.addImage(destImage, 0);
try {
tracker.waitForID(0);
} catch (InterruptedException ex) {}
warpGrid1 = new WarpGrid(rows, cols, sourceImage.getWidth(this), sourceImage.getHeight(this));
warpGrid2 = new WarpGrid(rows, cols, destImage.getWidth(this), destImage.getHeight(this));
warpGrid3 = new WarpGrid(rows, cols, sourceImage.getWidth(this), sourceImage.getHeight(this));
editor1.setBorder(new TitledBorder("Source"));
editor1.setImage(sourceImage);
editor1.setWarpGrid(warpGrid1);
editor1.addPropertyChangeListener(this);
editor2.setBorder(new TitledBorder("Destination"));
editor2.setImage(destImage);
editor2.setWarpGrid(warpGrid2);
editor2.addPropertyChangeListener(this);
editor3.setBorder(new TitledBorder("Intermediate"));
editor3.setImage(sourceImage);
editor3.setWarpGrid(warpGrid3);
editor3.setEnabled(false);
panel.add(editor1);
panel.add(editor2);
panel.add(editor3);
add(panel, BorderLayout.CENTER);
panel = new JPanel();
panel.add(tSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 0));
tSlider.setPaintTicks(true);
tSlider.setMajorTickSpacing(50);
tSlider.setMinorTickSpacing(10);
tSlider.setPaintLabels(true);
tSlider.setValue((int)(t * 100.0));
tSlider.addChangeListener(this);
panel.add(new JLabel("Grid Size:", SwingConstants.RIGHT));
panel.add(gridCombo = new JComboBox());
for (int i = 0; i < 12; i++) {
gridCombo.addItem((i + 3) + "x" + (i + 3));
}
gridCombo.setSelectedIndex(rows - 3);
gridCombo.addItemListener(this);
panel.add(dissolveCheck = new JCheckBox("Dissolve"));
dissolveCheck.setSelected(dissolveEm);
dissolveCheck.addChangeListener(this);
panel.add(showGridCheck = new JCheckBox("Show Grids"));
showGridCheck.setSelected(true);
showGridCheck.addChangeListener(this);
panel.add(animateButton = new JButton("Animate"));
animateButton.addActionListener(this);
add(panel, BorderLayout.SOUTH);
}
/*
public void setObject(Object o) {
super.setObject(o);
filter = (WarpFilter)o;
}
*/
@SuppressWarnings("deprecation")
public static void main(String[] args) {
if (args.length >= 2) {
Frame f = new Frame("Warp");
f.add(new Morph(args[0], args[1]));
f.pack();
f.show();
} else {
System.out.println("Usage: Morph image1 image2");
}
}
@SuppressWarnings("deprecation")
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == animateButton) {
String text = (String)JOptionPane.showInputDialog(
this,
"Number of Frames:",
"Animate",
JOptionPane.PLAIN_MESSAGE,
null,
null,
"10");
if (text != null) {
try {
int numFrames = Integer.parseInt(text);
WarpFilter wf = (WarpFilter)filter.clone();
wf.setSourceGrid(warpGrid1);
wf.setDestGrid(warpGrid2);
// wf.setFrames(numFrames);
// Image image = createImage(new FilteredImageSource(sourceImage.getSource(), wf));
int width = sourceImage.getWidth(this);
int height = sourceImage.getHeight(this);
Image image = createImage(numFrames * width, height);
Graphics g = image.getGraphics();
for (int i = 0; i < numFrames; i++) {
float t = (float)i / (numFrames - 1);
int[] srcPixels = filter.getPixels(sourceImage, width, height);
int[] destPixels = filter.getPixels(destImage, width, height);
int[] outPixels = new int[width * height];
filter.morph(srcPixels, destPixels, outPixels, warpGrid1, warpGrid2, width, height, t);
Image f = createImage(new MemoryImageSource(width, height, outPixels, 0, width));
g.drawImage(f, i * width, 0, this);
}
g.dispose();
Frame frame = new Frame();
ImageDisplay canvas = new ImageDisplay();
canvas.setImage(image);
frame.add(new JScrollPane(canvas), BorderLayout.CENTER);
frame.pack();
frame.show();
} catch (NumberFormatException ex) {
getToolkit().beep();
}
}
}
}
public void itemStateChanged(ItemEvent e) {
if (filter != null) {
Object source = e.getSource();
if (source == gridCombo) {
int i = gridCombo.getSelectedIndex();
setGridSize(i + 3, i + 3);
}
preview();
}
}
public void preview() {
if (dissolveEm) {
int width = sourceImage.getWidth(this);
int height = sourceImage.getHeight(this);
int[] srcPixels = filter.getPixels(sourceImage, width, height);
int[] destPixels = filter.getPixels(destImage, width, height);
int[] outPixels = new int[width * height];
filter.morph(srcPixels, destPixels, outPixels, warpGrid1, warpGrid2, width, height, t);
editor3.setImage(createImage(new MemoryImageSource(width, height, outPixels, 0, width)));
} else {
filter.setSourceGrid(warpGrid1);
warpGrid1.lerp(t, warpGrid2, warpGrid3);
filter.setDestGrid(warpGrid3);
editor3.setImage(createImage(new FilteredImageSource(sourceImage.getSource(), filter)));
}
}
public void propertyChange(PropertyChangeEvent e) {
preview();
}
public void setGridSize(int rows, int cols) {
warpGrid1 = new WarpGrid(rows, cols, sourceImage.getWidth(this), sourceImage.getHeight(this));
warpGrid2 = new WarpGrid(rows, cols, destImage.getWidth(this), destImage.getHeight(this));
warpGrid3 = new WarpGrid(rows, cols, sourceImage.getWidth(this), sourceImage.getHeight(this));
editor1.setWarpGrid(warpGrid1);
editor2.setWarpGrid(warpGrid2);
editor3.setWarpGrid(warpGrid3);
editor1.repaint();
editor2.repaint();
editor3.repaint();
preview();
}
public void stateChanged(ChangeEvent e) {
if (filter != null) {
Object source = e.getSource();
if ((source instanceof JSlider) && ((JSlider)source).getValueIsAdjusting()) {
return;
}
if (source == tSlider) {
t = tSlider.getValue() / 100.0f;
} else if (source == dissolveCheck) {
dissolveEm = dissolveCheck.isSelected();
} else if (source == showGridCheck) {
boolean b = showGridCheck.isSelected();
editor1.setShowGrid(b);
editor2.setShowGrid(b);
editor3.setShowGrid(b);
return;
}
preview();
}
}
}
class ImageDisplay extends JComponent {
private Image image;
public ImageDisplay() {
}
public Dimension getMinimumSize() {
return new Dimension(64, 64);
}
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(this), image.getHeight(this));
}
return new Dimension(164, 164);
}
public void paintComponent(Graphics g) {
if (image != null) {
Dimension size = getSize();
int x = (size.width - image.getWidth(this)) / 2;
int y = (size.height - image.getHeight(this)) / 2;
g.drawImage(image, x, y, this);
}
}
public void setImage(Image image) {
this.image = image;
repaint();
}
}
class WarpGridEditor extends JPanel {
class WarpGridCanvas extends JComponent {
private Graphics dragGraphics;
private int dragRow = -1;
private int dragCol = -1;
private int dragX = -1;
private int dragY = -1;
public WarpGridCanvas() {
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
}
public Dimension getMinimumSize() {
return new Dimension(64, 64);
}
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(this), image.getHeight(this));
}
return new Dimension(164, 164);
}
public void paintComponent(Graphics g) {
Dimension size = getSize();
if (image != null) {
g.drawImage(image, 0, 0, this);
}
if (showGrid) {
g.setColor(Color.yellow);
int index = 0;
for (int row = 0; row < warpGrid.rows; row++) {
for (int col = 0; col < warpGrid.cols; col++) {
int x = (int)warpGrid.xGrid[index];
int y = (int)warpGrid.yGrid[index];
if (row > 0) {
g.drawLine(x, y, (int)warpGrid.xGrid[index - warpGrid.cols], (int)warpGrid.yGrid[index
- warpGrid.cols]);
}
if (col > 0) {
g.drawLine(x, y, (int)warpGrid.xGrid[index - 1], (int)warpGrid.yGrid[index - 1]);
}
g.fillOval(x - 2, y - 2, 5, 5);
index++;
}
}
}
}
protected void processMouseEvent(MouseEvent e) {
if (!isEnabled() || (warpGrid == null)) {
return;
}
Dimension size = getSize();
int id = e.getID();
int x = e.getX();
int y = e.getY();
switch (id) {
case MouseEvent.MOUSE_PRESSED:
dragRow = -1;
int index = 0;
for (int row = 0; row < warpGrid.rows; row++) {
for (int col = 0; col < warpGrid.cols; col++) {
int wx = (int)warpGrid.xGrid[index];
int wy = (int)warpGrid.yGrid[index];
if (((wx - 2) <= x) && (x <= (wx + 2)) && ((wy - 2) <= y) && (y <= (wy + 2))) {
dragX = x;
dragY = y;
dragRow = row;
dragCol = col;
dragGraphics = getGraphics();
dragGraphics.setXORMode(getBackground());
dragGraphics.fillOval(dragX - 2, dragY - 2, 5, 5);
enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
return;
}
index++;
}
}
break;
case MouseEvent.MOUSE_RELEASED:
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
if (dragRow != -1) {
dragGraphics.fillOval(dragX - 2, dragY - 2, 5, 5);
dragGraphics.dispose();
dragGraphics = null;
index = (dragRow * warpGrid.cols) + dragCol;
warpGrid.xGrid[index] = ImageMath.clamp(x, 0, image.getWidth(this));
warpGrid.yGrid[index] = ImageMath.clamp(y, 0, image.getHeight(this));
repaint();
gridChanged();
}
dragRow = dragCol = -1;
break;
}
super.processMouseEvent(e);
}
protected void processMouseMotionEvent(MouseEvent e) {
int id = e.getID();
int x = e.getX();
int y = e.getY();
switch (id) {
case MouseEvent.MOUSE_DRAGGED:
if (dragRow != -1) {
dragGraphics.fillOval(dragX - 2, dragY - 2, 5, 5);
dragX = ImageMath.clamp(x, 0, image.getWidth(this));
dragY = ImageMath.clamp(y, 0, image.getHeight(this));
dragGraphics.fillOval(dragX - 2, dragY - 2, 5, 5);
}
break;
}
super.processMouseMotionEvent(e);
}
}
private Image image;
private WarpGrid warpGrid;
private boolean showGrid = true;
public WarpGridEditor() {
JScrollPane scrollPane;
setLayout(new BorderLayout());
WarpGridCanvas canvas = new WarpGridCanvas();
add(scrollPane = new JScrollPane(canvas), BorderLayout.CENTER);
}
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public boolean getShowGrid() {
return showGrid;
}
public WarpGrid getWarpGrid() {
return warpGrid;
}
public void setImage(Image image) {
this.image = image;
repaint();
}
public void setShowGrid(boolean showGrid) {
this.showGrid = showGrid;
repaint();
}
public void setWarpGrid(WarpGrid warpGrid) {
this.warpGrid = warpGrid;
}
private void gridChanged() {
firePropertyChange("warpGrid", null, warpGrid);
}
}