//
// WaveletGen.java
//
// Simple 2D Wavelet Transform Generator
//
// Copyright (c) 1997, Benjamin Nason Lipchak
//
import java.applet.Applet;
import java.net.*;
import java.awt.*;
import java.awt.image.*;
import benj.awt.image.*;
public class WaveletGen extends Applet implements Runnable {
TextField imageURLField;
Label imageURLLabel;
Label errorMessage;
ImageCanvas origCanvas;
Image origImage;
ImageCanvas waveletCanvas;
Image waveletImage;
String inputURL;
Image animation[];
AnimationCanvas animCanvas;
Thread workThread;
boolean processNewImage = false;
Object processNewImageSemaphore;
public void init() {
processNewImageSemaphore = new Object();
imageURLField = new TextField("http://");
imageURLLabel = new Label("image URL: ", Label.RIGHT);
errorMessage = new Label("", Label.LEFT);
origCanvas = new ImageCanvas(this, 256, 256);
waveletCanvas = new ImageCanvas(this, 256, 256);
animCanvas = new AnimationCanvas(this, 256, 256);
GridBagLayout myGridBag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(myGridBag);
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(10, 10, 0, 0);
c.gridx = 0;
c.gridwidth = 1;
myGridBag.setConstraints(imageURLLabel, c);
add(imageURLLabel);
c.insets = new Insets(10, 0, 0, 10);
c.gridx = 1;
c.gridwidth = GridBagConstraints.REMAINDER;
c.weightx = 1.0;
myGridBag.setConstraints(imageURLField, c);
add(imageURLField);
c.insets = new Insets(0, 0, 5, 10);
myGridBag.setConstraints(errorMessage, c);
add(errorMessage);
c.gridwidth = 2;
c.gridx = 0;
c.insets = new Insets(5, 10, 5, 5);
myGridBag.setConstraints(origCanvas, c);
add(origCanvas);
c.gridx = 2;
c.insets = new Insets(5, 5, 5, 10);
myGridBag.setConstraints(waveletCanvas, c);
add(waveletCanvas);
c.gridwidth = GridBagConstraints.REMAINDER;
c.gridx = 1;
c.insets = new Insets(5, 50, 10, 50);
myGridBag.setConstraints(animCanvas, c);
add(animCanvas);
validate();
}
public void start() {
if (workThread == null) {
workThread = new Thread(this, "Work Thread");
workThread.start();
}
}
public void run() {
while (workThread != null) {
synchronized (processNewImageSemaphore) {
while (processNewImage == false) {
try {
processNewImageSemaphore.wait();
} catch (InterruptedException e) {
}
}
}
origCanvas.setImage(null);
waveletCanvas.setImage(null);
//Track image loading. Indicate failure.
Image sourceImage;
try {
sourceImage = getImage(new URL(inputURL));
} catch (MalformedURLException e) {
errorMessage.setText("ERROR: Bad URL!");
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
continue;
}
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(sourceImage, 0);
boolean doneWaiting = false;
while (!doneWaiting) {
try {
tracker.waitForAll();
doneWaiting = true;
} catch (InterruptedException e) {
continue;
}
}
if (tracker.isErrorAny()) {
errorMessage.setText("ERROR: Unable to load image!");
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
continue;
}
//Crop if necessary
int imageWidth = sourceImage.getWidth(this);
int imageHeight = sourceImage.getHeight(this);
if (imageWidth <= 0 || imageHeight <= 0) {
errorMessage.setText("ERROR: Bad image!");
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
continue;
}
int newWidth = nextLowerPowerOfTwo(imageWidth);
int newHeight = nextLowerPowerOfTwo(imageHeight);
if (newWidth > newHeight)
newWidth = newHeight;
else if (newHeight > newWidth)
newHeight = newWidth;
if (imageWidth != newWidth || imageHeight != newHeight) {
errorMessage.setText("Cropping original image...");
ImageFilter filter = new CropImageFilter(0, 0, newWidth, newHeight);
ImageProducer producer = new FilteredImageSource(sourceImage.getSource(), filter);
origImage = createImage(producer);
tracker.addImage(origImage, 0);
doneWaiting = false;
while (!doneWaiting) {
try {
tracker.waitForAll();
doneWaiting = true;
} catch (InterruptedException e) {
continue;
}
}
if (tracker.isErrorAny()) {
errorMessage.setText("ERROR: Unable to crop image!");
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
continue;
}
} else {
origImage = sourceImage;
}
origCanvas.setImage(origImage);
errorMessage.setText("Generating wavelet transform...");
ImageFilter filter = new WaveletTransformFilter();
ImageProducer producer = new FilteredImageSource(origImage.getSource(), filter);
waveletImage = createImage(producer);
tracker.addImage(waveletImage, 0);
doneWaiting = false;
while (!doneWaiting) {
try {
tracker.waitForAll();
doneWaiting = true;
} catch (InterruptedException e) {
continue;
}
}
if (tracker.isErrorAny()) {
errorMessage.setText("ERROR: Unable to transform image!");
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
continue;
}
waveletCanvas.setImage(waveletImage);
//Generate frames for animation
errorMessage.setText("Generating wavelet subimages...");
int n = logBase2(newWidth);
animation = new Image[n + 1];
for (int j = 0; j <= n; j++) {
filter = new WaveletTransformFilter(j);
producer = new FilteredImageSource(origImage.getSource(), filter);
animation[j] = createImage(producer);
tracker.addImage(animation[j], 0);
}
doneWaiting = false;
while (!doneWaiting) {
try {
tracker.waitForAll();
doneWaiting = true;
} catch (InterruptedException e) {
continue;
}
}
if (tracker.isErrorAny()) {
errorMessage.setText("ERROR: Unable to generate animation!");
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
continue;
}
animCanvas.setAnim(animation, n + 1);
//Ready for another
synchronized (processNewImageSemaphore) {
processNewImage = false;
processNewImageSemaphore.notify();
}
errorMessage.setText("Done.");
}
}
public boolean action(Event event, Object what) {
if (event.target == imageURLField) {
synchronized (processNewImageSemaphore) {
if (processNewImage)
return true;
}
errorMessage.setText("Loading image...");
inputURL = imageURLField.getText();
synchronized (processNewImageSemaphore) {
processNewImage = true;
processNewImageSemaphore.notify();
}
return true;
}
return false;
}
int pow2(int degree) {
if (degree < 0)
return -1;
int result = 1;
while (degree > 0) {
result *= 2;
degree--;
}
return result;
}
int logBase2(int number) {
int result = 0;
while (pow2(result) != number) {
result++;
if (result >= number)
return -1;
}
return result;
}
int nextLowerPowerOfTwo(int i) {
int a = 1, b = 2;
if (i < 1)
return 0;
while (i > b) {
a += a;
b += b;
}
return a;
}
}
class ImageCanvas extends Canvas {
Container pappy;
Image image = null;
Dimension minSize;
int w, h;
public ImageCanvas(Container parent,
int initialWidth, int initialHeight)
{
pappy = parent;
w = initialWidth;
h = initialHeight;
minSize = new Dimension(w, h);
}
public synchronized void setImage(Image image) {
this.image = image;
repaint();
}
public Dimension preferredSize() {
return minimumSize();
}
public Dimension minimumSize() {
return minSize;
}
public void paint(Graphics g) {
update(g);
}
public void update(Graphics g) {
if (image == null) {
g.setColor(Color.black);
g.drawRect(0, 0, w - 1, h - 1);
} else {
g.drawImage(image, 0, 0, w, h, this);
}
}
}
class AnimationCanvas extends Canvas implements Runnable {
Container pappy;
Image[] animation = null;
int numImages;
Dimension minSize;
int w, h;
int frameNumber = 0;
int delay = 200;
Thread animatorThread;
Dimension offDimension;
Image offImage;
Graphics offGraphics;
public AnimationCanvas(Container parent,
int initialWidth, int initialHeight)
{
pappy = parent;
w = initialWidth;
h = initialHeight;
minSize = new Dimension(w, h);
animatorThread = new Thread(this);
animatorThread.start();
}
public synchronized void setAnim(Image[] animation, int n) {
this.animation = animation;
numImages = n;
}
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
long startTime = System.currentTimeMillis();
boolean goingUp = true;
while (Thread.currentThread() == animatorThread) {
if (animation != null) {
if (goingUp) {
frameNumber++;
if (frameNumber >= (numImages - 1)) {
goingUp = false;
frameNumber = numImages - 1;
}
} else {
frameNumber--;
if (frameNumber <= 0) {
goingUp = true;
frameNumber = 0;
}
}
repaint();
}
try {
startTime += delay;
Thread.sleep(Math.max(0, startTime - System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}
public Dimension preferredSize() {
return minimumSize();
}
public Dimension minimumSize() {
return minSize;
}
public void paint(Graphics g) {
update(g);
}
public void update(Graphics g) {
if (animation == null) {
g.setColor(Color.black);
g.drawRect(0, 0, w - 1, h - 1);
} else {
Dimension d = size();
if ((offGraphics == null) ||
(d.width != offDimension.width) ||
(d.height != offDimension.height))
{
offDimension = d;
offImage = createImage(d.width, d.height);
offGraphics = offImage.getGraphics();
}
offGraphics.setColor(getBackground());
offGraphics.fillRect(0, 0, d.width, d.height);
offGraphics.setColor(Color.black);
offGraphics.drawImage(animation[frameNumber], 0, 0, w, h, this);
g.drawImage(offImage, 0, 0, d.width, d.height, this);
}
}
}