001 /* 002 * ImageCodec 003 * 004 * Copyright (c) 2000, 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.codecs; 009 010 import java.io.BufferedInputStream; 011 import java.io.BufferedOutputStream; 012 import java.io.DataInput; 013 import java.io.DataInputStream; 014 import java.io.DataOutput; 015 import java.io.DataOutputStream; 016 import java.io.File; 017 import java.io.FileInputStream; 018 import java.io.FileOutputStream; 019 import java.io.InputStream; 020 import java.io.IOException; 021 import java.io.OutputStream; 022 import java.io.RandomAccessFile; 023 import java.util.Vector; 024 import net.sourceforge.jiu.ops.MissingParameterException; 025 import net.sourceforge.jiu.ops.Operation; 026 import net.sourceforge.jiu.ops.WrongParameterException; 027 import net.sourceforge.jiu.data.PixelImage; 028 029 /** 030 * The base class for <em>image codecs</em>, operations to read images from or write them to streams. 031 * A codec should support one file format only. 032 * The word codec is derived from <em>enCOder DECoder</em>. 033 * 034 * <h3>Usage</h3> 035 * The codecs differ quite a bit in what they support. 036 * But here are two code snippets that demonstrate how to do loading and saving in general. 037 * 038 * <h4>Load image</h4> 039 * <pre> 040 * ImageCodec codec = new BMPCodec(); // BMPCodec is just an example 041 * codec.setFile("image.bmp", CodecMode.LOAD); 042 * codec.process(); 043 * PixelImage image = codec.getImage(); 044 * </pre> 045 * 046 * <h4>Save image</h4> 047 * <pre> 048 * PixelImage image = ...; // the image to be saved 049 * ImageCodec codec = new BMPCodec(); // BMPCodec is just an example 050 * codec.setFile("image.bmp", CodecMode.SAVE); 051 * codec.setImage(image); 052 * codec.process(); 053 * </pre> 054 * 055 * <h3>I/O objects</h3> 056 * There are several set and get methods for I/O objects, including 057 * DataInput, DataOutput, InputStream, OutputStream and RandomAccessFile. 058 * If you are just using the codec (and not developing one) make it easier 059 * for yourself and use {@link #setFile(String, CodecMode)}. 060 * That way the picking of the right type of I/O class and the creation of a 061 * buffered stream wrapper is done automatically. 062 * <p> 063 * Codecs have different requirements concerning I/O objects. 064 * If an image is to be loaded, it's enough for some formats to linearly read 065 * from an {@link java.io.InputStream} to load the image. 066 * However, some formats (like TIFF) require random access. 067 * <p> 068 * When implementing a codec, take care that as many I/O classes as possible can be used. 069 * If possible, call {@link #getInputAsDataInput} when loading and {@link #getOutputAsDataOutput} 070 * when saving. 071 * That way, input / output streams, RandomAccessFiles and arbitrary DataInput / DataOutput objects 072 * can be used. 073 * <p> 074 * <h3>Mode</h3> 075 * Codecs can be used to save images or load them, or both. 076 * As was g; by default, no mode (of enumeration type {@link CodecMode}) 077 * is specified and {@link #getMode()} returns <code>null</code>. 078 * Mode only has two possible values, {@link CodecMode#LOAD} and 079 * {@link CodecMode#SAVE}. 080 * In some cases, the codec can find out whether to load or save from the I/O objects 081 * that were given to it; if it has an input stream, something must be loaded, 082 * if it has an output stream, something is to be saved. 083 * If a codec demands a {@link RandomAccessFile}, there is no way to find out 084 * the mode automatically, that is why {@link #setRandomAccessFile} also has an 085 * argument of type {@link CodecMode}. 086 * <p> 087 * <strong>Bounds</strong>; to load or save only part of an image. 088 * Defining bounds is optional; by default, the complete image is loaded 089 * or saved (no bounds). 090 * Using {@link #setBounds(int, int, int, int)}, one can specify the 091 * rectangle which will be loaded or saved. 092 * <p> 093 * <strong>PixelImage object</strong>; get and set methods for the image which is to be 094 * loaded or saved. 095 * If an image is to be loaded, a PixelImage object can optionally be specified so that the image will 096 * be written to that object; image type and resolution must of course match the image 097 * from input. 098 * Normally, the codec will create the appropriate image object 099 * itself. 100 * If an image is to be saved, an image object <em>must</em> be provided, otherwise there 101 * is nothing to do. 102 * <p> 103 * <strong>Image index</strong>; the index of the image that is to be loaded (int value, default 104 * is 0). For image formats that support more than one image in one stream, the index of the 105 * image to be loaded (zero-based) can be specified using {@link #setImageIndex(int)}. 106 * 107 * <h3>Textual comments</h3> 108 * Some file formats allow for the inclusion of textual comments, to 109 * store a description, creator, copyright owner or anything else within the image 110 * file without actually drawing that text on the image itself. 111 * Some codecs support reading and writing of comments. 112 * 113 * <h3>Other methods</h3> 114 * <p> 115 * Each file format must be able to return its name ({@link #getFormatName()}) and 116 * file extensions that are typical for it ({@link #getFileExtensions()}). 117 * <p> 118 * A related method suggests a file extension for a given PixelImage object ({@link #suggestFileExtension(PixelImage)}). 119 * That method need not be implemented, the default version returns simply <code>null</code>. 120 * However, it is encouraged that codec implementors provide this method as well. 121 * Most file formats only have one typical extension (e. g. <code>.bmp</code>). 122 * However, for a file format like PNM, the extension depends on the image type (a grayscale 123 * image would end in <code>.pgm</code>, a color image in <code>.ppm</code>). 124 * <p> 125 * @author Marco Schmidt 126 */ 127 public abstract class ImageCodec extends Operation 128 { 129 private int boundsX1; 130 private int boundsY1; 131 private int boundsX2; 132 private int boundsY2; 133 private boolean boundsAvail; 134 private int boundsWidth; 135 private int boundsHeight; 136 private Vector comments; 137 private int dpiX; 138 private int dpiY; 139 private DataInput din; 140 private DataOutput dout; 141 private PixelImage image; 142 private int imageIndex; 143 private InputStream in; 144 private CodecMode mode; 145 private OutputStream out; 146 private RandomAccessFile raf; 147 148 /** 149 * This constructor will be called by descendants. 150 * The bounds state is initialized to <em>no bounds</em>. 151 */ 152 public ImageCodec() 153 { 154 super(); 155 comments = new Vector(); 156 removeBounds(); 157 } 158 159 /** 160 * Appends a comment to the internal list of comments. 161 * If the argument comment is non-null, it will be added to the internal 162 * list of comment strings. 163 * @param comment the comment to be added 164 */ 165 public void appendComment(String comment) 166 { 167 if (comment != null) 168 { 169 comments.addElement(comment); 170 } 171 } 172 173 /** 174 * If bounds were defined for this codec, this method tests if the 175 * bounds rectangle fits into the rectangle <code>(0, 0) / (width - 1, height - 1)</code>. 176 * If the bounds are incorrect, a {@link WrongParameterException} 177 * is thrown, otherwise nothing happens. 178 * To be used within codecs that support the bounds concept. 179 */ 180 public void checkBounds(int width, int height) throws WrongParameterException 181 { 182 if (!hasBounds()) 183 { 184 return; 185 } 186 int x1 = getBoundsX1(); 187 if (x1 >= width) 188 { 189 throw new WrongParameterException("Codec bounds x1 (" + x1 + 190 ") must be smaller than image width (" + width + ")."); 191 } 192 int x2 = getBoundsX2(); 193 if (x2 >= width) 194 { 195 throw new WrongParameterException("Codec bounds x2 (" + x2 + 196 ") must be smaller than image width (" + width + ")."); 197 } 198 int y1 = getBoundsY1(); 199 if (y1 >= height) 200 { 201 throw new WrongParameterException("Codec bounds y1 (" + y1 + 202 ") must be smaller than image height (" + height + ")."); 203 } 204 int y2 = getBoundsY2(); 205 if (y2 >= height) 206 { 207 throw new WrongParameterException("Codec bounds y2 (" + y2 + 208 ") must be smaller than image height (" + height + ")."); 209 } 210 } 211 212 /** 213 * If an image object was provided to be used for loading via {@link #setImage}, 214 * this method checks if its resolution is the same as the bounds' resolution. 215 * If the two differ, a {@link net.sourceforge.jiu.ops.WrongParameterException} is thrown. 216 * @throws WrongParameterException if image resolution and bounds dimension differ 217 */ 218 public void checkImageResolution() throws WrongParameterException 219 { 220 PixelImage image = getImage(); 221 if (image != null) 222 { 223 if (image.getWidth() != getBoundsWidth()) 224 { 225 throw new WrongParameterException("Specified input image must have width equal to getBoundsWidth()."); 226 } 227 if (image.getHeight() != getBoundsHeight()) 228 { 229 throw new WrongParameterException("Specified input image must have height equal to getBoundsHeight()."); 230 } 231 } 232 } 233 234 /** 235 * Calls the close method of all input and output I/O objects 236 * that were given to this object. 237 * Catches and ignores any IOException objects that may be 238 * thrown in the process. 239 * Note that not all I/O objects have a close method (e.g. {@link java.io.DataInput} 240 * and {@link java.io.DataOutput} have not). 241 */ 242 public void close() 243 { 244 try 245 { 246 if (in != null) 247 { 248 in.close(); 249 } 250 if (out != null) 251 { 252 out.close(); 253 } 254 if (raf != null) 255 { 256 raf.close(); 257 } 258 } 259 catch (IOException ioe) 260 { 261 } 262 } 263 264 /** 265 * Returns x coordinate of the upper left corner of the bounds. 266 * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, 267 * otherwise the return value is undefined. 268 * @return x coordinate of the upper left corner of the bounds 269 */ 270 public int getBoundsX1() 271 { 272 return boundsX1; 273 } 274 275 /** 276 * Returns x coordinate of the lower right corner of the bounds. 277 * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, 278 * otherwise the return value is undefined. 279 * @return x coordinate of the lower right corner of the bounds 280 */ 281 public int getBoundsX2() 282 { 283 return boundsX2; 284 } 285 286 /** 287 * Returns y coordinate of the upper left corner of the bounds. 288 * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, 289 * otherwise the return value is undefined. 290 * @return y coordinate of the upper left corner of the bounds 291 */ 292 public int getBoundsY1() 293 { 294 return boundsY1; 295 } 296 297 /** 298 * Returns y coordinate of the lower right corner of the bounds. 299 * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, 300 * otherwise the return value is undefined. 301 * @return y coordinate of the lower right corner of the bounds 302 */ 303 public int getBoundsY2() 304 { 305 return boundsY2; 306 } 307 308 /** 309 * Returns the height of the rectangle specified by bounds. 310 * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, 311 * otherwise the return value is undefined. 312 * This equals {@link #getBoundsY2()} - {@link #getBoundsY1()} + 1. 313 * @return height of bounds rectangle 314 */ 315 public int getBoundsHeight() 316 { 317 return boundsHeight; 318 } 319 320 /** 321 * Returns the width of the rectangle specified by bounds. 322 * Bounds must have been specified using {@link #setBounds(int, int, int, int)}, 323 * otherwise the return value is undefined. 324 * This equals {@link #getBoundsX2()} - {@link #getBoundsX1()} + 1. 325 * @return width of bounds rectangle 326 */ 327 public int getBoundsWidth() 328 { 329 return boundsWidth; 330 } 331 332 /** 333 * Returns a comment from the internal list of comments. 334 * @param index the index of the comment to be returned, must be from 335 * <code>0</code> to {@link #getNumComments()}<code> - 1</code>; if this is not 336 * the case, <code>null</code> will be returned 337 * @see #getNumComments 338 * @see #appendComment 339 * @see #removeAllComments 340 */ 341 public String getComment(int index) 342 { 343 if (index >= 0 && index < comments.size()) 344 { 345 return (String)comments.elementAt(index); 346 } 347 else 348 { 349 return null; 350 } 351 } 352 353 /** 354 * Returns a {@link java.io.DataInput} object if one was provided 355 * via {@link #setDataInput(DataInput)} or <code>null</code> otherwise. 356 * @return the DataInput object 357 */ 358 public DataInput getDataInput() 359 { 360 return din; 361 } 362 363 /** 364 * Returns a {@link java.io.DataOutput} object if one was provided 365 * via {@link #setDataOutput(DataOutput)} or <code>null</code> otherwise. 366 * @return the DataInput object 367 */ 368 public DataOutput getDataOutput() 369 { 370 return dout; 371 } 372 373 /** 374 * Returns the horizontal physical resolution of the image associated 375 * with this codec. 376 * This resolution value was either retrieved from an image file or 377 * set via {@link #setDpi(int, int)}. 378 * @return horizontal physical resolution in dpi 379 * @see #getDpiY 380 */ 381 public int getDpiX() 382 { 383 return dpiX; 384 } 385 386 /** 387 * Returns the vertical physical resolution of the image associated 388 * with this codec. 389 * This resolution value was either retrieved from an image file or 390 * set via {@link #setDpi(int, int)}. 391 * @return horizontal physical resolution in dpi 392 * @see #getDpiX 393 */ 394 public int getDpiY() 395 { 396 return dpiY; 397 } 398 399 /** 400 * Returns all file extensions that are typical for this file format. 401 * The default implementation in ImageCodec returns <code>null</code>. 402 * The file extension strings should include a leading dot 403 * and are supposed to be lower case (if that is allowed for 404 * the given file format). 405 * Example: <code>{".jpg", ".jpeg"}</code> for the JPEG file format. 406 * @return String array with typical file extensions 407 */ 408 public String[] getFileExtensions() 409 { 410 return null; 411 } 412 413 /** 414 * Returns the name of the file format supported by this codec. 415 * All classes extending {@link ImageCodec} must override this method. 416 * When overriding, leave out any words in a particular language so 417 * that this format name can be understood by everyone. 418 * Usually it is enough to return the format creator plus a typical 419 * abbreviation, e.g. <code>Microsoft BMP</code> or <code>Portable Anymap (PNM)</code>. 420 * @return name of the file format supported by this codec 421 */ 422 public abstract String getFormatName(); 423 424 /** 425 * Returns the image object stored in this codec. 426 * This is either an image given to this object via 427 * {@link #setImage(PixelImage)} or it was created by the codec 428 * itself during a loading operation. 429 * @return PixelImage object stored in this codec 430 */ 431 public PixelImage getImage() 432 { 433 return image; 434 } 435 436 /** 437 * Returns the zero-based index of the image to be loaded. 438 * Default is zero. 439 * @return zero-based image index value 440 */ 441 public int getImageIndex() 442 { 443 return imageIndex; 444 } 445 446 /** 447 * Returns a {@link java.io.DataInput} object if one was specified 448 * using {@link #setDataInput(DataInput)}, 449 * or creates a {@link java.io.DataInputStream} if an 450 * {@link java.io.InputStream} was specified, 451 * or returns a {@link java.io.RandomAccessFile} if one was specified 452 * (RandomAccessFile implements DataInput). 453 * If neither of those has been given to this object, <code>null</code> is returned. 454 * @return DataInput object or <code>null</code> 455 */ 456 public DataInput getInputAsDataInput() 457 { 458 DataInput din = getDataInput(); 459 if (din != null) 460 { 461 return din; 462 } 463 RandomAccessFile raf = getRandomAccessFile(); 464 if (getMode() == CodecMode.LOAD && raf != null) 465 { 466 return raf; 467 } 468 InputStream in = getInputStream(); 469 if (in != null) 470 { 471 if (in instanceof DataInput) 472 { 473 return (DataInput)in; 474 } 475 else 476 { 477 return new DataInputStream(in); 478 } 479 } 480 return null; 481 } 482 483 /** 484 * Returns an {@link java.io.InputStream} object that was given to 485 * this codec via {@link #setInputStream(InputStream)} 486 * (or <code>null</code> otherwise). 487 * @return InputStream object 488 */ 489 public InputStream getInputStream() 490 { 491 return in; 492 } 493 494 /** 495 * Return the <a target="_top" href="http://www.faqs.org/rfcs/rfc2045.html">MIME</a> 496 * (Multipurpose Internet Mail Extensions) type strings for this format, or <code>null</code> 497 * if none are available. 498 * @return MIME type strings or null 499 */ 500 public abstract String[] getMimeTypes(); 501 502 /** 503 * Returns the mode this codec is in. 504 * Can be <code>null</code>, so that the codec will have to find out 505 * itself what to do. 506 * @return codec mode (load or save) 507 */ 508 public CodecMode getMode() 509 { 510 return mode; 511 } 512 513 /** 514 * Returns the current number of comments in the internal comment list. 515 * @return number of comments in the internal comment list 516 */ 517 public int getNumComments() 518 { 519 return comments.size(); 520 } 521 522 /** 523 * Attempts to return an output object as a {@link java.io.DataOutput} object. 524 * @return a DataOutput object or null if that was not possible 525 */ 526 public DataOutput getOutputAsDataOutput() 527 { 528 DataOutput dout = getDataOutput(); 529 if (dout != null) 530 { 531 return dout; 532 } 533 OutputStream out = getOutputStream(); 534 if (out != null) 535 { 536 if (out instanceof DataOutput) 537 { 538 return (DataOutput)out; 539 } 540 else 541 { 542 return new DataOutputStream(out); 543 } 544 } 545 RandomAccessFile raf = getRandomAccessFile(); 546 if (raf != null && getMode() == CodecMode.SAVE) 547 { 548 return raf; 549 } 550 return null; 551 } 552 553 /** 554 * Returns an {@link java.io.OutputStream} object that was given to 555 * this codec via {@link #setOutputStream(OutputStream)} 556 * (or <code>null</code> otherwise). 557 * @return OutputStream object 558 */ 559 public OutputStream getOutputStream() 560 { 561 return out; 562 } 563 564 /** 565 * Returns a {@link java.io.RandomAccessFile} object that was given to 566 * this codec via {@link #setRandomAccessFile(RandomAccessFile, CodecMode)} 567 * (or <code>null</code> otherwise). 568 * @return RandomAccessFile object 569 */ 570 public RandomAccessFile getRandomAccessFile() 571 { 572 return raf; 573 } 574 575 /** 576 * Returns if bounds have been specified. 577 * @return if bounds have been specified 578 * @see #removeBounds() 579 * @see #setBounds(int, int, int, int) 580 */ 581 public boolean hasBounds() 582 { 583 return boundsAvail; 584 } 585 586 protected void initModeFromIOObjects() throws MissingParameterException 587 { 588 if (getMode() != null) 589 { 590 return; 591 } 592 if (getInputStream() != null || getDataInput() != null) 593 { 594 mode = CodecMode.LOAD; 595 } 596 else 597 if (getOutputStream() != null || getDataOutput() != null) 598 { 599 mode = CodecMode.SAVE; 600 } 601 else 602 { 603 throw new MissingParameterException("No streams or files available."); 604 } 605 } 606 607 /** 608 * Returns if this codec is able to load images in the file format supported by this codec. 609 * If <code>true</code> is returned this does not necessarily mean that all files in this 610 * format can be read, but at least some. 611 * @return if loading is supported 612 */ 613 public abstract boolean isLoadingSupported(); 614 615 /** 616 * Returns if this codec is able to save images in the file format supported by this codec. 617 * If <code>true</code> is returned this does not necessarily mean that all types files in this 618 * format can be written, but at least some. 619 * @return if saving is supported 620 */ 621 public abstract boolean isSavingSupported(); 622 623 private static boolean isPointInRectangle(int x, int y, int x1, int y1, int x2, int y2) 624 { 625 return (x >= x1 && x <= x2 && y >= y1 && y <= y2); 626 } 627 628 /** 629 * Returns if an image row given by its number (zero-based) must be loaded 630 * in the context of the current bounds. 631 * <p> 632 * Example: if vertical bounds have been set to 34 and 37, image rows 34 to 633 * 37 as arguments to this method would result in <code>true</code>, anything 634 * else (e.g. 12 or 45) would result in <code>false</code>. 635 * 636 * @param row the number of the row to be checked 637 * @return if row must be loaded, regarding the current bounds 638 */ 639 public boolean isRowRequired(int row) 640 { 641 if (hasBounds()) 642 { 643 return (row >= boundsY1 && row <= boundsY2); 644 } 645 else 646 { 647 return (row >= 0 && row < getImage().getHeight()); 648 } 649 } 650 651 /** 652 * Returns if the tile formed by the argument coordinates 653 * form a rectangle that overlaps with the bounds. 654 * If no bounds were defined, returns <code>true</code>. 655 * @param x1 656 * @param y1 657 * @param x2 658 * @param y2 659 * @return if the argument tile is required 660 */ 661 public boolean isTileRequired(int x1, int y1, int x2, int y2) 662 { 663 if (hasBounds()) 664 { 665 //System.out.println("x1=" + x1 + " y1=" + y1 + " x2=" + x2 + " y2=" + y2); 666 return ! 667 (getBoundsY2() < y1 || 668 getBoundsY1() > y2 || 669 getBoundsX2() < x1 || 670 getBoundsX1() > x2); 671 } 672 else 673 { 674 return true; 675 } 676 } 677 678 /** 679 * Removes all entries from the internal list of comments. 680 */ 681 public void removeAllComments() 682 { 683 comments.removeAllElements(); 684 } 685 686 /** 687 * If bounds were set using {@link #setBounds(int, int, int, int)}, these 688 * bounds are no longer regarded after the call to this method. 689 */ 690 public void removeBounds() 691 { 692 boundsAvail = false; 693 } 694 695 /** 696 * Sets the bounds of a rectangular part of the image that 697 * is to be loaded or saved, instead of the complete image. 698 */ 699 public void setBounds(int x1, int y1, int x2, int y2) 700 { 701 if (x1 < 0 || y1 < 0 || x2 < x1 || y2 < y1) 702 { 703 throw new IllegalArgumentException("Not a valid bounds rectangle: " + 704 "x1=" + x1 + ", y1=" + y1 + ", x2=" + x2 + ", y2=" + y2); 705 } 706 boundsX1 = x1; 707 boundsY1 = y1; 708 boundsX2 = x2; 709 boundsY2 = y2; 710 boundsAvail = true; 711 boundsWidth = x2 - x1 + 1; 712 boundsHeight = y2 - y1 + 1; 713 } 714 715 /** 716 * If no bounds have been set ({@link #hasBounds()} returns <code>false</code>), 717 * this method will set the bounds to <code>0, 0, width - 1, height - 1</code>. 718 * By calling this method somewhere in the codec, no distinction has to 719 * be made for the two cases <em>bounds have been defined</em> and 720 * <em>bounds have not been defined</em>. 721 * @param width width of the image to be loaded or saved 722 * @param height height of the image to be loaded or saved 723 */ 724 public void setBoundsIfNecessary(int width, int height) 725 { 726 if (!hasBounds()) 727 { 728 setBounds(0, 0, width - 1, height - 1); 729 } 730 } 731 732 /** 733 * Specifies a DataInput object to be used for loading. 734 * @param dataInput DataInput object to be used for loading an image 735 */ 736 public void setDataInput(DataInput dataInput) 737 { 738 din = dataInput; 739 } 740 741 /** 742 * Sets a {@link java.io.DataOutput} object to be used for saving 743 * an image. 744 * @param dataOutput the object to be used for output 745 */ 746 public void setDataOutput(DataOutput dataOutput) 747 { 748 dout = dataOutput; 749 } 750 751 /** 752 * Sets the DPI values to be stored in the file to the argument values. 753 * @param horizontalDpi horizontal physical resolution in DPI (dots per inch) 754 * @param verticalDpi vertical physical resolution in DPI (dots per inch) 755 * @see #getDpiX 756 * @see #getDpiY 757 */ 758 public void setDpi(int horizontalDpi, int verticalDpi) 759 { 760 dpiX = horizontalDpi; 761 dpiY = verticalDpi; 762 } 763 764 /** 765 * Gives a File object and a codec mode to this codec and attempts 766 * to initialize the appropriate I/O objects. 767 * Simply calls {@link #setFile(String, CodecMode)} with the absolute 768 * path of the File object. 769 * @param file File object for the file to be used 770 * @param codecMode defines whether an image is to be loaded from or saved to the file 771 */ 772 public void setFile(File file, CodecMode codecMode) throws 773 IOException, 774 UnsupportedCodecModeException 775 { 776 setFile(file.getAbsolutePath(), codecMode); 777 } 778 779 /** 780 * Gives a file name and codec mode to the codec which will then 781 * try to create the corresponding I/O object. 782 * The default implementation in ImageCodec creates a DataInputStream object 783 * wrapped around a BufferedInputStream wrapped around a FileInputStream for 784 * CodecMode.LOAD. 785 * Similar for CodecMode.SAVE: a DataOutputStream around a BufferedOutputStream 786 * object around a FileOutputStream object. 787 * Codecs that need different I/O objects must override this method 788 * (some codecs may need random access and thus require a RandomAccessFile object). 789 * @param fileName name of the file to be used for loading or saving 790 * @param codecMode defines whether file is to be used for loading or saving 791 */ 792 public void setFile(String fileName, CodecMode codecMode) throws 793 IOException, 794 UnsupportedCodecModeException 795 { 796 if (codecMode == CodecMode.LOAD) 797 { 798 if (isLoadingSupported()) 799 { 800 setInputStream(new BufferedInputStream(new FileInputStream(fileName))); 801 } 802 else 803 { 804 throw new UnsupportedCodecModeException("Loading is not supported for this codec (" + getFormatName() + ")."); 805 } 806 } 807 else 808 { 809 if (isSavingSupported()) 810 { 811 setOutputStream(new BufferedOutputStream(new FileOutputStream(fileName))); 812 } 813 else 814 { 815 throw new UnsupportedCodecModeException("Saving is not supported for this codec (" + getFormatName() + ")."); 816 } 817 } 818 } 819 820 /** 821 * Give an image to this codec to be used for loading an image into it 822 * or saving the image. 823 * @param img image object to save or to load data into 824 */ 825 public void setImage(PixelImage img) 826 { 827 image = img; 828 } 829 830 /** 831 * Sets the index of the image to be loaded to the argument value 832 * (which must be zero or larger). 833 * @param index int index value (zero-based) of the image to be loaded 834 * @throws IllegalArgumentException if the argument is negative 835 */ 836 public void setImageIndex(int index) 837 { 838 if (index < 0) 839 { 840 throw new IllegalArgumentException("The index must be 0 or larger."); 841 } 842 imageIndex = index; 843 } 844 845 /** 846 * An {@link java.io.InputStream} can be given to this codec using this method. 847 * @param inputStream InputStream object to read from 848 */ 849 public void setInputStream(InputStream inputStream) 850 { 851 in = inputStream; 852 } 853 854 /** 855 * A method to give an {@link java.io.OutputStream} to this codec to be used 856 * for saving an image. 857 * @param outputStream the output stream to be used by this codec 858 */ 859 public void setOutputStream(OutputStream outputStream) 860 { 861 out = outputStream; 862 } 863 864 /** 865 * A method to give a {@link java.io.RandomAccessFile} to this codec to be used 866 * for loading or saving an image. 867 * It is not possible to determine from a RandomAccessFile object whether it 868 * was opened in read-only or read-and-write mode. 869 * To let the codec know whether the object is to be used for loading or saving 870 * the second argument is of type CodecMode. 871 * @param randomAccessFile the file to be used for loading or saving 872 * @param codecMode tells the codec whether the file is to be used for loading or saving 873 */ 874 public void setRandomAccessFile(RandomAccessFile randomAccessFile, CodecMode codecMode) 875 { 876 if (randomAccessFile == null) 877 { 878 throw new IllegalArgumentException("Argument RandomAccessFile must be non-null."); 879 } 880 if (codecMode == null) 881 { 882 throw new IllegalArgumentException("Argument codec mode must be non-null."); 883 } 884 raf = randomAccessFile; 885 mode = codecMode; 886 } 887 888 /** 889 * Attempts to suggest a filename extension. 890 * The type of the argument image will be taken into consideration, 891 * although this will be necessary for some file formats only (as an 892 * example, PNM has different extensions for different image types, see 893 * {@link PNMCodec}). 894 * This default implementation always returns <code>null</code>. 895 * @param image the image that is to be written to a file 896 * @return the file extension, including a leading dot, or <code>null</code> if no file extension can be recommended 897 */ 898 public String suggestFileExtension(PixelImage image) 899 { 900 return null; 901 } 902 }