ImageDicer.java


//Comment out the following package statement to compile separately.
//package com.javaworld.media.j2d;

/**
 * ImageDicer provides a grab-bag of image processing operators.
 * This version is compliant with Java 1.2 Beta 4, Aug 1998.
 * Please refer to: 
 * http://www.javaworld.com/javaworld/jw-09-1998/jw-09-media.html
 * 
 * @author Jonathan Knudsen <jonathan@oreilly.com>
 * @author Bill Day <bill.day@javaworld.com>
 * @version 1.0
 * @see java.awt.image.BufferedImage
 * @see java.awt.image.BufferedImageOp
 * @see java.awt.image.ConvolveOp
 * @see java.awt.image.LookupOp
 * @see java.awt.image.ThresholdOp
**/

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;

public class ImageDicer
    extends Frame {
  /**
   * Instantiates an ImageDicer with an image file name.
   * If no image file name is specified on the command line,
   * "default" is used.
  **/
  public static void main(String[] args) {
    String fileName = "penquin2.jpg";
    if (args.length > 0) fileName = args[0];
    new ImageDicer(fileName);
  }
  
  /**
   * kBanner holds the application title which is used
   * in the window title.
  **/
  private static final String kBanner = "ImageDicer v1.0";
  
  /**
   * ImageDicer's constructor creates a set of image
   * processing operations, creates user controls, and loads
   * the named image.
  **/
  public ImageDicer(String fileName) {
    super(kBanner);
    createOps();
    createUI();
    loadImage(fileName);
    setVisible(true);
  }

  /**
   * A Hashtable member variable holds the image processing
   * operations, keyed by their names.
  **/
  private Hashtable mOps;
  
  /**
   * The createOps() method creates the image processing
   * operations discussed in the column.
  **/
  private void createOps() {
    // Create a brand new Hashtable to hold the operations.
    mOps = new Hashtable();
    
    // Blurring
    float ninth = 1.0f / 9.0f;
    float[] blurKernel = {
        ninth, ninth, ninth,
        ninth, ninth, ninth,
        ninth, ninth, ninth,
    };
    mOps.put("Blur", new ConvolveOp(
        new Kernel(3, 3, blurKernel)));

    // Edge detection
    float[] edgeKernel = {
        0.0f, -1.0f, 0.0f,
        -1.0f, 4.0f, -1.0f,
        0.0f, -1.0f, 0.0f
    };
    mOps.put("Edge detector", new ConvolveOp(
        new Kernel(3, 3, edgeKernel)));

    // Sharpening
    float[] sharpKernel = {
        0.0f, -1.0f, 0.0f,
        -1.0f, 5.0f, -1.0f,
        0.0f, -1.0f, 0.0f
    };
    mOps.put("Sharpen", new ConvolveOp(
        new Kernel(3, 3, sharpKernel),
        ConvolveOp.EDGE_NO_OP, null));

    // Lookup table operations: posterizing and inversion.
    short[] posterize = new short[256];
    short[] invert = new short[256];
    short[] straight = new short[256];
    for (int i = 0; i < 256; i++) {
      posterize[i] = (short)(i - (i % 32));
      invert[i] = (short)(255 - i);
      straight[i] = (short)i;
    }
    
    mOps.put("Posterize", new LookupOp(new ShortLookupTable(0, posterize),null));
    mOps.put("Invert", new LookupOp(new ShortLookupTable(0, invert), null));
    short[][] blueInvert = new short[][] { straight, straight, invert };
    mOps.put("Invert blue", new LookupOp(new ShortLookupTable(0, blueInvert), null));
    
    // Thresholding
    mOps.put("Threshold 192", createThresholdOp(192, 0, 255));
    mOps.put("Threshold 128", createThresholdOp(128, 0, 255));
    mOps.put("Threshold 64", createThresholdOp(64, 0, 255));
  }
  

  /**
   * createThresholdOp() uses a LookupOp to simulate a
   * thresholding operation.
  **/
  private BufferedImageOp createThresholdOp(int threshold,
    int minimum, int maximum) {
    short[] thresholdArray = new short[256];
    for (int i = 0; i < 256; i++) {
      if (i < threshold)
        thresholdArray[i] = (short)minimum;
      else
        thresholdArray[i] = (short)maximum;
      }
    return new LookupOp(new ShortLookupTable(0, thresholdArray), null);
  }

  /**
   * A member variable, mControlPanel, keeps track of the panel
   * that contains the user controls. This panel's size has to
   * be accounted for later when we size the window to fit the
   * image.
  **/
  private Panel mControlPanel;
  
  /**
   * createUI() creates the user controls and lays out the window.
   * It also creates the event handlers (as inner classes) for
   * the user controls.
  **/
  private void createUI() {
    setFont(new Font("Serif", Font.PLAIN, 12));
    // Use a BorderLayout. The image will occupy most of the window,
    //   with the user controls at the bottom.
    setLayout(new BorderLayout());
  
    // Use a Label to display status messages to the user.
    final Label statusLabel = new Label("Welcome to " + kBanner + ".");
    
    // Create a list of operations.
    final Choice processChoice = new Choice();
    Enumeration e = mOps.keys();
    // Add all the operation names from the Hashtable.
    while (e.hasMoreElements())
      processChoice.add((String)e.nextElement());
    // Add an event listener. This is where the image processing
    //   actually occurs.
    processChoice.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent ie) {
        if (ie.getStateChange() != ItemEvent.SELECTED) return;
        String key = processChoice.getSelectedItem();
        statusLabel.setText("Working...");
        BufferedImageOp op = (BufferedImageOp)mOps.get(key);
        mBufferedImage = op.filter(mBufferedImage, null);
        statusLabel.setText("");
        repaint();
      }
    });
    
    // Create a Button for loading a new image.
    Button loadButton = new Button("Load...");
    // Add a listener for the button. It pops up a file dialog
    //   and loads the selected image file.
    loadButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        FileDialog fd = new FileDialog(ImageDicer.this);
        fd.setVisible(true);
        if (fd.getFile() == null) return;
        String path = fd.getDirectory() + fd.getFile();
        loadImage(path);
      }
    });

    // Add the user controls at the bottom of the window.
    mControlPanel = new Panel();
    mControlPanel.add(loadButton);
    mControlPanel.add(processChoice);
    mControlPanel.add(statusLabel);
    add(mControlPanel, BorderLayout.SOUTH);

    // Terminate the application if the user closes the window.
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        dispose();
        System.exit(0);
      }
    });
  }
  
  /**
   * When an image is loaded, the window is resized to
   * accomodate the image size and the user controls.
  **/
  private void adjustToImageSize() {
    if (!isDisplayable()) addNotify(); // Do this to get valid Insets.
    Insets insets = getInsets();
    int w = mBufferedImage.getWidth() + insets.left + insets.right;
    int h = mBufferedImage.getHeight() + insets.top + insets.bottom;
    h += mControlPanel.getPreferredSize().height;
    setSize(w, h);
  }
  
  /**
   * Center this window in the user's desktop.
  **/
  private void center() {
    Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension d = getSize();
    int x = (screen.width - d.width) / 2;
    int y = (screen.height - d.height) / 2;
    setLocation(x, y);
  }

  /**
   * This member variable holds the currently displayed image.
  **/
  private BufferedImage mBufferedImage;
  
  /**
   * loadImage() loads an image, using a MediaTracker to
   * ensure that the image data is fully loaded. Then it
   * converts the image to a BufferedImage. Finally, it
   * adjusts the window size and placement based on the
   * new image size.
  **/
  private void loadImage(String fileName) {
    // Use a MediaTracker to fully load the image.
    Image image = Toolkit.getDefaultToolkit().getImage(fileName);
    MediaTracker mt = new MediaTracker(this);
    mt.addImage(image, 0);
    try { mt.waitForID(0); }
    catch (InterruptedException ie) { return; }
    if (mt.isErrorID(0)) return;
    // Make a BufferedImage from the Image.
    mBufferedImage = new BufferedImage(
        image.getWidth(null), image.getHeight(null),
        BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = mBufferedImage.createGraphics();
    g2.drawImage(image, null, null);

    adjustToImageSize();
    center();
    validate();
    repaint();
    setTitle(kBanner + ": " + fileName);
  }
  
  /**
   * All paint() has to do is show the current image.
  **/
  public void paint(Graphics g) {
    if (mBufferedImage == null) return;
    Insets insets = getInsets();
    g.drawImage(mBufferedImage, insets.left, insets.top, null);
  }
}


Results



Maintained by John Loomis, updated Thu Nov 29 12:44:22 2012