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.
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.
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); } }