1 /* PixelGrabber.java -- retrieve a subset of an image's data
2    Copyright (C) 1999, 2003, 2004  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package java.awt.image;
40 
41 import java.awt.Image;
42 import java.util.Hashtable;
43 
44 /**
45  * PixelGrabber is an ImageConsumer that extracts a rectangular region
46  * of pixels from an Image.
47  */
48 public class PixelGrabber implements ImageConsumer
49 {
50   int x, y, offset;
51   int width = -1;
52   int height = -1;
53   int scansize = -1;
54   boolean forceRGB = true;
55 
56   ColorModel model = ColorModel.getRGBdefault();
57   int hints;
58   Hashtable<?,?> props;
59 
60   int int_pixel_buffer[];
61   boolean ints_delivered = false;
62   byte byte_pixel_buffer[];
63   boolean bytes_delivered = false;
64 
65   ImageProducer ip;
66   int observerStatus;
67   int consumerStatus;
68 
69   private Thread grabberThread;
70   boolean grabbing = false;
71 
72   /**
73    * Construct a PixelGrabber that will retrieve RGB data from a given
74    * Image.
75    *
76    * The RGB data will be retrieved from a rectangular region
77    * <code>(x, y, w, h)</code> within the image.  The data will be
78    * stored in the provided <code>pix</code> array, which must have
79    * been initialized to a size of at least <code>w * h</code>.  The
80    * data for a pixel (m, n) in the grab rectangle will be stored at
81    * <code>pix[(n - y) * scansize + (m - x) + off]</code>.
82    *
83    * @param img the Image from which to grab pixels
84    * @param x the x coordinate, relative to <code>img</code>'s
85    * top-left corner, of the grab rectangle's top-left pixel
86    * @param y the y coordinate, relative to <code>img</code>'s
87    * top-left corner, of the grab rectangle's top-left pixel
88    * @param w the width of the grab rectangle, in pixels
89    * @param h the height of the grab rectangle, in pixels
90    * @param pix the array in which to store grabbed RGB pixel data
91    * @param off the offset into the <code>pix</code> array at which to
92    * start storing RGB data
93    * @param scansize a set of <code>scansize</code> consecutive
94    * elements in the <code>pix</code> array represents one row of
95    * pixels in the grab rectangle
96    */
PixelGrabber(Image img, int x, int y, int w, int h, int pix[], int off, int scansize)97   public PixelGrabber(Image img, int x, int y, int w, int h,
98                       int pix[], int off, int scansize)
99   {
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    */
PixelGrabber(ImageProducer ip, int x, int y, int w, int h, int pix[], int off, int scansize)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    */
PixelGrabber(Image img, int x, int y, int w, int h, boolean forceRGB)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    */
startGrabbing()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    */
abortGrabbing()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    */
grabPixels()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    */
grabPixels(long ms)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.
setObserverStatus()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    */
getStatus()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    */
getWidth()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    */
getHeight()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    */
getPixels()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    */
getColorModel()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    */
setDimensions(int width, int height)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    */
setProperties(Hashtable<?,?> props)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    */
setColorModel(ColorModel model)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    */
setHints(int flags)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    */
setPixels(int x, int y, int w, int h, ColorModel model, byte[] pixels, int offset, int scansize)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    */
setPixels(int x, int y, int w, int h, ColorModel model, int[] pixels, int offset, int scansize)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    */
imageComplete(int status)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    */
status()627   public synchronized int status()
628   {
629     return getStatus();
630   }
631 }
632