Monday, July 12. 2010
As a little background to this post, during the course of a recent client project involving image manipulation, I wrote some code to handle images in the tiff format. I used the Java Advanced Imaging (JAI) library's ImageIO class, which made reading from the image file super easy through the use of ImageIO.read(File) which automatically determines the file format based on the extension and performs the file read internally, returning a fully usable BufferedImage object. It really makes reading data from an image super easy, and I highly recommend using it.
While working on the project I needed to write a simple utility to count the number of unique colors in an image and print out how many pixels in the image were that color (useful for debugging the main application). Its a really simple Java program that loops through the image, incrementing the count in a hash map of color values to pixel counts. I copy/pasted the image reading code from the main program, and was surprised to see the test program start generating errors stating that the tiff file could not be read as there was no image reader associated with the tiff file format. Lacking a tiff reader is really surprising because this test utility is just another class file within the same eclipse project as the main application, and therefore has the same class path. Both the jai_core.jar and the jai_codec.jar (the two jars that make up the JAI library) are on the project's class path, so there should be no reason that one java file would have access to them, but another java file does not.
It turns out this was also a problem for gif and jpeg images in versions past, as evidenced by this FAQ question on the JAI home page:
On Solaris, Java Advanced Imaging complains about lack of access to an X server.
Java Advanced Imaging versions previous to JAI 1.1.1 used the AWT toolkit to load GIF and JPEG files. This problem is a manifestation of a JDK bug in which creation of the AWT Toolkit class results in an attempt to open the X display. To work around this problem in Java Advanced Imaging versions prior to 1.1.1, either make an X display available to the Java runtime using the DISPLAY environment variable (no windows will appear on the display), or consider running a dummy X server that will satisfy the AWT, such as the Xvfb utility included with the X11R6.4 distribution.
In the JAI 1.1.1 version, the GIF and JPEG decoders were improved to no longer have a dependency on the X server.
The answer it turns out is that my simple utility does not set up the AWT windowing system (since I wrote it as a CLI utilizing System.in) and therefore does not end up loading the tiff image reader plugin because of this fact. It turns out that in order to utilize the tiff reader plugin from the JAI library your code must perform at least one of the following calls:
- Instantiate a JFrame
- Call Toolkit.getDefaultToolkit()
- Call Application.getApplication() (Mac OS X java extension)
While I still don't know the exact nature of the behavior, all evidence points to the fact that in a java program that needs to utilize the tiff image reader, you must set up the AWT windowing system in some manner. Even if your program (like my test utility) doesn't need to create a window or deal with the windowing system in any fashion, you must do one of the above methods in order to correctly register the tiff reader.