KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > image > MemoryImageSource


1 /*
2  * @(#)MemoryImageSource.java 1.33 04/07/16
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.awt.image;
9
10 import java.awt.image.ImageConsumer JavaDoc;
11 import java.awt.image.ImageProducer JavaDoc;
12 import java.awt.image.ColorModel JavaDoc;
13 import java.util.Hashtable JavaDoc;
14 import java.util.Vector JavaDoc;
15 import java.util.Enumeration JavaDoc;
16
17 /**
18  * This class is an implementation of the ImageProducer interface which
19  * uses an array to produce pixel values for an Image. Here is an example
20  * which calculates a 100x100 image representing a fade from black to blue
21  * along the X axis and a fade from black to red along the Y axis:
22  * <pre>
23  *
24  * int w = 100;
25  * int h = 100;
26  * int pix[] = new int[w * h];
27  * int index = 0;
28  * for (int y = 0; y < h; y++) {
29  * int red = (y * 255) / (h - 1);
30  * for (int x = 0; x < w; x++) {
31  * int blue = (x * 255) / (w - 1);
32  * pix[index++] = (255 << 24) | (red << 16) | blue;
33  * }
34  * }
35  * Image img = createImage(new MemoryImageSource(w, h, pix, 0, w));
36  *
37  * </pre>
38  * The MemoryImageSource is also capable of managing a memory image which
39  * varies over time to allow animation or custom rendering. Here is an
40  * example showing how to set up the animation source and signal changes
41  * in the data (adapted from the MemoryAnimationSourceDemo by Garth Dickie):
42  * <pre>
43  *
44  * int pixels[];
45  * MemoryImageSource source;
46  *
47  * public void init() {
48  * int width = 50;
49  * int height = 50;
50  * int size = width * height;
51  * pixels = new int[size];
52  *
53  * int value = getBackground().getRGB();
54  * for (int i = 0; i < size; i++) {
55  * pixels[i] = value;
56  * }
57  *
58  * source = new MemoryImageSource(width, height, pixels, 0, width);
59  * source.setAnimated(true);
60  * image = createImage(source);
61  * }
62  *
63  * public void run() {
64  * Thread me = Thread.currentThread( );
65  * me.setPriority(Thread.MIN_PRIORITY);
66  *
67  * while (true) {
68  * try {
69  * thread.sleep(10);
70  * } catch( InterruptedException e ) {
71  * return;
72  * }
73  *
74  * // Modify the values in the pixels array at (x, y, w, h)
75  *
76  * // Send the new data to the interested ImageConsumers
77  * source.newPixels(x, y, w, h);
78  * }
79  * }
80  *
81  * </pre>
82  *
83  * @see ImageProducer
84  *
85  * @version 1.33 07/16/04
86  * @author Jim Graham
87  * @author Animation capabilities inspired by the
88  * MemoryAnimationSource class written by Garth Dickie
89  */

90 public class MemoryImageSource implements ImageProducer JavaDoc {
91     int width;
92     int height;
93     ColorModel JavaDoc model;
94     Object JavaDoc pixels;
95     int pixeloffset;
96     int pixelscan;
97     Hashtable JavaDoc properties;
98     Vector JavaDoc theConsumers = new Vector JavaDoc();
99     boolean animating;
100     boolean fullbuffers;
101
102     /**
103      * Constructs an ImageProducer object which uses an array of bytes
104      * to produce data for an Image object.
105      * @param w the width of the rectangle of pixels
106      * @param h the height of the rectangle of pixels
107      * @param cm the specified <code>ColorModel</code>
108      * @param pix an array of pixels
109      * @param off the offset into the array of where to store the
110      * first pixel
111      * @param scan the distance from one row of pixels to the next in
112      * the array
113      * @see java.awt.Component#createImage
114      */

115     public MemoryImageSource(int w, int h, ColorModel JavaDoc cm,
116                  byte[] pix, int off, int scan) {
117     initialize(w, h, cm, (Object JavaDoc) pix, off, scan, null);
118     }
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      * @param props a list of properties that the <code>ImageProducer</code>
132      * uses to process an image
133      * @see java.awt.Component#createImage
134      */

135     public MemoryImageSource(int w, int h, ColorModel JavaDoc cm,
136                  byte[] pix, int off, int scan,
137                  Hashtable JavaDoc<?,?> props)
138     {
139     initialize(w, h, cm, (Object JavaDoc) 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      * @param w the width of the rectangle of pixels
146      * @param h the height of the rectangle of pixels
147      * @param cm the specified <code>ColorModel</code>
148      * @param pix an array of pixels
149      * @param off the offset into the array of where to store the
150      * first pixel
151      * @param scan the distance from one row of pixels to the next in
152      * the array
153      * @see java.awt.Component#createImage
154      */

155     public MemoryImageSource(int w, int h, ColorModel JavaDoc cm,
156                  int[] pix, int off, int scan) {
157     initialize(w, h, cm, (Object JavaDoc) pix, off, scan, null);
158     }
159
160     /**
161      * Constructs an ImageProducer object which uses an array of integers
162      * to produce data for an Image object.
163      * @param w the width of the rectangle of pixels
164      * @param h the height of the rectangle of pixels
165      * @param cm the specified <code>ColorModel</code>
166      * @param pix an array of pixels
167      * @param off the offset into the array of where to store the
168      * first pixel
169      * @param scan the distance from one row of pixels to the next in
170      * the array
171      * @param props a list of properties that the <code>ImageProducer</code>
172      * uses to process an image
173      * @see java.awt.Component#createImage
174      */

175     public MemoryImageSource(int w, int h, ColorModel JavaDoc cm,
176                  int[] pix, int off, int scan,
177                  Hashtable JavaDoc<?,?> props)
178     {
179     initialize(w, h, cm, (Object JavaDoc) pix, off, scan, props);
180     }
181
182     private void initialize(int w, int h, ColorModel JavaDoc cm,
183                 Object JavaDoc pix, int off, int scan, Hashtable JavaDoc props) {
184     width = w;
185     height = h;
186     model = cm;
187     pixels = pix;
188     pixeloffset = off;
189     pixelscan = scan;
190     if (props == null) {
191         props = new Hashtable JavaDoc();
192     }
193     properties = props;
194     }
195
196     /**
197      * Constructs an ImageProducer object which uses an array of integers
198      * in the default RGB ColorModel to produce data for an Image object.
199      * @param w the width of the rectangle of pixels
200      * @param h the height of the rectangle of pixels
201      * @param pix an array of pixels
202      * @param off the offset into the array of where to store the
203      * first pixel
204      * @param scan the distance from one row of pixels to the next in
205      * the array
206      * @see java.awt.Component#createImage
207      * @see ColorModel#getRGBdefault
208      */

209     public MemoryImageSource(int w, int h, int pix[], int off, int scan) {
210     initialize(w, h, ColorModel.getRGBdefault(),
211            (Object JavaDoc) pix, off, scan, null);
212     }
213
214     /**
215      * Constructs an ImageProducer object which uses an array of integers
216      * in the default RGB ColorModel to produce data for an Image object.
217      * @param w the width of the rectangle of pixels
218      * @param h the height of the rectangle of pixels
219      * @param pix an array of pixels
220      * @param off the offset into the array of where to store the
221      * first pixel
222      * @param scan the distance from one row of pixels to the next in
223      * the array
224      * @param props a list of properties that the <code>ImageProducer</code>
225      * uses to process an image
226      * @see java.awt.Component#createImage
227      * @see ColorModel#getRGBdefault
228      */

229     public MemoryImageSource(int w, int h, int pix[], int off, int scan,
230                  Hashtable JavaDoc<?,?> props)
231     {
232     initialize(w, h, ColorModel.getRGBdefault(),
233            (Object JavaDoc) pix, off, scan, props);
234     }
235
236     /**
237      * Adds an ImageConsumer to the list of consumers interested in
238      * data for this image.
239      * @param ic the specified <code>ImageConsumer</code>
240      * @throws NullPointerException if the specified
241      * <code>ImageConsumer</code> is null
242      * @see ImageConsumer
243      */

244     public synchronized void addConsumer(ImageConsumer JavaDoc ic) {
245     if (theConsumers.contains(ic)) {
246         return;
247     }
248     theConsumers.addElement(ic);
249     try {
250         initConsumer(ic);
251         sendPixels(ic, 0, 0, width, height);
252         if (isConsumer(ic)) {
253         ic.imageComplete(animating
254                  ? ImageConsumer.SINGLEFRAMEDONE
255                  : ImageConsumer.STATICIMAGEDONE);
256         if (!animating && isConsumer(ic)) {
257             ic.imageComplete(ImageConsumer.IMAGEERROR);
258             removeConsumer(ic);
259         }
260         }
261     } catch (Exception JavaDoc e) {
262         if (isConsumer(ic)) {
263         ic.imageComplete(ImageConsumer.IMAGEERROR);
264         }
265     }
266     }
267
268     /**
269      * Determines if an ImageConsumer is on the list of consumers currently
270      * interested in data for this image.
271      * @param ic the specified <code>ImageConsumer</code>
272      * @return <code>true</code> if the <code>ImageConsumer</code>
273      * is on the list; <code>false</code> otherwise.
274      * @see ImageConsumer
275      */

276     public synchronized boolean isConsumer(ImageConsumer JavaDoc ic) {
277     return theConsumers.contains(ic);
278     }
279
280     /**
281      * Removes an ImageConsumer from the list of consumers interested in
282      * data for this image.
283      * @param ic the specified <code>ImageConsumer</code>
284      * @see ImageConsumer
285      */

286     public synchronized void removeConsumer(ImageConsumer JavaDoc ic) {
287     theConsumers.removeElement(ic);
288     }
289
290     /**
291      * Adds an ImageConsumer to the list of consumers interested in
292      * data for this image and immediately starts delivery of the
293      * image data through the ImageConsumer interface.
294      * @param ic the specified <code>ImageConsumer</code>
295      * image data through the ImageConsumer interface.
296      * @see ImageConsumer
297      */

298     public void startProduction(ImageConsumer JavaDoc ic) {
299     addConsumer(ic);
300     }
301
302     /**
303      * Requests that a given ImageConsumer have the image data delivered
304      * one more time in top-down, left-right order.
305      * @param ic the specified <code>ImageConsumer</code>
306      * @see ImageConsumer
307      */

308     public void requestTopDownLeftRightResend(ImageConsumer JavaDoc ic) {
309     // Ignored. The data is either single frame and already in TDLR
310
// format or it is multi-frame and TDLR resends aren't critical.
311
}
312
313     /**
314      * Changes this memory image into a multi-frame animation or a
315      * single-frame static image depending on the animated parameter.
316      * <p>This method should be called immediately after the
317      * MemoryImageSource is constructed and before an image is
318      * created with it to ensure that all ImageConsumers will
319      * receive the correct multi-frame data. If an ImageConsumer
320      * is added to this ImageProducer before this flag is set then
321      * that ImageConsumer will see only a snapshot of the pixel
322      * data that was available when it connected.
323      * @param animated <code>true</code> if the image is a
324      * multi-frame animation
325      */

326     public synchronized void setAnimated(boolean animated) {
327     this.animating = animated;
328     if (!animating) {
329         Enumeration JavaDoc enum_ = theConsumers.elements();
330         while (enum_.hasMoreElements()) {
331             ImageConsumer JavaDoc ic = (ImageConsumer JavaDoc) enum_.nextElement();
332         ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
333         if (isConsumer(ic)) {
334             ic.imageComplete(ImageConsumer.IMAGEERROR);
335         }
336         }
337         theConsumers.removeAllElements();
338     }
339     }
340
341     /**
342      * Specifies whether this animated memory image should always be
343      * updated by sending the complete buffer of pixels whenever
344      * there is a change.
345      * This flag is ignored if the animation flag is not turned on
346      * through the setAnimated() method.
347      * <p>This method should be called immediately after the
348      * MemoryImageSource is constructed and before an image is
349      * created with it to ensure that all ImageConsumers will
350      * receive the correct pixel delivery hints.
351      * @param fullbuffers <code>true</code> if the complete pixel
352      * buffer should always
353      * be sent
354      * @see #setAnimated
355      */

356     public synchronized void setFullBufferUpdates(boolean fullbuffers) {
357     if (this.fullbuffers == fullbuffers) {
358         return;
359     }
360     this.fullbuffers = fullbuffers;
361     if (animating) {
362         Enumeration JavaDoc enum_ = theConsumers.elements();
363         while (enum_.hasMoreElements()) {
364             ImageConsumer JavaDoc ic = (ImageConsumer JavaDoc) enum_.nextElement();
365         ic.setHints(fullbuffers
366                 ? (ImageConsumer.TOPDOWNLEFTRIGHT |
367                    ImageConsumer.COMPLETESCANLINES)
368                 : ImageConsumer.RANDOMPIXELORDER);
369         }
370     }
371     }
372
373     /**
374      * Sends a whole new buffer of pixels to any ImageConsumers that
375      * are currently interested in the data for this image and notify
376      * them that an animation frame is complete.
377      * This method only has effect if the animation flag has been
378      * turned on through the setAnimated() method.
379      * @see #newPixels(int, int, int, int, boolean)
380      * @see ImageConsumer
381      * @see #setAnimated
382      */

383     public void newPixels() {
384     newPixels(0, 0, width, height, true);
385     }
386
387     /**
388      * Sends a rectangular region of the buffer of pixels to any
389      * ImageConsumers that are currently interested in the data for
390      * this image and notify them that an animation frame is complete.
391      * This method only has effect if the animation flag has been
392      * turned on through the setAnimated() method.
393      * If the full buffer update flag was turned on with the
394      * setFullBufferUpdates() method then the rectangle parameters
395      * will be ignored and the entire buffer will always be sent.
396      * @param x the x coordinate of the upper left corner of the rectangle
397      * of pixels to be sent
398      * @param y the y coordinate of the upper left corner of the rectangle
399      * of pixels to be sent
400      * @param w the width of the rectangle of pixels to be sent
401      * @param h the height of the rectangle of pixels to be sent
402      * @see #newPixels(int, int, int, int, boolean)
403      * @see ImageConsumer
404      * @see #setAnimated
405      * @see #setFullBufferUpdates
406      */

407     public synchronized void newPixels(int x, int y, int w, int h) {
408     newPixels(x, y, w, h, true);
409     }
410
411     /**
412      * Sends a rectangular region of the buffer of pixels to any
413      * ImageConsumers that are currently interested in the data for
414      * this image.
415      * If the framenotify parameter is true then the consumers are
416      * also notified that an animation frame is complete.
417      * This method only has effect if the animation flag has been
418      * turned on through the setAnimated() method.
419      * If the full buffer update flag was turned on with the
420      * setFullBufferUpdates() method then the rectangle parameters
421      * will be ignored and the entire buffer will always be sent.
422      * @param x the x coordinate of the upper left corner of the rectangle
423      * of pixels to be sent
424      * @param y the y coordinate of the upper left corner of the rectangle
425      * of pixels to be sent
426      * @param w the width of the rectangle of pixels to be sent
427      * @param h the height of the rectangle of pixels to be sent
428      * @param framenotify <code>true</code> if the consumers should be sent a
429      * {@link ImageConsumer#SINGLEFRAMEDONE SINGLEFRAMEDONE} notification
430      * @see ImageConsumer
431      * @see #setAnimated
432      * @see #setFullBufferUpdates
433      */

434     public synchronized void newPixels(int x, int y, int w, int h,
435                        boolean framenotify) {
436     if (animating) {
437         if (fullbuffers) {
438         x = y = 0;
439         w = width;
440         h = height;
441         } else {
442         if (x < 0) {
443             w += x;
444             x = 0;
445         }
446         if (x + w > width) {
447             w = width - x;
448         }
449         if (y < 0) {
450             h += y;
451             y = 0;
452         }
453         if (y + h > height) {
454             h = height - y;
455         }
456         }
457         if ((w <= 0 || h <= 0) && !framenotify) {
458         return;
459         }
460         Enumeration JavaDoc enum_ = theConsumers.elements();
461         while (enum_.hasMoreElements()) {
462             ImageConsumer JavaDoc ic = (ImageConsumer JavaDoc) enum_.nextElement();
463         if (w > 0 && h > 0) {
464             sendPixels(ic, x, y, w, h);
465         }
466         if (framenotify && isConsumer(ic)) {
467             ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
468         }
469         }
470     }
471     }
472
473     /**
474      * Changes to a new byte array to hold the pixels for this image.
475      * If the animation flag has been turned on through the setAnimated()
476      * method, then the new pixels will be immediately delivered to any
477      * ImageConsumers that are currently interested in the data for
478      * this image.
479      * @param newpix the new pixel array
480      * @param newmodel the specified <code>ColorModel</code>
481      * @param offset the offset into the array
482      * @param scansize the distance from one row of pixels to the next in
483      * the array
484      * @see #newPixels(int, int, int, int, boolean)
485      * @see #setAnimated
486      */

487     public synchronized void newPixels(byte[] newpix, ColorModel JavaDoc newmodel,
488                        int offset, int scansize) {
489     this.pixels = newpix;
490     this.model = newmodel;
491     this.pixeloffset = offset;
492     this.pixelscan = scansize;
493     newPixels();
494     }
495
496     /**
497      * Changes to a new int array to hold the pixels for this image.
498      * If the animation flag has been turned on through the setAnimated()
499      * method, then the new pixels will be immediately delivered to any
500      * ImageConsumers that are currently interested in the data for
501      * this image.
502      * @param newpix the new pixel array
503      * @param newmodel the specified <code>ColorModel</code>
504      * @param offset the offset into the array
505      * @param scansize the distance from one row of pixels to the next in
506      * the array
507      * @see #newPixels(int, int, int, int, boolean)
508      * @see #setAnimated
509      */

510     public synchronized void newPixels(int[] newpix, ColorModel JavaDoc newmodel,
511                        int offset, int scansize) {
512     this.pixels = newpix;
513     this.model = newmodel;
514     this.pixeloffset = offset;
515     this.pixelscan = scansize;
516     newPixels();
517     }
518
519     private void initConsumer(ImageConsumer JavaDoc ic) {
520     if (isConsumer(ic)) {
521         ic.setDimensions(width, height);
522     }
523     if (isConsumer(ic)) {
524         ic.setProperties(properties);
525     }
526     if (isConsumer(ic)) {
527         ic.setColorModel(model);
528     }
529     if (isConsumer(ic)) {
530         ic.setHints(animating
531             ? (fullbuffers
532                ? (ImageConsumer.TOPDOWNLEFTRIGHT |
533                   ImageConsumer.COMPLETESCANLINES)
534                : ImageConsumer.RANDOMPIXELORDER)
535             : (ImageConsumer.TOPDOWNLEFTRIGHT |
536                ImageConsumer.COMPLETESCANLINES |
537                ImageConsumer.SINGLEPASS |
538                ImageConsumer.SINGLEFRAME));
539     }
540     }
541
542     private void sendPixels(ImageConsumer JavaDoc ic, int x, int y, int w, int h) {
543     int off = pixeloffset + pixelscan * y + x;
544     if (isConsumer(ic)) {
545         if (pixels instanceof byte[]) {
546         ic.setPixels(x, y, w, h, model,
547                  ((byte[]) pixels), off, pixelscan);
548         } else {
549         ic.setPixels(x, y, w, h, model,
550                  ((int[]) pixels), off, pixelscan);
551         }
552     }
553     }
554 }
555
Popular Tags