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    }