001/* PixelGrabber.java -- retrieve a subset of an image's data
002   Copyright (C) 1999, 2003, 2004  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt.image;
040
041import java.awt.Image;
042import java.util.Hashtable;
043
044/**
045 * PixelGrabber is an ImageConsumer that extracts a rectangular region
046 * of pixels from an Image.
047 */
048public 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}