Source for javax.imageio.ImageReader

   1: /* ImageReader.java -- Decodes raster images.
   2:    Copyright (C) 2004, 2005  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 javax.imageio;
  40: 
  41: import java.awt.Point;
  42: import java.awt.Rectangle;
  43: import java.awt.image.BufferedImage;
  44: import java.awt.image.Raster;
  45: import java.awt.image.RenderedImage;
  46: import java.io.IOException;
  47: import java.util.ArrayList;
  48: import java.util.Iterator;
  49: import java.util.List;
  50: import java.util.Locale;
  51: import java.util.ResourceBundle;
  52: import java.util.MissingResourceException;
  53: import java.util.Set;
  54: 
  55: import javax.imageio.event.IIOReadProgressListener;
  56: import javax.imageio.event.IIOReadUpdateListener;
  57: import javax.imageio.event.IIOReadWarningListener;
  58: import javax.imageio.metadata.IIOMetadata;
  59: import javax.imageio.spi.ImageReaderSpi;
  60: import javax.imageio.stream.ImageInputStream;
  61: 
  62: /**
  63:  * A class for decoding images within the ImageIO framework.
  64:  *
  65:  * An ImageReader for a given format is instantiated by an
  66:  * ImageReaderSpi for that format.  ImageReaderSpis are registered
  67:  * with the IIORegistry.
  68:  *
  69:  * The ImageReader API supports reading animated images that may have
  70:  * multiple frames; to support such images many methods take an index
  71:  * parameter.
  72:  *
  73:  * Images may also be read in multiple passes, where each successive
  74:  * pass increases the level of detail in the destination image.
  75:  */
  76: public abstract class ImageReader
  77: {
  78:   private boolean aborted;
  79: 
  80:   /**
  81:    * All locales available for localization of warning messages, or
  82:    * null if localization is not supported.
  83:    */
  84:   protected Locale[] availableLocales = null;
  85: 
  86:   /**
  87:    * true if the input source does not require metadata to be read,
  88:    * false otherwise.
  89:    */
  90:   protected boolean ignoreMetadata = false;
  91: 
  92:   /**
  93:    * An ImageInputStream from which image data is read.
  94:    */
  95:   protected Object input = null;
  96: 
  97:   /**
  98:    * The current locale used to localize warning messages, or null if
  99:    * no locale has been set.
 100:    */
 101:   protected Locale locale = null;
 102: 
 103:   /**
 104:    * The minimum index at which data can be read.  Constantly 0 if
 105:    * seekForwardOnly is false, always increasing if seekForwardOnly is
 106:    * true.
 107:    */
 108:   protected int minIndex = 0;
 109: 
 110:   /**
 111:    * The image reader SPI that instantiated this reader.
 112:    */
 113:   protected ImageReaderSpi originatingProvider = null;
 114: 
 115:   /**
 116:    * A list of installed progress listeners.  Initially null, meaning
 117:    * no installed listeners.
 118:    */
 119:   protected List<IIOReadProgressListener> progressListeners = null;
 120: 
 121:   /**
 122:    * true if this reader should only read data further ahead in the
 123:    * stream than its current location.  false if it can read backwards
 124:    * in the stream.  If this is true then caching can be avoided.
 125:    */
 126:   protected boolean seekForwardOnly = false;
 127: 
 128:   /**
 129:    * A list of installed update listeners.  Initially null, meaning no
 130:    * installed listeners.
 131:    */
 132:   protected List<IIOReadUpdateListener> updateListeners = null;
 133: 
 134:   /**
 135:    * A list of installed warning listeners.  Initially null, meaning
 136:    * no installed listeners.
 137:    */
 138:   protected List<IIOReadWarningListener> warningListeners = null;
 139: 
 140:   /**
 141:    * A list of warning locales corresponding with the list of
 142:    * installed warning listeners.  Initially null, meaning no locales.
 143:    */
 144:   protected List<Locale> warningLocales = null;
 145: 
 146:   /**
 147:    * Construct an image reader.
 148:    *
 149:    * @param originatingProvider the provider that is constructing this
 150:    * image reader, or null
 151:    */
 152:   protected ImageReader(ImageReaderSpi originatingProvider)
 153:   {
 154:     this.originatingProvider = originatingProvider;
 155:   }
 156: 
 157:   /**
 158:    * Request that reading be aborted.  The unread contents of the
 159:    * image will be undefined.
 160:    *
 161:    * Readers should clear the abort flag before starting a read
 162:    * operation, then poll it periodically during the read operation.
 163:    */
 164:   public void abort()
 165:   {
 166:     aborted = true;
 167:   }
 168: 
 169:   /**
 170:    * Check if the abort flag is set.
 171:    *
 172:    * @return true if the current read operation should be aborted,
 173:    * false otherwise
 174:    */
 175:   protected boolean abortRequested()
 176:   {
 177:     return aborted;
 178:   }
 179: 
 180:   /**
 181:    * Install a read progress listener.  This method will return
 182:    * immediately if listener is null.
 183:    *
 184:    * @param listener a read progress listener or null
 185:    */
 186:   public void addIIOReadProgressListener(IIOReadProgressListener listener)
 187:   {
 188:     if (listener == null)
 189:       return;
 190:     if (progressListeners == null)
 191:       progressListeners = new ArrayList ();
 192:     progressListeners.add(listener);
 193:   }
 194: 
 195:   /**
 196:    * Install a read update listener.  This method will return
 197:    * immediately if listener is null.
 198:    *
 199:    * @param listener a read update listener
 200:    */
 201:   public void addIIOReadUpdateListener(IIOReadUpdateListener listener)
 202:   {
 203:     if (listener == null)
 204:       return;
 205:     if (updateListeners == null)
 206:       updateListeners = new ArrayList ();
 207:     updateListeners.add(listener);
 208:   }
 209: 
 210:   /**
 211:    * Install a read warning listener.  This method will return
 212:    * immediately if listener is null.  Warning messages sent to this
 213:    * listener will be localized using the current locale.  If the
 214:    * current locale is null then this reader will select a sensible
 215:    * default.
 216:    *
 217:    * @param listener a read warning listener
 218:    */
 219:   public void addIIOReadWarningListener(IIOReadWarningListener listener)
 220:   {
 221:     if (listener == null)
 222:       return;
 223:     if (warningListeners == null)
 224:       warningListeners = new ArrayList ();
 225:     warningListeners.add(listener);
 226:   }
 227: 
 228:   /**
 229:    * Check if this reader can handle raster data.  Determines whether
 230:    * or not readRaster and readTileRaster throw
 231:    * UnsupportedOperationException.
 232:    *
 233:    * @return true if this reader supports raster data, false if not
 234:    */
 235:   public boolean canReadRaster()
 236:   {
 237:     return false;
 238:   }
 239: 
 240:   /**
 241:    * Clear the abort flag.
 242:    */
 243:   protected void clearAbortRequest()
 244:   {
 245:     aborted = false;
 246:   }
 247: 
 248:   /**
 249:    * Releases any resources allocated to this object.  Subsequent
 250:    * calls to methods on this object will produce undefined results.
 251:    *
 252:    * The default implementation does nothing; subclasses should use
 253:    * this method ensure that native resources are released.
 254:    */
 255:   public void dispose()
 256:   {
 257:     // The default implementation does nothing.
 258:   }
 259: 
 260:   /**
 261:    * Returns the aspect ratio of this image, the ration of its width
 262:    * to its height.  The aspect ratio is useful when resizing an image
 263:    * while keeping its proportions constant.
 264:    *
 265:    * @param imageIndex the frame index
 266:    *
 267:    * @return the image's aspect ratio
 268:    *
 269:    * @exception IllegalStateException if input is null
 270:    * @exception IndexOutOfBoundsException if the frame index is
 271:    * out-of-bounds
 272:    * @exception IOException if a read error occurs
 273:    */
 274:   public float getAspectRatio(int imageIndex)
 275:     throws IOException
 276:   {
 277:     if (input == null)
 278:       throw new IllegalStateException("input is null");
 279: 
 280:     return (float) (getWidth(imageIndex) / getHeight(imageIndex));
 281:   }
 282: 
 283:   /**
 284:    * Retrieve the available locales.  Return null if no locales are
 285:    * available or a clone of availableLocales.
 286:    *
 287:    * @return an array of locales or null
 288:    */
 289:   public Locale[] getAvailableLocales()
 290:   {
 291:     if (availableLocales == null)
 292:       return null;
 293: 
 294:     return (Locale[]) availableLocales.clone();
 295:   }
 296: 
 297:   /**
 298:    * Retrieve the default read parameters for this reader's image
 299:    * format.
 300:    *
 301:    * The default implementation returns new ImageReadParam().
 302:    *
 303:    * @return image reading parameters
 304:    */
 305:   public ImageReadParam getDefaultReadParam()
 306:   {
 307:     return new ImageReadParam();
 308:   }
 309: 
 310:   /**
 311:    * Retrieve the format of the input source.
 312:    *
 313:    * @return the input source format name
 314:    *
 315:    * @exception IOException if a read error occurs
 316:    */
 317:   public String getFormatName()
 318:     throws IOException
 319:   {
 320:     return originatingProvider.getFormatNames()[0];
 321:   }
 322: 
 323:   /**
 324:    * Get the height of the input image in pixels.  If the input image
 325:    * is resizable then a default height is returned.
 326:    *
 327:    * @param imageIndex the frame index
 328:    *
 329:    * @return the height of the input image
 330:    *
 331:    * @exception IllegalStateException if input has not been set
 332:    * @exception IndexOutOfBoundsException if the frame index is
 333:    * out-of-bounds
 334:    * @exception IOException if a read error occurs
 335:    */
 336:   public abstract int getHeight(int imageIndex)
 337:     throws IOException;
 338: 
 339:   /**
 340:    * Get the metadata associated with this image.  If the reader is
 341:    * set to ignore metadata or does not support reading metadata, or
 342:    * if no metadata is available then null is returned.
 343:    *
 344:    * @param imageIndex the frame index
 345:    *
 346:    * @return a metadata object, or null
 347:    *
 348:    * @exception IllegalStateException if input has not been set
 349:    * @exception IndexOutOfBoundsException if the frame index is
 350:    * out-of-bounds
 351:    * @exception IOException if a read error occurs
 352:    */
 353:   public abstract IIOMetadata getImageMetadata(int imageIndex)
 354:     throws IOException;
 355: 
 356:   /**
 357:    * Get an iterator over the collection of image types into which
 358:    * this reader can decode image data.  This method is guaranteed to
 359:    * return at least one valid image type specifier.
 360:    *
 361:    * The elements of the iterator should be ordered; the first element
 362:    * should be the most appropriate image type for this decoder,
 363:    * followed by the second-most appropriate, and so on.
 364:    *
 365:    * @param imageIndex the frame index
 366:    *
 367:    * @return an iterator over a collection of image type specifiers
 368:    *
 369:    * @exception IllegalStateException if input has not been set
 370:    * @exception IndexOutOfBoundsException if the frame index is
 371:    * out-of-bounds
 372:    * @exception IOException if a read error occurs
 373:    */
 374:   public abstract Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
 375:     throws IOException;
 376: 
 377:   /**
 378:    * Set the input source to the given object, specify whether this
 379:    * reader should be allowed to read input from the data stream more
 380:    * than once, and specify whether this reader should ignore metadata
 381:    * in the input stream.  The input source must be set before many
 382:    * methods can be called on this reader. (see all ImageReader
 383:    * methods that throw IllegalStateException).  If input is null then
 384:    * the current input source will be removed.
 385:    *
 386:    * Unless this reader has direct access with imaging hardware, input
 387:    * should be an ImageInputStream.
 388:    *
 389:    * @param input the input source object
 390:    * @param seekForwardOnly true if this reader should be allowed to
 391:    * read input from the data stream more than once, false otherwise
 392:    * @param ignoreMetadata true if this reader should ignore metadata
 393:    * associated with the input source, false otherwise
 394:    *
 395:    * @exception IllegalArgumentException if input is not a valid input
 396:    * source for this reader and is not an ImageInputStream
 397:    */
 398:   public void setInput(Object input,
 399:                        boolean seekForwardOnly,
 400:                        boolean ignoreMetadata)
 401:   {
 402:     Class[] okClasses = originatingProvider.getInputTypes();
 403:     if (okClasses == null)
 404:       {
 405:         if (!(input instanceof ImageInputStream))
 406:           throw new IllegalArgumentException();
 407:       }
 408:     else
 409:       {
 410:         boolean classOk = false;
 411:         for (int i = 0; i < okClasses.length; ++i)
 412:           if (okClasses[i].isInstance(input))
 413:             classOk = true;
 414:         if (!classOk)
 415:           throw new IllegalArgumentException();
 416:       }
 417: 
 418:     this.input = input;
 419:     this.seekForwardOnly = seekForwardOnly;
 420:     this.ignoreMetadata = ignoreMetadata;
 421:     this.minIndex = 0;
 422:   }
 423: 
 424:   /**
 425:    * Set the input source to the given object and specify whether this
 426:    * reader should be allowed to read input from the data stream more
 427:    * than once.  The input source must be set before many methods can
 428:    * be called on this reader. (see all ImageReader methods that throw
 429:    * IllegalStateException).  If input is null then the current input
 430:    * source will be removed.
 431:    *
 432:    * @param in the input source object
 433:    * @param seekForwardOnly true if this reader should be allowed to
 434:    * read input from the data stream more than once, false otherwise
 435:    *
 436:    * @exception IllegalArgumentException if input is not a valid input
 437:    * source for this reader and is not an ImageInputStream
 438:    */
 439:   public void setInput(Object in, boolean seekForwardOnly)
 440:   {
 441:     setInput(in, seekForwardOnly, false);
 442:   }
 443: 
 444:   /**
 445:    * Set the input source to the given object.  The input source must
 446:    * be set before many methods can be called on this reader. (see all
 447:    * ImageReader methods that throw IllegalStateException).  If input
 448:    * is null then the current input source will be removed.
 449:    *
 450:    * @param input the input source object
 451:    *
 452:    * @exception IllegalArgumentException if input is not a valid input
 453:    * source for this reader and is not an ImageInputStream
 454:    */
 455:   public void setInput(Object input)
 456:   {
 457:     setInput(input, false, false);
 458:   }
 459: 
 460:   /**
 461:    * Get this reader's image input source.  null is returned if the
 462:    * image source has not been set.
 463:    *
 464:    * @return an image input source object, or null
 465:    */
 466:   public Object getInput()
 467:   {
 468:     return input;
 469:   }
 470: 
 471:   /**
 472:    * Get this reader's locale.  null is returned if the locale has not
 473:    * been set.
 474:    *
 475:    * @return this reader's locale, or null
 476:    */
 477:   public Locale getLocale()
 478:   {
 479:     return locale;
 480:   }
 481: 
 482:   /**
 483:    * Return the number of images available from the image input
 484:    * source, not including thumbnails.  This method will return 1
 485:    * unless this reader is reading an animated image.
 486:    *
 487:    * Certain multi-image formats do not encode the total number of
 488:    * images.  When reading images in those formats it may be necessary
 489:    * to repeatedly call read, incrementing the image index at each
 490:    * call, until an IndexOutOfBoundsException is thrown.
 491:    *
 492:    * The allowSearch parameter determines whether all images must be
 493:    * available at all times.  When allowSearch is false, getNumImages
 494:    * will return -1 if the total number of images is unknown.
 495:    * Otherwise this method returns the number of images.
 496:    *
 497:    * @param allowSearch true if all images should be available at
 498:    * once, false otherwise
 499:    *
 500:    * @return -1 if allowSearch is false and the total number of images
 501:    * is currently unknown, or the number of images
 502:    *
 503:    * @exception IllegalStateException if input has not been set, or if
 504:    * seekForwardOnly is true
 505:    * @exception IOException if a read error occurs
 506:    */
 507:   public abstract int getNumImages(boolean allowSearch)
 508:     throws IOException;
 509: 
 510:   /**
 511:    * Get the number of thumbnails associated with an image.
 512:    *
 513:    * @param imageIndex the frame index
 514:    *
 515:    * @return the number of thumbnails associated with this image
 516:    */
 517:   public int getNumThumbnails(int imageIndex)
 518:     throws IOException
 519:   {
 520:     return 0;
 521:   }
 522: 
 523:   /**
 524:    * Get the ImageReaderSpi that created this reader or null.
 525:    *
 526:    * @return an ImageReaderSpi, or null
 527:    */
 528:   public ImageReaderSpi getOriginatingProvider()
 529:   {
 530:     return originatingProvider;
 531:   }
 532: 
 533:   /**
 534:    * Get the metadata associated with the image being read.  If the
 535:    * reader is set to ignore metadata or does not support reading
 536:    * metadata, or if no metadata is available then null is returned.
 537:    * This method returns metadata associated with the entirety of the
 538:    * image data, whereas getImageMetadata(int) returns metadata
 539:    * associated with a frame within a multi-image data stream.
 540:    *
 541:    * @return metadata associated with the image being read, or null
 542:    *
 543:    * @exception IOException if a read error occurs
 544:    */
 545:   public abstract IIOMetadata getStreamMetadata()
 546:     throws IOException;
 547: 
 548:   /**
 549:    * Get the height of a thumbnail image.
 550:    *
 551:    * @param imageIndex the frame index
 552:    * @param thumbnailIndex the thumbnail index
 553:    *
 554:    * @return the height of the thumbnail image
 555:    *
 556:    * @exception UnsupportedOperationException if this reader does not
 557:    * support thumbnails
 558:    * @exception IllegalStateException if input is null
 559:    * @exception IndexOutOfBoundsException if either index is
 560:    * out-of-bounds
 561:    * @exception IOException if a read error occurs
 562:    */
 563:   public int getThumbnailHeight(int imageIndex, int thumbnailIndex)
 564:     throws IOException
 565:   {
 566:     return readThumbnail(imageIndex, thumbnailIndex).getHeight();
 567:   }
 568: 
 569:   /**
 570:    * Get the width of a thumbnail image.
 571:    *
 572:    * @param imageIndex the frame index
 573:    * @param thumbnailIndex the thumbnail index
 574:    *
 575:    * @return the width of the thumbnail image
 576:    *
 577:    * @exception UnsupportedOperationException if this reader does not
 578:    * support thumbnails
 579:    * @exception IllegalStateException if input is null
 580:    * @exception IndexOutOfBoundsException if either index is
 581:    * out-of-bounds
 582:    * @exception IOException if a read error occurs
 583:    */
 584:   public int getThumbnailWidth(int imageIndex, int thumbnailIndex)
 585:     throws IOException
 586:   {
 587:     return readThumbnail(imageIndex, thumbnailIndex).getWidth();
 588:   }
 589: 
 590:   /**
 591:    * Get the X coordinate in pixels of the top-left corner of the
 592:    * first tile in this image.
 593:    *
 594:    * @param imageIndex the frame index
 595:    *
 596:    * @return the X coordinate of this image's first tile
 597:    *
 598:    * @exception IllegalStateException if input is needed but the input
 599:    * source is not set
 600:    * @exception IndexOutOfBoundsException if the frame index is
 601:    * out-of-bounds
 602:    * @exception IOException if a read error occurs
 603:    */
 604:   public int getTileGridXOffset(int imageIndex)
 605:     throws IOException
 606:   {
 607:     return 0;
 608:   }
 609: 
 610:   /**
 611:    * Get the Y coordinate in pixels of the top-left corner of the
 612:    * first tile in this image.
 613:    *
 614:    * @param imageIndex the frame index
 615:    *
 616:    * @return the Y coordinate of this image's first tile
 617:    *
 618:    * @exception IllegalStateException if input is needed but the input
 619:    * source is not set
 620:    * @exception IndexOutOfBoundsException if the frame index is
 621:    * out-of-bounds
 622:    * @exception IOException if a read error occurs
 623:    */
 624:   public int getTileGridYOffset(int imageIndex)
 625:     throws IOException
 626:   {
 627:     return 0;
 628:   }
 629: 
 630:   /**
 631:    * Get the height of an image tile.
 632:    *
 633:    * @param imageIndex the frame index
 634:    *
 635:    * @return the tile height for the given image
 636:    *
 637:    * @exception IllegalStateException if input is null
 638:    * @exception IndexOutOfBoundsException if the frame index is
 639:    * out-of-bounds
 640:    * @exception IOException if a read error occurs
 641:    */
 642:   public int getTileHeight(int imageIndex)
 643:     throws IOException
 644:   {
 645:     return getHeight(imageIndex);
 646:   }
 647: 
 648:   /**
 649:    * Get the width of an image tile.
 650:    *
 651:    * @param imageIndex the frame index
 652:    *
 653:    * @return the tile width for the given image
 654:    *
 655:    * @exception IllegalStateException if input is null
 656:    * @exception IndexOutOfBoundsException if the frame index is
 657:    * out-of-bounds
 658:    * @exception IOException if a read error occurs
 659:    */
 660:   public int getTileWidth(int imageIndex)
 661:     throws IOException
 662:   {
 663:     return getWidth(imageIndex);
 664:   }
 665: 
 666:   /**
 667:    * Get the width of the input image in pixels.  If the input image
 668:    * is resizable then a default width is returned.
 669:    *
 670:    * @param imageIndex the image's index
 671:    *
 672:    * @return the width of the input image
 673:    *
 674:    * @exception IllegalStateException if input has not been set
 675:    * @exception IndexOutOfBoundsException if the frame index is
 676:    * out-of-bounds
 677:    * @exception IOException if a read error occurs
 678:    */
 679:   public abstract int getWidth(int imageIndex)
 680:     throws IOException;
 681: 
 682:   /**
 683:    * Check whether or not the given image has thumbnails associated
 684:    * with it.
 685:    *
 686:    * @return true if the given image has thumbnails, false otherwise
 687:    *
 688:    * @exception IllegalStateException if input is null
 689:    * @exception IndexOutOfBoundsException if the frame index is
 690:    * out-of-bounds
 691:    * @exception IOException if a read error occurs
 692:    */
 693:   public boolean hasThumbnails(int imageIndex)
 694:     throws IOException
 695:   {
 696:     return getNumThumbnails(imageIndex) > 0;
 697:   }
 698: 
 699:   /**
 700:    * Check if this image reader ignores metadata.  This method simply
 701:    * returns the value of ignoreMetadata.
 702:    *
 703:    * @return true if metadata is being ignored, false otherwise
 704:    */
 705:   public boolean isIgnoringMetadata()
 706:   {
 707:     return ignoreMetadata;
 708:   }
 709: 
 710:   /**
 711:    * Check if the given image is sub-divided into equal-sized
 712:    * non-overlapping pixel rectangles.
 713:    *
 714:    * A reader may expose tiling in the underlying format, hide it, or
 715:    * simulate tiling even if the underlying format is not tiled.
 716:    *
 717:    * @return true if the given image is tiled, false otherwise
 718:    *
 719:    * @exception IllegalStateException if input is null
 720:    * @exception IndexOutOfBoundsException if the frame index is
 721:    * out-of-bounds
 722:    * @exception IOException if a read error occurs
 723:    */
 724:   public boolean isImageTiled(int imageIndex)
 725:     throws IOException
 726:   {
 727:     return false;
 728:   }
 729: 
 730:   /**
 731:    * Check if all pixels in this image are readily accessible.  This
 732:    * method should return false for compressed formats.  The return
 733:    * value is a hint as to the efficiency of certain image reader
 734:    * operations.
 735:    *
 736:    * @param imageIndex the frame index
 737:    *
 738:    * @return true if random pixel access is fast, false otherwise
 739:    *
 740:    * @exception IllegalStateException if input is null and it is
 741:    * needed to determine the return value
 742:    * @exception IndexOutOfBoundsException if the frame index is
 743:    * out-of-bounds but the frame data must be accessed to determine
 744:    * the return value
 745:    * @exception IOException if a read error occurs
 746:    */
 747:   public boolean isRandomAccessEasy(int imageIndex)
 748:     throws IOException
 749:   {
 750:     return false;
 751:   }
 752: 
 753:   /**
 754:    * Check if this image reader may only seek forward within the input
 755:    * stream.
 756:    *
 757:    * @return true if this reader may only seek forward, false
 758:    * otherwise
 759:    */
 760:   public boolean isSeekForwardOnly()
 761:   {
 762:     return seekForwardOnly;
 763:   }
 764: 
 765:   /**
 766:    * Notifies all installed read progress listeners that image loading
 767:    * has completed by calling their imageComplete methods.
 768:    */
 769:   protected void processImageComplete()
 770:   {
 771:     if (progressListeners != null)
 772:       {
 773:         Iterator it = progressListeners.iterator();
 774: 
 775:         while (it.hasNext())
 776:           {
 777:             IIOReadProgressListener listener =
 778:               (IIOReadProgressListener) it.next();
 779:             listener.imageComplete (this);
 780:           }
 781:       }
 782:   }
 783: 
 784:   /**
 785:    * Notifies all installed read progress listeners that a certain
 786:    * percentage of the image has been loaded, by calling their
 787:    * imageProgress methods.
 788:    *
 789:    * @param percentageDone the percentage of image data that has been
 790:    * loaded
 791:    */
 792:   protected void processImageProgress(float percentageDone)
 793:   {
 794:      if (progressListeners != null)
 795:       {
 796:         Iterator it = progressListeners.iterator();
 797: 
 798:         while (it.hasNext())
 799:           {
 800:             IIOReadProgressListener listener =
 801:               (IIOReadProgressListener) it.next();
 802:             listener.imageProgress(this, percentageDone);
 803:           }
 804:       }
 805:   }
 806:   /**
 807:    * Notifies all installed read progress listeners, by calling their
 808:    * imageStarted methods, that image loading has started on the given
 809:    * image.
 810:    *
 811:    * @param imageIndex the frame index of the image that has started
 812:    * loading
 813:    */
 814:   protected void processImageStarted(int imageIndex)
 815:   {
 816:      if (progressListeners != null)
 817:       {
 818:         Iterator it = progressListeners.iterator();
 819: 
 820:         while (it.hasNext())
 821:           {
 822:             IIOReadProgressListener listener =
 823:               (IIOReadProgressListener) it.next();
 824:             listener.imageStarted(this, imageIndex);
 825:           }
 826:       }
 827:   }
 828: 
 829:   /**
 830:    * Notifies all installed read update listeners, by calling their
 831:    * imageUpdate methods, that the set of samples has changed.
 832:    *
 833:    * @param image the buffered image that is being updated
 834:    * @param minX the X coordinate of the top-left pixel in this pass
 835:    * @param minY the Y coordinate of the top-left pixel in this pass
 836:    * @param width the total width of the rectangle covered by this
 837:    * pass, including skipped pixels
 838:    * @param height the total height of the rectangle covered by this
 839:    * pass, including skipped pixels
 840:    * @param periodX the horizontal sample interval
 841:    * @param periodY the vertical sample interval
 842:    * @param bands the affected bands in the destination
 843:    */
 844:   protected void processImageUpdate(BufferedImage image, int minX, int minY,
 845:                                     int width, int height, int periodX,
 846:                                     int periodY, int[] bands)
 847:   {
 848:     if (updateListeners != null)
 849:       {
 850:         Iterator it = updateListeners.iterator();
 851: 
 852:         while (it.hasNext())
 853:           {
 854:             IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next();
 855:             listener.imageUpdate(this, image, minX, minY, width, height,
 856:                                  periodX, periodY, bands);
 857:           }
 858:       }
 859:   }
 860: 
 861:   /**
 862:    * Notifies all installed update progress listeners, by calling
 863:    * their passComplete methods, that a progressive pass has
 864:    * completed.
 865:    *
 866:    * @param image the image that has being updated
 867:    */
 868:   protected void processPassComplete(BufferedImage image)
 869:   {
 870:     if (updateListeners != null)
 871:       {
 872:         Iterator it = updateListeners.iterator();
 873: 
 874:         while (it.hasNext())
 875:           {
 876:             IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next();
 877:             listener.passComplete(this, image);
 878:           }
 879:       }
 880:   }
 881: 
 882:   /**
 883:    * Notifies all installed read update listeners, by calling their
 884:    * passStarted methods, that a new pass has begun.
 885:    *
 886:    * @param image the buffered image that is being updated
 887:    * @param pass the current pass number
 888:    * @param minPass the pass at which decoding will begin
 889:    * @param maxPass the pass at which decoding will end
 890:    * @param minX the X coordinate of the top-left pixel in this pass
 891:    * @param minY the Y coordinate of the top-left pixel in this pass
 892:    * @param width the total width of the rectangle covered by this
 893:    * pass, including skipped pixels
 894:    * @param height the total height of the rectangle covered by this
 895:    * pass, including skipped pixels
 896:    * @param periodX the horizontal sample interval
 897:    * @param periodY the vertical sample interval
 898:    * @param bands the affected bands in the destination
 899:    */
 900:   protected void processPassStarted(BufferedImage image, int pass, int minPass,
 901:                                     int maxPass, int minX, int minY,
 902:                                     int periodX, int periodY, int[] bands)
 903:   {
 904:     if (updateListeners != null)
 905:       {
 906:         Iterator it = updateListeners.iterator();
 907: 
 908:         while (it.hasNext())
 909:           {
 910:             IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next();
 911:             listener.passStarted(this, image, pass, minPass, maxPass, minX,
 912:                                  minY, periodX, periodY, bands);
 913:           }
 914:       }
 915:   }
 916: 
 917:   /**
 918:    * Notifies all installed read progress listeners that image loading
 919:    * has been aborted by calling their readAborted methods.
 920:    */
 921:   protected void processReadAborted()
 922:   {
 923:      if (progressListeners != null)
 924:       {
 925:         Iterator it = progressListeners.iterator();
 926: 
 927:         while (it.hasNext())
 928:           {
 929:             IIOReadProgressListener listener =
 930:               (IIOReadProgressListener) it.next();
 931:             listener.readAborted(this);
 932:           }
 933:       }
 934:   }
 935:   /**
 936:    * Notifies all installed read progress listeners, by calling their
 937:    * sequenceComplete methods, that a sequence of images has completed
 938:    * loading.
 939:    */
 940:   protected void processSequenceComplete()
 941:   {
 942:      if (progressListeners != null)
 943:       {
 944:         Iterator it = progressListeners.iterator();
 945: 
 946:         while (it.hasNext())
 947:           {
 948:             IIOReadProgressListener listener =
 949:               (IIOReadProgressListener) it.next();
 950:             listener.sequenceComplete(this);
 951:           }
 952:       }
 953:   }
 954: 
 955:   /**
 956:    * Notifies all installed read progress listeners, by calling their
 957:    * sequenceStarted methods, a sequence of images has started
 958:    * loading.
 959:    *
 960:    * @param minIndex the index of the first image in the sequence
 961:    */
 962:   protected void processSequenceStarted(int minIndex)
 963:   {
 964: 
 965:     if (progressListeners != null)
 966:       {
 967:         Iterator it = progressListeners.iterator();
 968: 
 969:         while (it.hasNext())
 970:           {
 971:             IIOReadProgressListener listener =
 972:               (IIOReadProgressListener) it.next();
 973:             listener.sequenceStarted(this, minIndex);
 974:           }
 975:       }
 976:   }
 977: 
 978:   /**
 979:    * Notifies all installed read progress listeners, by calling their
 980:    * thumbnailComplete methods, that a thumbnail has completed
 981:    * loading.
 982:    */
 983:   protected void processThumbnailComplete()
 984:   {
 985:     if (progressListeners != null)
 986:       {
 987:         Iterator it = progressListeners.iterator();
 988: 
 989:         while (it.hasNext())
 990:           {
 991:             IIOReadProgressListener listener =
 992:               (IIOReadProgressListener) it.next();
 993:             listener.thumbnailComplete(this);
 994:           }
 995:       }
 996:   }
 997: 
 998:   /**
 999:    * Notifies all installed update progress listeners, by calling
1000:    * their thumbnailPassComplete methods, that a progressive pass has
1001:    * completed on a thumbnail.
1002:    *
1003:    * @param thumbnail the thumbnail that has being updated
1004:    */
1005:   protected void processThumbnailPassComplete(BufferedImage thumbnail)
1006:   {
1007:     if (updateListeners != null)
1008:       {
1009:         Iterator it = updateListeners.iterator();
1010: 
1011:         while (it.hasNext())
1012:           {
1013:             IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next();
1014:             listener.thumbnailPassComplete(this, thumbnail);
1015:           }
1016:       }
1017:   }
1018: 
1019:   /**
1020:    * Notifies all installed read update listeners, by calling their
1021:    * thumbnailPassStarted methods, that a new pass has begun.
1022:    *
1023:    * @param thumbnail the thumbnail that is being updated
1024:    * @param pass the current pass number
1025:    * @param minPass the pass at which decoding will begin
1026:    * @param maxPass the pass at which decoding will end
1027:    * @param minX the X coordinate of the top-left pixel in this pass
1028:    * @param minY the Y coordinate of the top-left pixel in this pass
1029:    * @param width the total width of the rectangle covered by this
1030:    * pass, including skipped pixels
1031:    * @param height the total height of the rectangle covered by this
1032:    * pass, including skipped pixels
1033:    * @param periodX the horizontal sample interval
1034:    * @param periodY the vertical sample interval
1035:    * @param bands the affected bands in the destination
1036:    */
1037:   protected void processThumbnailPassStarted(BufferedImage thumbnail, int pass,
1038:                                              int minPass, int maxPass, int minX,
1039:                                              int minY, int periodX, int periodY,
1040:                                              int[] bands)
1041:   {
1042:     if (updateListeners != null)
1043:       {
1044:         Iterator it = updateListeners.iterator();
1045: 
1046:         while (it.hasNext())
1047:           {
1048:             IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next();
1049:             listener.thumbnailPassStarted(this, thumbnail, pass, minPass,
1050:                                           maxPass, minX, minY, periodX,
1051:                                           periodY, bands);
1052:           }
1053:       }
1054:   }
1055: 
1056:   /**
1057:    * Notifies all installed read progress listeners that a certain
1058:    * percentage of a thumbnail has been loaded, by calling their
1059:    * thumbnailProgress methods.
1060:    *
1061:    * @param percentageDone the percentage of thumbnail data that has
1062:    * been loaded
1063:    */
1064:   protected void processThumbnailProgress(float percentageDone)
1065:   {
1066:     if (progressListeners != null)
1067:       {
1068:         Iterator it = progressListeners.iterator();
1069: 
1070:         while (it.hasNext())
1071:           {
1072:             IIOReadProgressListener listener =
1073:               (IIOReadProgressListener) it.next();
1074:             listener.thumbnailProgress(this, percentageDone);
1075:           }
1076:       }
1077:   }
1078: 
1079:   /**
1080:    * Notifies all installed read progress listeners, by calling their
1081:    * imageStarted methods, that thumbnail loading has started on the
1082:    * given thumbnail of the given image.
1083:    *
1084:    * @param imageIndex the frame index of the image one of who's
1085:    * thumbnails has started loading
1086:    * @param thumbnailIndex the index of the thumbnail that has started
1087:    * loading
1088:    */
1089:   protected void processThumbnailStarted(int imageIndex, int thumbnailIndex)
1090:   {
1091:     if (progressListeners != null)
1092:       {
1093:         Iterator it = progressListeners.iterator();
1094: 
1095:         while (it.hasNext())
1096:           {
1097:             IIOReadProgressListener listener =
1098:               (IIOReadProgressListener) it.next();
1099:             listener.thumbnailStarted(this, imageIndex, thumbnailIndex);
1100:           }
1101:       }
1102:   }
1103: 
1104:   /**
1105:    * Notifies all installed read update listeners, by calling their
1106:    * thumbnailUpdate methods, that the set of samples has changed.
1107:    *
1108:    * @param image the buffered image that is being updated
1109:    * @param minX the X coordinate of the top-left pixel in this pass
1110:    * @param minY the Y coordinate of the top-left pixel in this pass
1111:    * @param width the total width of the rectangle covered by this
1112:    * pass, including skipped pixels
1113:    * @param height the total height of the rectangle covered by this
1114:    * pass, including skipped pixels
1115:    * @param periodX the horizontal sample interval
1116:    * @param periodY the vertical sample interval
1117:    * @param bands the affected bands in the destination
1118:    */
1119:   protected void processThumbnailUpdate(BufferedImage image, int minX, int minY,
1120:                                         int width, int height, int periodX,
1121:                                         int periodY, int[] bands)
1122:   {
1123:     if (updateListeners != null)
1124:       {
1125:         Iterator it = updateListeners.iterator();
1126: 
1127:         while (it.hasNext())
1128:           {
1129:             IIOReadUpdateListener listener = (IIOReadUpdateListener) it.next();
1130:             listener.thumbnailUpdate(this, image, minX, minY, width, height,
1131:                                      periodX, periodY, bands);
1132:           }
1133:       }
1134:   }
1135: 
1136:   /**
1137:    * Notifies all installed warning listeners, by calling their
1138:    * warningOccurred methods, that a warning message has been raised.
1139:    *
1140:    * @param warning the warning message
1141:    *
1142:    * @exception IllegalArgumentException if warning is null
1143:    */
1144:   protected void processWarningOccurred(String warning)
1145:   {
1146:     if (warning == null)
1147:       throw new IllegalArgumentException ("null argument");
1148:     if (warningListeners != null)
1149:       {
1150:         Iterator it = warningListeners.iterator();
1151: 
1152:         while (it.hasNext())
1153:           {
1154:             IIOReadWarningListener listener =
1155:               (IIOReadWarningListener) it.next();
1156:             listener.warningOccurred(this, warning);
1157:           }
1158:       }
1159:   }
1160: 
1161:   /**
1162:    * Notify all installed warning listeners, by calling their
1163:    * warningOccurred methods, that a warning message has been raised.
1164:    * The warning message is retrieved from a resource bundle, using
1165:    * the given basename and keyword.
1166:    *
1167:    * @param baseName the basename of the resource from which to
1168:    * retrieve the warning message
1169:    * @param keyword the keyword used to retrieve the warning from the
1170:    * resource bundle
1171:    *
1172:    * @exception IllegalArgumentException if either baseName or keyword
1173:    * is null
1174:    * @exception IllegalArgumentException if no resource bundle is
1175:    * found using baseName
1176:    * @exception IllegalArgumentException if the given keyword produces
1177:    * no results from the resource bundle
1178:    * @exception IllegalArgumentException if the retrieved object is
1179:    * not a String
1180:    */
1181:   protected void processWarningOccurred(String baseName,
1182:                                         String keyword)
1183:   {
1184:     if (baseName == null || keyword == null)
1185:       throw new IllegalArgumentException ("null argument");
1186: 
1187:     ResourceBundle b = null;
1188: 
1189:     try
1190:       {
1191:         b = ResourceBundle.getBundle(baseName, getLocale());
1192:       }
1193:     catch (MissingResourceException e)
1194:       {
1195:         throw new IllegalArgumentException ("no resource bundle found");
1196:       }
1197: 
1198:     Object str = null;
1199: 
1200:     try
1201:       {
1202:         str = b.getObject(keyword);
1203:       }
1204:     catch (MissingResourceException e)
1205:       {
1206:         throw new IllegalArgumentException ("no results found for keyword");
1207:       }
1208: 
1209:     if (! (str instanceof String))
1210:       throw new IllegalArgumentException ("retrieved object not a String");
1211: 
1212:     String warning = (String) str;
1213: 
1214:     if (warningListeners != null)
1215:       {
1216:         Iterator it = warningListeners.iterator();
1217: 
1218:         while (it.hasNext())
1219:           {
1220:             IIOReadWarningListener listener =
1221:               (IIOReadWarningListener) it.next();
1222:             listener.warningOccurred(this, warning);
1223:           }
1224:       }
1225:   }
1226: 
1227:   /**
1228:    * Read the given frame into a buffered image using the given read
1229:    * parameters.  Listeners will be notified of image loading progress
1230:    * and warnings.
1231:    *
1232:    * @param imageIndex the index of the frame to read
1233:    * @param param the image read parameters to use when reading
1234:    *
1235:    * @return a buffered image
1236:    *
1237:    * @exception IllegalStateException if input is null
1238:    * @exception IndexOutOfBoundsException if the frame index is
1239:    * out-of-bounds
1240:    * @exception IOException if a read error occurs
1241:    */
1242:   public abstract BufferedImage read(int imageIndex, ImageReadParam param)
1243:     throws IOException;
1244: 
1245:   /**
1246:    * Check if this reader supports reading thumbnails.
1247:    *
1248:    * @return true if this reader supports reading thumbnails, false
1249:    * otherwise
1250:    */
1251:   public boolean readerSupportsThumbnails()
1252:   {
1253:     return false;
1254:   }
1255: 
1256:   /**
1257:    * Read raw raster data.  The image type specifier in param is
1258:    * ignored but all other parameters are used.  Offset parameters are
1259:    * translated into the raster's coordinate space.  This method may
1260:    * be implemented by image readers that want to provide direct
1261:    * access to raw image data.
1262:    *
1263:    * @param imageIndex the frame index
1264:    * @param param the image read parameters
1265:    *
1266:    * @return a raster containing the read image data
1267:    *
1268:    * @exception UnsupportedOperationException if this reader doesn't
1269:    * support rasters
1270:    * @exception IllegalStateException if input is null
1271:    * @exception IndexOutOfBoundsException if the frame index is
1272:    * out-of-bounds
1273:    * @exception IOException if a read error occurs
1274:    */
1275:   public Raster readRaster(int imageIndex, ImageReadParam param)
1276:     throws IOException
1277:   {
1278:     throw new UnsupportedOperationException();
1279:   }
1280: 
1281:   /**
1282:    * Read a thumbnail.
1283:    *
1284:    * @param imageIndex the frame index
1285:    * @param thumbnailIndex the thumbnail index
1286:    *
1287:    * @return a buffered image of the thumbnail
1288:    *
1289:    * @exception UnsupportedOperationException if this reader doesn't
1290:    * support thumbnails
1291:    * @exception IllegalStateException if input is null
1292:    * @exception IndexOutOfBoundsException if either the frame index or
1293:    * the thumbnail index is out-of-bounds
1294:    * @exception IOException if a read error occurs
1295:    *
1296:    */
1297:   public BufferedImage readThumbnail(int imageIndex, int thumbnailIndex)
1298:     throws IOException
1299:   {
1300:     throw new UnsupportedOperationException();
1301:   }
1302: 
1303:   /**
1304:    * Uninstall all read progress listeners.
1305:    */
1306:   public void removeAllIIOReadProgressListeners()
1307:   {
1308:     progressListeners = null;
1309:   }
1310: 
1311:   /**
1312:    * Uninstall all read update listeners.
1313:    */
1314:   public void removeAllIIOReadUpdateListeners()
1315:   {
1316:     updateListeners = null;
1317:   }
1318: 
1319:   /**
1320:    * Uninstall all read warning listeners.
1321:    */
1322:   public void removeAllIIOReadWarningListeners()
1323:   {
1324:     warningListeners = null;
1325:   }
1326: 
1327:   /**
1328:    * Uninstall the given read progress listener.
1329:    *
1330:    * @param listener the listener to remove
1331:    */
1332:   public void removeIIOReadProgressListener(IIOReadProgressListener listener)
1333:   {
1334:     if (listener == null)
1335:       return;
1336:     if (progressListeners != null)
1337:       {
1338:         progressListeners.remove(listener);
1339:       }
1340:   }
1341: 
1342:   /**
1343:    * Uninstall the given read update listener.
1344:    *
1345:    * @param listener the listener to remove
1346:    */
1347:   public void removeIIOReadUpdateListener(IIOReadUpdateListener listener)
1348:   {
1349:     if (listener == null)
1350:       return;
1351: 
1352:     if (updateListeners != null)
1353:       {
1354:         updateListeners.remove(listener);
1355:       }
1356:   }
1357: 
1358:   /**
1359:    * Uninstall the given read warning listener.
1360:    *
1361:    * @param listener the listener to remove
1362:    */
1363:   public void removeIIOReadWarningListener(IIOReadWarningListener listener)
1364:   {
1365:     if (listener == null)
1366:       return;
1367:     if (warningListeners != null)
1368:       {
1369:         warningListeners.remove(listener);
1370:       }
1371:   }
1372: 
1373:   /**
1374:    * Set the current locale or use the default locale.
1375:    *
1376:    * @param locale the locale to set, or null
1377:    */
1378:   public void setLocale(Locale locale)
1379:   {
1380:     if (locale != null)
1381:       {
1382:         // Check if its a valid locale.
1383:         boolean found = false;
1384: 
1385:         if (availableLocales != null)
1386:           for (int i = availableLocales.length - 1; i >= 0; --i)
1387:             if (availableLocales[i].equals(locale))
1388:               found = true;
1389: 
1390:         if (! found)
1391:           throw new IllegalArgumentException("looale not available");
1392:       }
1393: 
1394:     this.locale = locale;
1395:   }
1396: 
1397:   /**
1398:    * Check that the given read parameters have valid source and
1399:    * destination band settings.  If the param.getSourceBands() returns
1400:    * null, the array is assumed to include all band indices, 0 to
1401:    * numSrcBands - 1; likewise if param.getDestinationBands() returns
1402:    * null, it is assumed to be an array containing indices 0 to
1403:    * numDstBands - 1.  A failure will cause this method to throw
1404:    * IllegalArgumentException.
1405:    *
1406:    * @param param the image parameters to check
1407:    * @param numSrcBands the number of input source bands
1408:    * @param numDstBands the number of ouput destination bands
1409:    *
1410:    * @exception IllegalArgumentException if either the given source or
1411:    * destination band indices are invalid
1412:    */
1413:   protected static void checkReadParamBandSettings(ImageReadParam param,
1414:                                                    int numSrcBands,
1415:                                                    int numDstBands)
1416:   {
1417:     int[] srcBands = param.getSourceBands();
1418:     int[] dstBands = param.getDestinationBands();
1419:     boolean lengthsDiffer = false;
1420:     boolean srcOOB = false;
1421:     boolean dstOOB = false;
1422: 
1423:     if (srcBands == null)
1424:       {
1425:         if (dstBands == null)
1426:           {
1427:             if (numSrcBands != numDstBands)
1428:               lengthsDiffer = true;
1429:           }
1430:         else
1431:           {
1432:             if (numSrcBands != dstBands.length)
1433:               lengthsDiffer = true;
1434: 
1435:             for (int i = 0; i < dstBands.length; i++)
1436:               if (dstBands[i] > numSrcBands - 1)
1437:                 {
1438:                   dstOOB = true;
1439:                   break;
1440:                 }
1441:           }
1442:       }
1443:     else
1444:       {
1445:         if (dstBands == null)
1446:           {
1447:             if (srcBands.length != numDstBands)
1448:               lengthsDiffer = true;
1449: 
1450:             for (int i = 0; i < srcBands.length; i++)
1451:               if (srcBands[i] > numDstBands - 1)
1452:                 {
1453:                   srcOOB = true;
1454:                   break;
1455:                 }
1456:           }
1457:         else
1458:           {
1459:             if (srcBands.length != dstBands.length)
1460:               lengthsDiffer = true;
1461: 
1462:             for (int i = 0; i < srcBands.length; i++)
1463:               if (srcBands[i] > numDstBands - 1)
1464:                 {
1465:                   srcOOB = true;
1466:                   break;
1467:                 }
1468: 
1469:             for (int i = 0; i < dstBands.length; i++)
1470:               if (dstBands[i] > numSrcBands - 1)
1471:                 {
1472:                   dstOOB = true;
1473:                   break;
1474:                 }
1475:           }
1476:       }
1477: 
1478:     if (lengthsDiffer)
1479:       throw new IllegalArgumentException ("array lengths differ");
1480: 
1481:     if (srcOOB)
1482:       throw new IllegalArgumentException ("source band index"
1483:                                           + " out-of-bounds");
1484: 
1485:     if (dstOOB)
1486:       throw new IllegalArgumentException ("destination band index"
1487:                                           + " out-of-bounds");
1488:   }
1489: 
1490:   /**
1491:    * Calcluate the source and destination regions that will be read
1492:    * from and written to, given image parameters and/or a destination
1493:    * buffered image.  The source region will be clipped if any of its
1494:    * bounds are outside the destination region.  Clipping will account
1495:    * for subsampling and destination offsets.  Likewise, the
1496:    * destination region is clipped to the given destination image, if
1497:    * it is not null, using the given image parameters, if they are not
1498:    * null.  IllegalArgumentException is thrown if either region will
1499:    * contain 0 pixels after clipping.
1500:    *
1501:    * @param param read parameters, or null
1502:    * @param srcWidth the width of the source image
1503:    * @param srcHeight the height of the source image
1504:    * @param image the destination image, or null
1505:    * @param srcRegion a rectangle whose values will be set to the
1506:    * clipped source region
1507:    * @param destRegion a rectangle whose values will be set to the
1508:    * clipped destination region
1509:    *
1510:    * @exception IllegalArgumentException if either srcRegion or
1511:    * destRegion is null
1512:    * @exception IllegalArgumentException if either of the calculated
1513:    * regions is empty
1514:    */
1515:   protected static void computeRegions (ImageReadParam param,
1516:                                         int srcWidth,
1517:                                         int srcHeight,
1518:                                         BufferedImage image,
1519:                                         Rectangle srcRegion,
1520:                                         Rectangle destRegion)
1521:   {
1522:     if (srcRegion == null || destRegion == null)
1523:       throw new IllegalArgumentException ("null region");
1524: 
1525:     if (srcWidth == 0 || srcHeight == 0)
1526:       throw new IllegalArgumentException ("zero-sized region");
1527: 
1528:     srcRegion = getSourceRegion(param, srcWidth, srcHeight);
1529:     if (image != null)
1530:       destRegion = new Rectangle (0, 0, image.getWidth(), image.getHeight());
1531:     else
1532:       destRegion = new Rectangle (0, 0, srcWidth, srcHeight);
1533: 
1534:     if (param != null)
1535:       {
1536:         Point offset = param.getDestinationOffset();
1537: 
1538:         if (offset.x < 0)
1539:           {
1540:             srcRegion.x -= offset.x;
1541:             srcRegion.width += offset.x;
1542:           }
1543:         if (offset.y < 0)
1544:           {
1545:             srcRegion.y -= offset.y;
1546:             srcRegion.height += offset.y;
1547:           }
1548: 
1549:         srcRegion.width = srcRegion.width > destRegion.width
1550:           ? destRegion.width : srcRegion.width;
1551:         srcRegion.height = srcRegion.height > destRegion.height
1552:           ? destRegion.height : srcRegion.height;
1553: 
1554:         if (offset.x >= 0)
1555:           {
1556:             destRegion.x += offset.x;
1557:             destRegion.width -= offset.x;
1558:           }
1559:         if (offset.y >= 0)
1560:           {
1561:             destRegion.y += offset.y;
1562:             destRegion.height -= offset.y;
1563:           }
1564:       }
1565: 
1566:     if (srcRegion.isEmpty() || destRegion.isEmpty())
1567:       throw new IllegalArgumentException ("zero-sized region");
1568:   }
1569: 
1570:   /**
1571:    * Return a suitable destination buffered image.  If
1572:    * param.getDestination() is non-null, then it is returned,
1573:    * otherwise a buffered image is created using
1574:    * param.getDestinationType() if it is non-null and also in the
1575:    * given imageTypes collection, or the first element of imageTypes
1576:    * otherwise.
1577:    *
1578:    * @param param image read parameters from which a destination image
1579:    * or image type is retrieved, or null
1580:    * @param imageTypes a collection of legal image types
1581:    * @param width the width of the source image
1582:    * @param height the height of the source image
1583:    *
1584:    * @return a suitable destination buffered image
1585:    *
1586:    * @exception IIOException if param.getDestinationType() does not
1587:    * return an image type in imageTypes
1588:    * @exception IllegalArgumentException if imageTypes is null or
1589:    * empty, or if a non-ImageTypeSpecifier object is retrieved from
1590:    * imageTypes
1591:    * @exception IllegalArgumentException if the resulting destination
1592:    * region is empty
1593:    * @exception IllegalArgumentException if the product of width and
1594:    * height is greater than Integer.MAX_VALUE
1595:    */
1596:   protected static BufferedImage getDestination (ImageReadParam param,
1597:                                                  Iterator<ImageTypeSpecifier> imageTypes,
1598:                                                  int width,
1599:                                                  int height)
1600:     throws IIOException
1601:   {
1602:     if (imageTypes == null || !imageTypes.hasNext())
1603:       throw new IllegalArgumentException ("imageTypes null or empty");
1604: 
1605:     if (width < 0 || height < 0)
1606:       throw new IllegalArgumentException ("negative dimension");
1607: 
1608:     // test for overflow
1609:     if (width * height < Math.min (width, height))
1610:       throw new IllegalArgumentException ("width * height > Integer.MAX_VALUE");
1611: 
1612:     BufferedImage dest = null;
1613:     ImageTypeSpecifier destType = null;
1614: 
1615:     if (param != null)
1616:       {
1617:         dest = param.getDestination ();
1618:         if (dest == null)
1619:           {
1620:             ImageTypeSpecifier type = param.getDestinationType();
1621:             if (type != null)
1622:               {
1623:                 Iterator it = imageTypes;
1624: 
1625:                 while (it.hasNext())
1626:                   {
1627:                     Object o = it.next ();
1628:                     if (! (o instanceof ImageTypeSpecifier))
1629:                       throw new IllegalArgumentException ("non-ImageTypeSpecifier object");
1630: 
1631:                     ImageTypeSpecifier t = (ImageTypeSpecifier) o;
1632:                     if (t.equals (type))
1633:                       {
1634:                         dest = t.createBufferedImage (width, height);
1635:                         break;
1636:                       }
1637:                     if (destType == null)
1638:                       throw new IIOException ("invalid destination type");
1639: 
1640:                   }
1641:               }
1642:           }
1643:       }
1644:     if (dest == null)
1645:       {
1646:         Rectangle srcRegion = new Rectangle ();
1647:         Rectangle destRegion = new Rectangle ();
1648: 
1649:         computeRegions (param, width, height, null, srcRegion, destRegion);
1650: 
1651:         if (destRegion.isEmpty())
1652:           throw new IllegalArgumentException ("destination region empty");
1653: 
1654:         if (destType == null)
1655:           {
1656:             Object o = imageTypes.next();
1657:             if (! (o instanceof ImageTypeSpecifier))
1658:               throw new IllegalArgumentException ("non-ImageTypeSpecifier"
1659:                                                   + " object");
1660: 
1661:             dest = ((ImageTypeSpecifier) o).createBufferedImage
1662:               (destRegion.width, destRegion.height);
1663:           }
1664:         else
1665:           dest = destType.createBufferedImage
1666:             (destRegion.width, destRegion.height);
1667:       }
1668:     return dest;
1669:   }
1670: 
1671:   /**
1672:    * Get the metadata associated with this image.  If the reader is
1673:    * set to ignore metadata or does not support reading metadata, or
1674:    * if no metadata is available then null is returned.
1675:    *
1676:    * This more specific version of getImageMetadata(int) can be used
1677:    * to restrict metadata retrieval to specific formats and node
1678:    * names, which can limit the amount of data that needs to be
1679:    * processed.
1680:    *
1681:    * @param imageIndex the frame index
1682:    * @param formatName the format of metadata requested
1683:    * @param nodeNames a set of Strings specifiying node names to be
1684:    * retrieved
1685:    *
1686:    * @return a metadata object, or null
1687:    *
1688:    * @exception IllegalStateException if input has not been set
1689:    * @exception IndexOutOfBoundsException if the frame index is
1690:    * out-of-bounds
1691:    * @exception IllegalArgumentException if formatName is null
1692:    * @exception IllegalArgumentException if nodeNames is null
1693:    * @exception IOException if a read error occurs
1694:    */
1695:   public IIOMetadata getImageMetadata (int imageIndex,
1696:                                        String formatName,
1697:                                        Set<String> nodeNames)
1698:     throws IOException
1699:   {
1700:     if (formatName == null || nodeNames == null)
1701:       throw new IllegalArgumentException ("null argument");
1702: 
1703:     return getImageMetadata (imageIndex);
1704:   }
1705: 
1706:   /**
1707:    * Get the index at which the next image will be read.  If
1708:    * seekForwardOnly is true then the returned value will increase
1709:    * monotonically each time an image frame is read.  If
1710:    * seekForwardOnly is false then the returned value will always be
1711:    * 0.
1712:    *
1713:    * @return the current frame index
1714:    */
1715:   public int getMinIndex()
1716:   {
1717:     return minIndex;
1718:   }
1719: 
1720:   /**
1721:    * Get the image type specifier that most closely represents the
1722:    * internal data representation used by this reader.  This value
1723:    * should be included in the return value of getImageTypes.
1724:    *
1725:    * @param imageIndex the frame index
1726:    *
1727:    * @return an image type specifier
1728:    *
1729:    * @exception IllegalStateException if input has not been set
1730:    * @exception IndexOutOfBoundsException if the frame index is
1731:    * out-of-bounds
1732:    * @exception IOException if a read error occurs
1733:    */
1734:   public ImageTypeSpecifier getRawImageType (int imageIndex)
1735:     throws IOException
1736:   {
1737:     return getImageTypes(imageIndex).next();
1738:   }
1739: 
1740:   /**
1741:    * Calculate a source region based on the given source image
1742:    * dimensions and parameters.  Subsampling offsets and a source
1743:    * region are taken from the given image read parameters and used to
1744:    * clip the given image dimensions, returning a new rectangular
1745:    * region as a result.
1746:    *
1747:    * @param param image parameters, or null
1748:    * @param srcWidth the width of the source image
1749:    * @param srcHeight the height of the source image
1750:    *
1751:    * @return a clipped rectangle
1752:    */
1753:   protected static Rectangle getSourceRegion (ImageReadParam param,
1754:                                               int srcWidth,
1755:                                               int srcHeight)
1756:   {
1757:     Rectangle clippedRegion = new Rectangle (0, 0, srcWidth, srcHeight);
1758: 
1759:     if (param != null)
1760:       {
1761:         Rectangle srcRegion = param.getSourceRegion();
1762: 
1763:         if (srcRegion != null)
1764:           {
1765:             clippedRegion.x = srcRegion.x > clippedRegion.x
1766:               ? srcRegion.x : clippedRegion.x;
1767:             clippedRegion.y = srcRegion.y > clippedRegion.y
1768:               ? srcRegion.y : clippedRegion.y;
1769:             clippedRegion.width = srcRegion.width > clippedRegion.width
1770:               ? srcRegion.width : clippedRegion.width;
1771:             clippedRegion.height = srcRegion.height > clippedRegion.height
1772:               ? srcRegion.height : clippedRegion.height;
1773:           }
1774: 
1775:         int xOffset = param.getSubsamplingXOffset();
1776: 
1777:         clippedRegion.x += xOffset;
1778:         clippedRegion.width -= xOffset;
1779: 
1780:         int yOffset = param.getSubsamplingYOffset();
1781: 
1782:         clippedRegion.y += yOffset;
1783:         clippedRegion.height -= yOffset;
1784:       }
1785:     return clippedRegion;
1786:   }
1787: 
1788:   /**
1789:    * Get the metadata associated with the image being read.  If the
1790:    * reader is set to ignore metadata or does not support reading
1791:    * metadata, or if no metadata is available then null is returned.
1792:    * This method returns metadata associated with the entirety of the
1793:    * image data, whereas getStreamMetadata() returns metadata
1794:    * associated with a frame within a multi-image data stream.
1795:    *
1796:    * This more specific version of getStreamMetadata() can be used to
1797:    * restrict metadata retrieval to specific formats and node names,
1798:    * which can limit the amount of data that needs to be processed.
1799:    *
1800:    * @param formatName the format of metadata requested
1801:    * @param nodeNames a set of Strings specifiying node names to be
1802:    * retrieved
1803:    *
1804:    * @return metadata associated with the image being read, or null
1805:    *
1806:    * @exception IllegalArgumentException if formatName is null
1807:    * @exception IllegalArgumentException if nodeNames is null
1808:    * @exception IOException if a read error occurs
1809:    */
1810:   public IIOMetadata getStreamMetadata (String formatName,
1811:                                         Set<String> nodeNames)
1812:     throws IOException
1813:   {
1814:     if (formatName == null || nodeNames == null)
1815:       throw new IllegalArgumentException ("null argument");
1816: 
1817:     return getStreamMetadata();
1818:   }
1819: 
1820:   /**
1821:    * Read the given frame all at once, using default image read
1822:    * parameters, and return a buffered image.
1823:    *
1824:    * The returned image will be formatted according to the
1825:    * currently-preferred image type specifier.
1826:    *
1827:    * Installed read progress listeners, update progress listeners and
1828:    * warning listeners will be notified of read progress, changes in
1829:    * sample sets and warnings respectively.
1830:    *
1831:    * @param imageIndex the index of the image frame to read
1832:    *
1833:    * @return a buffered image
1834:    *
1835:    * @exception IllegalStateException if input has not been set
1836:    * @exception IndexOutOfBoundsException if the frame index is
1837:    * out-of-bounds
1838:    * @exception IOException if a read error occurs
1839:    */
1840:   public BufferedImage read (int imageIndex)
1841:     throws IOException
1842:   {
1843:     return read (imageIndex, null);
1844:   }
1845: 
1846:   /**
1847:    * Read the given frame all at once, using the given image read
1848:    * parameters, and return an IIOImage.  The IIOImage will contain a
1849:    * buffered image as returned by getDestination.
1850:    *
1851:    * Installed read progress listeners, update progress listeners and
1852:    * warning listeners will be notified of read progress, changes in
1853:    * sample sets and warnings respectively.
1854:    *
1855:    * The source and destination band settings are checked with a call
1856:    * to checkReadParamBandSettings.
1857:    *
1858:    * @param imageIndex the index of the image frame to read
1859:    * @param param the image read parameters
1860:    *
1861:    * @return an IIOImage
1862:    *
1863:    * @exception IllegalStateException if input has not been set
1864:    * @exception IndexOutOfBoundsException if the frame index is
1865:    * out-of-bounds
1866:    * @exception IllegalArgumentException if param.getSourceBands() and
1867:    * param.getDestinationBands() are incompatible
1868:    * @exception IllegalArgumentException if either the source or
1869:    * destination image regions are empty
1870:    * @exception IOException if a read error occurs
1871:    */
1872:   public IIOImage readAll (int imageIndex,
1873:                            ImageReadParam param)
1874:     throws IOException
1875:   {
1876:     checkReadParamBandSettings (param,
1877:                                 param.getSourceBands().length,
1878:                                 param.getDestinationBands().length);
1879: 
1880:     List l = new ArrayList ();
1881: 
1882:     for (int i = 0; i < getNumThumbnails (imageIndex); i++)
1883:       l.add (readThumbnail(imageIndex, i));
1884: 
1885:     return new IIOImage (getDestination(param, getImageTypes(imageIndex),
1886:                                         getWidth(imageIndex),
1887:                                         getHeight(imageIndex)),
1888:                          l,
1889:                          getImageMetadata (imageIndex));
1890:   }
1891: 
1892:   /**
1893:    * Read all image frames all at once, using the given image read
1894:    * parameters iterator, and return an iterator over a collection of
1895:    * IIOImages.  Each IIOImage in the collection will contain a
1896:    * buffered image as returned by getDestination.
1897:    *
1898:    * Installed read progress listeners, update progress listeners and
1899:    * warning listeners will be notified of read progress, changes in
1900:    * sample sets and warnings respectively.
1901:    *
1902:    * Each set of source and destination band settings are checked with
1903:    * a call to checkReadParamBandSettings.
1904:    *
1905:    * @param params iterator over the image read parameters
1906:    *
1907:    * @return an IIOImage
1908:    *
1909:    * @exception IllegalStateException if input has not been set
1910:    * @exception IllegalArgumentException if a non-ImageReadParam is
1911:    * found in params
1912:    * @exception IllegalArgumentException if param.getSourceBands() and
1913:    * param.getDestinationBands() are incompatible
1914:    * @exception IllegalArgumentException if either the source or
1915:    * destination image regions are empty
1916:    * @exception IOException if a read error occurs
1917:    */
1918:   public Iterator<IIOImage> readAll (Iterator<? extends ImageReadParam> params)
1919:     throws IOException
1920:   {
1921:     List l = new ArrayList ();
1922:     int index = 0;
1923: 
1924:     while (params.hasNext())
1925:       {
1926:         if (params != null && ! (params instanceof ImageReadParam))
1927:           throw new IllegalArgumentException ("non-ImageReadParam found");
1928: 
1929:         l.add (readAll(index++, (ImageReadParam) params.next ()));
1930:       }
1931: 
1932:     return l.iterator();
1933:   }
1934: 
1935:   /**
1936:    * Read a rendered image.  This is a more general counterpart to
1937:    * read (int, ImageReadParam).  All image data may not be read
1938:    * before this method returns and so listeners will not necessarily
1939:    * be notified.
1940:    *
1941:    * @param imageIndex the index of the image frame to read
1942:    * @param param the image read parameters
1943:    *
1944:    * @return a rendered image
1945:    *
1946:    * @exception IllegalStateException if input is null
1947:    * @exception IndexOutOfBoundsException if the frame index is
1948:    * out-of-bounds
1949:    * @exception IllegalArgumentException if param.getSourceBands() and
1950:    * param.getDestinationBands() are incompatible
1951:    * @exception IllegalArgumentException if either the source or
1952:    * destination image regions are empty
1953:    * @exception IOException if a read error occurs
1954:    */
1955:   public RenderedImage readAsRenderedImage (int imageIndex,
1956:                                             ImageReadParam param)
1957:     throws IOException
1958:   {
1959:     return read (imageIndex, param);
1960:   }
1961: 
1962:   /**
1963:    * Read the given tile into a buffered image.  If the tile
1964:    * coordinates are out-of-bounds an exception is thrown.  If the
1965:    * image is not tiled then the coordinates 0, 0 are expected and the
1966:    * entire image will be read.
1967:    *
1968:    * @param imageIndex the frame index
1969:    * @param tileX the horizontal tile coordinate
1970:    * @param tileY the vertical tile coordinate
1971:    *
1972:    * @return the contents of the tile as a buffered image
1973:    *
1974:    * @exception IllegalStateException if input is null
1975:    * @exception IndexOutOfBoundsException if the frame index is
1976:    * out-of-bounds
1977:    * @exception IllegalArgumentException if the tile coordinates are
1978:    * out-of-bounds
1979:    * @exception IOException if a read error occurs
1980:    */
1981:   public BufferedImage readTile (int imageIndex, int tileX, int tileY)
1982:     throws IOException
1983:   {
1984:     if (tileX != 0 || tileY != 0)
1985:       throw new IllegalArgumentException ("tileX not 0 or tileY not 0");
1986: 
1987:     return read (imageIndex);
1988:   }
1989: 
1990:   /**
1991:    * Read the given tile into a raster containing the raw image data.
1992:    * If the tile coordinates are out-of-bounds an exception is thrown.
1993:    * If the image is not tiled then the coordinates 0, 0 are expected
1994:    * and the entire image will be read.
1995:    *
1996:    * @param imageIndex the frame index
1997:    * @param tileX the horizontal tile coordinate
1998:    * @param tileY the vertical tile coordinate
1999:    *
2000:    * @return the contents of the tile as a raster
2001:    *
2002:    * @exception UnsupportedOperationException if rasters are not
2003:    * supported
2004:    * @exception IllegalStateException if input is null
2005:    * @exception IndexOutOfBoundsException if the frame index is
2006:    * out-of-bounds
2007:    * @exception IllegalArgumentException if the tile coordinates are
2008:    * out-of-bounds
2009:    * @exception IOException if a read error occurs
2010:    */
2011:   public Raster readTileRaster (int imageIndex, int tileX, int tileY)
2012:     throws IOException
2013:   {
2014:     if (!canReadRaster())
2015:       throw new UnsupportedOperationException ("cannot read rasters");
2016: 
2017:     if (tileX != 0 || tileY != 0)
2018:       throw new IllegalArgumentException ("tileX not 0 or tileY not 0");
2019: 
2020:     return readRaster (imageIndex, null);
2021:   }
2022: 
2023:   /**
2024:    * Reset this reader's internal state.
2025:    */
2026:   public void reset ()
2027:   {
2028:     setInput (null, false);
2029:     setLocale (null);
2030:     removeAllIIOReadUpdateListeners ();
2031:     removeAllIIOReadWarningListeners ();
2032:     removeAllIIOReadProgressListeners ();
2033:     clearAbortRequest ();
2034:   }
2035: }