AWT memory image dynamic update API


last updated: November 14, 1996

The Issue

Currently the AWT provides a standard utility class to make an image out of an array of pixel values. This class isolates developers from the complex ImageProducer/ImageConsumer mechanisms if they simply want to calculate an image using a Java algorithm and then display the resulting image on the screen.

The MemoryImageSource class provides this facility but it only implements a static image capability. The first time an image is prepared for a screen by drawing the image, using the prepareImage method, or by using a MediaTracker to build the image, a snapshot of the pixels in the array will be converted into a screen representation. Then every time the image is drawn again at that same scale, the same buffered screen representation will be used, ignoring any changes the developer may have made to the original array of pixels in the meantime.

The implementation specifically does not reread the original array of pixel information for each rendering operation since image conversion is a slow process when compared to simply moving converted pixels to the screen. For drawImage to be fast, it works better if it can use a copy of the image that is already in the format that the display hardware uses to represent pixels. And thus we have a tradeoff between showing the image fast when it is not changing and dynamically incorporating changes to the original image data.

This implementation worked fine for images that were computed once and then the results were final. It was even workable for images that changed rarely since a new MemoryImageSource image could be constructed after each infrequent change or the original image could be flushed using the Image.flush() method to decache any existing screen representations. These workarounds were inefficient or unwieldy for more frequent updates such as those involved in animations since the entire image had to be reconverted even for minor modifications to the data, or even worse requiring more garbage collection than strictly necessary to reclaim space used by old discarded versions of the image.

The new memory image animation API

To simplify this capability, new methods are being added to MemoryImageSource so that developers can notify the image that new data is available from the original array. Since the image conversion process only occurs when the data in the original array actually changes, drawImage can still execute very fast. The new methods are:
	setAnimated(boolean animated)
	setFullBufferUpdates(boolean animated)
	newPixels()
	newPixels(int x, int y, int w, int h)
	newPixels(int x, int y, int w, int h,
		  boolean framenotify)
	newPixels(byte[] newpix, ColorModel newmodel,
		  int offset, int scansize)
	newPixels(int[] newpix, ColorModel newmodel,
		  int offset, int scansize)
These new methods give the developer control over when the screen representations are updated, which region of the pixels is updated, when ImageObserver.FRAMEBITS notifications (which indicate when a single "frame" of animation is complete) are sent to the image observers, and the ability to switch to a new set of pixels and ColorModel.

Sample Code

Following is sample code showing the use of the old API to perform memory image buffer animation:


    import java.awt.*;
    import java.awt.image.*;
    import java.applet.*;

    public class AnimationExample extends Applet implements Runnable {
    	Thread anim;
	MemoryImageSource imgsrc;
	Image memimg;
	int[] pixels;

	public void init() {
	    pixels = new int[100 * 100];
	    imgsrc = new MemoryImageSource(100, 100, pixels, 0, 100);
	    memimg = createImage(imgsrc);
	}

    	public void start() {
	    anim = new Thread(this);
	    anim.start();
	}

	public synchronized void stop() {
	    anim = null;
	    notify();
	}

	public synchronized void run() {
	    while (Thread.currentThread() == anim) {
	    	int x = (int) (Math.random() * 100);
	    	int y = (int) (Math.random() * 100);
		int r = (int) (Math.random() * 255);
		int g = (int) (Math.random() * 255);
		int b = (int) (Math.random() * 255);
		pixels[y * 100 + x] =
		    ((int) (Math.random() * 0xffffff)) | (0xff<<24);
		memimg.flush();
		repaint();
		try {wait(100);} catch (InterruptedException e) {return;}
	    }
	}

	public void paint(Graphics g) {
	    // Draw the animated image
	    g.drawImage(memimg, 0, 0, this);
	}
    }

Following is sample code showing the use of the new API to perform memory image buffer animation:


    import java.awt.*;
    import java.awt.image.*;
    import java.applet.*;

    public class AnimationExample extends Applet implements Runnable {
    	Thread anim;
	MemoryImageSource imgsrc;
	Image memimg;
	int[] pixels;

	public void init() {
	    pixels = new int[100 * 100];
	    imgsrc = new MemoryImageSource(100, 100, pixels, 0, 100);
	    imgsrc.setAnimated(true);
	    memimg = createImage(imgsrc);
	}

    	public void start() {
	    anim = new Thread(this);
	    anim.start();
	}

	public synchronized void stop() {
	    anim = null;
	    notify();
	}

	public synchronized void run() {
	    while (Thread.currentThread() == anim) {
	    	int x = (int) (Math.random() * 100);
	    	int y = (int) (Math.random() * 100);
		int r = (int) (Math.random() * 255);
		int g = (int) (Math.random() * 255);
		int b = (int) (Math.random() * 255);
		pixels[y * 100 + x] =
		    ((int) (Math.random() * 0xffffff)) | (0xff<<24);
		imgsrc.newPixels(x, y, 1, 1);
		repaint();
		try {wait(100);} catch (InterruptedException e) {return;}
	    }
	}

	public void paint(Graphics g) {
	    // Draw the animated image
	    g.drawImage(memimg, 0, 0, this);
	}
    }


Send feedback to:java-awt@java.sun.com
Copyright © 1996, Sun Microsystems, Inc. All rights reserved.