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