Source for java.awt.image.PixelGrabber

   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:    */
  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:    */
 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: }