001: /*
002: * @(#)MemoryImageSource.java 1.28 06/10/11
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.awt.image;
029:
030: import java.awt.image.ImageConsumer;
031: import java.awt.image.ImageProducer;
032: import java.awt.image.ColorModel;
033: import java.util.Hashtable;
034: import java.util.Vector;
035: import java.util.Enumeration;
036:
037: /**
038: * This class is an implementation of the ImageProducer interface which
039: * uses an array to produce pixel values for an Image. Here is an example
040: * which calculates a 100x100 image representing a fade from black to blue
041: * along the X axis and a fade from black to red along the Y axis:
042: * <pre>
043: *
044: * int w = 100;
045: * int h = 100;
046: * int pix[] = new int[w * h];
047: * int index = 0;
048: * for (int y = 0; y < h; y++) {
049: * int red = (y * 255) / (h - 1);
050: * for (int x = 0; x < w; x++) {
051: * int blue = (x * 255) / (w - 1);
052: * pix[index++] = (255 << 24) | (red << 16) | blue;
053: * }
054: * }
055: * Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
056: *
057: * </pre>
058: * The MemoryImageSource is also capable of managing a memory image which
059: * varies over time to allow animation or custom rendering. Here is an
060: * example showing how to set up the animation source and signal changes
061: * in the data (adapted from the MemoryAnimationSourceDemo by Garth Dickie):
062: * <pre>
063: *
064: * int pixels[];
065: * MemoryImageSource source;
066: *
067: * public void init() {
068: * int width = 50;
069: * int height = 50;
070: * int size = width * height;
071: * pixels = new int[size];
072: *
073: * int value = getBackground().getRGB();
074: * for (int i = 0; i < size; i++) {
075: * pixels[i] = value;
076: * }
077: *
078: * source = new MemoryImageSource(width, height, pixels, 0, width);
079: * source.setAnimated(true);
080: * image = createImage(source);
081: * }
082: *
083: * public void run() {
084: * Thread me = Thread.currentThread( );
085: * me.setPriority(Thread.MIN_PRIORITY);
086: *
087: * while (true) {
088: * try {
089: * thread.sleep(10);
090: * } catch( InterruptedException e ) {
091: * return;
092: * }
093: *
094: * // Modify the values in the pixels array at (x, y, w, h)
095: *
096: * // Send the new data to the interested ImageConsumers
097: * source.newPixels(x, y, w, h);
098: * }
099: * }
100: *
101: * </pre>
102: *
103: * @see ImageProducer
104: *
105: * @version 1.23 08/19/02
106: * @author Jim Graham
107: * @author Animation capabilities inspired by the
108: * MemoryAnimationSource class written by Garth Dickie
109: */
110: public class MemoryImageSource implements ImageProducer {
111: int width;
112: int height;
113: ColorModel model;
114: Object pixels;
115: int pixeloffset;
116: int pixelscan;
117: Hashtable properties;
118: Vector theConsumers = new Vector();
119: boolean animating;
120: boolean fullbuffers;
121:
122: /**
123: * Constructs an ImageProducer object which uses an array of bytes
124: * to produce data for an Image object.
125: * @see java.awt.Component#createImage
126: */
127: public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix,
128: int off, int scan) {
129: initialize(w, h, cm, (Object) pix, off, scan, null);
130: }
131:
132: /**
133: * Constructs an ImageProducer object which uses an array of bytes
134: * to produce data for an Image object.
135: * @see java.awt.Component#createImage
136: */
137: public MemoryImageSource(int w, int h, ColorModel cm, byte[] pix,
138: int off, int scan, Hashtable props) {
139: initialize(w, h, cm, (Object) pix, off, scan, props);
140: }
141:
142: /**
143: * Constructs an ImageProducer object which uses an array of integers
144: * to produce data for an Image object.
145: * @see java.awt.Component#createImage
146: */
147: public MemoryImageSource(int w, int h, ColorModel cm, int[] pix,
148: int off, int scan) {
149: initialize(w, h, cm, (Object) pix, off, scan, null);
150: }
151:
152: /**
153: * Constructs an ImageProducer object which uses an array of integers
154: * to produce data for an Image object.
155: * @see java.awt.Component#createImage
156: */
157: public MemoryImageSource(int w, int h, ColorModel cm, int[] pix,
158: int off, int scan, Hashtable props) {
159: initialize(w, h, cm, (Object) pix, off, scan, props);
160: }
161:
162: private void initialize(int w, int h, ColorModel cm, Object pix,
163: int off, int scan, Hashtable props) {
164: width = w;
165: height = h;
166: model = cm;
167: pixels = pix;
168: pixeloffset = off;
169: pixelscan = scan;
170: if (props == null) {
171: props = new Hashtable();
172: }
173: properties = props;
174: }
175:
176: /**
177: * Constructs an ImageProducer object which uses an array of integers
178: * in the default RGB ColorModel to produce data for an Image object.
179: * @see java.awt.Component#createImage
180: * @see ColorModel#getRGBdefault
181: */
182: public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
183: initialize(w, h, ColorModel.getRGBdefault(), (Object) pix, off,
184: scan, null);
185: }
186:
187: /**
188: * Constructs an ImageProducer object which uses an array of integers
189: * in the default RGB ColorModel to produce data for an Image object.
190: * @see java.awt.Component#createImage
191: * @see ColorModel#getRGBdefault
192: */
193: public MemoryImageSource(int w, int h, int pix[], int off,
194: int scan, Hashtable props) {
195: initialize(w, h, ColorModel.getRGBdefault(), (Object) pix, off,
196: scan, props);
197: }
198:
199: /**
200: * Adds an ImageConsumer to the list of consumers interested in
201: * data for this image.
202: * @see ImageConsumer
203: */
204: public synchronized void addConsumer(ImageConsumer ic) {
205: if (theConsumers.contains(ic)) {
206: return;
207: }
208: theConsumers.addElement(ic);
209: try {
210: initConsumer(ic);
211: sendPixels(ic, 0, 0, width, height);
212: if (isConsumer(ic)) {
213: ic
214: .imageComplete(animating ? ImageConsumer.SINGLEFRAMEDONE
215: : ImageConsumer.STATICIMAGEDONE);
216: if (!animating && isConsumer(ic)) {
217: ic.imageComplete(ImageConsumer.IMAGEERROR);
218: removeConsumer(ic);
219: }
220: }
221: } catch (Exception e) {
222: if (isConsumer(ic)) {
223: ic.imageComplete(ImageConsumer.IMAGEERROR);
224: }
225: }
226: }
227:
228: /**
229: * Determine if an ImageConsumer is on the list of consumers currently
230: * interested in data for this image.
231: * @return true if the ImageConsumer is on the list; false otherwise
232: * @see ImageConsumer
233: */
234: public synchronized boolean isConsumer(ImageConsumer ic) {
235: return theConsumers.contains(ic);
236: }
237:
238: /**
239: * Remove an ImageConsumer from the list of consumers interested in
240: * data for this image.
241: * @see ImageConsumer
242: */
243: public synchronized void removeConsumer(ImageConsumer ic) {
244: theConsumers.removeElement(ic);
245: }
246:
247: /**
248: * Adds an ImageConsumer to the list of consumers interested in
249: * data for this image, and immediately start delivery of the
250: * image data through the ImageConsumer interface.
251: * @see ImageConsumer
252: */
253: public void startProduction(ImageConsumer ic) {
254: addConsumer(ic);
255: }
256:
257: /**
258: * Requests that a given ImageConsumer have the image data delivered
259: * one more time in top-down, left-right order.
260: * @see ImageConsumer
261: */
262: public void requestTopDownLeftRightResend(ImageConsumer ic) {// Ignored. The data is either single frame and already in TDLR
263: // format or it is multi-frame and TDLR resends aren't critical.
264: }
265:
266: /**
267: * Change this memory image into a multi-frame animation or a
268: * single-frame static image depending on the animated parameter.
269: * <p>This method should be called immediately after the
270: * MemoryImageSource is constructed and before an image is
271: * created with it to ensure that all ImageConsumers will
272: * receive the correct multi-frame data. If an ImageConsumer
273: * is added to this ImageProducer before this flag is set then
274: * that ImageConsumer will see only a snapshot of the pixel
275: * data that was available when it connected.
276: * @param animated true if the image is a multi-frame animation
277: */
278: public synchronized void setAnimated(boolean animated) {
279: this .animating = animated;
280: if (!animating) {
281: Enumeration enum_ = theConsumers.elements();
282: while (enum_.hasMoreElements()) {
283: ImageConsumer ic = (ImageConsumer) enum_.nextElement();
284: ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
285: if (isConsumer(ic)) {
286: ic.imageComplete(ImageConsumer.IMAGEERROR);
287: }
288: }
289: theConsumers.removeAllElements();
290: }
291: }
292:
293: /**
294: * Specify whether this animated memory image should always be
295: * updated by sending the complete buffer of pixels whenever
296: * there is a change.
297: * This flag is ignored if the animation flag is not turned on
298: * through the setAnimated() method.
299: * <p>This method should be called immediately after the
300: * MemoryImageSource is constructed and before an image is
301: * created with it to ensure that all ImageConsumers will
302: * receive the correct pixel delivery hints.
303: * @param fullbuffers true if the complete pixel buffer should always
304: * be sent
305: * @see #setAnimated
306: */
307: public synchronized void setFullBufferUpdates(boolean fullbuffers) {
308: if (this .fullbuffers == fullbuffers) {
309: return;
310: }
311: this .fullbuffers = fullbuffers;
312: if (animating) {
313: Enumeration enum_ = theConsumers.elements();
314: while (enum_.hasMoreElements()) {
315: ImageConsumer ic = (ImageConsumer) enum_.nextElement();
316: ic
317: .setHints(fullbuffers ? (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES)
318: : ImageConsumer.RANDOMPIXELORDER);
319: }
320: }
321: }
322:
323: /**
324: * Send a whole new buffer of pixels to any ImageConsumers that
325: * are currently interested in the data for this image and notify
326: * them that an animation frame is complete.
327: * This method only has effect if the animation flag has been
328: * turned on through the setAnimated() method.
329: * @see ImageConsumer
330: * @see #setAnimated
331: */
332: public void newPixels() {
333: newPixels(0, 0, width, height, true);
334: }
335:
336: /**
337: * Send a rectangular region of the buffer of pixels to any
338: * ImageConsumers that are currently interested in the data for
339: * this image and notify them that an animation frame is complete.
340: * This method only has effect if the animation flag has been
341: * turned on through the setAnimated() method.
342: * If the full buffer update flag was turned on with the
343: * setFullBufferUpdates() method then the rectangle parameters
344: * will be ignored and the entire buffer will always be sent.
345: * @param x the x coordinate of the upper left corner of the rectangle
346: * of pixels to be sent
347: * @param y the y coordinate of the upper left corner of the rectangle
348: * of pixels to be sent
349: * @param w the width of the rectangle of pixels to be sent
350: * @param h the height of the rectangle of pixels to be sent
351: * @see ImageConsumer
352: * @see #setAnimated
353: * @see #setFullBufferUpdates
354: */
355: public synchronized void newPixels(int x, int y, int w, int h) {
356: newPixels(x, y, w, h, true);
357: }
358:
359: /**
360: * Send a rectangular region of the buffer of pixels to any
361: * ImageConsumers that are currently interested in the data for
362: * this image.
363: * If the framenotify parameter is true then the consumers are
364: * also notified that an animation frame is complete.
365: * This method only has effect if the animation flag has been
366: * turned on through the setAnimated() method.
367: * If the full buffer update flag was turned on with the
368: * setFullBufferUpdates() method then the rectangle parameters
369: * will be ignored and the entire buffer will always be sent.
370: * @param x the x coordinate of the upper left corner of the rectangle
371: * of pixels to be sent
372: * @param y the y coordinate of the upper left corner of the rectangle
373: * of pixels to be sent
374: * @param w the width of the rectangle of pixels to be sent
375: * @param h the height of the rectangle of pixels to be sent
376: * @param framenotify true if the consumers should be sent a
377: * SINGLEFRAMEDONE notification
378: * @see ImageConsumer
379: * @see #setAnimated
380: * @see #setFullBufferUpdates
381: */
382: public synchronized void newPixels(int x, int y, int w, int h,
383: boolean framenotify) {
384: if (animating) {
385: if (fullbuffers) {
386: x = y = 0;
387: w = width;
388: h = height;
389: } else {
390: if (x < 0) {
391: w += x;
392: x = 0;
393: }
394: if (x + w > width) {
395: w = width - x;
396: }
397: if (y < 0) {
398: h += y;
399: y = 0;
400: }
401: if (y + h > height) {
402: h = height - y;
403: }
404: }
405: if ((w <= 0 || h <= 0) && !framenotify) {
406: return;
407: }
408: Enumeration enum_ = theConsumers.elements();
409: while (enum_.hasMoreElements()) {
410: ImageConsumer ic = (ImageConsumer) enum_.nextElement();
411: if (w > 0 && h > 0) {
412: sendPixels(ic, x, y, w, h);
413: }
414: if (framenotify && isConsumer(ic)) {
415: ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
416: }
417: }
418: }
419: }
420:
421: /**
422: * Change to a new byte array to hold the pixels for this image.
423: * If the animation flag has been turned on through the setAnimated()
424: * method, then the new pixels will be immediately delivered to any
425: * ImageConsumers that are currently interested in the data for
426: * this image.
427: * @see #setAnimated
428: */
429: public synchronized void newPixels(byte[] newpix,
430: ColorModel newmodel, int offset, int scansize) {
431: this .pixels = newpix;
432: this .model = newmodel;
433: this .pixeloffset = offset;
434: this .pixelscan = scansize;
435: newPixels();
436: }
437:
438: /**
439: * Change to a new int array to hold the pixels for this image.
440: * If the animation flag has been turned on through the setAnimated()
441: * method, then the new pixels will be immediately delivered to any
442: * ImageConsumers that are currently interested in the data for
443: * this image.
444: * @see #setAnimated
445: */
446: public synchronized void newPixels(int[] newpix,
447: ColorModel newmodel, int offset, int scansize) {
448: this .pixels = newpix;
449: this .model = newmodel;
450: this .pixeloffset = offset;
451: this .pixelscan = scansize;
452: newPixels();
453: }
454:
455: private void initConsumer(ImageConsumer ic) {
456: if (isConsumer(ic)) {
457: ic.setDimensions(width, height);
458: }
459: if (isConsumer(ic)) {
460: ic.setProperties(properties);
461: }
462: if (isConsumer(ic)) {
463: ic.setColorModel(model);
464: }
465: if (isConsumer(ic)) {
466: ic
467: .setHints(animating ? (fullbuffers ? (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES)
468: : ImageConsumer.RANDOMPIXELORDER)
469: : (ImageConsumer.TOPDOWNLEFTRIGHT
470: | ImageConsumer.COMPLETESCANLINES
471: | ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME));
472: }
473: }
474:
475: private void sendPixels(ImageConsumer ic, int x, int y, int w, int h) {
476: int off = pixeloffset + pixelscan * y + x;
477: if (isConsumer(ic)) {
478: if (pixels instanceof byte[]) {
479: ic.setPixels(x, y, w, h, model, ((byte[]) pixels), off,
480: pixelscan);
481: } else {
482: ic.setPixels(x, y, w, h, model, ((int[]) pixels), off,
483: pixelscan);
484: }
485: }
486: }
487: }
|