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
 116:    * @param x the x coordinate of the grab rectangle's top-left pixel,
 117:    * specified relative to the top-left corner of the image produced
 118:    * by <code>ip</code>
 119:    * @param y the y coordinate of the grab rectangle's top-left pixel,
 120:    * specified relative to the top-left corner of the image produced
 121:    * by <code>ip</code>
 122:    * @param w the width of the grab rectangle, in pixels
 123:    * @param h the height of the grab rectangle, in pixels
 124:    * @param pix the array in which to store grabbed RGB pixel data
 125:    * @param off the offset into the <code>pix</code> array at which to
 126:    * start storing RGB data
 127:    * @param scansize a set of <code>scansize</code> consecutive
 128:    * elements in the <code>pix</code> array represents one row of
 129:    * pixels in the grab rectangle
 130:    */
 131:   public PixelGrabber(ImageProducer ip, int x, int y, int w, int h,
 132:               int pix[], int off, int scansize)
 133:   {
 134:     if (ip == null)
 135:       throw new NullPointerException("The ImageProducer must not be null.");
 136: 
 137:     this.ip = ip;
 138:     this.x = x;
 139:     this.y = y;
 140:     this.width = w;
 141:     this.height = h;
 142:     this.offset = off;
 143:     this.scansize = scansize;
 144: 
 145:     int_pixel_buffer = pix;
 146:     // Initialize the byte array in case ip sends us byte-formatted
 147:     // pixel data.
 148:     byte_pixel_buffer = new byte[pix.length * 4];
 149:   }
 150: 
 151:   /**
 152:    * Construct a PixelGrabber that will retrieve data from a given
 153:    * Image.
 154:    *
 155:    * The RGB data will be retrieved from a rectangular region
 156:    * <code>(x, y, w, h)</code> within the image.  The data will be
 157:    * stored in an internal array which can be accessed by calling
 158:    * <code>getPixels</code>.  The data for a pixel (m, n) in the grab
 159:    * rectangle will be stored in the returned array at index
 160:    * <code>(n - y) * scansize + (m - x) + off</code>.
 161:    * If forceRGB is false, then the returned data will be not be
 162:    * converted to RGB from its format in <code>img</code>.
 163:    *
 164:    * If <code>w</code> is negative, the width of the grab region will
 165:    * be from x to the right edge of the image.  Likewise, if
 166:    * <code>h</code> is negative, the height of the grab region will be
 167:    * from y to the bottom edge of the image.
 168:    *
 169:    * @param img the Image from which to grab pixels
 170:    * @param x the x coordinate, relative to <code>img</code>'s
 171:    * top-left corner, of the grab rectangle's top-left pixel
 172:    * @param y the y coordinate, relative to <code>img</code>'s
 173:    * top-left corner, of the grab rectangle's top-left pixel
 174:    * @param w the width of the grab rectangle, in pixels
 175:    * @param h the height of the grab rectangle, in pixels
 176:    * @param forceRGB true to force conversion of the rectangular
 177:    * region's pixel data to RGB
 178:    */
 179:   public PixelGrabber(Image img,
 180:               int x, int y,
 181:               int w, int h,
 182:               boolean forceRGB)
 183:   {
 184:     this.ip = img.getSource();
 185: 
 186:     if (this.ip == null)
 187:       throw new NullPointerException("The ImageProducer must not be null.");
 188: 
 189:     this.x = x;
 190:     this.y = y;
 191:     width = w;
 192:     height = h;
 193:     // If width or height is negative, postpone pixel buffer
 194:     // initialization until setDimensions is called back by ip.
 195:     if (width >= 0 && height >= 0)
 196:       {
 197:     int_pixel_buffer = new int[width * height];
 198:     byte_pixel_buffer = new byte[width * height];
 199:       }
 200:     this.forceRGB = forceRGB;
 201:   }
 202: 
 203:   /**
 204:    * Start grabbing pixels.
 205:    *
 206:    * Spawns an image production thread that calls back to this
 207:    * PixelGrabber's ImageConsumer methods.
 208:    */
 209:   public synchronized void startGrabbing()
 210:   {
 211:     // Make sure we're not already grabbing.
 212:     if (grabbing == false)
 213:       {
 214:     grabbing = true;
 215:     grabberThread = new Thread ()
 216:       {
 217:         public void run ()
 218:         {
 219:               try
 220:                 {
 221:                   ip.startProduction (PixelGrabber.this);
 222:                 }
 223:               catch (Exception ex)
 224:                 {
 225:                   ex.printStackTrace();
 226:                   imageComplete(ImageConsumer.IMAGEABORTED);
 227:                 }
 228:         }
 229:       };
 230:     grabberThread.start ();
 231:       }
 232:   }
 233: 
 234:   /**
 235:    * Abort pixel grabbing.
 236:    */
 237:   public synchronized void abortGrabbing()
 238:   {
 239:     if (grabbing)
 240:       {
 241:     // Interrupt the grabbing thread.
 242:         Thread moribund = grabberThread;
 243:         grabberThread = null;
 244:         moribund.interrupt();
 245: 
 246:     imageComplete (ImageConsumer.IMAGEABORTED);
 247:       }
 248:   }
 249: 
 250:   /**
 251:    * Have our Image or ImageProducer start sending us pixels via our
 252:    * ImageConsumer methods and wait for all pixels in the grab
 253:    * rectangle to be delivered.
 254:    *
 255:    * @return true if successful, false on abort or error
 256:    *
 257:    * @throws InterruptedException if interrupted by another thread.
 258:    */
 259:   public synchronized boolean grabPixels() throws InterruptedException
 260:   {
 261:     return grabPixels(0);
 262:   }
 263: 
 264:   /**
 265:    * grabPixels's behavior depends on the value of <code>ms</code>.
 266:    *
 267:    * If ms < 0, return true if all pixels from the source image have
 268:    * been delivered, false otherwise.  Do not wait.
 269:    *
 270:    * If ms >= 0 then we request that our Image or ImageProducer start
 271:    * delivering pixels to us via our ImageConsumer methods.
 272:    *
 273:    * If ms > 0, wait at most <code>ms</code> milliseconds for
 274:    * delivery of all pixels within the grab rectangle.
 275:    *
 276:    * If ms == 0, wait until all pixels have been delivered.
 277:    *
 278:    * @return true if all pixels from the source image have been
 279:    * delivered, false otherwise
 280:    *
 281:    * @throws InterruptedException if this thread is interrupted while
 282:    * we are waiting for pixels to be delivered
 283:    */
 284:   public synchronized boolean grabPixels(long ms) throws InterruptedException
 285:   {
 286:     if (ms < 0)
 287:       return ((observerStatus & (ImageObserver.FRAMEBITS
 288:                  | ImageObserver.ALLBITS)) != 0);
 289: 
 290:     // Spawn a new ImageProducer thread to send us the image data via
 291:     // our ImageConsumer methods.
 292:     startGrabbing();
 293: 
 294:     if (ms > 0)
 295:       {
 296:     long stop_time = System.currentTimeMillis() + ms;
 297:     long time_remaining;
 298:     while (grabbing)
 299:       {
 300:         time_remaining = stop_time - System.currentTimeMillis();
 301:         if (time_remaining <= 0)
 302:           break;
 303:         wait (time_remaining);
 304:       }
 305:     abortGrabbing ();
 306:       }
 307:     else
 308:       wait ();
 309: 
 310:     // If consumerStatus is non-zero then the image is done loading or
 311:     // an error has occurred.
 312:     if (consumerStatus != 0)
 313:       return setObserverStatus ();
 314: 
 315:     return ((observerStatus & (ImageObserver.FRAMEBITS
 316:                    | ImageObserver.ALLBITS)) != 0);
 317:   }
 318: 
 319:   // Set observer status flags based on the current consumer status
 320:   // flags.  Return true if the consumer flags indicate that the
 321:   // image was loaded successfully, or false otherwise.
 322:   private synchronized boolean setObserverStatus ()
 323:   {
 324:     boolean retval = false;
 325: 
 326:     if ((consumerStatus & IMAGEERROR) != 0)
 327:       observerStatus |= ImageObserver.ERROR;
 328: 
 329:     if ((consumerStatus & IMAGEABORTED) != 0)
 330:       observerStatus |= ImageObserver.ABORT;
 331: 
 332:     if ((consumerStatus & STATICIMAGEDONE) != 0)
 333:       {
 334:     observerStatus |= ImageObserver.ALLBITS;
 335:     retval = true;
 336:       }
 337: 
 338:     if ((consumerStatus & SINGLEFRAMEDONE) != 0)
 339:       {
 340:     observerStatus |= ImageObserver.FRAMEBITS;
 341:     retval = true;
 342:       }
 343: 
 344:     return retval;
 345:   }
 346: 
 347:   /**
 348:    * @return the status of the pixel grabbing thread, represented by a
 349:    * bitwise OR of ImageObserver flags
 350:    */
 351:   public synchronized int getStatus()
 352:   {
 353:     return observerStatus;
 354:   }
 355: 
 356:   /**
 357:    * @return the width of the grab rectangle in pixels, or a negative
 358:    * number if the ImageProducer has not yet called our setDimensions
 359:    * method
 360:    */
 361:   public synchronized int getWidth()
 362:   {
 363:     return width;
 364:   }
 365: 
 366:   /**
 367:    * @return the height of the grab rectangle in pixels, or a negative
 368:    * number if the ImageProducer has not yet called our setDimensions
 369:    * method
 370:    */
 371:   public synchronized int getHeight()
 372:   {
 373:     return height;
 374:   }
 375: 
 376:   /**
 377:    * @return a byte array of pixel data if ImageProducer delivered
 378:    * pixel data using the byte[] variant of setPixels, or an int array
 379:    * otherwise
 380:    */
 381:   public synchronized Object getPixels()
 382:   {
 383:     if (ints_delivered)
 384:       return int_pixel_buffer;
 385:     else if (bytes_delivered)
 386:       return byte_pixel_buffer;
 387:     else
 388:       return null;
 389:   }
 390: 
 391:   /**
 392:    * @return the ColorModel currently being used for the majority of
 393:    * pixel data conversions
 394:    */
 395:   public synchronized ColorModel getColorModel()
 396:   {
 397:     return model;
 398:   }
 399: 
 400:   /**
 401:    * Our <code>ImageProducer</code> calls this method to indicate the
 402:    * size of the image being produced.
 403:    *
 404:    * setDimensions is an ImageConsumer method.  None of PixelGrabber's
 405:    * ImageConsumer methods should be called by code that instantiates
 406:    * a PixelGrabber.  They are only made public so they can be called
 407:    * by the PixelGrabber's ImageProducer.
 408:    * 
 409:    * @param width the width of the image
 410:    * @param height the height of the image
 411:    */
 412:   public synchronized void setDimensions(int width, int height)
 413:   {
 414:     // Our width wasn't set when we were constructed.  Set our width
 415:     // so that the grab region includes all pixels from x to the right
 416:     // edge of the source image.
 417:     if (this.width < 0)
 418:       this.width = width - x;
 419: 
 420:     // Our height wasn't set when we were constructed.  Set our height
 421:     // so that the grab region includes all pixels from y to the
 422:     // bottom edge of the source image.
 423:     if (this.height < 0)
 424:       this.height = height - y;
 425: 
 426:     if (scansize < 0)
 427:       scansize = this.width;
 428: 
 429:     if (int_pixel_buffer == null)
 430:       int_pixel_buffer = new int[this.width * this.height];
 431: 
 432:     if (byte_pixel_buffer == null)
 433:       byte_pixel_buffer = new byte[this.width * this.height];
 434:   }
 435: 
 436:   /**
 437:    * Our <code>ImageProducer</code> may call this method to send us a
 438:    * list of its image's properties.
 439:    *
 440:    * setProperties is an ImageConsumer method.  None of PixelGrabber's
 441:    * ImageConsumer methods should be called by code that instantiates
 442:    * a PixelGrabber.  They are only made public so they can be called
 443:    * by the PixelGrabber's ImageProducer.
 444:    *
 445:    * @param props a list of properties associated with the image being
 446:    * produced
 447:    */
 448:   public synchronized void setProperties(Hashtable props)
 449:   {
 450:     this.props = props;
 451:   }
 452: 
 453:   /**
 454:    * Our ImageProducer will call <code>setColorModel</code> to
 455:    * indicate the model used by the majority of calls to
 456:    * <code>setPixels</code>.  Each call to <code>setPixels</code>
 457:    * could however indicate a different <code>ColorModel</code>.
 458:    *
 459:    * setColorModel is an ImageConsumer method.  None of PixelGrabber's
 460:    * ImageConsumer methods should be called by code that instantiates
 461:    * a PixelGrabber.  They are only made public so they can be called
 462:    * by the PixelGrabber's ImageProducer.
 463:    *
 464:    * @param model the color model to be used most often by setPixels
 465:    *
 466:    * @see ColorModel
 467:    */
 468:   public synchronized void setColorModel(ColorModel model)
 469:   {
 470:     this.model = model;
 471:   }
 472: 
 473:   /**
 474:    * Our <code>ImageProducer</code> may call this method with a
 475:    * bit mask of hints from any of <code>RANDOMPIXELORDER</code>,
 476:    * <code>TOPDOWNLEFTRIGHT</code>, <code>COMPLETESCANLINES</code>,
 477:    * <code>SINGLEPASS</code>, <code>SINGLEFRAME</code>.
 478:    * 
 479:    * setHints is an ImageConsumer method.  None of PixelGrabber's
 480:    * ImageConsumer methods should be called by code that instantiates
 481:    * a PixelGrabber.  They are only made public so they can be called
 482:    * by the PixelGrabber's ImageProducer.
 483:    *
 484:    * @param flags a bit mask of hints
 485:    */
 486:   public synchronized void setHints(int flags)
 487:   {
 488:     hints = flags;
 489:   }
 490: 
 491:   /**
 492:    * Our ImageProducer calls setPixels to deliver a subset of its
 493:    * pixels.
 494:    *
 495:    * Each element of the pixels array represents one pixel.  The
 496:    * pixel data is formatted according to the color model model.
 497:    * The x and y parameters are the coordinates of the rectangular
 498:    * region of pixels being delivered to this ImageConsumer,
 499:    * specified relative to the top left corner of the image being
 500:    * produced.  Likewise, w and h are the pixel region's dimensions.
 501:    *
 502:    * @param x x coordinate of pixel block
 503:    * @param y y coordinate of pixel block
 504:    * @param w width of pixel block
 505:    * @param h height of pixel block
 506:    * @param model color model used to interpret pixel data
 507:    * @param pixels pixel block data
 508:    * @param offset offset into pixels array
 509:    * @param scansize width of one row in the pixel block
 510:    */
 511:   public synchronized void setPixels(int x, int y, int w, int h, 
 512:                      ColorModel model, byte[] pixels,
 513:                      int offset, int scansize)
 514:   {
 515:     ColorModel currentModel;
 516:     if (model != null)
 517:       currentModel = model;
 518:     else
 519:       currentModel = this.model;
 520: 
 521:     for(int yp = y; yp < (y + h); yp++)
 522:       {
 523:     for(int xp = x; xp < (x + w); xp++)
 524:       {
 525:         // Check if the coordinates (xp, yp) are within the
 526:         // pixel block that we are grabbing.
 527:         if(xp >= this.x
 528:            && yp >= this.y
 529:            && xp < (this.x + this.width)
 530:            && yp < (this.y + this.height))
 531:           {
 532:         int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset;
 533:         int p = (yp - y) * scansize + (xp - x) + offset;
 534:         if (forceRGB)
 535:           {
 536:             ints_delivered = true;
 537: 
 538:             int_pixel_buffer[i] = currentModel.getRGB (pixels[p] & 0xFF);
 539:           }
 540:         else
 541:           {
 542:             bytes_delivered = true;
 543: 
 544:             byte_pixel_buffer[i] = pixels[p];
 545:           }
 546:           }
 547:       }
 548:       }
 549:   }
 550: 
 551:   /**
 552:    * Our ImageProducer calls setPixels to deliver a subset of its
 553:    * pixels.
 554:    *
 555:    * Each element of the pixels array represents one pixel.  The
 556:    * pixel data is formatted according to the color model model.
 557:    * The x and y parameters are the coordinates of the rectangular
 558:    * region of pixels being delivered to this ImageConsumer,
 559:    * specified relative to the top left corner of the image being
 560:    * produced.  Likewise, w and h are the pixel region's dimensions.
 561:    *
 562:    * @param x x coordinate of pixel block
 563:    * @param y y coordinate of pixel block
 564:    * @param w width of pixel block
 565:    * @param h height of pixel block
 566:    * @param model color model used to interpret pixel data
 567:    * @param pixels pixel block data
 568:    * @param offset offset into pixels array
 569:    * @param scansize width of one row in the pixel block
 570:    */
 571:   public synchronized void setPixels(int x, int y, int w, int h, 
 572:                      ColorModel model, int[] pixels,
 573:                      int offset, int scansize)
 574:   {
 575:     ColorModel currentModel;
 576:     if (model != null)
 577:       currentModel = model;
 578:     else
 579:       currentModel = this.model;
 580: 
 581:     ints_delivered = true;
 582: 
 583:     for(int yp = y; yp < (y + h); yp++)
 584:       {
 585:     for(int xp = x; xp < (x + w); xp++)
 586:       {
 587:         // Check if the coordinates (xp, yp) are within the
 588:         // pixel block that we are grabbing.
 589:         if(xp >= this.x
 590:            && yp >= this.y
 591:            && xp < (this.x + this.width)
 592:            && yp < (this.y + this.height))
 593:           {
 594:         int i = (yp - this.y) * this.scansize + (xp - this.x) + this.offset;
 595:         int p = (yp - y) * scansize + (xp - x) + offset;
 596:         if (forceRGB)
 597:           int_pixel_buffer[i] = currentModel.getRGB (pixels[p]);
 598:         else
 599:           int_pixel_buffer[i] = pixels[p];
 600:           }
 601:       }
 602:       }
 603:   }
 604: 
 605:   /**
 606:    * Our <code>ImageProducer</code> calls this method to inform us
 607:    * that a single frame or the entire image is complete.  The method
 608:    * is also used to inform us of an error in loading or producing the
 609:    * image.
 610:    *
 611:    * @param status the status of image production, represented by a
 612:    * bitwise OR of ImageConsumer flags
 613:    */
 614:   public synchronized void imageComplete(int status)
 615:   {
 616:     consumerStatus = status;
 617:     setObserverStatus ();
 618:     grabbing = false;
 619:     if (ip != null)
 620:       ip.removeConsumer (this);
 621: 
 622:     notifyAll ();
 623:   }
 624: 
 625:   /**
 626:    * @return the return value of getStatus
 627:    *
 628:    * @specnote The newer getStatus should be used in place of status.
 629:    */
 630:   public synchronized int status()
 631:   {
 632:     return getStatus();
 633:   }
 634: }