Just to confuse matters, there is also the Icon interface that ImageIcon implements. Whenever you see a method taking a Icon, you can feed it an ImageIcon.
ImageIcon.getImage will convert an ImageIcon to an Image. The constructor new ImageIcon( Image image ) will convert an Image to an ImageIcon.
The ImageIcon class and the Icon interface have nothing whatsover to do with Windows *.ico icon format that supports multiple resolutions in one file. Java ImageIcons don’t have to be magic sizes. They don’t have to be square. They can be *.png, *.gif or *.jpg just like Images.
You can think of the scheme as like an assembly line. At each stage, the image consumer asks the previous stage image producer for a chunk of the partly processed image to work on. The producer gives it as much as it can, and the consumer then asks again later for the remaining bits. With this scheme, you do not have to wait until all the image has completely arrived before getting on with the later stages of processing, e.g. getting at least part of the image up there on the screen. The details of how it all works are mind boggling, but surprisingly, you can write code even if you have only the vaguest understanding of how they work.
You can just close you eyes to those complexities at first, but you will have to understand them once you start fine tuning your apps when you want to get rid of strange behaviours and flicker.
The Image class is abstract. You can’t instantiate an Image with a constructor. Part of the problem is Image is associated with some very platform dependent code. Besides the terrifying ways of dealing with ImageProducers, ImageConsumers and ImageObservers, there are some simple techniques to create an Image.
Saving an image in gif or jpg format is referred to as encoding.
Class.getResource makes these changes to the resource name: if the resource name starts with "/", it is unchanged; otherwise, the package name is prepended to the resource name after converting "." to "/". This allows you to use either dots or slashes to separate the components of the name. The name is case sensitive. Double check the case by studying your jar files with Winzip.
Unfortunately class.getResource, upon which that preceding technique depends, is broken, in that Netscape version 4.x does not support it. Now that Netscape 8.0 is available, perhaps we can ignore that restriction.
Toolkit.getDefaultToolkit().createImage( rawByteArray , start, length );
MediaTracker t = new MediaTracker( this ); t.addImage( orangeball, 0 ); t.addImage( pinkball, 1 ); try { t.waitForAll(); } catch ( InterruptedException e ) { Thread.currentThread().interrupt(); }You must download the *.gif, *.png, or *.jpg over the Internet or read it from disk, which could be very time-consuming. That is why, by default, Java starts painting the Image even before it has completely arrived. However, it also has to be converted from *.gif, *.png or *.jpg format to standard format then to whatever native format the video card uses. That too is time consuming. MediaTracker does not ensure that process is complete. It only ensures the file in fully loaded into RAM. Oddly, even when you create Images from raw bytes already in RAM, you still need a MediaTracker.
There is no Component in JDK 1.1 that simply displays an Image. I have written one here called ImageViewer which is a greatly simplified version of the one that ships with Café. The component automatically sizes itself to fit the size of the Image you feed it. You can attach a standard MouseListener to it to trigger some action when somebody clicks it. Here is the source for it:
You can download the latest source, and a fancier one called ResizingImageViewer as part of the Canadian Mind Products Business package
Without Swing, you have to handle double buffering yourself. With Swing, all you need is Component.setDoubleBuffered( true ). If you don’t use double buffering you paint directly to the screen. Double buffering is not always faster. In JDK 1.2+ Swing components, double buffering is applied by default. Sometimes you have to turn double buffering off to avoid strange ghosting effects. A simple offscreen drawing technique works like this:
// Use Component.createImage to get you a blank image to draw on. // Do this in your init method, not static init, or you will just get null. Image img = createImage( width, height ); Graphics gi = img.getGraphics(); // Now use gi.drawString, gi.clearRect etc. to draw, // just as you would on-screen graphics. // That gives you an off-screen bit map of your image. // To blast the image onto the screen in your paint routine use: g.drawImage( img, x, y, /* image observer */ this ); // And override update to call paint without clearing first.
I have experimented with Component.createImage( width, height). It is a tricky mechanism. It behaves differently in different JVMs and different browsers. The problem is it uses the current Component’s (e.g. the Applet’s) peer object to create the offscreen Image object. createImage is an instance method of the current Component, not an independent static method as you might expect. That is how it finds the Component’s peer object in the native GUI that knows the details of the image format used by the particular video card we are using.
That peer does not get created until the last minute when Component.setVisible ( true ). Since you are presumably using createImage to eventually create that screen image, you have a minor catch-22. You can’t create the Image well ahead of time. You somehow must postpone any use of createImage until the Component’s addNotify method has been called assigning it a peer object in the native GUI that knows the native format for images on that particular video card. Hooking your code into the addNotify is an essay in itself.
In Java 1.2 Component.isDisplayAble was introduced that lets you know if the peer object is ready yet for createImage to work. It definitely won’t work in static init. It sometimes works in init. Best to put it in an overriding addNotify method which is used to create the peer object. When it returns from super.addNotify is the earliest possible time you can use the peer.
public void addNotify() { super.addNotify(); offScreenImage = createImage ( width, height ); }
png format bypasses the patent problem, but it is not as widely supported. Java did not support it until version 1.4. The easiest way is to use an external screen capture utility such as Paint Shop Pro. if you want to do it under java program control, you have to look for third party image-savers. See Peter van der Linden’s FAQ for some options. You can of course serialise the Image, but you can’t reconstitute it on a different platform because the format of the pickled (serialized) Image is platform-dependent. For speed, Images are stored in a ColorModel (internal format) matching or closely matching the local video card.
Don’t try to send serialised Images over RMI either for the same reason. One way to do it is to use uk.co.demon.windsong.image.ZipImage in to convert the Image to a device Independent GZIPOutputStream, and the companion uk.co.demon.windsong.image.ImageProducer to reconstitute it.
When you want animation speed/smoothness, look into VolatileImage available since Java 1.4 which is implemented where possible inside the video hardware. BitBlts on such images would be done with the 128-bit GPU (graphics processing unit) which is much better designed than the CPU for shovelling misaligned bits around. You build your images in the offscreen part of the hardware video REGEN buffer. Then when you blast them to the screen, they go instantaneously, since the BitBlt video hardware moves it without having to cross the blood-brain boundary to the video hardware memory buffer. It is a high speed double buffering technique.
In your AWT paint method or equivalent Swing paintComponent method you use you use the drawImage method typically with code like this:
What is all this?
g is the Graphics object. It represents the place where we are painting to, usually the screen, but it could be printer or offscreen image. It knows what sort of image format the video card likes. It is passed as a parameter to your paint or paintComponent method.
image is the usually-MediaTracked image more or less ready to draw. It may be part way through the conversion process to internal format.
x,y is where on screen you want to paint the Image.
width, height is how much of the Image you want to copy, usually the whole thing.
this is the Canvas or other Component we are painting on. It is used to reschedule work that cannot be completed now.
done is true if drawImage was able to complete the work.
If done is false then things get interesting. The work is left unfinished and your paint method completes.
When more of the Image is ready, the ImageProducer will use the Component’s ImageObserver interface to call its default Component.updateImage method. That method then calls repaint with a clipregion describing the uncompleted part of the work.
repaint schedules a repaint event, which eventually ends up calling your paint or paintComponent again, but this time with Graphics object with a reduced clipregion so you don’t have to bother redoing the completed work if you are clever.
This process repeats until eventually drawImage returns true.
The key point to understand is that your paint or paintComponent method may be called several times to complete the drawing from an initial repaint request. You can write your paint method oblivious to this madness. The Graphics object automatically clips everything you draw to the uncompleted region. However, ideally you should avoid excessive painting outside the clip region.
| Image Processing Tools | |
|---|---|
| Class or Method | Purpose |
| BufferedImage | A subclass of Image, that gives you additional ability to study and manipulate its bits. With ImageIO you can efficiently create them from images or disk or in RAM. You can actually instantiate an BufferedImage, unlike an abstract Image. You can examine and set individual bits with getRGB and setRGB. When you create a BufferedImage you decide the ColorModel it will use, e.g. how will will pack the image and its colours into bits. BufferedImage is only available in JDK 1.2+. |
| Class.getResource | get file in jar. |
| ColorModel | abstract class that describes an internal bit map format. It is merely a set of methods for unpacking a single pixel coded as an int into alpha, red, green and blue. It does not know how to pack it up again. They are used by ImageConsumers to unpack the raw bytes and raw ints that ImageProducers throw at them. |
| Component.createImage | creates empty Image, or creates an Image given any ImageProducer. Cannot be used until after addNotify. Similar to Toolkit.createImage. |
| Component.prepareImage | starts the file loading, but does not ensure completion. |
| Component.updateImage | ImageObserver interface to reschedule incomplete drawing. The ImageProducer notifies of the progress in preparing the Image by repeatedly calling Component.updateImage. |
| Graphics.drawImage | copies bits from an Image to the screen or to another Image. |
| Image | abstract class for plugging ImageProducers and ImageConsumers together. Much to my surprise, I discovered Image objects do not actually contain any bits of the picture. Image objects contain a reference to the ImageProducer, but not the ImageConsumer. The ImageProducers track all their ImageConsumers. getScaledInstance can be used to create a larger or smaller version of an image in one step. There are two concrete implementions of the Image abstract class: BufferedImage and VolatileImage. |
| ImageConsumer | interface capable of accepting image bits in one of Java’s standard internal formats (ColorModels) It may not necessarily receive them at once, or even in order. Graphics objects have ImageConsumers. They accept bits and convert them to the native GUI format for the video card. Other Graphics objects created by offscreen Image.getGraphics simply store the bits fed them in RAM somewhere, usually in one of Java’s standard formats. A VolatileImage.createGraphics stores its bits in native video card format right inside the video card’s private memory. ImageConsumers passively accept whatever bits the ImageProducers send to them. I don’t even see a way they can provide a clipregion to the ImageProducer. On the other hand they are just passed the address of the bits. They are not obligated to copy any of them, so ImageConsumerss can do their own clipping fairly efficiently. You can write ImageFilters that act both as ImageConsumer and ImageProducer. They could clip, change colors, change ColorModels etc. |
| ImageIcon | a wrapper class consisting of a reference an Image, a built-in MediaTracker and a text description. It is a convienient way to package an Image to hand to a JComponent such as a JButton to decorate it. |
| ImageProducer | interface capable of providing image bits in one of Java’s standard internal formats. It does not have to provide them all at once, or even in order. It might be a method like URL.getContent that can fetch files off disk or the Internet and decompress the jpg format into standard Java internal bitmap format. Whenever it has more bits prepared, it hands them off to the ImageConsumer via ImageConsumer.setPixels. |
| ImageObserver | interface used to reschedule incomplete painting. It gets notified of progress in preparing the Image via its ImageObserver.imageUpdate method. |
| MediaTracker | Ensures a group of Images are fully loaded in RAM. |
| PixelGrabber | captures pixels from any ImageProducer, and converts them into an array of pixel ints. It gives you direct access to the raw bits behind an Image for bulk import/export. You can change individual bits more easily with BufferedImage. getRGB and BufferedImage. setRGB. Pixels are stored in arrays and are subject to the Integer. MAX_VALUE addressing limit. This limit will bite you sooner than you think. The biggest square of pixels you can address is 46,340 × 46,340. |
| ToolKit.createImage | create an image Image out of raw bytes, an URL a file or any ImageProducer. Similar to Component.createImage. |
| ToolKit.getImage | get an Image from local hard disk. Beware. getImage caches results. Use createImage to bypass the caching. If you change the disk file of the image, and refetch you will likely get the old version again. Where do you get your Toolkit? from Component.getToolkit or Toolkit.getDefaultToolkit. If you get it from a Component, it must have a peer, e.g. after super.addNotify has returned. However, this is preferable since the Component will be automatically repainted as parts of the Image dribble in. |
| VolatileImage | A Image living on borrowed time inside the regen buffer — video card’s private offscreen memory, already converted to the video card’s native internal format. It can be copied to the onscreen part of the REGEN by the video card 128-bit hardware in a twinkling. |
| WriteableRaster | Lets you modify individual pixels in a BufferedImage. |
![]() |
and suggestions to improve this page to Roedy Green : | ||
| Canadian Mind Products | |||
| mindprod.com IP:[65.110.21.43] | |||
| Your face IP:[38.103.63.17] | The information on this page is for non-military use only. | ||
| You are visitor number 86,009. | Military use includes use by defence contractors. | ||
| You can get a fresh copy of this page from: | or possibly from your local J: drive (Java virtual drive/Mindprod website mirror) | ||
| http://mindprod.com/jgloss/image.html | J:\mindprod\jgloss\image.html | ||