001 /* 002 * TIFFCodec 003 * 004 * Copyright (c) 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.codecs.tiff; 009 010 import java.io.IOException; 011 import java.io.RandomAccessFile; 012 import java.util.Hashtable; 013 import java.util.Vector; 014 import net.sourceforge.jiu.codecs.CodecMode; 015 import net.sourceforge.jiu.codecs.ImageCodec; 016 import net.sourceforge.jiu.codecs.InvalidFileStructureException; 017 import net.sourceforge.jiu.codecs.UnsupportedCodecModeException; 018 import net.sourceforge.jiu.codecs.UnsupportedTypeException; 019 import net.sourceforge.jiu.codecs.WrongFileFormatException; 020 import net.sourceforge.jiu.data.MemoryBilevelImage; 021 import net.sourceforge.jiu.data.MemoryGray16Image; 022 import net.sourceforge.jiu.data.MemoryGray8Image; 023 import net.sourceforge.jiu.data.MemoryPaletted8Image; 024 import net.sourceforge.jiu.data.MemoryRGB24Image; 025 import net.sourceforge.jiu.data.MemoryRGB48Image; 026 import net.sourceforge.jiu.data.PixelImage; 027 import net.sourceforge.jiu.ops.MissingParameterException; 028 import net.sourceforge.jiu.ops.OperationFailedException; 029 import net.sourceforge.jiu.ops.WrongParameterException; 030 031 /** 032 * A codec to read Tagged Image File Format (TIFF) image files. 033 * 034 * <h3>Usage example</h3> 035 * Load an image from a TIFF file. 036 * <pre> 037 * TIFFCodec codec = new TIFFCodec(); 038 * codec.setFile("image.tif", CodecMode.LOAD); 039 * codec.process(); 040 * PixelImage loadedImage = codec.getImage(); 041 * </pre> 042 * Saving images is not supported by this codec. 043 * 044 * <h3>Compression types</h3> 045 * <h4>Reading</h4> 046 * The TIFF package supports the following compression types when reading: 047 * <ul> 048 * <li><em>Uncompressed</em>. Compression method number 1. Works with all types of image data. See {@link TIFFDecoderUncompressed}.</li> 049 * <li><em>Packbits</em>. Compression method number 32773. Works with all types of image data. See {@link TIFFDecoderPackbits}.</li> 050 * <li><em>CCITT Group 3 1-Dimensional Modified Huffman runlength encoding</em>. Compression method number 2. 051 * Works with bilevel image data only. See {@link TIFFDecoderModifiedHuffman}.</li> 052 * <li><em>Deflated</em>. Compression method number 8 or 32946. Works with all types of image data. See {@link TIFFDecoderDeflated}.</li> 053 * <li><em>LogLuv RLE</em> and <em>LogLuv 24</em>. Compression method numbers 34676 and 34677. Works only with LogLuv color data. See {@link TIFFDecoderLogLuv}.</li> 054 * </ul> 055 * <p> 056 * Note that you can write your own decoder (extending {@link TIFFDecoder}) for any compression type 057 * you want. 058 * </p> 059 * 060 * <h3>Image types</h3> 061 * <h4>Reading</h4> 062 * The TIFF package supports the following image / color types when reading: 063 * <ul> 064 * <li><em>Black & white</em>. JIU image data type {@link net.sourceforge.jiu.data.BilevelImage}.</li> 065 * <li><em>Grayscale, 4 and 8 bits per pixel</em>. JIU image data type {@link net.sourceforge.jiu.data.Gray8Image}.</li> 066 * <li>TODO add other image types</li> 067 * </ul> 068 * <p> 069 * Note that you can write your own decoder (extending {@link TIFFDecoder}) for any compression type 070 * you want. 071 * </p> 072 * 073 * <h4>Writing</h4> 074 * <p>Writing TIFFs is not supported. 075 * I don't know if or when it will be supported.</p> 076 * 077 * <h3>Strips and tiles</h3> 078 * The early versions of TIFF considered an image to be a sequence of <em>strips</em>. 079 * Each strip was a rectangular part of the image, as wide as the complete image, 080 * and with a certain height defined by the <em>rows per strip</em> tag. 081 * So with a number of rows per strip of 10, and an image height of 200, you would 082 * have to store 20 strips. 083 * It was recommended that a strip should not be larger than 8 KB (RAM was tighter 084 * in those days). 085 * The rule of thumb to define the number of rows per strip was to see how many rows 086 * would fit into 8 KB. 087 * <p> 088 * Later, the concept of <em>tiles</em> was added to the TIFF specs. 089 * Tiled TIFFs are separated into rectangles that not only had a defineable 090 * height but also a defineable width (tile width and tile height are also stored in 091 * corresponding tags). 092 * <p> 093 * Obviously, strips are just a special case of tiles, with the tile width being equal 094 * to image width. 095 * That is why JIU internally only deals with tiles. 096 * The only difference: No row padding takes place for strips. 097 * In a tiled image with a tile height of 10 and an image height of 14, 098 * the image is two tiles high. 099 * 100 * <h3>Number of images</h3> 101 * TIFF allows for multiple images in a single file. 102 * This codec regards the image index, queries {@link #getImageIndex} and skips to the 103 * correct image. 104 * 105 * <h3>Bounds</h3> 106 * The bounds concept of JIU is supported by this codec. 107 * So you can specify bounds of a rectangular part of an image that you want to load 108 * instead of loading the complete image. 109 * 110 * <h3>Color spaces</h3> 111 * The following color spaces are understood when reading truecolor TIFF files. 112 * <ul> 113 * <li><em>RGB</em> - should cover most truecolor files.</li> 114 * <li><em>CMYK</em> - is supported, but colors may not be exactly right. 115 * CMYK data is converted to RGB on the fly, so the codec user never accesses CMYK data.</li> 116 * <li><em>LogLuv</em> - is supported, but not all flavors yet.</li> 117 * </ul> 118 * <h3>Physical resolution</h3> 119 * DPI information can be stored in TIFF files. 120 * If that information is available, this codec retrieves it so that it 121 * can be queried using {@link #getDpiX} and {@link #getDpiY}. 122 * 123 * <h3>Background information on TIFF</h3> 124 * TIFF is an important image file format for DTP (desktop publishing). 125 * The advantages of TIFF include its flexibility, availability of libraries to read 126 * and write TIFF files and its good support in existing software. 127 * The major disadvantage of TIFF is its complexity, which makes it hard for software 128 * to support all possible valid TIFF files. 129 * <p> 130 * TIFF was created by Aldus and now <em>belongs</em> to Adobe, who offer a specification document: 131 * <a target="_top" href="http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf">TIFF 132 * (Tagged Image File Format) 6.0 Specification</a> (updated on Web September, 20 1995, 133 * document dated June, 3 1992) (PDF: 385 KB / 121 pages). 134 * <p> 135 * Other good references include the <a target="_top" href="http://www.libtiff.org">homepage 136 * of libtiff</a>, a free C library to read and write TIFF files and 137 * <a target="_top" href="http://home.earthlink.net/~ritter/tiff/">The Unofficial TIFF 138 * homepage</a> by <a href="mailto:ritter@earthlink.net">Niles Ritter</a>. 139 * Also see <a target="_top" href="http://dmoz.org/Computers/Data_Formats/Graphics/Pixmap/TIFF/">the TIFF section</a> 140 * of the <a target="_top" href="http://www.dmoz.org">Open Directory</a>. 141 * <p> 142 * TIFF is used for various specialized tasks. 143 * As an example, see <a target="_top" href="http://www.remotesensing.org/geotiff/geotiff.html">GeoTIFF</a> (geographical 144 * data) or <a target="_top" href="http://www.ba.wakwak.com/~tsuruzoh/index-e.html">EXIF</a> 145 * (digital camera metadata; this is actually a TIFF directory embedded in a JPEG header). 146 * <p> 147 * Here's a list of features that make TIFF quite complex: 148 * <ul> 149 * <li>More than one image can be stored in a TIFF file.</li> 150 * <li>Integer values that are larger than one byte can be in either little or big endian byte order.</li> 151 * <li>Various color types are supported (bilevel, gray, paletted, all kinds of color spaces (RGB / YCbCr / CMYK). 152 * It's easy to add new color types, so this list can grow.</li> 153 * <li>The meta data (information that describes the image and how it is stored) can be distributed all over 154 * the file.</li> 155 * <li>Image data is stored as packed bytes, 4-bit-nibbles, bytes and 16-bit-integers. Other types are possible 156 * as well.</li> 157 * <li>Various compression types are supported; not all types can be used on all color types.</li> 158 * <li>Image data can be stored in strips or tiles.</li> 159 * <li>An arbitrary number of non-image-data samples can stored within the image data.</li> 160 * <li>Color types with more than one sample per pixel can store data in an interleaved (<em>chunky</em>) 161 * way or in planes.</li> 162 * <li>Different ways of defining black and white are possible with bilevel and grayscale images.</li> 163 * </ul> 164 * 165 * @author Marco Schmidt 166 */ 167 public class TIFFCodec extends ImageCodec implements TIFFConstants 168 { 169 private static final boolean DEBUG = true; 170 public static final int BYTE_ORDER_MOTOROLA = 0; 171 public static final int BYTE_ORDER_INTEL = 1; 172 private static final int MAGIC_INTEL = 0x49492a00; 173 private static final int MAGIC_MOTOROLA = 0x4d4d002a; 174 private static final int MAX_PRINT_ELEMENTS = 20; 175 176 private int byteOrder; 177 private int nextIfdOffset; 178 179 private static Hashtable decoders; 180 static 181 { 182 decoders = new Hashtable(); 183 registerDecoder(TIFFDecoderDeflated.class); 184 registerDecoder(TIFFDecoderModifiedHuffman.class); 185 registerDecoder(TIFFDecoderPackbits.class); 186 registerDecoder(TIFFDecoderUncompressed.class); 187 registerDecoder(TIFFDecoderLogLuv.class); 188 } 189 190 /** 191 * If the current byte order is {@link BYTE_ORDER_MOTOROLA} and the type 192 * argument is {@link TiffConstants.TAG_TYPE_BYTE} or 193 * {@link TiffConstants.TAG_TYPE_SHORT}, the value parameter must 194 * be adjusted by some bitshifting. 195 * If the above mentioned criteria are not met, the value argument is 196 * returned without any modifications. 197 * <p> 198 * <em>Why this is necessary remains a mystery to me. Marco</em> 199 * 200 * @param value the int value which may have to be adjusted 201 * @return the value parameter which may have been modified 202 */ 203 private int adjustInt(int value, int type) 204 { 205 if (getByteOrder() == BYTE_ORDER_MOTOROLA) 206 { 207 if (type == TAG_TYPE_BYTE) 208 { 209 return ((value >> 24) & 0xff); 210 } 211 else 212 if (type == TAG_TYPE_SHORT) 213 { 214 return ((value >> 16) & 0xff) | (((value >> 24) & 0xff) << 8); 215 } 216 else 217 { 218 return value; 219 } 220 } 221 else 222 { 223 return value; 224 } 225 } 226 227 private static TIFFDecoder createDecoder(TIFFCodec codec, TIFFImageFileDirectory ifd, int tileIndex) throws 228 IOException, 229 UnsupportedTypeException 230 { 231 Integer compression = new Integer(ifd.getCompression()); 232 Class decoderClass = (Class)decoders.get(compression); 233 if (decoderClass == null) 234 { 235 throw new UnsupportedTypeException("Could not create decoder for this compression type: " + 236 compression.intValue()); 237 } 238 Object instance; 239 try 240 { 241 instance = decoderClass.newInstance(); 242 } 243 catch (Exception e) 244 { 245 throw new UnsupportedTypeException("Could not create decoder for this compression type."); 246 } 247 if (instance instanceof TIFFDecoder) 248 { 249 TIFFDecoder decoder = (TIFFDecoder)instance; 250 decoder.setCodec(codec); 251 decoder.setTileIndex(tileIndex); 252 decoder.setImageFileDirectory(ifd); 253 try 254 { 255 decoder.initialize(); 256 } 257 catch (MissingParameterException mpe) 258 { 259 throw new UnsupportedTypeException("Unable to initialize decoder: " + mpe.toString()); 260 } 261 return decoder; 262 } 263 else 264 { 265 throw new UnsupportedTypeException("Could not create decoder for this compression type."); 266 } 267 } 268 269 /** 270 * Returns the current byte order, either 271 * {@link #BYTE_ORDER_INTEL} or 272 * {@link #BYTE_ORDER_MOTOROLA}. 273 * @return current byte order 274 */ 275 public int getByteOrder() 276 { 277 return byteOrder; 278 } 279 280 public String getFormatName() 281 { 282 return "Tagged Image File Format (TIFF)"; 283 } 284 285 public String[] getMimeTypes() 286 { 287 return new String[] {"image/tiff", "image/tif"}; 288 } 289 290 /** 291 * Returns the name of a tag in English. 292 * @param id of the tag for which a name is to be returned 293 * @return tag name as String or a question mark <code>?</code> 294 */ 295 public static String getTagName(int id) 296 { 297 switch(id) 298 { 299 case(TAG_ARTIST): return "Artist"; 300 case(TAG_BAD_FAX_LINES): return "Bad fax lines"; 301 case(TAG_BITS_PER_SAMPLE): return "Bits per sample"; 302 case(TAG_CELL_LENGTH): return "Cell length"; 303 case(TAG_CELL_WIDTH): return "Cell width"; 304 case(TAG_CLEAN_FAX_DATA): return "Clean fax data"; 305 case(TAG_COLOR_MAP): return "Color map"; 306 case(TAG_COMPRESSION): return "Compression"; 307 case(TAG_CONSECUTIVE_BAD_FAX_LINES): return "Consecutive bad fax lines"; 308 case(TAG_COPYRIGHT): return "Copyright"; 309 case(TAG_DATE_TIME): return "Date and time"; 310 case(TAG_DOCUMENT_NAME): return "Document name"; 311 case(TAG_EXTRA_SAMPLES): return "Extra samples"; 312 case(TAG_FILL_ORDER): return "Fill order"; 313 case(TAG_FREE_BYTE_COUNTS): return "Free byte counts"; 314 case(TAG_FREE_OFFSETS): return "Free offsets"; 315 case(TAG_GRAY_RESPONSE_CURVE): return "Gray response curve"; 316 case(TAG_GRAY_RESPONSE_UNIT): return "Gray response unit"; 317 case(TAG_HOST_COMPUTER): return "Host computer"; 318 case(TAG_IMAGE_DESCRIPTION): return "Image description"; 319 case(TAG_IMAGE_LENGTH): return "Image length"; 320 case(TAG_IMAGE_WIDTH): return "Image width"; 321 case(TAG_MAKE): return "Make"; 322 case(TAG_MAX_SAMPLE_VALUE): return "Maximum sample value"; 323 case(TAG_MIN_SAMPLE_VALUE): return "Minimum sample value"; 324 case(TAG_MODEL): return "Model"; 325 case(TAG_NEW_SUBFILE_TYPE): return "New subfile type"; 326 case(TAG_ORIENTATION): return "Orientation"; 327 case(TAG_PHOTOMETRIC_INTERPRETATION): return "Photometric interpretation"; 328 case(TAG_PLANAR_CONFIGURATION): return "Planar configuration"; 329 case(TAG_PREDICTOR): return "Predictor"; 330 case(TAG_RESOLUTION_UNIT): return "Resolution unit"; 331 case(TAG_RESOLUTION_X): return "Resolution X"; 332 case(TAG_RESOLUTION_Y): return "Resolution Y"; 333 case(TAG_ROWS_PER_STRIP): return "Rows per strip"; 334 case(TAG_SAMPLES_PER_PIXEL): return "Samples per pixel"; 335 case(TAG_SOFTWARE): return "Software"; 336 case(TAG_STRIP_BYTE_COUNTS): return "Strip byte counts"; 337 case(TAG_STRIP_OFFSETS): return "Strip offsets"; 338 case(TAG_TILE_BYTE_COUNTS): return "Byte counts"; 339 case(TAG_TILE_HEIGHT): return "Tile height"; 340 case(TAG_TILE_OFFSETS): return "Tile offsets"; 341 case(TAG_TILE_WIDTH): return "Tile widths"; 342 default: return "?"; 343 } 344 } 345 346 public boolean isLoadingSupported() 347 { 348 return true; 349 } 350 351 public boolean isSavingSupported() 352 { 353 return false; 354 } 355 356 /** 357 * Attempts to load an image from a file in the TIFF format. 358 * Some options can be given to this codec before the call 359 * to this load method. 360 * <ul> 361 * <li>You must provide a {@link java.io.RandomAccessFile} using 362 * {@link #setInput(java.io.RandomAccessFile)}.</li> 363 * <li>If there is more than one image in the input file, you 364 * can make the codec load it by calling {@link #setImageIndex(int)}. 365 * The argument is the index of the image, the first being <code>0</code>, 366 * the second <code>1</code> and so on. The default is <code>0</code>.</li> 367 * </ul> 368 * 369 * @return the image if everything was successful 370 * @throws InvalidFileStructureException if the TIFF file was corrupt in some way 371 * @throws IOException if there were errors reading from the input file 372 * @throws UnsupportedTypeException if the flavour of TIFF encountered in the input 373 * file is not supported yet 374 * @throws WrongFileFormatException 375 */ 376 private void load() throws 377 InvalidFileStructureException, 378 IOException, 379 UnsupportedTypeException, 380 WrongFileFormatException, 381 WrongParameterException 382 { 383 readHeader(); 384 skipImageFileDirectories(getImageIndex()); 385 TIFFImageFileDirectory ifd = readImageFileDirectory(); 386 ifd.initFromTags(true); 387 println("IFD OK"); 388 int dpiX = ifd.getDpiX(); 389 int dpiY = ifd.getDpiY(); 390 if (dpiX > 0 && dpiY > 0) 391 { 392 println("DPI: " + dpiX + " x " + dpiY); 393 setDpi(dpiX, dpiY); 394 } 395 //ifd.dump(); 396 load(ifd); 397 } 398 399 private void load(TIFFImageFileDirectory ifd) throws 400 InvalidFileStructureException, 401 IOException, 402 UnsupportedTypeException, 403 WrongFileFormatException, 404 WrongParameterException 405 { 406 setBoundsIfNecessary(ifd.getWidth(), ifd.getHeight()); 407 checkImageResolution(); 408 int width = getBoundsWidth(); 409 int height = getBoundsHeight(); 410 // create image if necessary 411 PixelImage image = getImage(); 412 if (image == null) 413 { 414 int imageType = ifd.getImageType(); 415 switch (imageType) 416 { 417 case(TIFFImageFileDirectory.TYPE_BILEVEL_BYTE): 418 case(TIFFImageFileDirectory.TYPE_BILEVEL_PACKED): 419 { 420 image = new MemoryBilevelImage(width, height); 421 break; 422 } 423 case(TIFFImageFileDirectory.TYPE_GRAY4): 424 case(TIFFImageFileDirectory.TYPE_GRAY8): 425 case(TIFFImageFileDirectory.TYPE_LOGL): 426 { 427 image = new MemoryGray8Image(width, height); 428 break; 429 } 430 case(TIFFImageFileDirectory.TYPE_GRAY16): 431 { 432 image = new MemoryGray16Image(width, height); 433 break; 434 } 435 case(TIFFImageFileDirectory.TYPE_PALETTED4): 436 case(TIFFImageFileDirectory.TYPE_PALETTED8): 437 { 438 image = new MemoryPaletted8Image(width, height, ifd.getPalette()); 439 break; 440 } 441 case(TIFFImageFileDirectory.TYPE_CMYK32_INTERLEAVED): 442 case(TIFFImageFileDirectory.TYPE_CMYK32_PLANAR): 443 case(TIFFImageFileDirectory.TYPE_RGB24_INTERLEAVED): 444 case(TIFFImageFileDirectory.TYPE_LOGLUV32_INTERLEAVED): 445 { 446 image = new MemoryRGB24Image(width, height); 447 break; 448 } 449 case(TIFFImageFileDirectory.TYPE_RGB48_INTERLEAVED): 450 { 451 image = new MemoryRGB48Image(width, height); 452 break; 453 } 454 default: 455 { 456 throw new UnsupportedTypeException("Unsupported image type."); 457 } 458 } 459 setImage(image); 460 } 461 int tileIndex = 0; 462 int numTiles = ifd.getNumTiles(); 463 while (tileIndex < numTiles && !getAbort()) 464 { 465 println("Decode tile / strip index #" + (tileIndex + 1) + "/" + numTiles); 466 int x1 = ifd.getTileX1(tileIndex); 467 int y1 = ifd.getTileY1(tileIndex); 468 int x2 = ifd.getTileX2(tileIndex); 469 int y2 = ifd.getTileY2(tileIndex); 470 if (isTileRequired(x1, y1, x2, y2)) 471 { 472 println("About to create decoder."); 473 TIFFDecoder decoder = createDecoder(this, ifd, tileIndex); 474 decoder.decode(); 475 } 476 tileIndex++; 477 } 478 } 479 480 private void print(String s) 481 { 482 if (DEBUG) 483 { 484 System.out.print(s); 485 } 486 } 487 488 private void println(String s) 489 { 490 if (DEBUG) 491 { 492 System.out.println(s); 493 } 494 } 495 496 public void process() throws 497 MissingParameterException, 498 OperationFailedException 499 { 500 initModeFromIOObjects(); 501 try 502 { 503 if (getMode() == CodecMode.LOAD && getRandomAccessFile() != null) 504 { 505 load(); 506 } 507 else 508 { 509 throw new MissingParameterException("TIFF codec must have RandomAccessFile object opened for reading."); 510 } 511 } 512 catch (IOException ioe) 513 { 514 close(); 515 throw new OperationFailedException("I/O error occurred: " + ioe.toString()); 516 } 517 } 518 519 /** 520 * Reads the first eight bytes from the input file, checks if this is a 521 * valid TIFF file and stores byte order and offset of the first image 522 * file directory. 523 * @throws IOException if there were reading errors 524 * @throws WrongFileFormatException if this is not a valid TIFF file 525 */ 526 private void readHeader() throws 527 IOException, 528 WrongFileFormatException 529 { 530 RandomAccessFile in = getRandomAccessFile(); 531 // the argument to in.seek must be changed to a variable in the future for 532 // this codec to be used to read EXIF information from JPEGs; 533 // for some reason, TIFF was chosen for that 534 in.seek(0); 535 // note: this is the only place where we use in.readInt() 536 // directly; afterwards, the detected byte order 537 // is regarded via this class' methods readInt() and readShort() 538 // methods 539 int magic = in.readInt(); 540 if (magic == MAGIC_INTEL) 541 { 542 setByteOrder(BYTE_ORDER_INTEL); 543 } 544 else 545 if (magic == MAGIC_MOTOROLA) 546 { 547 setByteOrder(BYTE_ORDER_MOTOROLA); 548 } 549 else 550 { 551 throw new WrongFileFormatException("Not a TIFF file (does not " + 552 "begin with II or MM followed by 42)."); 553 } 554 nextIfdOffset = readInt(); 555 println("\nifd=" + nextIfdOffset); 556 } 557 558 /** 559 * Reads a complete TIFF image file directory including all data that is 560 * pointed to using the offset components and returns it. 561 * 562 * @return the image file directory data or <code>null</code> on failure 563 */ 564 private TIFFImageFileDirectory readImageFileDirectory() throws 565 InvalidFileStructureException, 566 IOException 567 { 568 TIFFImageFileDirectory result = new TIFFImageFileDirectory(); 569 RandomAccessFile in = getRandomAccessFile(); 570 in.seek(nextIfdOffset); 571 short numTags = readShort(); 572 if (numTags < 0) 573 { 574 throw new InvalidFileStructureException("Number of tags in IFD " + 575 "smaller than 1 @" + nextIfdOffset + ": " + numTags); 576 } 577 for (int i = 0; i < numTags; i++) 578 { 579 TIFFTag tag = readTag(); 580 if (tag != null) 581 { 582 result.append(tag); 583 } 584 } 585 nextIfdOffset = in.readInt(); 586 println("Next IFD " + nextIfdOffset); 587 return result; 588 } 589 590 /** 591 * Reads a 32 bit signed integer value, regarding the current byte order. 592 * @return the loaded value 593 * @see #getByteOrder 594 */ 595 private int readInt() throws IOException 596 { 597 RandomAccessFile in = getRandomAccessFile(); 598 int result = in.readInt(); 599 if (getByteOrder() == BYTE_ORDER_INTEL) 600 { 601 int r1 = (result >> 24) & 0xff; 602 int r2 = (result >> 16) & 0xff; 603 int r3 = (result >> 8) & 0xff; 604 int r4 = result & 0xff; 605 return r1 | (r2 << 8) | (r3 << 16) | (r4 << 24); 606 } 607 else 608 { 609 return result; 610 } 611 } 612 613 /** 614 * Reads a 16 bit signed integer value, regarding the current byte order. 615 * @return the loaded value 616 */ 617 private short readShort() throws IOException 618 { 619 RandomAccessFile in = getRandomAccessFile(); 620 short result = in.readShort(); 621 if (getByteOrder() == BYTE_ORDER_INTEL) 622 { 623 int r1 = (result >> 8) & 0xff; 624 int r2 = result & 0xff; 625 return (short)((r2 << 8) | r1); 626 } 627 else 628 { 629 return result; 630 } 631 } 632 633 /** 634 * Loads a String of a given length from current position of input file. 635 * Characters are one-byte ASCII. 636 * Non-text characters are dropped. 637 * @param length number of characters in a row to be loaded 638 * @return loaded String 639 * @throws IOException if there were reading errors or an unexpected 640 * end of file 641 */ 642 private String readString(int length) throws IOException 643 { 644 RandomAccessFile in = getRandomAccessFile(); 645 StringBuffer sb = new StringBuffer(length - 1); 646 while (length-- > 0) 647 { 648 int value = in.read(); 649 if (value >= 32 && value < 256) 650 { 651 sb.append((char)value); 652 } 653 } 654 return sb.toString(); 655 } 656 657 /** 658 * Reads a TIFF tag and all data belonging to it and returns a 659 * TIFFTag object. 660 * The additional data is somewhere in the TIFF file. 661 * The current position will be stored, the method will seek to the offset 662 * position and load the data. 663 * 664 * @return TIFFTag containing information on the tag 665 */ 666 private TIFFTag readTag() throws 667 InvalidFileStructureException, 668 IOException 669 { 670 RandomAccessFile in = getRandomAccessFile(); 671 int id = readShort() & 0xffff; 672 int type = readShort() & 0xffff; 673 int count = readInt(); 674 print(getTagName(id) + " (" + id + "), type " + type + ", count " + count + ", "); 675 int offset = readInt(); 676 if (count < 1) 677 { 678 //throw new InvalidFileStructureException("Invalid count value for tag " + id + " (" + count + ")."); 679 return null; 680 } 681 Vector vector = null; 682 // perform weird bitshifting magic if necessary 683 if (count == 1 && 684 (type == TAG_TYPE_BYTE || type == TAG_TYPE_SHORT || type == TAG_TYPE_LONG)) 685 { 686 offset = adjustInt(offset, type); 687 println("value " + offset); 688 } 689 else 690 if (count <= 4 && type == TAG_TYPE_BYTE) 691 { 692 vector = new Vector(); 693 for (int i = 0; i < count; i++) 694 { 695 byte b = (byte)((offset << (i * 8)) & 0xff); 696 vector.addElement(new Byte(b)); 697 } 698 } 699 else 700 if (count >= 1) 701 { 702 long oldOffset = in.getFilePointer(); 703 in.seek(offset); 704 vector = new Vector(); 705 if (type == TAG_TYPE_ASCII) 706 { 707 vector.addElement(readString(count)); 708 println("string " + vector.elementAt(0)); 709 } 710 else 711 if (type == TAG_TYPE_BYTE) 712 { 713 for (int i = 0; i < count; i++) 714 { 715 byte b = in.readByte(); 716 println("byte #" + i + "=" + (b & 0xff)); 717 vector.addElement(new Byte(b)); 718 } 719 } 720 else 721 if (type == TAG_TYPE_SHORT) 722 { 723 for (int i = 0; i < count; i++) 724 { 725 int s = readShort(); 726 println("short #" + i + "=" + (s & 0xffff)); 727 vector.addElement(new Short((short)s)); 728 } 729 } 730 else 731 if (type == TAG_TYPE_LONG) 732 { 733 for (int i = 0; i < count; i++) 734 { 735 int v = adjustInt(readInt(), type); 736 println("long #" + i + "=" + v); 737 vector.addElement(new Integer(v)); 738 } 739 } 740 else 741 if (type == TAG_TYPE_RATIONAL) 742 { 743 for (int i = 0; i < count; i++) 744 { 745 int v1 = adjustInt(readInt(), TAG_TYPE_LONG); 746 int v2 = adjustInt(readInt(), TAG_TYPE_LONG); 747 println("rational #" + i + "=" + v1 + "/" + v2); 748 vector.addElement(new TIFFRational(v1, v2)); 749 } 750 } 751 in.seek(oldOffset); 752 } 753 TIFFTag result = new TIFFTag(id, type, count, offset); 754 result.setVector(vector); 755 return result; 756 } 757 758 /** 759 * Register a {@link TIFFDecoder} class. 760 * TIFF knows many compression types, and JIU only supports some of them. 761 * To register an external TIFFDecoder class with TIFFCodec, call this method 762 * with the class field of your decoder. 763 * As an example, for your TIFFDecoderMyCompression class, 764 * call <code>TIFFCodec.registerDecoder(TIFFDecoderMyCompression.class)</code>. 765 * It will be checked if 766 * <code>decoderClass.newInstance() instanceof TIFFDecoder</code> 767 * is true and, if so, the class will be added to an internal list. 768 * Whenever a TIFF file is to be decoded, the correct decoder is determined 769 * (each decoder knows about the compression types it supports via the getCompressionTypes method) 770 * and for each tile or strip such a decoder object will be created. 771 */ 772 public static void registerDecoder(Class decoderClass) 773 { 774 if (decoderClass == null) 775 { 776 return; 777 } 778 Object instance; 779 try 780 { 781 instance = decoderClass.newInstance(); 782 } 783 catch (Exception e) 784 { 785 return; 786 } 787 if (instance instanceof TIFFDecoder) 788 { 789 TIFFDecoder decoder = (TIFFDecoder)instance; 790 Integer[] compressionTypes = decoder.getCompressionTypes(); 791 if (compressionTypes == null) 792 { 793 return; 794 } 795 int index = 0; 796 while (index < compressionTypes.length) 797 { 798 Integer type = compressionTypes[index++]; 799 if (type != null) 800 { 801 decoders.put(type, decoderClass); 802 } 803 } 804 } 805 } 806 807 /** 808 * Sets the byte order to the argument. 809 * The byte order in a TIFF file is either {@link #BYTE_ORDER_INTEL} or 810 * {@link #BYTE_ORDER_MOTOROLA}. 811 * @param newByteOrder the new byte order to be set 812 * @throws IllegalArgumentException if the argument is not one of the above 813 * mentioned constants 814 */ 815 private void setByteOrder(int newByteOrder) 816 { 817 if (newByteOrder == BYTE_ORDER_INTEL || 818 newByteOrder == BYTE_ORDER_MOTOROLA) 819 { 820 byteOrder = newByteOrder; 821 } 822 else 823 { 824 throw new IllegalArgumentException("Byte order must be either " + 825 "BYTE_ORDER_INTEL or BYTE_ORDER_MOTOROLA."); 826 } 827 } 828 829 public void setFile(String fileName, CodecMode codecMode) throws 830 IOException, 831 UnsupportedCodecModeException 832 { 833 if (codecMode == CodecMode.LOAD) 834 { 835 setRandomAccessFile(new RandomAccessFile(fileName, "r"), CodecMode.LOAD); 836 } 837 else 838 { 839 throw new UnsupportedCodecModeException("This TIFF codec can only load images."); 840 } 841 } 842 843 /** 844 * Skips a given number of image file directories in this TIFF files. 845 * Throws an exception if there were errors or not enough image file 846 * directories. 847 * @param numDirectories the number of directories to be skipped, 848 * should be non-negative 849 * @throws IllegalArgumentException if argument is negative 850 * @throws InvalidFileStructureException if there aren't enough image 851 * file directories 852 * @throws IOExceptions if there were errors reading or skipping data 853 */ 854 private void skipImageFileDirectories(int numDirectories) throws 855 InvalidFileStructureException, 856 IOException 857 { 858 RandomAccessFile in = getRandomAccessFile(); 859 if (numDirectories < 0) 860 { 861 throw new IllegalArgumentException("Cannot skip negative number " + 862 "of image file directories: " + numDirectories); 863 } 864 int skipped = 0; 865 while (numDirectories-- > 0) 866 { 867 in.seek(nextIfdOffset); 868 short numTags = readShort(); 869 in.skipBytes(numTags * 12); 870 nextIfdOffset = readInt(); 871 if (nextIfdOffset == 0) 872 { 873 throw new InvalidFileStructureException("Could only skip " + 874 skipped + " image file directories, no more images in file."); 875 } 876 skipped++; 877 } 878 } 879 }