Catching events while an image is being read using ImageIO

Add an IIOReadProgressListener to your reader.

   IIOReadProgressListener listener = new MyReadProgressListener();
   reader.addIIOReadProgressListener(listener);

It’ll throw events while the reading process is going on. The following example shows a JProgressBar while reading the image fruit.png, to be used as background.

Main.java:

import javax.imageio.metadata.*; 
import javax.imageio.stream.*;
import javax.imageio.event.*;
import javax.imageio.*;
 
import javax.swing.event.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*; 
import java.io.*;
  
public class Main extends JFrame
{
   JProgressBar progressBar;
   BufferedImage background = null;
  
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      progressBar = new JProgressBar(0, 100);
 
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.NORTH, progressBar);
    }
 
   public void loadBackgroundImage(String filename) throws Exception {
      Iterator readers = ImageIO.getImageReadersByFormatName("png");
      ImageReader reader = (ImageReader) readers.next();
 
      IIOReadProgressListener listener = new MyReadProgressListener();
      reader.addIIOReadProgressListener(listener);
 
      ImageInputStream iis = ImageIO.createImageInputStream(new File(filename));
      reader.setInput(iis, true);
      background = reader.read(0);
   }
 
   public static void main(String []args) throws Exception {
      Main main = new Main();
      main.setSize(400, 150);
      main.setVisible(true);
  
      main.loadBackgroundImage("fruit.png");
   }
 
   public void paint(Graphics g) {
      System.out.println(background);
      if (background != null) {
         g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
      }
   }
 
   class MyReadProgressListener implements IIOReadProgressListener {
 
	public MyReadProgressListener() {}
 
        public void imageProgress(ImageReader source, final float percentageDone) {
           System.out.println("imageProgress: " + percentageDone);
 
           SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                 progressBar.setValue((int) percentageDone);
              }
           });   
        }
 
        public void imageComplete(ImageReader source) {
           SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                 try {
                    // wait for a bit until bufferedimage is assigned by reader
                    Thread.sleep(50);
                 }
                 catch(InterruptedException e) { }
                 repaint();
              }
           });              
        }
 
        public void imageStarted(ImageReader source, int imageIndex)  { }
        public void readAborted(ImageReader source)                   { }
        public void sequenceComplete(ImageReader source)              { }
        public void sequenceStarted(ImageReader source, int minIndex) { }
        public void thumbnailComplete(ImageReader source)             { }
        public void thumbnailProgress(ImageReader source, float percentageDone) { }
        public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) { }
   }
}

Displaying image metadata with ImageIO

IIOMetadata contains meta information about the image, so not the actual pixels, but stuff like for example compression, keywords, comments, … If you convert from one format to another, you don’t want to loose this information. A ImageTranscoder understands this meta data and maps it onto another format. Internally, Metadata is stored as a bunch of IIOMetadataNodes (they implement the org.w3c.dom.Element interface). The format of this DOM tree is plug-in dependent: the native format (as format features are different), but plug-ins may support the plug-in neutral format. The following example program displays (using the XSLT transformation package) the plug-in neutral format.

Main.java:

import javax.imageio.metadata.*; 
import javax.imageio.stream.*;
import javax.imageio.*;
 
import java.awt.image.*;
import java.util.*;
import java.io.*;
  
import javax.xml.transform.stream.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.*;
import javax.xml.parsers.*;
  
import org.w3c.dom.*; 
  
public class Main
{
   public static void main(String []args) throws Exception {
      Iterator readers = ImageIO.getImageReadersByFormatName("png");
      ImageReader reader = (ImageReader) readers.next();
 
      ImageInputStream iis = ImageIO.createImageInputStream(new File("coverpng.png"));
      reader.setInput(iis, true);
      BufferedImage bi = reader.read(0);

      IIOMetadata metadata = reader.getImageMetadata(0);
      Node node = (Node) metadata.getAsTree(metadata.getNativeMetadataFormatName());
 
      // use the XSLT transformation package to output the DOM tree we just created
      TransformerFactory tf = TransformerFactory.newInstance();
      Transformer transformer = tf.newTransformer();
      transformer.transform(new DOMSource(node), new StreamResult(System.out));
   }
}

outputs:

<? xml version="1.0" encoding="UTF-8"?>
<javax_imageio_png_1.0>
   <IHDR width="50" height="66" bitDepth="4" colorType="Palette" 
         compressionMethod="deflate" filterMethod="adaptive" 
         interlaceMethod="none"/>
   <PLTE>
      <PLTEEntry index="0" red="0" green="0" blue="0"/>
      <PLTEEntry index="1" red="128" green="192" blue="184"/>
      <PLTEEntry index="2" red="8" green="0" blue="0"/>
      <PLTEEntry index="3" red="248" green="252" blue="248"/>
      <PLTEEntry index="4" red="176" green="176" blue="176"/>
      <PLTEEntry index="5" red="184" green="220" blue="216"/>
      <PLTEEntry index="6" red="120" green="120" blue="120"/>
      <PLTEEntry index="7" red="16" green="152" blue="136"/>
      <PLTEEntry index="8" red="88" green="168" blue="160"/>
      <PLTEEntry index="9" red="72" green="72" blue="72"/>
      <PLTEEntry index="10" red="0" green="0" blue="0"/>
      <PLTEEntry index="11" red="0" green="0" blue="0"/>
      <PLTEEntry index="12" red="0" green="0" blue="0"/>
      <PLTEEntry index="13" red="0" green="0" blue="0"/>
      <PLTEEntry index="14" red="0" green="0" blue="0"/>
      <PLTEEntry index="15" red="0" green="0" blue="0"/>
   </PLTE>
</javax_imageio_png_1.0>

coverpng.png:

Example of an ImageIO transcoder

A transcoder is responsible for converting one particular image type to another and is designed in such a way that metadata specific to that image type is not lost.

The following example outputs all transcoders that are defined for converting gif to jpg. Currently there are none, but since ImageIO is pluggable, third-party transcoders should become available soon.

Main.java:

import javax.imageio.stream.*;
import javax.imageio.*;
import java.util.*;
  
public class Main
{
   public static void main(String []args) {
      Iterator readers = ImageIO.getImageReadersByFormatName("gif");
      ImageReader reader = (ImageReader) readers.next();
 
      Iterator writers = ImageIO.getImageWritersByFormatName("jpg");
      ImageWriter writer = (ImageWriter) writers.next();
 
      Iterator transcoders = ImageIO.getImageTranscoders(reader, writer);
      while (transcoders.hasNext()) {
         ImageTranscoder tc = (ImageTranscoder) transcoders.next();
         System.out.println(tc);
      }
   }
}

Reading a specific region of an image file with the ImageIO APIs

With the class ImageReadParam, you have control over the decoding of the image and the region. Specify the region with a Rectangle instance and pass this to the ImageReadParam instance with setSourceRegion. Then, when you execute BufferedImage bi = reader.read(imageindex, param), the resulting BufferedImage will only contain that region. (imageindex specifies the number of the image in the image file, possible in some image formats).

It is especially useful for handling large images.

In the following example, a large image is selected as the ImageInputStream. Every 10ms, a region as small as the size of the JFrame is read, and only that region. At every step, a new window region is selected; it moves about horizontally and vertically.

Main.java:

import javax.imageio.stream.*;
import javax.swing.event.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.imageio.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
import java.io.*;
  
public class Main extends JFrame implements Runnable
{
   BufferedImage background;
   ImageReader reader;
   ImageReadParam param;
   int imageWidth, imageHeight;
   int currentx, currenty, dx, dy;
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      setResizable(false);
   }
   
   public void initImageReading() throws Exception {
      Iterator readers = ImageIO.getImageReadersByFormatName("jpg");
      reader = (ImageReader)readers.next();
 
      ImageInputStream iis = ImageIO.createImageInputStream(new File("autumn16.jpg"));
      reader.setInput(iis, true);
      param = reader.getDefaultReadParam();
 
      imageWidth = reader.getWidth(0);
      imageHeight = reader.getHeight(0);
 
      Rectangle rect = new Rectangle(0, 0, getWidth(), getHeight()); 
      param.setSourceRegion(rect);
 
      currentx = imageWidth/2;
      currenty = imageHeight/2;
      background = reader.read(0, param);
 
      dx = getWidth() / 2;
      dy = getHeight() / 2;
   }
 
   public void run() {
      while (true) {
         try {
            Thread.sleep(10);
         }
         catch(InterruptedException e) { }
 
         if (currentx+dx > imageWidth-getWidth() || currentx+dx <= 0) {
            dx *= -1;
            currenty += dy;
            if (currenty+dy > imageHeight-getHeight() || currenty+dy <= 0) {
               dy *= -1;
            }
            currenty += dy;
         }
         currentx += dx;
 
         Rectangle rect = new Rectangle(currentx, currenty, getWidth(), getHeight()); 
         param.setSourceRegion(rect);
 
         try {
            background = reader.read(0, param);
         }
         catch(IOException e) {
            e.printStackTrace();
         }
 
         repaint();
      }
   }
  
   public void paint(Graphics g) {
      if (background != null)
         g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
   }
 
   public static void main(String []args) throws Exception {
      Main main = new Main();
      main.setSize(100, 100);
      main.setVisible(true);
 
      main.initImageReading();
      new Thread(main).start();
   }
}

Determining what formats are supported by ImageIO

ImageIO is extensible. You can add third party plug-ins for other image formats than the standard ones.

Main.java:

import javax.imageio.*;
 
public class Main
{
   public static void main(String []args) {
      String[] formatNames = ImageIO.getReaderFormatNames();
 
      for (int i=0; i<formatNames.length; i++) {
         System.out.println(formatNames[i]);
      }
   }
}

outputs:

png
jpeg
JPEG
gif
jpg
JPG

Reading a BufferedImage using the ImageIO API

The new ImageIO API allows you to read and write any type of image files as long
as there is a plug-in written for it. Those plug-ins can be provided by third parties.
The appropriate plug-in is choosen depending on the file suffix, mime type, contents or format name.

The following example uses the built-in JPG plug-in.

Main.java:

import javax.swing.event.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.imageio.*;
import javax.swing.*;
import java.net.*;
import java.awt.*;
import java.io.*;
 
public class Main extends JFrame
{
   BufferedImage background;
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      try {
         background = ImageIO.read(new URL(&quot;http://www.esus.com/images/mong.jpg&quot;));
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   }
 
   public void paint(Graphics g) {
      g.drawImage(background, 0, 0, getWidth(), getHeight(), this);
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(400, 400);
      main.setVisible(true);
   }
}

Generating a mouse event programatically in Swing

In JDK 1.3, you can use the Robot class, very useful for testing purposes. The following example creates a JFrame with a button and left clicks in the center.

Main.java:

import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.*;
  
public class Main extends JFrame
{
   public Main() throws Exception {
      JButton button = new JButton("This button will be clicked");
      getContentPane().add(button);
      pack();
 
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
            System.out.println("Button was clicked!");
         }
      });
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }
 
   public void generateClickEvent() throws Exception {
      Robot robot = new Robot();
      robot.mouseMove((int) (getSize().getWidth() / 2), (int) (getSize().getHeight() / 2));
      robot.mousePress(InputEvent.BUTTON1_MASK);
      robot.mouseRelease(InputEvent.BUTTON1_MASK);
   }
  
   public static void main(String args[]) throws Exception {
      Main main = new Main();
      main.show();
      try { Thread.sleep(1000); } catch(InterruptedException e) { } 
      main.generateClickEvent();
   }
}

Moving the mouse cursor programatically

From JDK1.3, you can use the robot class, very useful for testing purposes.
Following example shows you how to take a desktop screenshot (0,0) –> (200,200)
as well as manipulating the mouse cursor. The cursor will traverse diagonally
from the top left corner to the bottom right.

import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{
   public Main() throws Exception {
      Robot robot = new Robot();
      BufferedImage image = robot.createScreenCapture(new Rectangle(0, 0, 200, 200));
      getContentPane().add(new JLabel(new ImageIcon(image)));
      //setSize(200, 200);
      pack();
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }
 
   public void animateMouse() throws Exception {
      Robot robot = new Robot();
 
      double w = getSize().getWidth();
      double h = getSize().getHeight();
 
      double x1=0, y1=0;
      double dx = w/h;
      double dy = h/w;
      if (dx > dy) dx = 1; else dy = 1;
      do {
         robot.mouseMove((int) x1, (int) y1);
         x1 += dx;
         y1 += dy;
 
         try { Thread.sleep(10); } catch(InterruptedException e) { }
      } while (x1 < w || y1 < h);
   }
 
   public static void main(String args[]) throws Exception {
      Main main = new Main();
      main.show();
      main.animateMouse();
   }
}

Changing the mouse cursor when moving over a component

Add a mouse listeners to the component and set another cursor when the mouse
enters it. Set it to the default cursor when the mouse moves out of it.

Here’s an example that creates a JFrame with a number of buttons, each with
a different mouse cursor. Our method createCursor will associate a cursor
to a component.

import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.awt.*;
 
public class Main extends JFrame
{
   private Vector buttons = new Vector();
     
   public static void main(String []args) {
      Main main = new Main();
      main.show();
   }
 
   public Main() {
      buttons.addElement(CreateCursor(new JButton("DEFAULT_CURSOR"), Cursor.DEFAULT_CURSOR));
      buttons.addElement(CreateCursor(new JButton("CROSSHAIR_CURSOR"), Cursor.CROSSHAIR_CURSOR));
      buttons.addElement(CreateCursor(new JButton("TEXT_CURSOR"), Cursor.TEXT_CURSOR));
      buttons.addElement(CreateCursor(new JButton("WAIT_CURSOR"), Cursor.WAIT_CURSOR));
      buttons.addElement(CreateCursor(new JButton("SW_RESIZE_CURSOR"), Cursor.SW_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("SE_RESIZE_CURSOR"), Cursor.SE_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("NW_RESIZE_CURSOR"), Cursor.NW_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("NE_RESIZE_CURSOR"), Cursor.NE_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("N_RESIZE_CURSOR"), Cursor.N_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("S_RESIZE_CURSOR"), Cursor.S_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("W_RESIZE_CURSOR"), Cursor.W_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("E_RESIZE_CURSOR"), Cursor.E_RESIZE_CURSOR));
      buttons.addElement(CreateCursor(new JButton("HAND_CURSOR"), Cursor.HAND_CURSOR));
      buttons.addElement(CreateCursor(new JButton("MOVE_CURSOR"), Cursor.MOVE_CURSOR));
 
	getContentPane().setLayout(new GridLayout(buttons.size(), 1));
 
      for (int i=0; i<buttons.size(); i++) {
         getContentPane().add((JButton) buttons.elementAt(i));
      }
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
 
      pack();
   }
 
   private JComponent CreateCursor(final JComponent component, final int cursor) {
	component.addMouseListener(new MouseAdapter() {
	   public void mouseEntered(MouseEvent e) {
		component.setCursor(Cursor.getPredefinedCursor(cursor));
	   }
	   public void mouseExited(MouseEvent e) {
		component.setCursor(Cursor.getDefaultCursor());
	   }
 	});
 
      return component;
   }  
}