- /*
 - * @(#)MemoryImageSource.java 1.23 00/02/02
 - *
 - * Copyright 1995-2000 Sun Microsystems, Inc. All Rights Reserved.
 - *
 - * This software is the proprietary information of Sun Microsystems, Inc.
 - * Use is subject to license terms.
 - *
 - */
 - package java.awt.image;
 - import java.awt.image.ImageConsumer;
 - import java.awt.image.ImageProducer;
 - import java.awt.image.ColorModel;
 - import java.util.Hashtable;
 - import java.util.Vector;
 - import java.util.Enumeration;
 - /**
 - * This class is an implementation of the ImageProducer interface which
 - * uses an array to produce pixel values for an Image. Here is an example
 - * which calculates a 100x100 image representing a fade from black to blue
 - * along the X axis and a fade from black to red along the Y axis:
 - * <pre>
 - *
 - * int w = 100;
 - * int h = 100;
 - * int pix[] = new int[w * h];
 - * int index = 0;
 - * for (int y = 0; y < h; y++) {
 - * int red = (y * 255) / (h - 1);
 - * for (int x = 0; x < w; x++) {
 - * int blue = (x * 255) / (w - 1);
 - * pix[index++] = (255 << 24) | (red << 16) | blue;
 - * }
 - * }
 - * Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
 - *
 - * </pre>
 - * The MemoryImageSource is also capable of managing a memory image which
 - * varies over time to allow animation or custom rendering. Here is an
 - * example showing how to set up the animation source and signal changes
 - * in the data (adapted from the MemoryAnimationSourceDemo by Garth Dickie):
 - * <pre>
 - *
 - * int pixels[];
 - * MemoryImageSource source;
 - *
 - * public void init() {
 - * int width = 50;
 - * int height = 50;
 - * int size = width * height;
 - * pixels = new int[size];
 - *
 - * int value = getBackground().getRGB();
 - * for (int i = 0; i < size; i++) {
 - * pixels[i] = value;
 - * }
 - *
 - * source = new MemoryImageSource(width, height, pixels, 0, width);
 - * source.setAnimated(true);
 - * image = createImage(source);
 - * }
 - *
 - * public void run() {
 - * Thread me = Thread.currentThread( );
 - * me.setPriority(Thread.MIN_PRIORITY);
 - *
 - * while (true) {
 - * try {
 - * thread.sleep(10);
 - * } catch( InterruptedException e ) {
 - * return;
 - * }
 - *
 - * // Modify the values in the pixels array at (x, y, w, h)
 - *
 - * // Send the new data to the interested ImageConsumers
 - * source.newPixels(x, y, w, h);
 - * }
 - * }
 - *
 - * </pre>
 - *
 - * @see ImageProducer
 - *
 - * @version 1.23 02/02/00
 - * @author Jim Graham
 - * @author Animation capabilities inspired by the
 - * MemoryAnimationSource class written by Garth Dickie
 - */
 - public class MemoryImageSource implements ImageProducer {
 - int width;
 - int height;
 - ColorModel model;
 - Object pixels;
 - int pixeloffset;
 - int pixelscan;
 - Hashtable properties;
 - Vector theConsumers = new Vector();
 - boolean animating;
 - boolean fullbuffers;
 - /**
 - * Constructs an ImageProducer object which uses an array of bytes
 - * to produce data for an Image object.
 - * @see java.awt.Component#createImage
 - */
 - public MemoryImageSource(int w, int h, ColorModel cm,
 - byte[] pix, int off, int scan) {
 - initialize(w, h, cm, (Object) pix, off, scan, null);
 - }
 - /**
 - * Constructs an ImageProducer object which uses an array of bytes
 - * to produce data for an Image object.
 - * @see java.awt.Component#createImage
 - */
 - public MemoryImageSource(int w, int h, ColorModel cm,
 - byte[] pix, int off, int scan, Hashtable props) {
 - initialize(w, h, cm, (Object) pix, off, scan, props);
 - }
 - /**
 - * Constructs an ImageProducer object which uses an array of integers
 - * to produce data for an Image object.
 - * @see java.awt.Component#createImage
 - */
 - public MemoryImageSource(int w, int h, ColorModel cm,
 - int[] pix, int off, int scan) {
 - initialize(w, h, cm, (Object) pix, off, scan, null);
 - }
 - /**
 - * Constructs an ImageProducer object which uses an array of integers
 - * to produce data for an Image object.
 - * @see java.awt.Component#createImage
 - */
 - public MemoryImageSource(int w, int h, ColorModel cm,
 - int[] pix, int off, int scan, Hashtable props) {
 - initialize(w, h, cm, (Object) pix, off, scan, props);
 - }
 - private void initialize(int w, int h, ColorModel cm,
 - Object pix, int off, int scan, Hashtable props) {
 - width = w;
 - height = h;
 - model = cm;
 - pixels = pix;
 - pixeloffset = off;
 - pixelscan = scan;
 - if (props == null) {
 - props = new Hashtable();
 - }
 - properties = props;
 - }
 - /**
 - * Constructs an ImageProducer object which uses an array of integers
 - * in the default RGB ColorModel to produce data for an Image object.
 - * @see java.awt.Component#createImage
 - * @see ColorModel#getRGBdefault
 - */
 - public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
 - initialize(w, h, ColorModel.getRGBdefault(),
 - (Object) pix, off, scan, null);
 - }
 - /**
 - * Constructs an ImageProducer object which uses an array of integers
 - * in the default RGB ColorModel to produce data for an Image object.
 - * @see java.awt.Component#createImage
 - * @see ColorModel#getRGBdefault
 - */
 - public MemoryImageSource(int w, int h, int pix[], int off, int scan,
 - Hashtable props) {
 - initialize(w, h, ColorModel.getRGBdefault(),
 - (Object) pix, off, scan, props);
 - }
 - /**
 - * Adds an ImageConsumer to the list of consumers interested in
 - * data for this image.
 - * @see ImageConsumer
 - */
 - public synchronized void addConsumer(ImageConsumer ic) {
 - if (theConsumers.contains(ic)) {
 - return;
 - }
 - theConsumers.addElement(ic);
 - try {
 - initConsumer(ic);
 - sendPixels(ic, 0, 0, width, height);
 - if (isConsumer(ic)) {
 - ic.imageComplete(animating
 - ? ImageConsumer.SINGLEFRAMEDONE
 - : ImageConsumer.STATICIMAGEDONE);
 - if (!animating && isConsumer(ic)) {
 - ic.imageComplete(ImageConsumer.IMAGEERROR);
 - removeConsumer(ic);
 - }
 - }
 - } catch (Exception e) {
 - if (isConsumer(ic)) {
 - ic.imageComplete(ImageConsumer.IMAGEERROR);
 - }
 - }
 - }
 - /**
 - * Determine if an ImageConsumer is on the list of consumers currently
 - * interested in data for this image.
 - * @return true if the ImageConsumer is on the list; false otherwise
 - * @see ImageConsumer
 - */
 - public synchronized boolean isConsumer(ImageConsumer ic) {
 - return theConsumers.contains(ic);
 - }
 - /**
 - * Remove an ImageConsumer from the list of consumers interested in
 - * data for this image.
 - * @see ImageConsumer
 - */
 - public synchronized void removeConsumer(ImageConsumer ic) {
 - theConsumers.removeElement(ic);
 - }
 - /**
 - * Adds an ImageConsumer to the list of consumers interested in
 - * data for this image, and immediately start delivery of the
 - * image data through the ImageConsumer interface.
 - * @see ImageConsumer
 - */
 - public void startProduction(ImageConsumer ic) {
 - addConsumer(ic);
 - }
 - /**
 - * Requests that a given ImageConsumer have the image data delivered
 - * one more time in top-down, left-right order.
 - * @see ImageConsumer
 - */
 - public void requestTopDownLeftRightResend(ImageConsumer ic) {
 - // Ignored. The data is either single frame and already in TDLR
 - // format or it is multi-frame and TDLR resends aren't critical.
 - }
 - /**
 - * Change this memory image into a multi-frame animation or a
 - * single-frame static image depending on the animated parameter.
 - * <p>This method should be called immediately after the
 - * MemoryImageSource is constructed and before an image is
 - * created with it to ensure that all ImageConsumers will
 - * receive the correct multi-frame data. If an ImageConsumer
 - * is added to this ImageProducer before this flag is set then
 - * that ImageConsumer will see only a snapshot of the pixel
 - * data that was available when it connected.
 - * @param animated true if the image is a multi-frame animation
 - */
 - public synchronized void setAnimated(boolean animated) {
 - this.animating = animated;
 - if (!animating) {
 - Enumeration enum = theConsumers.elements();
 - while (enum.hasMoreElements()) {
 - ImageConsumer ic = (ImageConsumer) enum.nextElement();
 - ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
 - if (isConsumer(ic)) {
 - ic.imageComplete(ImageConsumer.IMAGEERROR);
 - }
 - }
 - theConsumers.removeAllElements();
 - }
 - }
 - /**
 - * Specify whether this animated memory image should always be
 - * updated by sending the complete buffer of pixels whenever
 - * there is a change.
 - * This flag is ignored if the animation flag is not turned on
 - * through the setAnimated() method.
 - * <p>This method should be called immediately after the
 - * MemoryImageSource is constructed and before an image is
 - * created with it to ensure that all ImageConsumers will
 - * receive the correct pixel delivery hints.
 - * @param fullbuffers true if the complete pixel buffer should always
 - * be sent
 - * @see #setAnimated
 - */
 - public synchronized void setFullBufferUpdates(boolean fullbuffers) {
 - if (this.fullbuffers == fullbuffers) {
 - return;
 - }
 - this.fullbuffers = fullbuffers;
 - if (animating) {
 - Enumeration enum = theConsumers.elements();
 - while (enum.hasMoreElements()) {
 - ImageConsumer ic = (ImageConsumer) enum.nextElement();
 - ic.setHints(fullbuffers
 - ? (ImageConsumer.TOPDOWNLEFTRIGHT |
 - ImageConsumer.COMPLETESCANLINES)
 - : ImageConsumer.RANDOMPIXELORDER);
 - }
 - }
 - }
 - /**
 - * Send a whole new buffer of pixels to any ImageConsumers that
 - * are currently interested in the data for this image and notify
 - * them that an animation frame is complete.
 - * This method only has effect if the animation flag has been
 - * turned on through the setAnimated() method.
 - * @see ImageConsumer
 - * @see #setAnimated
 - */
 - public void newPixels() {
 - newPixels(0, 0, width, height, true);
 - }
 - /**
 - * Send a rectangular region of the buffer of pixels to any
 - * ImageConsumers that are currently interested in the data for
 - * this image and notify them that an animation frame is complete.
 - * This method only has effect if the animation flag has been
 - * turned on through the setAnimated() method.
 - * If the full buffer update flag was turned on with the
 - * setFullBufferUpdates() method then the rectangle parameters
 - * will be ignored and the entire buffer will always be sent.
 - * @param x the x coordinate of the upper left corner of the rectangle
 - * of pixels to be sent
 - * @param y the y coordinate of the upper left corner of the rectangle
 - * of pixels to be sent
 - * @param w the width of the rectangle of pixels to be sent
 - * @param h the height of the rectangle of pixels to be sent
 - * @see ImageConsumer
 - * @see #setAnimated
 - * @see #setFullBufferUpdates
 - */
 - public synchronized void newPixels(int x, int y, int w, int h) {
 - newPixels(x, y, w, h, true);
 - }
 - /**
 - * Send a rectangular region of the buffer of pixels to any
 - * ImageConsumers that are currently interested in the data for
 - * this image.
 - * If the framenotify parameter is true then the consumers are
 - * also notified that an animation frame is complete.
 - * This method only has effect if the animation flag has been
 - * turned on through the setAnimated() method.
 - * If the full buffer update flag was turned on with the
 - * setFullBufferUpdates() method then the rectangle parameters
 - * will be ignored and the entire buffer will always be sent.
 - * @param x the x coordinate of the upper left corner of the rectangle
 - * of pixels to be sent
 - * @param y the y coordinate of the upper left corner of the rectangle
 - * of pixels to be sent
 - * @param w the width of the rectangle of pixels to be sent
 - * @param h the height of the rectangle of pixels to be sent
 - * @param framenotify true if the consumers should be sent a
 - * SINGLEFRAMEDONE notification
 - * @see ImageConsumer
 - * @see #setAnimated
 - * @see #setFullBufferUpdates
 - */
 - public synchronized void newPixels(int x, int y, int w, int h,
 - boolean framenotify) {
 - if (animating) {
 - if (fullbuffers) {
 - x = y = 0;
 - w = width;
 - h = height;
 - } else {
 - if (x < 0) {
 - w += x;
 - x = 0;
 - }
 - if (x + w > width) {
 - w = width - x;
 - }
 - if (y < 0) {
 - h += y;
 - y = 0;
 - }
 - if (y + h > height) {
 - h = height - y;
 - }
 - }
 - if ((w <= 0 || h <= 0) && !framenotify) {
 - return;
 - }
 - Enumeration enum = theConsumers.elements();
 - while (enum.hasMoreElements()) {
 - ImageConsumer ic = (ImageConsumer) enum.nextElement();
 - if (w > 0 && h > 0) {
 - sendPixels(ic, x, y, w, h);
 - }
 - if (framenotify && isConsumer(ic)) {
 - ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
 - }
 - }
 - }
 - }
 - /**
 - * Change to a new byte array to hold the pixels for this image.
 - * If the animation flag has been turned on through the setAnimated()
 - * method, then the new pixels will be immediately delivered to any
 - * ImageConsumers that are currently interested in the data for
 - * this image.
 - * @see #setAnimated
 - */
 - public synchronized void newPixels(byte[] newpix, ColorModel newmodel,
 - int offset, int scansize) {
 - this.pixels = newpix;
 - this.model = newmodel;
 - this.pixeloffset = offset;
 - this.pixelscan = scansize;
 - newPixels();
 - }
 - /**
 - * Change to a new int array to hold the pixels for this image.
 - * If the animation flag has been turned on through the setAnimated()
 - * method, then the new pixels will be immediately delivered to any
 - * ImageConsumers that are currently interested in the data for
 - * this image.
 - * @see #setAnimated
 - */
 - public synchronized void newPixels(int[] newpix, ColorModel newmodel,
 - int offset, int scansize) {
 - this.pixels = newpix;
 - this.model = newmodel;
 - this.pixeloffset = offset;
 - this.pixelscan = scansize;
 - newPixels();
 - }
 - private void initConsumer(ImageConsumer ic) {
 - if (isConsumer(ic)) {
 - ic.setDimensions(width, height);
 - }
 - if (isConsumer(ic)) {
 - ic.setProperties(properties);
 - }
 - if (isConsumer(ic)) {
 - ic.setColorModel(model);
 - }
 - if (isConsumer(ic)) {
 - ic.setHints(animating
 - ? (fullbuffers
 - ? (ImageConsumer.TOPDOWNLEFTRIGHT |
 - ImageConsumer.COMPLETESCANLINES)
 - : ImageConsumer.RANDOMPIXELORDER)
 - : (ImageConsumer.TOPDOWNLEFTRIGHT |
 - ImageConsumer.COMPLETESCANLINES |
 - ImageConsumer.SINGLEPASS |
 - ImageConsumer.SINGLEFRAME));
 - }
 - }
 - private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
 - int off = pixeloffset + pixelscan * y + x;
 - if (isConsumer(ic)) {
 - if (pixels instanceof byte[]) {
 - ic.setPixels(x, y, w, h, model,
 - ((byte[]) pixels), off, pixelscan);
 - } else {
 - ic.setPixels(x, y, w, h, model,
 - ((int[]) pixels), off, pixelscan);
 - }
 - }
 - }
 - }