001 /* PixelGrabber.java -- retrieve a subset of an image's data
002 Copyright (C) 1999, 2003, 2004 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath 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 for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt.image;
040
041 import java.awt.Image;
042 import java.util.Hashtable;
043
044 /**
045 * PixelGrabber is an ImageConsumer that extracts a rectangular region
046 * of pixels from an Image.
047 */
048 public class PixelGrabber implements ImageConsumer
049 {
050 int x, y, offset;
051 int width = -1;
052 int height = -1;
053 int scansize = -1;
054 boolean forceRGB = true;
055
056 ColorModel model = ColorModel.getRGBdefault();
057 int hints;
058 Hashtable<?,?> props;
059
060 int int_pixel_buffer[];
061 boolean ints_delivered = false;
062 byte byte_pixel_buffer[];
063 boolean bytes_delivered = false;
064
065 ImageProducer ip;
066 int observerStatus;
067 int consumerStatus;
068
069 private Thread grabberThread;
070 boolean grabbing = false;
071
072 /**
073 * Construct a PixelGrabber that will retrieve RGB data from a given
074 * Image.
075 *
076 * The RGB data will be retrieved from a rectangular region
077 * <code>(x, y, w, h)</code> within the image. The data will be
078 * stored in the provided <code>pix</code> array, which must have
079 * been initialized to a size of at least <code>w * h</code>. The
080 * data for a pixel (m, n) in the grab rectangle will be stored at
081 * <code>pix[(n - y) * scansize + (m - x) + off]</code>.
082 *
083 * @param img the Image from which to grab pixels
084 * @param x the x coordinate, relative to <code>img</code>'s
085 * top-left corner, of the grab rectangle's top-left pixel
086 * @param y the y coordinate, relative to <code>img</code>'s
087 * top-left corner, of the grab rectangle's top-left pixel
088 * @param w the width of the grab rectangle, in pixels
089 * @param h the height of the grab rectangle, in pixels
090 * @param pix the array in which to store grabbed RGB pixel data
091 * @param off the offset into the <code>pix</code> array at which to
092 * start storing RGB data
093 * @param scansize a set of <code>scansize</code> consecutive
094 * elements in the <code>pix</code> array represents one row of
095 * pixels in the grab rectangle
096 */
097 public PixelGrabber(Image img, int x, int y, int w, int h,
098 int pix[], int off, int scansize)
099 {
100 this (img.getSource(), x, y, w, h, pix, off, scansize);
101 }
102
103 /**
104 * Construct a PixelGrabber that will retrieve RGB data from a given
105 * ImageProducer.
106 *
107 * The RGB data will be retrieved from a rectangular region
108 * <code>(x, y, w, h)</code> within the image produced by
109 * <code>ip</code>. The data will be stored in the provided
110 * <code>pix</code> array, which must have been initialized to a
111 * size of at least <code>w * h</code>. The data for a pixel (m, n)
112 * in the grab rectangle will be stored at
113 * <code>pix[(n - y) * scansize + (m - x) + off]</code>.
114 *
115 * @param ip the ImageProducer from which to grab pixels. This can
116 * be null.
117 * @param x the x coordinate of the grab rectangle's top-left pixel,
118 * specified relative to the top-left corner of the image produced
119 * by <code>ip</code>
120 * @param y the y coordinate of the grab rectangle's top-left pixel,
121 * specified relative to the top-left corner of the image produced
122 * by <code>ip</code>
123 * @param w the width of the grab rectangle, in pixels
124 * @param h the height of the grab rectangle, in pixels
125 * @param pix the array in which to store grabbed RGB pixel data
126 * @param off the offset into the <code>pix</code> array at which to
127 * start storing RGB data
128 * @param scansize a set of <code>scansize</code> consecutive
129 * elements in the <code>pix</code> array represents one row of
130 * pixels in the grab rectangle
131 */
132 public PixelGrabber(ImageProducer ip, int x, int y, int w, int h,
133 int pix[], int off, int scansize)
134 {
135 this.ip = ip;
136 this.x = x;
137 this.y = y;
138 this.width = w;
139 this.height = h;
140 this.offset = off;
141 this.scansize = scansize;
142
143 int_pixel_buffer = pix;
144 // Initialize the byte array in case ip sends us byte-formatted
145 // pixel data.
146 byte_pixel_buffer = new byte[pix.length * 4];
147 }
148
149 /**
150 * Construct a PixelGrabber that will retrieve data from a given
151 * Image.
152 *
153 * The RGB data will be retrieved from a rectangular region
154 * <code>(x, y, w, h)</code> within the image. The data will be
155 * stored in an internal array which can be accessed by calling
156 * <code>getPixels</code>. The data for a pixel (m, n) in the grab
157 * rectangle will be stored in the returned array at index
158 * <code>(n - y) * scansize + (m - x) + off</code>.
159 * If forceRGB is false, then the returned data will be not be
160 * converted to RGB from its format in <code>img</code>.
161 *
162 * If <code>w</code> is negative, the width of the grab region will
163 * be from x to the right edge of the image. Likewise, if
164 * <code>h</code> is negative, the height of the grab region will be
165 * from y to the bottom edge of the image.
166 *
167 * @param img the Image from which to grab pixels
168 * @param x the x coordinate, relative to <code>img</code>'s
169 * top-left corner, of the grab rectangle's top-left pixel
170 * @param y the y coordinate, relative to <code>img</code>'s
171 * top-left corner, of the grab rectangle's top-left pixel
172 * @param w the width of the grab rectangle, in pixels
173 * @param h the height of the grab rectangle, in pixels
174 * @param forceRGB true to force conversion of the rectangular
175 * region's pixel data to RGB
176 */
177 public PixelGrabber(Image img,
178 int x, int y,
179 int w, int h,
180 boolean forceRGB)
181 {
182 this.ip = img.getSource();
183
184 if (this.ip == null)
185 throw new NullPointerException("The ImageProducer must not be null.");
186
187 this.x = x;
188 this.y = y;
189 width = w;
190 height = h;
191 // If width or height is negative, postpone pixel buffer
192 // initialization until setDimensions is called back by ip.
193 if (width >= 0 && height >= 0)
194 {
195 int_pixel_buffer = new int[width * height];
196 byte_pixel_buffer = new byte[width * height];
197 }
198 this.forceRGB = forceRGB;
199 }
200
201 /**
202 * Start grabbing pixels.
203 *
204 * Spawns an image production thread that calls back to this
205 * PixelGrabber's ImageConsumer methods.
206 */
207 public synchronized void startGrabbing()
208 {
209 // Make sure we're not already grabbing.
210 if (grabbing == false)
211 {
212 grabbing = true;
213 grabberThread = new Thread ()
214 {
215 public void run ()
216 {
217 try
218 {
219 ip.startProduction (PixelGrabber.this);
220 }
221 catch (Exception ex)
222 {
223 imageComplete(ImageConsumer.IMAGEABORTED);
224 }
225 }
226 };
227 grabberThread.start ();
228 }
229 }
230
231 /**
232 * Abort pixel grabbing.
233 */
234 public synchronized void abortGrabbing()
235 {
236 if (grabbing)
237 {
238 // Interrupt the grabbing thread.
239 Thread moribund = grabberThread;
240 grabberThread = null;
241 moribund.interrupt();
242
243 imageComplete (ImageConsumer.IMAGEABORTED);
244 }
245 }
246
247 /**
248 * Have our Image or ImageProducer start sending us pixels via our
249 * ImageConsumer methods and wait for all pixels in the grab
250 * rectangle to be delivered.
251 *
252 * @return true if successful, false on abort or error
253 *
254 * @throws InterruptedException if interrupted by another thread.
255 */
256 public synchronized boolean grabPixels() throws InterruptedException
257 {
258 return grabPixels(0);
259 }
260
261 /**
262 * grabPixels's behavior depends on the value of <code>ms</code>.
263 *
264 * If ms < 0, return true if all pixels from the source image have
265 * been delivered, false otherwise. Do not wait.
266 *
267 * If ms >= 0 then we request that our Image or ImageProducer start
268 * delivering pixels to us via our ImageConsumer methods.
269 *
270 * If ms > 0, wait at most <code>ms</code> milliseconds for
271 * delivery of all pixels within the grab rectangle.
272 *
273 * If ms == 0, wait until all pixels have been delivered.
274 *
275 * @return true if all pixels from the source image have been
276 * delivered, false otherwise
277 *
278 * @throws InterruptedException if this thread is interrupted while
279 * we are waiting for pixels to be delivered
280 */
281 public synchronized boolean grabPixels(long ms) throws InterruptedException
282 {
283 if (ms < 0)
284 return ((observerStatus & (ImageObserver.FRAMEBITS
285 | ImageObserver.ALLBITS)) != 0);
286
287 // Spawn a new ImageProducer thread to send us the image data via
288 // our ImageConsumer methods.
289 startGrabbing();
290
291 if (ms > 0)
292 {
293 long stop_time = System.currentTimeMillis() + ms;
294 long time_remaining;
295 while (grabbing)
296 {
297 time_remaining = stop_time - System.currentTimeMillis();
298 if (time_remaining <= 0)
299 break;
300 wait (time_remaining);
301 }
302 abortGrabbing ();
303 }
304 else
305 wait ();
306
307 // If consumerStatus is non-zero then the image is done loading or
308 // an error has occurred.
309 if (consumerStatus != 0)
310 return setObserverStatus ();
311
312 return ((observerStatus & (ImageObserver.FRAMEBITS
313 | ImageObserver.ALLBITS)) != 0);
314 }
315
316 // Set observer status flags based on the current consumer status
317 // flags. Return true if the consumer flags indicate that the
318 // image was loaded successfully, or false otherwise.
319 private synchronized boolean setObserverStatus ()
320 {
321 boolean retval = false;
322
323 if ((consumerStatus & IMAGEERROR) != 0)
324 observerStatus |= ImageObserver.ERROR;
325
326 if ((consumerStatus & IMAGEABORTED) != 0)
327 observerStatus |= ImageObserver.ABORT;
328
329 if ((consumerStatus & STATICIMAGEDONE) != 0)
330 {
331 observerStatus |= ImageObserver.ALLBITS;
332 retval = true;
333 }
334
335 if ((consumerStatus & SINGLEFRAMEDONE) != 0)
336 {
337 observerStatus |= ImageObserver.FRAMEBITS;
338 retval = true;
339 }
340
341 return retval;
342 }
343
344 /**
345 * @return the status of the pixel grabbing thread, represented by a
346 * bitwise OR of ImageObserver flags
347 */
348 public synchronized int getStatus()
349 {
350 return observerStatus;
351 }
352
353 /**
354 * @return the width of the grab rectangle in pixels, or a negative
355 * number if the ImageProducer has not yet called our setDimensions
356 * method
357 */
358 public synchronized int getWidth()
359 {
360 return width;
361 }
362
363 /**
364 * @return the height of the grab rectangle in pixels, or a negative
365 * number if the ImageProducer has not yet called our setDimensions
366 * method
367 */
368 public synchronized int getHeight()
369 {
370 return height;
371 }
372
373 /**
374 * @return a byte array of pixel data if ImageProducer delivered
375 * pixel data using the byte[] variant of setPixels, or an int array
376 * otherwise
377 */
378 public synchronized Object getPixels()
379 {
380 if (ints_delivered)
381 return int_pixel_buffer;
382 else if (bytes_delivered)
383 return byte_pixel_buffer;
384 else
385 return null;
386 }
387
388 /**
389 * @return the ColorModel currently being used for the majority of
390 * pixel data conversions
391 */
392 public synchronized ColorModel getColorModel()
393 {
394 return model;
395 }
396
397 /**
398 * Our <code>ImageProducer</code> calls this method to indicate the
399 * size of the image being produced.
400 *
401 * setDimensions is an ImageConsumer method. None of PixelGrabber's
402 * ImageConsumer methods should be called by code that instantiates
403 * a PixelGrabber. They are only made public so they can be called
404 * by the PixelGrabber's ImageProducer.
405 *
406 * @param width the width of the image
407 * @param height the height of the image
408 */
409 public synchronized void setDimensions(int width, int height)
410 {
411 // Our width wasn't set when we were constructed. Set our width
412 // so that the grab region includes all pixels from x to the right
413 // edge of the source image.
414 if (this.width < 0)
415 this.width = width - x;
416
417 // Our height wasn't set when we were constructed. Set our height
418 // so that the grab region includes all pixels from y to the
419 // bottom edge of the source image.
420 if (this.height < 0)
421 this.height = height - y;
422
423 if (scansize < 0)
424 scansize = this.width;
425
426 if (int_pixel_buffer == null)
427 int_pixel_buffer = new int[this.width * this.height];
428
429 if (byte_pixel_buffer == null)
430 byte_pixel_buffer = new byte[this.width * this.height];
431 }
432
433 /**
434 * Our <code>ImageProducer</code> may call this method to send us a
435 * list of its image's properties.
436 *
437 * setProperties is an ImageConsumer method. None of PixelGrabber's
438 * ImageConsumer methods should be called by code that instantiates
439 * a PixelGrabber. They are only made public so they can be called
440 * by the PixelGrabber's ImageProducer.
441 *
442 * @param props a list of properties associated with the image being
443 * produced
444 */
445 public synchronized void setProperties(Hashtable<?,?> props)
446 {
447 this.props = props;
448 }
449
450 /**
451 * Our ImageProducer will call <code>setColorModel</code> to
452 * indicate the model used by the majority of calls to
453 * <code>setPixels</code>. Each call to <code>setPixels</code>
454 * could however indicate a different <code>ColorModel</code>.
455 *
456 * setColorModel is an ImageConsumer method. None of PixelGrabber's
457 * ImageConsumer methods should be called by code that instantiates
458 * a PixelGrabber. They are only made public so they can be called
459 * by the PixelGrabber's ImageProducer.
460 *
461 * @param model the color model to be used most often by setPixels
462 *
463 * @see ColorModel
464 */
465 public synchronized void setColorModel(ColorModel model)
466 {
467 this.model = model;
468 }
469
470 /**
471 * Our <code>ImageProducer</code> may call this method with a
472 * bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
473 * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
474 * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>.
475 *
476 * setHints is an ImageConsumer method. None of PixelGrabber's
477 * ImageConsumer methods should be called by code that instantiates
478 * a PixelGrabber. They are only made public so they can be called
479 * by the PixelGrabber's ImageProducer.
480 *
481 * @param flags a bit mask of hints
482 */
483 public synchronized void setHints(int flags)
484 {
485 hints = flags;
486 }
487
488 /**
489 * Our ImageProducer calls setPixels to deliver a subset of its
490 * pixels.
491 *
492 * Each element of the pixels array represents one pixel. The
493 * pixel data is formatted according to the color model model.
494 * The x and y parameters are the coordinates of the rectangular
495 * region of pixels being delivered to this ImageConsumer,
496 * specified relative to the top left corner of the image being
497 * produced. Likewise, w and h are the pixel region's dimensions.
498 *
499 * @param x x coordinate of pixel block
500 * @param y y coordinate of pixel block
501 * @param w width of pixel block
502 * @param h height of pixel block
503 * @param model color model used to interpret pixel data
504 * @param pixels pixel block data
505 * @param offset offset into pixels array
506 * @param scansize width of one row in the pixel block
507 */
508 public synchronized void setPixels(int x, int y, int w, int h,
509 ColorModel model, byte[] pixels,
510 int offset, int scansize)
511 {
512 ColorModel currentModel;
513 if (model != null)
514 currentModel = model;
515 else
516 currentModel = this.model;
517
518 for(int yp = y; yp < (y + h); yp++)
519 {
520 for(int xp = x; xp < (x + w); xp++)
521 {
522 // Check if the coordinates (xp, yp) are within the
523 // pixel block that we are grabbing.
524 if(xp >= this.x
525 && yp >= this.y
526 && xp < (this.x + this.width)
527 && yp < (this.y + this.height))
528 {
529 int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset;
530 int p = (yp - y) * scansize + (xp - x) + offset;
531 if (forceRGB)
532 {
533 ints_delivered = true;
534
535 int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF);
536 }
537 else
538 {
539 bytes_delivered = true;
540
541 byte_pixel_buffer[i] = pixels[p];
542 }
543 }
544 }
545 }
546 }
547
548 /**
549 * Our ImageProducer calls setPixels to deliver a subset of its
550 * pixels.
551 *
552 * Each element of the pixels array represents one pixel. The
553 * pixel data is formatted according to the color model model.
554 * The x and y parameters are the coordinates of the rectangular
555 * region of pixels being delivered to this ImageConsumer,
556 * specified relative to the top left corner of the image being
557 * produced. Likewise, w and h are the pixel region's dimensions.
558 *
559 * @param x x coordinate of pixel block
560 * @param y y coordinate of pixel block
561 * @param w width of pixel block
562 * @param h height of pixel block
563 * @param model color model used to interpret pixel data
564 * @param pixels pixel block data
565 * @param offset offset into pixels array
566 * @param scansize width of one row in the pixel block
567 */
568 public synchronized void setPixels(int x, int y, int w, int h,
569 ColorModel model, int[] pixels,
570 int offset, int scansize)
571 {
572 ColorModel currentModel;
573 if (model != null)
574 currentModel = model;
575 else
576 currentModel = this.model;
577
578 ints_delivered = true;
579
580 for(int yp = y; yp < (y + h); yp++)
581 {
582 for(int xp = x; xp < (x + w); xp++)
583 {
584 // Check if the coordinates (xp, yp) are within the
585 // pixel block that we are grabbing.
586 if(xp >= this.x
587 && yp >= this.y
588 && xp < (this.x + this.width)
589 && yp < (this.y + this.height))
590 {
591 int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset;
592 int p = (yp - y) * scansize + (xp - x) + offset;
593 if (forceRGB)
594 int_pixel_buffer[i] = currentModel.getRGB (pixels[p]);
595 else
596 int_pixel_buffer[i] = pixels[p];
597 }
598 }
599 }
600 }
601
602 /**
603 * Our <code>ImageProducer</code> calls this method to inform us
604 * that a single frame or the entire image is complete. The method
605 * is also used to inform us of an error in loading or producing the
606 * image.
607 *
608 * @param status the status of image production, represented by a
609 * bitwise OR of ImageConsumer flags
610 */
611 public synchronized void imageComplete(int status)
612 {
613 consumerStatus = status;
614 setObserverStatus ();
615 grabbing = false;
616 if (ip != null)
617 ip.removeConsumer (this);
618
619 notifyAll ();
620 }
621
622 /**
623 * @return the return value of getStatus
624 *
625 * @specnote The newer getStatus should be used in place of status.
626 */
627 public synchronized int status()
628 {
629 return getStatus();
630 }
631 }