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