1 /* 2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.imageio; 27 28 import java.awt.image.BufferedImage; 29 import java.awt.image.RenderedImage; 30 import java.io.File; 31 import java.io.FilePermission; 32 import java.io.InputStream; 33 import java.io.IOException; 34 import java.io.OutputStream; 35 import java.lang.reflect.Method; 36 import java.net.URL; 37 import java.security.AccessController; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.HashSet; 41 import java.util.Iterator; 42 import java.util.NoSuchElementException; 43 import java.util.Set; 44 import javax.imageio.spi.IIORegistry; 45 import javax.imageio.spi.ImageReaderSpi; 46 import javax.imageio.spi.ImageReaderWriterSpi; 47 import javax.imageio.spi.ImageWriterSpi; 48 import javax.imageio.spi.ImageInputStreamSpi; 49 import javax.imageio.spi.ImageOutputStreamSpi; 50 import javax.imageio.spi.ImageTranscoderSpi; 51 import javax.imageio.spi.ServiceRegistry; 52 import javax.imageio.stream.ImageInputStream; 53 import javax.imageio.stream.ImageOutputStream; 54 import sun.awt.AppContext; 55 import sun.security.action.GetPropertyAction; 56 57 /** 58 * A class containing static convenience methods for locating 59 * <code>ImageReader</code>s and <code>ImageWriter</code>s, and 60 * performing simple encoding and decoding. 61 * 62 */ 63 public final class ImageIO { 64 65 private static final IIORegistry theRegistry = 66 IIORegistry.getDefaultInstance(); 67 68 /** 69 * Constructor is private to prevent instantiation. 70 */ 71 private ImageIO() {} 72 73 /** 74 * Scans for plug-ins on the application class path, 75 * loads their service provider classes, and registers a service 76 * provider instance for each one found with the 77 * <code>IIORegistry</code>. 78 * 79 * <p>This method is needed because the application class path can 80 * theoretically change, or additional plug-ins may become available. 81 * Rather than re-scanning the classpath on every invocation of the 82 * API, the class path is scanned automatically only on the first 83 * invocation. Clients can call this method to prompt a re-scan. 84 * Thus this method need only be invoked by sophisticated applications 85 * which dynamically make new plug-ins available at runtime. 86 * 87 * <p> The <code>getResources</code> method of the context 88 * <code>ClassLoader</code> is used locate JAR files containing 89 * files named 90 * <code>META-INF/services/javax.imageio.spi.</code><i>classname</i>, 91 * where <i>classname</i> is one of <code>ImageReaderSpi</code>, 92 * <code>ImageWriterSpi</code>, <code>ImageTranscoderSpi</code>, 93 * <code>ImageInputStreamSpi</code>, or 94 * <code>ImageOutputStreamSpi</code>, along the application class 95 * path. 96 * 97 * <p> The contents of the located files indicate the names of 98 * actual implementation classes which implement the 99 * aforementioned service provider interfaces; the default class 100 * loader is then used to load each of these classes and to 101 * instantiate an instance of each class, which is then placed 102 * into the registry for later retrieval. 103 * 104 * <p> The exact set of locations searched depends on the 105 * implementation of the Java runtime environment. 106 * 107 * @see ClassLoader#getResources 108 */ 109 public static void scanForPlugins() { 110 theRegistry.registerApplicationClasspathSpis(); 111 } 112 113 // ImageInputStreams 114 115 /** 116 * A class to hold information about caching. Each 117 * <code>ThreadGroup</code> will have its own copy 118 * via the <code>AppContext</code> mechanism. 119 */ 120 static class CacheInfo { 121 boolean useCache = true; 122 File cacheDirectory = null; 123 Boolean hasPermission = null; 124 125 public CacheInfo() {} 126 127 public boolean getUseCache() { 128 return useCache; 129 } 130 131 public void setUseCache(boolean useCache) { 132 this.useCache = useCache; 133 } 134 135 public File getCacheDirectory() { 136 return cacheDirectory; 137 } 138 139 public void setCacheDirectory(File cacheDirectory) { 140 this.cacheDirectory = cacheDirectory; 141 } 142 143 public Boolean getHasPermission() { 144 return hasPermission; 145 } 146 147 public void setHasPermission(Boolean hasPermission) { 148 this.hasPermission = hasPermission; 149 } 150 } 151 152 /** 153 * Returns the <code>CacheInfo</code> object associated with this 154 * <code>ThreadGroup</code>. 155 */ 156 private static synchronized CacheInfo getCacheInfo() { 157 AppContext context = AppContext.getAppContext(); 158 CacheInfo info = (CacheInfo)context.get(CacheInfo.class); 159 if (info == null) { 160 info = new CacheInfo(); 161 context.put(CacheInfo.class, info); 162 } 163 return info; 164 } 165 166 /** 167 * Returns the default temporary (cache) directory as defined by the 168 * java.io.tmpdir system property. 169 */ 170 private static String getTempDir() { 171 GetPropertyAction a = new GetPropertyAction("java.io.tmpdir"); 172 return AccessController.doPrivileged(a); 173 } 174 175 /** 176 * Determines whether the caller has write access to the cache 177 * directory, stores the result in the <code>CacheInfo</code> object, 178 * and returns the decision. This method helps to prevent mysterious 179 * SecurityExceptions to be thrown when this convenience class is used 180 * in an applet, for example. 181 */ 182 private static boolean hasCachePermission() { 183 Boolean hasPermission = getCacheInfo().getHasPermission(); 184 185 if (hasPermission != null) { 186 return hasPermission.booleanValue(); 187 } else { 188 try { 189 SecurityManager security = System.getSecurityManager(); 190 if (security != null) { 191 File cachedir = getCacheDirectory(); 192 String cachepath; 193 194 if (cachedir != null) { 195 cachepath = cachedir.getPath(); 196 } else { 197 cachepath = getTempDir(); 198 199 if (cachepath == null || cachepath.isEmpty()) { 200 getCacheInfo().setHasPermission(Boolean.FALSE); 201 return false; 202 } 203 } 204 205 // we have to check whether we can read, write, 206 // and delete cache files. 207 // So, compose cache file path and check it. 208 String filepath = cachepath; 209 if (!filepath.endsWith(File.separator)) { 210 filepath += File.separator; 211 } 212 filepath += "*"; 213 214 security.checkPermission(new FilePermission(filepath, "read, write, delete")); 215 } 216 } catch (SecurityException e) { 217 getCacheInfo().setHasPermission(Boolean.FALSE); 218 return false; 219 } 220 221 getCacheInfo().setHasPermission(Boolean.TRUE); 222 return true; 223 } 224 } 225 226 /** 227 * Sets a flag indicating whether a disk-based cache file should 228 * be used when creating <code>ImageInputStream</code>s and 229 * <code>ImageOutputStream</code>s. 230 * 231 * <p> When reading from a standard <code>InputStream</code>, it 232 * may be necessary to save previously read information in a cache 233 * since the underlying stream does not allow data to be re-read. 234 * Similarly, when writing to a standard 235 * <code>OutputStream</code>, a cache may be used to allow a 236 * previously written value to be changed before flushing it to 237 * the final destination. 238 * 239 * <p> The cache may reside in main memory or on disk. Setting 240 * this flag to <code>false</code> disallows the use of disk for 241 * future streams, which may be advantageous when working with 242 * small images, as the overhead of creating and destroying files 243 * is removed. 244 * 245 * <p> On startup, the value is set to <code>true</code>. 246 * 247 * @param useCache a <code>boolean</code> indicating whether a 248 * cache file should be used, in cases where it is optional. 249 * 250 * @see #getUseCache 251 */ 252 public static void setUseCache(boolean useCache) { 253 getCacheInfo().setUseCache(useCache); 254 } 255 256 /** 257 * Returns the current value set by <code>setUseCache</code>, or 258 * <code>true</code> if no explicit setting has been made. 259 * 260 * @return true if a disk-based cache may be used for 261 * <code>ImageInputStream</code>s and 262 * <code>ImageOutputStream</code>s. 263 * 264 * @see #setUseCache 265 */ 266 public static boolean getUseCache() { 267 return getCacheInfo().getUseCache(); 268 } 269 270 /** 271 * Sets the directory where cache files are to be created. A 272 * value of <code>null</code> indicates that the system-dependent 273 * default temporary-file directory is to be used. If 274 * <code>getUseCache</code> returns false, this value is ignored. 275 * 276 * @param cacheDirectory a <code>File</code> specifying a directory. 277 * 278 * @see File#createTempFile(String, String, File) 279 * 280 * @exception SecurityException if the security manager denies 281 * access to the directory. 282 * @exception IllegalArgumentException if <code>cacheDir</code> is 283 * non-<code>null</code> but is not a directory. 284 * 285 * @see #getCacheDirectory 286 */ 287 public static void setCacheDirectory(File cacheDirectory) { 288 if ((cacheDirectory != null) && !(cacheDirectory.isDirectory())) { 289 throw new IllegalArgumentException("Not a directory!"); 290 } 291 getCacheInfo().setCacheDirectory(cacheDirectory); 292 getCacheInfo().setHasPermission(null); 293 } 294 295 /** 296 * Returns the current value set by 297 * <code>setCacheDirectory</code>, or <code>null</code> if no 298 * explicit setting has been made. 299 * 300 * @return a <code>File</code> indicating the directory where 301 * cache files will be created, or <code>null</code> to indicate 302 * the system-dependent default temporary-file directory. 303 * 304 * @see #setCacheDirectory 305 */ 306 public static File getCacheDirectory() { 307 return getCacheInfo().getCacheDirectory(); 308 } 309 310 /** 311 * Returns an <code>ImageInputStream</code> that will take its 312 * input from the given <code>Object</code>. The set of 313 * <code>ImageInputStreamSpi</code>s registered with the 314 * <code>IIORegistry</code> class is queried and the first one 315 * that is able to take input from the supplied object is used to 316 * create the returned <code>ImageInputStream</code>. If no 317 * suitable <code>ImageInputStreamSpi</code> exists, 318 * <code>null</code> is returned. 319 * 320 * <p> The current cache settings from <code>getUseCache</code>and 321 * <code>getCacheDirectory</code> will be used to control caching. 322 * 323 * @param input an <code>Object</code> to be used as an input 324 * source, such as a <code>File</code>, readable 325 * <code>RandomAccessFile</code>, or <code>InputStream</code>. 326 * 327 * @return an <code>ImageInputStream</code>, or <code>null</code>. 328 * 329 * @exception IllegalArgumentException if <code>input</code> 330 * is <code>null</code>. 331 * @exception IOException if a cache file is needed but cannot be 332 * created. 333 * 334 * @see javax.imageio.spi.ImageInputStreamSpi 335 */ 336 public static ImageInputStream createImageInputStream(Object input) 337 throws IOException { 338 if (input == null) { 339 throw new IllegalArgumentException("input == null!"); 340 } 341 342 Iterator<ImageInputStreamSpi> iter; 343 // Ensure category is present 344 try { 345 iter = theRegistry.getServiceProviders(ImageInputStreamSpi.class, 346 true); 347 } catch (IllegalArgumentException e) { 348 return null; 349 } 350 351 boolean usecache = getUseCache() && hasCachePermission(); 352 353 while (iter.hasNext()) { 354 ImageInputStreamSpi spi = iter.next(); 355 if (spi.getInputClass().isInstance(input)) { 356 try { 357 return spi.createInputStreamInstance(input, 358 usecache, 359 getCacheDirectory()); 360 } catch (IOException e) { 361 throw new IIOException("Can't create cache file!", e); 362 } 363 } 364 } 365 366 return null; 367 } 368 369 // ImageOutputStreams 370 371 /** 372 * Returns an <code>ImageOutputStream</code> that will send its 373 * output to the given <code>Object</code>. The set of 374 * <code>ImageOutputStreamSpi</code>s registered with the 375 * <code>IIORegistry</code> class is queried and the first one 376 * that is able to send output from the supplied object is used to 377 * create the returned <code>ImageOutputStream</code>. If no 378 * suitable <code>ImageOutputStreamSpi</code> exists, 379 * <code>null</code> is returned. 380 * 381 * <p> The current cache settings from <code>getUseCache</code>and 382 * <code>getCacheDirectory</code> will be used to control caching. 383 * 384 * @param output an <code>Object</code> to be used as an output 385 * destination, such as a <code>File</code>, writable 386 * <code>RandomAccessFile</code>, or <code>OutputStream</code>. 387 * 388 * @return an <code>ImageOutputStream</code>, or 389 * <code>null</code>. 390 * 391 * @exception IllegalArgumentException if <code>output</code> is 392 * <code>null</code>. 393 * @exception IOException if a cache file is needed but cannot be 394 * created. 395 * 396 * @see javax.imageio.spi.ImageOutputStreamSpi 397 */ 398 public static ImageOutputStream createImageOutputStream(Object output) 399 throws IOException { 400 if (output == null) { 401 throw new IllegalArgumentException("output == null!"); 402 } 403 404 Iterator<ImageOutputStreamSpi> iter; 405 // Ensure category is present 406 try { 407 iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class, 408 true); 409 } catch (IllegalArgumentException e) { 410 return null; 411 } 412 413 boolean usecache = getUseCache() && hasCachePermission(); 414 415 while (iter.hasNext()) { 416 ImageOutputStreamSpi spi = iter.next(); 417 if (spi.getOutputClass().isInstance(output)) { 418 try { 419 return spi.createOutputStreamInstance(output, 420 usecache, 421 getCacheDirectory()); 422 } catch (IOException e) { 423 throw new IIOException("Can't create cache file!", e); 424 } 425 } 426 } 427 428 return null; 429 } 430 431 private static enum SpiInfo { 432 FORMAT_NAMES { 433 @Override 434 String[] info(ImageReaderWriterSpi spi) { 435 return spi.getFormatNames(); 436 } 437 }, 438 MIME_TYPES { 439 @Override 440 String[] info(ImageReaderWriterSpi spi) { 441 return spi.getMIMETypes(); 442 } 443 }, 444 FILE_SUFFIXES { 445 @Override 446 String[] info(ImageReaderWriterSpi spi) { 447 return spi.getFileSuffixes(); 448 } 449 }; 450 451 abstract String[] info(ImageReaderWriterSpi spi); 452 } 453 454 private static <S extends ImageReaderWriterSpi> 455 String[] getReaderWriterInfo(Class<S> spiClass, SpiInfo spiInfo) 456 { 457 // Ensure category is present 458 Iterator<S> iter; 459 try { 460 iter = theRegistry.getServiceProviders(spiClass, true); 461 } catch (IllegalArgumentException e) { 462 return new String[0]; 463 } 464 465 HashSet<String> s = new HashSet<String>(); 466 while (iter.hasNext()) { 467 ImageReaderWriterSpi spi = iter.next(); 468 Collections.addAll(s, spiInfo.info(spi)); 469 } 470 471 return s.toArray(new String[s.size()]); 472 } 473 474 // Readers 475 476 /** 477 * Returns an array of <code>String</code>s listing all of the 478 * informal format names understood by the current set of registered 479 * readers. 480 * 481 * @return an array of <code>String</code>s. 482 */ 483 public static String[] getReaderFormatNames() { 484 return getReaderWriterInfo(ImageReaderSpi.class, 485 SpiInfo.FORMAT_NAMES); 486 } 487 488 /** 489 * Returns an array of <code>String</code>s listing all of the 490 * MIME types understood by the current set of registered 491 * readers. 492 * 493 * @return an array of <code>String</code>s. 494 */ 495 public static String[] getReaderMIMETypes() { 496 return getReaderWriterInfo(ImageReaderSpi.class, 497 SpiInfo.MIME_TYPES); 498 } 499 500 /** 501 * Returns an array of <code>String</code>s listing all of the 502 * file suffixes associated with the formats understood 503 * by the current set of registered readers. 504 * 505 * @return an array of <code>String</code>s. 506 * @since 1.6 507 */ 508 public static String[] getReaderFileSuffixes() { 509 return getReaderWriterInfo(ImageReaderSpi.class, 510 SpiInfo.FILE_SUFFIXES); 511 } 512 513 static class ImageReaderIterator implements Iterator<ImageReader> { 514 // Contains ImageReaderSpis 515 private Iterator<ImageReaderSpi> iter; 516 517 public ImageReaderIterator(Iterator<ImageReaderSpi> iter) { 518 this.iter = iter; 519 } 520 521 public boolean hasNext() { 522 return iter.hasNext(); 523 } 524 525 public ImageReader next() { 526 ImageReaderSpi spi = null; 527 try { 528 spi = iter.next(); 529 return spi.createReaderInstance(); 530 } catch (IOException e) { 531 // Deregister the spi in this case, but only as 532 // an ImageReaderSpi 533 theRegistry.deregisterServiceProvider(spi, ImageReaderSpi.class); 534 } 535 return null; 536 } 537 538 public void remove() { 539 throw new UnsupportedOperationException(); 540 } 541 } 542 543 static class CanDecodeInputFilter 544 implements ServiceRegistry.Filter { 545 546 Object input; 547 548 public CanDecodeInputFilter(Object input) { 549 this.input = input; 550 } 551 552 public boolean filter(Object elt) { 553 try { 554 ImageReaderSpi spi = (ImageReaderSpi)elt; 555 ImageInputStream stream = null; 556 if (input instanceof ImageInputStream) { 557 stream = (ImageInputStream)input; 558 } 559 560 // Perform mark/reset as a defensive measure 561 // even though plug-ins are supposed to take 562 // care of it. 563 boolean canDecode = false; 564 if (stream != null) { 565 stream.mark(); 566 } 567 try { 568 canDecode = spi.canDecodeInput(input); 569 } finally { 570 if (stream != null) { 571 stream.reset(); 572 } 573 } 574 575 return canDecode; 576 } catch (IOException e) { 577 return false; 578 } 579 } 580 } 581 582 static class CanEncodeImageAndFormatFilter 583 implements ServiceRegistry.Filter { 584 585 ImageTypeSpecifier type; 586 String formatName; 587 588 public CanEncodeImageAndFormatFilter(ImageTypeSpecifier type, 589 String formatName) { 590 this.type = type; 591 this.formatName = formatName; 592 } 593 594 public boolean filter(Object elt) { 595 ImageWriterSpi spi = (ImageWriterSpi)elt; 596 return Arrays.asList(spi.getFormatNames()).contains(formatName) && 597 spi.canEncodeImage(type); 598 } 599 } 600 601 static class ContainsFilter 602 implements ServiceRegistry.Filter { 603 604 Method method; 605 String name; 606 607 // method returns an array of Strings 608 public ContainsFilter(Method method, 609 String name) { 610 this.method = method; 611 this.name = name; 612 } 613 614 public boolean filter(Object elt) { 615 try { 616 return contains((String[])method.invoke(elt), name); 617 } catch (Exception e) { 618 return false; 619 } 620 } 621 } 622 623 /** 624 * Returns an <code>Iterator</code> containing all currently 625 * registered <code>ImageReader</code>s that claim to be able to 626 * decode the supplied <code>Object</code>, typically an 627 * <code>ImageInputStream</code>. 628 * 629 * <p> The stream position is left at its prior position upon 630 * exit from this method. 631 * 632 * @param input an <code>ImageInputStream</code> or other 633 * <code>Object</code> containing encoded image data. 634 * 635 * @return an <code>Iterator</code> containing <code>ImageReader</code>s. 636 * 637 * @exception IllegalArgumentException if <code>input</code> is 638 * <code>null</code>. 639 * 640 * @see javax.imageio.spi.ImageReaderSpi#canDecodeInput 641 */ 642 public static Iterator<ImageReader> getImageReaders(Object input) { 643 if (input == null) { 644 throw new IllegalArgumentException("input == null!"); 645 } 646 Iterator<ImageReaderSpi> iter; 647 // Ensure category is present 648 try { 649 iter = theRegistry.getServiceProviders(ImageReaderSpi.class, 650 new CanDecodeInputFilter(input), 651 true); 652 } catch (IllegalArgumentException e) { 653 return Collections.emptyIterator(); 654 } 655 656 return new ImageReaderIterator(iter); 657 } 658 659 private static Method readerFormatNamesMethod; 660 private static Method readerFileSuffixesMethod; 661 private static Method readerMIMETypesMethod; 662 private static Method writerFormatNamesMethod; 663 private static Method writerFileSuffixesMethod; 664 private static Method writerMIMETypesMethod; 665 666 static { 667 try { 668 readerFormatNamesMethod = 669 ImageReaderSpi.class.getMethod("getFormatNames"); 670 readerFileSuffixesMethod = 671 ImageReaderSpi.class.getMethod("getFileSuffixes"); 672 readerMIMETypesMethod = 673 ImageReaderSpi.class.getMethod("getMIMETypes"); 674 675 writerFormatNamesMethod = 676 ImageWriterSpi.class.getMethod("getFormatNames"); 677 writerFileSuffixesMethod = 678 ImageWriterSpi.class.getMethod("getFileSuffixes"); 679 writerMIMETypesMethod = 680 ImageWriterSpi.class.getMethod("getMIMETypes"); 681 } catch (NoSuchMethodException e) { 682 e.printStackTrace(); 683 } 684 } 685 686 /** 687 * Returns an <code>Iterator</code> containing all currently 688 * registered <code>ImageReader</code>s that claim to be able to 689 * decode the named format. 690 * 691 * @param formatName a <code>String</code> containing the informal 692 * name of a format (<i>e.g.</i>, "jpeg" or "tiff". 693 * 694 * @return an <code>Iterator</code> containing 695 * <code>ImageReader</code>s. 696 * 697 * @exception IllegalArgumentException if <code>formatName</code> 698 * is <code>null</code>. 699 * 700 * @see javax.imageio.spi.ImageReaderSpi#getFormatNames 701 */ 702 public static Iterator<ImageReader> 703 getImageReadersByFormatName(String formatName) 704 { 705 if (formatName == null) { 706 throw new IllegalArgumentException("formatName == null!"); 707 } 708 Iterator<ImageReaderSpi> iter; 709 // Ensure category is present 710 try { 711 iter = theRegistry.getServiceProviders(ImageReaderSpi.class, 712 new ContainsFilter(readerFormatNamesMethod, 713 formatName), 714 true); 715 } catch (IllegalArgumentException e) { 716 return Collections.emptyIterator(); 717 } 718 return new ImageReaderIterator(iter); 719 } 720 721 /** 722 * Returns an <code>Iterator</code> containing all currently 723 * registered <code>ImageReader</code>s that claim to be able to 724 * decode files with the given suffix. 725 * 726 * @param fileSuffix a <code>String</code> containing a file 727 * suffix (<i>e.g.</i>, "jpg" or "tiff"). 728 * 729 * @return an <code>Iterator</code> containing 730 * <code>ImageReader</code>s. 731 * 732 * @exception IllegalArgumentException if <code>fileSuffix</code> 733 * is <code>null</code>. 734 * 735 * @see javax.imageio.spi.ImageReaderSpi#getFileSuffixes 736 */ 737 public static Iterator<ImageReader> 738 getImageReadersBySuffix(String fileSuffix) 739 { 740 if (fileSuffix == null) { 741 throw new IllegalArgumentException("fileSuffix == null!"); 742 } 743 // Ensure category is present 744 Iterator<ImageReaderSpi> iter; 745 try { 746 iter = theRegistry.getServiceProviders(ImageReaderSpi.class, 747 new ContainsFilter(readerFileSuffixesMethod, 748 fileSuffix), 749 true); 750 } catch (IllegalArgumentException e) { 751 return Collections.emptyIterator(); 752 } 753 return new ImageReaderIterator(iter); 754 } 755 756 /** 757 * Returns an <code>Iterator</code> containing all currently 758 * registered <code>ImageReader</code>s that claim to be able to 759 * decode files with the given MIME type. 760 * 761 * @param MIMEType a <code>String</code> containing a file 762 * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp"). 763 * 764 * @return an <code>Iterator</code> containing 765 * <code>ImageReader</code>s. 766 * 767 * @exception IllegalArgumentException if <code>MIMEType</code> is 768 * <code>null</code>. 769 * 770 * @see javax.imageio.spi.ImageReaderSpi#getMIMETypes 771 */ 772 public static Iterator<ImageReader> 773 getImageReadersByMIMEType(String MIMEType) 774 { 775 if (MIMEType == null) { 776 throw new IllegalArgumentException("MIMEType == null!"); 777 } 778 // Ensure category is present 779 Iterator<ImageReaderSpi> iter; 780 try { 781 iter = theRegistry.getServiceProviders(ImageReaderSpi.class, 782 new ContainsFilter(readerMIMETypesMethod, 783 MIMEType), 784 true); 785 } catch (IllegalArgumentException e) { 786 return Collections.emptyIterator(); 787 } 788 return new ImageReaderIterator(iter); 789 } 790 791 // Writers 792 793 /** 794 * Returns an array of <code>String</code>s listing all of the 795 * informal format names understood by the current set of registered 796 * writers. 797 * 798 * @return an array of <code>String</code>s. 799 */ 800 public static String[] getWriterFormatNames() { 801 return getReaderWriterInfo(ImageWriterSpi.class, 802 SpiInfo.FORMAT_NAMES); 803 } 804 805 /** 806 * Returns an array of <code>String</code>s listing all of the 807 * MIME types understood by the current set of registered 808 * writers. 809 * 810 * @return an array of <code>String</code>s. 811 */ 812 public static String[] getWriterMIMETypes() { 813 return getReaderWriterInfo(ImageWriterSpi.class, 814 SpiInfo.MIME_TYPES); 815 } 816 817 /** 818 * Returns an array of <code>String</code>s listing all of the 819 * file suffixes associated with the formats understood 820 * by the current set of registered writers. 821 * 822 * @return an array of <code>String</code>s. 823 * @since 1.6 824 */ 825 public static String[] getWriterFileSuffixes() { 826 return getReaderWriterInfo(ImageWriterSpi.class, 827 SpiInfo.FILE_SUFFIXES); 828 } 829 830 static class ImageWriterIterator implements Iterator<ImageWriter> { 831 // Contains ImageWriterSpis 832 private Iterator<ImageWriterSpi> iter; 833 834 public ImageWriterIterator(Iterator<ImageWriterSpi> iter) { 835 this.iter = iter; 836 } 837 838 public boolean hasNext() { 839 return iter.hasNext(); 840 } 841 842 public ImageWriter next() { 843 ImageWriterSpi spi = null; 844 try { 845 spi = iter.next(); 846 return spi.createWriterInstance(); 847 } catch (IOException e) { 848 // Deregister the spi in this case, but only as a writerSpi 849 theRegistry.deregisterServiceProvider(spi, ImageWriterSpi.class); 850 } 851 return null; 852 } 853 854 public void remove() { 855 throw new UnsupportedOperationException(); 856 } 857 } 858 859 private static boolean contains(String[] names, String name) { 860 for (int i = 0; i < names.length; i++) { 861 if (name.equalsIgnoreCase(names[i])) { 862 return true; 863 } 864 } 865 866 return false; 867 } 868 869 /** 870 * Returns an <code>Iterator</code> containing all currently 871 * registered <code>ImageWriter</code>s that claim to be able to 872 * encode the named format. 873 * 874 * @param formatName a <code>String</code> containing the informal 875 * name of a format (<i>e.g.</i>, "jpeg" or "tiff". 876 * 877 * @return an <code>Iterator</code> containing 878 * <code>ImageWriter</code>s. 879 * 880 * @exception IllegalArgumentException if <code>formatName</code> is 881 * <code>null</code>. 882 * 883 * @see javax.imageio.spi.ImageWriterSpi#getFormatNames 884 */ 885 public static Iterator<ImageWriter> 886 getImageWritersByFormatName(String formatName) 887 { 888 if (formatName == null) { 889 throw new IllegalArgumentException("formatName == null!"); 890 } 891 Iterator<ImageWriterSpi> iter; 892 // Ensure category is present 893 try { 894 iter = theRegistry.getServiceProviders(ImageWriterSpi.class, 895 new ContainsFilter(writerFormatNamesMethod, 896 formatName), 897 true); 898 } catch (IllegalArgumentException e) { 899 return Collections.emptyIterator(); 900 } 901 return new ImageWriterIterator(iter); 902 } 903 904 /** 905 * Returns an <code>Iterator</code> containing all currently 906 * registered <code>ImageWriter</code>s that claim to be able to 907 * encode files with the given suffix. 908 * 909 * @param fileSuffix a <code>String</code> containing a file 910 * suffix (<i>e.g.</i>, "jpg" or "tiff"). 911 * 912 * @return an <code>Iterator</code> containing <code>ImageWriter</code>s. 913 * 914 * @exception IllegalArgumentException if <code>fileSuffix</code> is 915 * <code>null</code>. 916 * 917 * @see javax.imageio.spi.ImageWriterSpi#getFileSuffixes 918 */ 919 public static Iterator<ImageWriter> 920 getImageWritersBySuffix(String fileSuffix) 921 { 922 if (fileSuffix == null) { 923 throw new IllegalArgumentException("fileSuffix == null!"); 924 } 925 Iterator<ImageWriterSpi> iter; 926 // Ensure category is present 927 try { 928 iter = theRegistry.getServiceProviders(ImageWriterSpi.class, 929 new ContainsFilter(writerFileSuffixesMethod, 930 fileSuffix), 931 true); 932 } catch (IllegalArgumentException e) { 933 return Collections.emptyIterator(); 934 } 935 return new ImageWriterIterator(iter); 936 } 937 938 /** 939 * Returns an <code>Iterator</code> containing all currently 940 * registered <code>ImageWriter</code>s that claim to be able to 941 * encode files with the given MIME type. 942 * 943 * @param MIMEType a <code>String</code> containing a file 944 * suffix (<i>e.g.</i>, "image/jpeg" or "image/x-bmp"). 945 * 946 * @return an <code>Iterator</code> containing <code>ImageWriter</code>s. 947 * 948 * @exception IllegalArgumentException if <code>MIMEType</code> is 949 * <code>null</code>. 950 * 951 * @see javax.imageio.spi.ImageWriterSpi#getMIMETypes 952 */ 953 public static Iterator<ImageWriter> 954 getImageWritersByMIMEType(String MIMEType) 955 { 956 if (MIMEType == null) { 957 throw new IllegalArgumentException("MIMEType == null!"); 958 } 959 Iterator<ImageWriterSpi> iter; 960 // Ensure category is present 961 try { 962 iter = theRegistry.getServiceProviders(ImageWriterSpi.class, 963 new ContainsFilter(writerMIMETypesMethod, 964 MIMEType), 965 true); 966 } catch (IllegalArgumentException e) { 967 return Collections.emptyIterator(); 968 } 969 return new ImageWriterIterator(iter); 970 } 971 972 /** 973 * Returns an <code>ImageWriter</code>corresponding to the given 974 * <code>ImageReader</code>, if there is one, or <code>null</code> 975 * if the plug-in for this <code>ImageReader</code> does not 976 * specify a corresponding <code>ImageWriter</code>, or if the 977 * given <code>ImageReader</code> is not registered. This 978 * mechanism may be used to obtain an <code>ImageWriter</code> 979 * that will understand the internal structure of non-pixel 980 * metadata (as encoded by <code>IIOMetadata</code> objects) 981 * generated by the <code>ImageReader</code>. By obtaining this 982 * data from the <code>ImageReader</code> and passing it on to the 983 * <code>ImageWriter</code> obtained with this method, a client 984 * program can read an image, modify it in some way, and write it 985 * back out preserving all metadata, without having to understand 986 * anything about the structure of the metadata, or even about 987 * the image format. Note that this method returns the 988 * "preferred" writer, which is the first in the list returned by 989 * <code>javax.imageio.spi.ImageReaderSpi.getImageWriterSpiNames()</code>. 990 * 991 * @param reader an instance of a registered <code>ImageReader</code>. 992 * 993 * @return an <code>ImageWriter</code>, or null. 994 * 995 * @exception IllegalArgumentException if <code>reader</code> is 996 * <code>null</code>. 997 * 998 * @see #getImageReader(ImageWriter) 999 * @see javax.imageio.spi.ImageReaderSpi#getImageWriterSpiNames() 1000 */ 1001 public static ImageWriter getImageWriter(ImageReader reader) { 1002 if (reader == null) { 1003 throw new IllegalArgumentException("reader == null!"); 1004 } 1005 1006 ImageReaderSpi readerSpi = reader.getOriginatingProvider(); 1007 if (readerSpi == null) { 1008 Iterator<ImageReaderSpi> readerSpiIter; 1009 // Ensure category is present 1010 try { 1011 readerSpiIter = 1012 theRegistry.getServiceProviders(ImageReaderSpi.class, 1013 false); 1014 } catch (IllegalArgumentException e) { 1015 return null; 1016 } 1017 1018 while (readerSpiIter.hasNext()) { 1019 ImageReaderSpi temp = readerSpiIter.next(); 1020 if (temp.isOwnReader(reader)) { 1021 readerSpi = temp; 1022 break; 1023 } 1024 } 1025 if (readerSpi == null) { 1026 return null; 1027 } 1028 } 1029 1030 String[] writerNames = readerSpi.getImageWriterSpiNames(); 1031 if (writerNames == null) { 1032 return null; 1033 } 1034 1035 Class<?> writerSpiClass = null; 1036 try { 1037 writerSpiClass = Class.forName(writerNames[0], true, 1038 ClassLoader.getSystemClassLoader()); 1039 } catch (ClassNotFoundException e) { 1040 return null; 1041 } 1042 1043 ImageWriterSpi writerSpi = (ImageWriterSpi) 1044 theRegistry.getServiceProviderByClass(writerSpiClass); 1045 if (writerSpi == null) { 1046 return null; 1047 } 1048 1049 try { 1050 return writerSpi.createWriterInstance(); 1051 } catch (IOException e) { 1052 // Deregister the spi in this case, but only as a writerSpi 1053 theRegistry.deregisterServiceProvider(writerSpi, 1054 ImageWriterSpi.class); 1055 return null; 1056 } 1057 } 1058 1059 /** 1060 * Returns an <code>ImageReader</code>corresponding to the given 1061 * <code>ImageWriter</code>, if there is one, or <code>null</code> 1062 * if the plug-in for this <code>ImageWriter</code> does not 1063 * specify a corresponding <code>ImageReader</code>, or if the 1064 * given <code>ImageWriter</code> is not registered. This method 1065 * is provided principally for symmetry with 1066 * <code>getImageWriter(ImageReader)</code>. Note that this 1067 * method returns the "preferred" reader, which is the first in 1068 * the list returned by 1069 * javax.imageio.spi.ImageWriterSpi.<code>getImageReaderSpiNames()</code>. 1070 * 1071 * @param writer an instance of a registered <code>ImageWriter</code>. 1072 * 1073 * @return an <code>ImageReader</code>, or null. 1074 * 1075 * @exception IllegalArgumentException if <code>writer</code> is 1076 * <code>null</code>. 1077 * 1078 * @see #getImageWriter(ImageReader) 1079 * @see javax.imageio.spi.ImageWriterSpi#getImageReaderSpiNames() 1080 */ 1081 public static ImageReader getImageReader(ImageWriter writer) { 1082 if (writer == null) { 1083 throw new IllegalArgumentException("writer == null!"); 1084 } 1085 1086 ImageWriterSpi writerSpi = writer.getOriginatingProvider(); 1087 if (writerSpi == null) { 1088 Iterator<ImageWriterSpi> writerSpiIter; 1089 // Ensure category is present 1090 try { 1091 writerSpiIter = 1092 theRegistry.getServiceProviders(ImageWriterSpi.class, 1093 false); 1094 } catch (IllegalArgumentException e) { 1095 return null; 1096 } 1097 1098 while (writerSpiIter.hasNext()) { 1099 ImageWriterSpi temp = writerSpiIter.next(); 1100 if (temp.isOwnWriter(writer)) { 1101 writerSpi = temp; 1102 break; 1103 } 1104 } 1105 if (writerSpi == null) { 1106 return null; 1107 } 1108 } 1109 1110 String[] readerNames = writerSpi.getImageReaderSpiNames(); 1111 if (readerNames == null) { 1112 return null; 1113 } 1114 1115 Class<?> readerSpiClass = null; 1116 try { 1117 readerSpiClass = Class.forName(readerNames[0], true, 1118 ClassLoader.getSystemClassLoader()); 1119 } catch (ClassNotFoundException e) { 1120 return null; 1121 } 1122 1123 ImageReaderSpi readerSpi = (ImageReaderSpi) 1124 theRegistry.getServiceProviderByClass(readerSpiClass); 1125 if (readerSpi == null) { 1126 return null; 1127 } 1128 1129 try { 1130 return readerSpi.createReaderInstance(); 1131 } catch (IOException e) { 1132 // Deregister the spi in this case, but only as a readerSpi 1133 theRegistry.deregisterServiceProvider(readerSpi, 1134 ImageReaderSpi.class); 1135 return null; 1136 } 1137 } 1138 1139 /** 1140 * Returns an <code>Iterator</code> containing all currently 1141 * registered <code>ImageWriter</code>s that claim to be able to 1142 * encode images of the given layout (specified using an 1143 * <code>ImageTypeSpecifier</code>) in the given format. 1144 * 1145 * @param type an <code>ImageTypeSpecifier</code> indicating the 1146 * layout of the image to be written. 1147 * @param formatName the informal name of the <code>format</code>. 1148 * 1149 * @return an <code>Iterator</code> containing <code>ImageWriter</code>s. 1150 * 1151 * @exception IllegalArgumentException if any parameter is 1152 * <code>null</code>. 1153 * 1154 * @see javax.imageio.spi.ImageWriterSpi#canEncodeImage(ImageTypeSpecifier) 1155 */ 1156 public static Iterator<ImageWriter> 1157 getImageWriters(ImageTypeSpecifier type, String formatName) 1158 { 1159 if (type == null) { 1160 throw new IllegalArgumentException("type == null!"); 1161 } 1162 if (formatName == null) { 1163 throw new IllegalArgumentException("formatName == null!"); 1164 } 1165 1166 Iterator<ImageWriterSpi> iter; 1167 // Ensure category is present 1168 try { 1169 iter = theRegistry.getServiceProviders(ImageWriterSpi.class, 1170 new CanEncodeImageAndFormatFilter(type, 1171 formatName), 1172 true); 1173 } catch (IllegalArgumentException e) { 1174 return Collections.emptyIterator(); 1175 } 1176 1177 return new ImageWriterIterator(iter); 1178 } 1179 1180 static class ImageTranscoderIterator 1181 implements Iterator<ImageTranscoder> 1182 { 1183 // Contains ImageTranscoderSpis 1184 public Iterator<ImageTranscoderSpi> iter; 1185 1186 public ImageTranscoderIterator(Iterator<ImageTranscoderSpi> iter) { 1187 this.iter = iter; 1188 } 1189 1190 public boolean hasNext() { 1191 return iter.hasNext(); 1192 } 1193 1194 public ImageTranscoder next() { 1195 ImageTranscoderSpi spi = null; 1196 spi = iter.next(); 1197 return spi.createTranscoderInstance(); 1198 } 1199 1200 public void remove() { 1201 throw new UnsupportedOperationException(); 1202 } 1203 } 1204 1205 static class TranscoderFilter 1206 implements ServiceRegistry.Filter { 1207 1208 String readerSpiName; 1209 String writerSpiName; 1210 1211 public TranscoderFilter(ImageReaderSpi readerSpi, 1212 ImageWriterSpi writerSpi) { 1213 this.readerSpiName = readerSpi.getClass().getName(); 1214 this.writerSpiName = writerSpi.getClass().getName(); 1215 } 1216 1217 public boolean filter(Object elt) { 1218 ImageTranscoderSpi spi = (ImageTranscoderSpi)elt; 1219 String readerName = spi.getReaderServiceProviderName(); 1220 String writerName = spi.getWriterServiceProviderName(); 1221 return (readerName.equals(readerSpiName) && 1222 writerName.equals(writerSpiName)); 1223 } 1224 } 1225 1226 /** 1227 * Returns an <code>Iterator</code> containing all currently 1228 * registered <code>ImageTranscoder</code>s that claim to be 1229 * able to transcode between the metadata of the given 1230 * <code>ImageReader</code> and <code>ImageWriter</code>. 1231 * 1232 * @param reader an <code>ImageReader</code>. 1233 * @param writer an <code>ImageWriter</code>. 1234 * 1235 * @return an <code>Iterator</code> containing 1236 * <code>ImageTranscoder</code>s. 1237 * 1238 * @exception IllegalArgumentException if <code>reader</code> or 1239 * <code>writer</code> is <code>null</code>. 1240 */ 1241 public static Iterator<ImageTranscoder> 1242 getImageTranscoders(ImageReader reader, ImageWriter writer) 1243 { 1244 if (reader == null) { 1245 throw new IllegalArgumentException("reader == null!"); 1246 } 1247 if (writer == null) { 1248 throw new IllegalArgumentException("writer == null!"); 1249 } 1250 ImageReaderSpi readerSpi = reader.getOriginatingProvider(); 1251 ImageWriterSpi writerSpi = writer.getOriginatingProvider(); 1252 ServiceRegistry.Filter filter = 1253 new TranscoderFilter(readerSpi, writerSpi); 1254 1255 Iterator<ImageTranscoderSpi> iter; 1256 // Ensure category is present 1257 try { 1258 iter = theRegistry.getServiceProviders(ImageTranscoderSpi.class, 1259 filter, true); 1260 } catch (IllegalArgumentException e) { 1261 return Collections.emptyIterator(); 1262 } 1263 return new ImageTranscoderIterator(iter); 1264 } 1265 1266 // All-in-one methods 1267 1268 /** 1269 * Returns a <code>BufferedImage</code> as the result of decoding 1270 * a supplied <code>File</code> with an <code>ImageReader</code> 1271 * chosen automatically from among those currently registered. 1272 * The <code>File</code> is wrapped in an 1273 * <code>ImageInputStream</code>. If no registered 1274 * <code>ImageReader</code> claims to be able to read the 1275 * resulting stream, <code>null</code> is returned. 1276 * 1277 * <p> The current cache settings from <code>getUseCache</code>and 1278 * <code>getCacheDirectory</code> will be used to control caching in the 1279 * <code>ImageInputStream</code> that is created. 1280 * 1281 * <p> Note that there is no <code>read</code> method that takes a 1282 * filename as a <code>String</code>; use this method instead after 1283 * creating a <code>File</code> from the filename. 1284 * 1285 * <p> This method does not attempt to locate 1286 * <code>ImageReader</code>s that can read directly from a 1287 * <code>File</code>; that may be accomplished using 1288 * <code>IIORegistry</code> and <code>ImageReaderSpi</code>. 1289 * 1290 * @param input a <code>File</code> to read from. 1291 * 1292 * @return a <code>BufferedImage</code> containing the decoded 1293 * contents of the input, or <code>null</code>. 1294 * 1295 * @exception IllegalArgumentException if <code>input</code> is 1296 * <code>null</code>. 1297 * @exception IOException if an error occurs during reading. 1298 */ 1299 public static BufferedImage read(File input) throws IOException { 1300 if (input == null) { 1301 throw new IllegalArgumentException("input == null!"); 1302 } 1303 if (!input.canRead()) { 1304 throw new IIOException("Can't read input file!"); 1305 } 1306 1307 ImageInputStream stream = createImageInputStream(input); 1308 if (stream == null) { 1309 throw new IIOException("Can't create an ImageInputStream!"); 1310 } 1311 BufferedImage bi = read(stream); 1312 if (bi == null) { 1313 stream.close(); 1314 } 1315 return bi; 1316 } 1317 1318 /** 1319 * Returns a <code>BufferedImage</code> as the result of decoding 1320 * a supplied <code>InputStream</code> with an <code>ImageReader</code> 1321 * chosen automatically from among those currently registered. 1322 * The <code>InputStream</code> is wrapped in an 1323 * <code>ImageInputStream</code>. If no registered 1324 * <code>ImageReader</code> claims to be able to read the 1325 * resulting stream, <code>null</code> is returned. 1326 * 1327 * <p> The current cache settings from <code>getUseCache</code>and 1328 * <code>getCacheDirectory</code> will be used to control caching in the 1329 * <code>ImageInputStream</code> that is created. 1330 * 1331 * <p> This method does not attempt to locate 1332 * <code>ImageReader</code>s that can read directly from an 1333 * <code>InputStream</code>; that may be accomplished using 1334 * <code>IIORegistry</code> and <code>ImageReaderSpi</code>. 1335 * 1336 * <p> This method <em>does not</em> close the provided 1337 * <code>InputStream</code> after the read operation has completed; 1338 * it is the responsibility of the caller to close the stream, if desired. 1339 * 1340 * @param input an <code>InputStream</code> to read from. 1341 * 1342 * @return a <code>BufferedImage</code> containing the decoded 1343 * contents of the input, or <code>null</code>. 1344 * 1345 * @exception IllegalArgumentException if <code>input</code> is 1346 * <code>null</code>. 1347 * @exception IOException if an error occurs during reading. 1348 */ 1349 public static BufferedImage read(InputStream input) throws IOException { 1350 if (input == null) { 1351 throw new IllegalArgumentException("input == null!"); 1352 } 1353 1354 ImageInputStream stream = createImageInputStream(input); 1355 BufferedImage bi = read(stream); 1356 if (bi == null) { 1357 stream.close(); 1358 } 1359 return bi; 1360 } 1361 1362 /** 1363 * Returns a <code>BufferedImage</code> as the result of decoding 1364 * a supplied <code>URL</code> with an <code>ImageReader</code> 1365 * chosen automatically from among those currently registered. An 1366 * <code>InputStream</code> is obtained from the <code>URL</code>, 1367 * which is wrapped in an <code>ImageInputStream</code>. If no 1368 * registered <code>ImageReader</code> claims to be able to read 1369 * the resulting stream, <code>null</code> is returned. 1370 * 1371 * <p> The current cache settings from <code>getUseCache</code>and 1372 * <code>getCacheDirectory</code> will be used to control caching in the 1373 * <code>ImageInputStream</code> that is created. 1374 * 1375 * <p> This method does not attempt to locate 1376 * <code>ImageReader</code>s that can read directly from a 1377 * <code>URL</code>; that may be accomplished using 1378 * <code>IIORegistry</code> and <code>ImageReaderSpi</code>. 1379 * 1380 * @param input a <code>URL</code> to read from. 1381 * 1382 * @return a <code>BufferedImage</code> containing the decoded 1383 * contents of the input, or <code>null</code>. 1384 * 1385 * @exception IllegalArgumentException if <code>input</code> is 1386 * <code>null</code>. 1387 * @exception IOException if an error occurs during reading. 1388 */ 1389 public static BufferedImage read(URL input) throws IOException { 1390 if (input == null) { 1391 throw new IllegalArgumentException("input == null!"); 1392 } 1393 1394 InputStream istream = null; 1395 try { 1396 istream = input.openStream(); 1397 } catch (IOException e) { 1398 throw new IIOException("Can't get input stream from URL!", e); 1399 } 1400 ImageInputStream stream = createImageInputStream(istream); 1401 BufferedImage bi; 1402 try { 1403 bi = read(stream); 1404 if (bi == null) { 1405 stream.close(); 1406 } 1407 } finally { 1408 istream.close(); 1409 } 1410 return bi; 1411 } 1412 1413 /** 1414 * Returns a <code>BufferedImage</code> as the result of decoding 1415 * a supplied <code>ImageInputStream</code> with an 1416 * <code>ImageReader</code> chosen automatically from among those 1417 * currently registered. If no registered 1418 * <code>ImageReader</code> claims to be able to read the stream, 1419 * <code>null</code> is returned. 1420 * 1421 * <p> Unlike most other methods in this class, this method <em>does</em> 1422 * close the provided <code>ImageInputStream</code> after the read 1423 * operation has completed, unless <code>null</code> is returned, 1424 * in which case this method <em>does not</em> close the stream. 1425 * 1426 * @param stream an <code>ImageInputStream</code> to read from. 1427 * 1428 * @return a <code>BufferedImage</code> containing the decoded 1429 * contents of the input, or <code>null</code>. 1430 * 1431 * @exception IllegalArgumentException if <code>stream</code> is 1432 * <code>null</code>. 1433 * @exception IOException if an error occurs during reading. 1434 */ 1435 public static BufferedImage read(ImageInputStream stream) 1436 throws IOException { 1437 if (stream == null) { 1438 throw new IllegalArgumentException("stream == null!"); 1439 } 1440 1441 Iterator<ImageReader> iter = getImageReaders(stream); 1442 if (!iter.hasNext()) { 1443 return null; 1444 } 1445 1446 ImageReader reader = iter.next(); 1447 ImageReadParam param = reader.getDefaultReadParam(); 1448 reader.setInput(stream, true, true); 1449 BufferedImage bi; 1450 try { 1451 bi = reader.read(0, param); 1452 } finally { 1453 reader.dispose(); 1454 stream.close(); 1455 } 1456 return bi; 1457 } 1458 1459 /** 1460 * Writes an image using the an arbitrary <code>ImageWriter</code> 1461 * that supports the given format to an 1462 * <code>ImageOutputStream</code>. The image is written to the 1463 * <code>ImageOutputStream</code> starting at the current stream 1464 * pointer, overwriting existing stream data from that point 1465 * forward, if present. 1466 * 1467 * <p> This method <em>does not</em> close the provided 1468 * <code>ImageOutputStream</code> after the write operation has completed; 1469 * it is the responsibility of the caller to close the stream, if desired. 1470 * 1471 * @param im a <code>RenderedImage</code> to be written. 1472 * @param formatName a <code>String</code> containing the informal 1473 * name of the format. 1474 * @param output an <code>ImageOutputStream</code> to be written to. 1475 * 1476 * @return <code>false</code> if no appropriate writer is found. 1477 * 1478 * @exception IllegalArgumentException if any parameter is 1479 * <code>null</code>. 1480 * @exception IOException if an error occurs during writing. 1481 */ 1482 public static boolean write(RenderedImage im, 1483 String formatName, 1484 ImageOutputStream output) throws IOException { 1485 if (im == null) { 1486 throw new IllegalArgumentException("im == null!"); 1487 } 1488 if (formatName == null) { 1489 throw new IllegalArgumentException("formatName == null!"); 1490 } 1491 if (output == null) { 1492 throw new IllegalArgumentException("output == null!"); 1493 } 1494 1495 return doWrite(im, getWriter(im, formatName), output); 1496 } 1497 1498 /** 1499 * Writes an image using an arbitrary <code>ImageWriter</code> 1500 * that supports the given format to a <code>File</code>. If 1501 * there is already a <code>File</code> present, its contents are 1502 * discarded. 1503 * 1504 * @param im a <code>RenderedImage</code> to be written. 1505 * @param formatName a <code>String</code> containing the informal 1506 * name of the format. 1507 * @param output a <code>File</code> to be written to. 1508 * 1509 * @return <code>false</code> if no appropriate writer is found or 1510 * not able to create required ImageOutputStream. 1511 * 1512 * @exception IllegalArgumentException if any parameter is 1513 * <code>null</code>. 1514 * @exception IOException if an error occurs during writing. 1515 */ 1516 public static boolean write(RenderedImage im, 1517 String formatName, 1518 File output) throws IOException { 1519 if (output == null) { 1520 throw new IllegalArgumentException("output == null!"); 1521 } 1522 ImageOutputStream stream = null; 1523 1524 ImageWriter writer = getWriter(im, formatName); 1525 if (writer == null) { 1526 /* Do not make changes in the file system if we have 1527 * no appropriate writer. 1528 */ 1529 return false; 1530 } 1531 1532 try { 1533 output.delete(); 1534 stream = createImageOutputStream(output); 1535 } catch (IOException e) { 1536 throw new IIOException("Can't create output stream!", e); 1537 } 1538 1539 try { 1540 return doWrite(im, writer, stream); 1541 } finally { 1542 if (stream != null) 1543 stream.close(); 1544 } 1545 } 1546 1547 /** 1548 * Writes an image using an arbitrary <code>ImageWriter</code> 1549 * that supports the given format to an <code>OutputStream</code>. 1550 * 1551 * <p> This method <em>does not</em> close the provided 1552 * <code>OutputStream</code> after the write operation has completed; 1553 * it is the responsibility of the caller to close the stream, if desired. 1554 * 1555 * <p> The current cache settings from <code>getUseCache</code>and 1556 * <code>getCacheDirectory</code> will be used to control caching. 1557 * 1558 * @param im a <code>RenderedImage</code> to be written. 1559 * @param formatName a <code>String</code> containing the informal 1560 * name of the format. 1561 * @param output an <code>OutputStream</code> to be written to. 1562 * 1563 * @return <code>false</code> if no appropriate writer is found or 1564 * not able to create required ImageOutputStream. 1565 * 1566 * @exception IllegalArgumentException if any parameter is 1567 * <code>null</code>. 1568 * @exception IOException if an error occurs during writing. 1569 */ 1570 public static boolean write(RenderedImage im, 1571 String formatName, 1572 OutputStream output) throws IOException { 1573 if (output == null) { 1574 throw new IllegalArgumentException("output == null!"); 1575 } 1576 ImageOutputStream stream = null; 1577 try { 1578 stream = createImageOutputStream(output); 1579 } catch (IOException e) { 1580 throw new IIOException("Can't create output stream!", e); 1581 } 1582 1583 try { 1584 return doWrite(im, getWriter(im, formatName), stream); 1585 } finally { 1586 if (stream != null) 1587 stream.close(); 1588 } 1589 } 1590 1591 /** 1592 * Returns <code>ImageWriter</code> instance according to given 1593 * rendered image and image format or <code>null</code> if there 1594 * is no appropriate writer. 1595 */ 1596 private static ImageWriter getWriter(RenderedImage im, 1597 String formatName) { 1598 ImageTypeSpecifier type = 1599 ImageTypeSpecifier.createFromRenderedImage(im); 1600 Iterator<ImageWriter> iter = getImageWriters(type, formatName); 1601 1602 if (iter.hasNext()) { 1603 return iter.next(); 1604 } else { 1605 return null; 1606 } 1607 } 1608 1609 /** 1610 * Writes image to output stream using given image writer. 1611 */ 1612 private static boolean doWrite(RenderedImage im, ImageWriter writer, 1613 ImageOutputStream output) throws IOException { 1614 if (writer == null) { 1615 return false; 1616 } 1617 if (output == null) { 1618 return false; 1619 } 1620 writer.setOutput(output); 1621 try { 1622 writer.write(im); 1623 } finally { 1624 writer.dispose(); 1625 output.flush(); 1626 } 1627 return true; 1628 } 1629 }