TIFFDirectory.java
上传用户:btjssb159
上传日期:2018-01-04
资源大小:241k
文件大小:20k
源码类别:

DNA

开发平台:

Java

  1. /*
  2.  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without 
  5.  * modification, are permitted provided that the following conditions are met:
  6.  * 
  7.  * -Redistributions of source code must retain the above copyright notice, this 
  8.  * list of conditions and the following disclaimer.
  9.  *
  10.  * -Redistribution in binary form must reproduct the above copyright notice,
  11.  * this list of conditions and the following disclaimer in the documentation
  12.  * and/or other materials provided with the distribution.
  13.  * 
  14.  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
  15.  * be used to endorse or promote products derived from this software without
  16.  * specific prior written permission.
  17.  * 
  18.  * This software is provided "AS IS," without a warranty of any kind. ALL
  19.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
  20.  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
  21.  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
  22.  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
  23.  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
  24.  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
  25.  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
  26.  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
  27.  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
  28.  * POSSIBILITY OF SUCH DAMAGES.
  29.  * 
  30.  * You acknowledge that Software is not designed,licensed or intended for use in 
  31.  * the design, construction, operation or maintenance of any nuclear facility.
  32.  */
  33. import java.io.IOException;
  34. import java.io.Serializable;
  35. import java.util.Enumeration;
  36. import java.util.Hashtable;
  37. import java.util.Vector;
  38. /**
  39.  * A class representing an Image File Directory (IFD) from a TIFF 6.0
  40.  * stream.  The TIFF file format is described in more detail in the
  41.  * comments for the TIFFDescriptor class.
  42.  *
  43.  * <p> A TIFF IFD consists of a set of TIFFField tags.  Methods are
  44.  * provided to query the set of tags and to obtain the raw field
  45.  * array.  In addition, convenience methods are provided for acquiring
  46.  * the values of tags that contain a single value that fits into a
  47.  * byte, int, long, float, or double.
  48.  *
  49.  * <p> Every TIFF file is made up of one or more public IFDs that are
  50.  * joined in a linked list, rooted in the file header.  A file may
  51.  * also contain so-called private IFDs that are referenced from
  52.  * tag data and do not appear in the main list.
  53.  *
  54.  * <p><b> This class is not a committed part of the JAI API.  It may
  55.  * be removed or changed in future releases of JAI.</b>
  56.  *
  57.  * @see javax.media.jai.operator.TIFFDescriptor
  58.  * @see TIFFField
  59.  */
  60. public class TIFFDirectory extends Object implements Serializable {
  61.     /** A boolean storing the endianness of the stream. */
  62.     boolean isBigEndian;
  63.     
  64.     /** The number of entries in the IFD. */
  65.     int numEntries;
  66.     /** An array of TIFFFields. */
  67.     TIFFField[] fields;
  68.     /** A Hashtable indexing the fields by tag number. */
  69.     Hashtable fieldIndex = new Hashtable();
  70.     /** The offset of this IFD. */
  71.     long IFDOffset = 8;
  72.     /** The offset of the next IFD. */
  73.     long nextIFDOffset = 0;
  74.     /** The default constructor. */
  75.     TIFFDirectory() {}
  76.     private static boolean isValidEndianTag(int endian) {
  77.         return ((endian == 0x4949) || (endian == 0x4d4d));
  78.     }
  79.     /**
  80.      * Constructs a TIFFDirectory from a SeekableStream.
  81.      * The directory parameter specifies which directory to read from
  82.      * the linked list present in the stream; directory 0 is normally
  83.      * read but it is possible to store multiple images in a single
  84.      * TIFF file by maintaing multiple directories.
  85.      *
  86.      * @param stream a SeekableStream to read from.
  87.      * @param directory the index of the directory to read.
  88.      */
  89.     public TIFFDirectory(SeekableStream stream, int directory)
  90.         throws IOException {
  91.         long global_save_offset = stream.getFilePointer();
  92.         long ifd_offset;
  93.         // Read the TIFF header
  94.         stream.seek(0L);
  95.         int endian = stream.readUnsignedShort();
  96.         if (!isValidEndianTag(endian)) {
  97.             throw new 
  98. IllegalArgumentException(JaiI18N.getString("TIFFDirectory1"));
  99.         }
  100.         isBigEndian = (endian == 0x4d4d);
  101.         int magic = readUnsignedShort(stream);
  102.         if (magic != 42) {
  103.             throw new 
  104. IllegalArgumentException(JaiI18N.getString("TIFFDirectory2"));
  105.         }
  106.         // Get the initial ifd offset as an unsigned int (using a long)
  107.         ifd_offset = readUnsignedInt(stream);
  108.         
  109.         for (int i = 0; i < directory; i++) {
  110.             if (ifd_offset == 0L) {
  111.                 throw new 
  112.    IllegalArgumentException(JaiI18N.getString("TIFFDirectory3"));
  113.             }
  114.             
  115.             stream.seek(ifd_offset);
  116.             int entries = readUnsignedShort(stream);
  117.             stream.skip(12*entries);
  118.             ifd_offset = readUnsignedInt(stream);
  119.         }
  120.         stream.seek(ifd_offset);
  121.         initialize(stream);
  122.         stream.seek(global_save_offset);
  123.     }
  124.     /**
  125.      * Constructs a TIFFDirectory by reading a SeekableStream.
  126.      * The ifd_offset parameter specifies the stream offset from which
  127.      * to begin reading; this mechanism is sometimes used to store
  128.      * private IFDs within a TIFF file that are not part of the normal
  129.      * sequence of IFDs.
  130.      *
  131.      * @param stream a SeekableStream to read from.
  132.      * @param ifd_offset the long byte offset of the directory.
  133.      * @param directory the index of the directory to read beyond the
  134.      *        one at the current stream offset; zero indicates the IFD
  135.      *        at the current offset.
  136.      */
  137.     public TIFFDirectory(SeekableStream stream, long ifd_offset, int directory)
  138.         throws IOException {
  139.         long global_save_offset = stream.getFilePointer();
  140.         stream.seek(0L);
  141.         int endian = stream.readUnsignedShort();
  142.         if (!isValidEndianTag(endian)) {
  143.             throw new 
  144. IllegalArgumentException(JaiI18N.getString("TIFFDirectory1"));
  145.         }
  146.         isBigEndian = (endian == 0x4d4d);
  147.         // Seek to the first IFD.
  148.         stream.seek(ifd_offset);
  149.         // Seek to desired IFD if necessary.
  150.         int dirNum = 0;
  151.         while(dirNum < directory) {
  152.             // Get the number of fields in the current IFD.
  153.             int numEntries = readUnsignedShort(stream);
  154.             // Skip to the next IFD offset value field.
  155.             stream.seek(ifd_offset + 12*numEntries);
  156.             // Read the offset to the next IFD beyond this one.
  157.             ifd_offset = readUnsignedInt(stream);
  158.             // Seek to the next IFD.
  159.             stream.seek(ifd_offset);
  160.             // Increment the directory.
  161.             dirNum++;
  162.         }
  163.         initialize(stream);
  164.         stream.seek(global_save_offset);
  165.     }
  166.     private static final int[] sizeOfType = {
  167.         0, //  0 = n/a
  168.         1, //  1 = byte
  169.         1, //  2 = ascii
  170.         2, //  3 = short
  171.         4, //  4 = long
  172.         8, //  5 = rational
  173.         1, //  6 = sbyte
  174.         1, //  7 = undefined
  175.         2, //  8 = sshort
  176.         4, //  9 = slong
  177.         8, // 10 = srational
  178.         4, // 11 = float
  179.         8  // 12 = double 
  180.     };
  181.     private void initialize(SeekableStream stream) throws IOException {
  182.         long nextTagOffset;
  183.         int i, j;
  184.         IFDOffset = stream.getFilePointer();
  185.         numEntries = readUnsignedShort(stream);
  186.         fields = new TIFFField[numEntries];
  187.         
  188.         for (i = 0; i < numEntries; i++) {
  189.             int tag = readUnsignedShort(stream);
  190.             int type = readUnsignedShort(stream);
  191.             int count = (int)(readUnsignedInt(stream));
  192.             int value = 0;
  193.     
  194.             // The place to return to to read the next tag
  195.             nextTagOffset = stream.getFilePointer() + 4;
  196.     try {
  197. // If the tag data can't fit in 4 bytes, the next 4 bytes
  198. // contain the starting offset of the data
  199. if (count*sizeOfType[type] > 4) {
  200.     value = (int)(readUnsignedInt(stream));
  201.     stream.seek(value);
  202. }
  203.     } catch (ArrayIndexOutOfBoundsException ae) {
  204. System.err.println(tag + " " + 
  205.    JaiI18N.getString("TIFFDirectory4"));
  206. // if the data type is unknown we should skip this TIFF Field
  207. stream.seek(nextTagOffset);
  208. continue;
  209.     }
  210.             fieldIndex.put(new Integer(tag), new Integer(i));
  211.             Object obj = null;
  212.             switch (type) {
  213.             case TIFFField.TIFF_BYTE:
  214.             case TIFFField.TIFF_SBYTE:
  215.             case TIFFField.TIFF_UNDEFINED:
  216.             case TIFFField.TIFF_ASCII:
  217.                 byte[] bvalues = new byte[count];
  218.                 stream.readFully(bvalues, 0, count);
  219. if (type == TIFFField.TIFF_ASCII) {
  220.     // Can be multiple strings
  221.     int index = 0, prevIndex = 0;
  222.     Vector v = new Vector();
  223.     while (index < count) {
  224.                         while ((index < count) && (bvalues[index++] != 0));
  225. // When we encountered zero, means one string has ended
  226. v.add(new String(bvalues, prevIndex, 
  227.  (index - prevIndex)) );
  228. prevIndex = index;
  229.     }
  230.     count = v.size();
  231.     String strings[] = new String[count];
  232.     for (int c = 0 ; c < count; c++) {
  233. strings[c] = (String)v.elementAt(c);
  234.     }
  235.     obj = strings;
  236. } else {
  237.     obj = bvalues;
  238. }
  239.                 break;
  240.             case TIFFField.TIFF_SHORT:
  241.                 char[] cvalues = new char[count];
  242.                 for (j = 0; j < count; j++) {
  243.     cvalues[j] = (char)(readUnsignedShort(stream));
  244.                 }
  245.                 obj = cvalues;
  246.                 break;
  247.                 
  248.             case TIFFField.TIFF_LONG:
  249.                 long[] lvalues = new long[count];
  250.                 for (j = 0; j < count; j++) {
  251.                     lvalues[j] = readUnsignedInt(stream);
  252.                 }
  253.                 obj = lvalues;
  254.                 break;
  255.                 
  256.             case TIFFField.TIFF_RATIONAL:
  257.                 long[][] llvalues = new long[count][2];
  258.                 for (j = 0; j < count; j++) {
  259.                     llvalues[j][0] = readUnsignedInt(stream);
  260.                     llvalues[j][1] = readUnsignedInt(stream);
  261.                 }
  262. obj = llvalues;
  263.                 break;
  264.                 
  265.             case TIFFField.TIFF_SSHORT:
  266.                 short[] svalues = new short[count];
  267.                 for (j = 0; j < count; j++) {
  268.     svalues[j] = readShort(stream);
  269.                 }
  270.                 obj = svalues;
  271.                 break;
  272.                 
  273.             case TIFFField.TIFF_SLONG:
  274.                 int[] ivalues = new int[count];
  275.                 for (j = 0; j < count; j++) {
  276.                     ivalues[j] = readInt(stream);
  277.                 }
  278.                 obj = ivalues;
  279.                 break;
  280.                 
  281.             case TIFFField.TIFF_SRATIONAL:
  282.                 int[][] iivalues = new int[count][2];
  283.                 for (j = 0; j < count; j++) {
  284.                     iivalues[j][0] = readInt(stream);
  285.                     iivalues[j][1] = readInt(stream);
  286.                 }
  287.                 obj = iivalues;
  288.                 break;
  289.             case TIFFField.TIFF_FLOAT:
  290.                 float[] fvalues = new float[count];
  291.                 for (j = 0; j < count; j++) {
  292.                     fvalues[j] = readFloat(stream);
  293.                 }
  294.                 obj = fvalues;
  295.                 break;
  296.             case TIFFField.TIFF_DOUBLE:
  297.                 double[] dvalues = new double[count];
  298.                 for (j = 0; j < count; j++) {
  299.                     dvalues[j] = readDouble(stream);
  300.                 }
  301.                 obj = dvalues;
  302.                 break;
  303.             default:
  304.                 System.err.println(JaiI18N.getString("TIFFDirectory0"));
  305.                 break;
  306.             }
  307.             fields[i] = new TIFFField(tag, type, count, obj);
  308.             stream.seek(nextTagOffset);
  309.         }
  310.         // Read the offset of the next IFD.
  311.         nextIFDOffset = readUnsignedInt(stream);
  312.     }
  313.     /** Returns the number of directory entries. */
  314.     public int getNumEntries() {
  315.         return numEntries;
  316.     }
  317.     /**
  318.      * Returns the value of a given tag as a TIFFField,
  319.      * or null if the tag is not present.
  320.      */
  321.     public TIFFField getField(int tag) {
  322.         Integer i = (Integer)fieldIndex.get(new Integer(tag));
  323.         if (i == null) {
  324.             return null;
  325.         } else {
  326.             return fields[i.intValue()];
  327.         }
  328.     }
  329.     /**
  330.      * Returns true if a tag appears in the directory. 
  331.      */
  332.     public boolean isTagPresent(int tag) {
  333.         return fieldIndex.containsKey(new Integer(tag));
  334.     }
  335.     /**
  336.      * Returns an ordered array of ints indicating the tag
  337.      * values.
  338.      */
  339.     public int[] getTags() {
  340.         int[] tags = new int[fieldIndex.size()];
  341.         Enumeration enum = fieldIndex.keys();
  342.         int i = 0;
  343.         while (enum.hasMoreElements()) {
  344.             tags[i++] = ((Integer)enum.nextElement()).intValue();
  345.         }
  346.         return tags;
  347.     }
  348.     /**
  349.      * Returns an array of TIFFFields containing all the fields
  350.      * in this directory.
  351.      */
  352.     public TIFFField[] getFields() {
  353.         return fields;
  354.     }
  355.     /**
  356.      * Returns the value of a particular index of a given tag as a
  357.      * byte.  The caller is responsible for ensuring that the tag is
  358.      * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
  359.      * TIFF_UNDEFINED.
  360.      */
  361.     public byte getFieldAsByte(int tag, int index) {
  362.         Integer i = (Integer)fieldIndex.get(new Integer(tag));
  363.         byte [] b = (fields[i.intValue()]).getAsBytes();
  364.         return b[index];
  365.     }
  366.     /**
  367.      * Returns the value of index 0 of a given tag as a
  368.      * byte.  The caller is responsible for ensuring that the tag is
  369.      * present and has  type TIFFField.TIFF_SBYTE, TIFF_BYTE, or
  370.      * TIFF_UNDEFINED.
  371.      */
  372.     public byte getFieldAsByte(int tag) {
  373.         return getFieldAsByte(tag, 0);
  374.     }
  375.     /**
  376.      * Returns the value of a particular index of a given tag as a
  377.      * long.  The caller is responsible for ensuring that the tag is
  378.      * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
  379.      * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
  380.      */
  381.     public long getFieldAsLong(int tag, int index) {
  382.         Integer i = (Integer)fieldIndex.get(new Integer(tag));
  383.         return (fields[i.intValue()]).getAsLong(index);
  384.     }
  385.     /**
  386.      * Returns the value of index 0 of a given tag as a
  387.      * long.  The caller is responsible for ensuring that the tag is
  388.      * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED,
  389.      * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG.
  390.      */
  391.     public long getFieldAsLong(int tag) {
  392.         return getFieldAsLong(tag, 0);
  393.     }
  394.     /**
  395.      * Returns the value of a particular index of a given tag as a
  396.      * float.  The caller is responsible for ensuring that the tag is
  397.      * present and has numeric type (all but TIFF_UNDEFINED and
  398.      * TIFF_ASCII).
  399.      */
  400.     public float getFieldAsFloat(int tag, int index) {
  401.         Integer i = (Integer)fieldIndex.get(new Integer(tag));
  402.         return fields[i.intValue()].getAsFloat(index);
  403.     }
  404.     /**
  405.      * Returns the value of index 0 of a given tag as a float.  The
  406.      * caller is responsible for ensuring that the tag is present and
  407.      * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
  408.      */
  409.     public float getFieldAsFloat(int tag) {
  410.         return getFieldAsFloat(tag, 0);
  411.     }
  412.     /**
  413.      * Returns the value of a particular index of a given tag as a
  414.      * double.  The caller is responsible for ensuring that the tag is
  415.      * present and has numeric type (all but TIFF_UNDEFINED and
  416.      * TIFF_ASCII).
  417.      */
  418.     public double getFieldAsDouble(int tag, int index) {
  419.         Integer i = (Integer)fieldIndex.get(new Integer(tag));
  420.         return fields[i.intValue()].getAsDouble(index);
  421.     }
  422.     /**
  423.      * Returns the value of index 0 of a given tag as a double.  The
  424.      * caller is responsible for ensuring that the tag is present and
  425.      * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII).
  426.      */
  427.     public double getFieldAsDouble(int tag) {
  428.         return getFieldAsDouble(tag, 0);
  429.     }
  430.     // Methods to read primitive data types from the stream
  431.     private short readShort(SeekableStream stream)
  432.         throws IOException {
  433.         if (isBigEndian) {
  434.             return stream.readShort();
  435.         } else {
  436.             return stream.readShortLE();
  437.         }
  438.     }
  439.     private int readUnsignedShort(SeekableStream stream)
  440.         throws IOException {
  441.         if (isBigEndian) {
  442.             return stream.readUnsignedShort();
  443.         } else {
  444.             return stream.readUnsignedShortLE();
  445.         }
  446.     }
  447.     private int readInt(SeekableStream stream) 
  448.         throws IOException {
  449.         if (isBigEndian) {
  450.             return stream.readInt();
  451.         } else {
  452.             return stream.readIntLE();
  453.         }
  454.     }
  455.     private long readUnsignedInt(SeekableStream stream) 
  456.         throws IOException {
  457.         if (isBigEndian) {
  458.             return stream.readUnsignedInt();
  459.         } else {
  460.             return stream.readUnsignedIntLE();
  461.         }
  462.     }
  463.     private long readLong(SeekableStream stream)
  464.         throws IOException {
  465.         if (isBigEndian) {
  466.             return stream.readLong();
  467.         } else {
  468.             return stream.readLongLE();
  469.         }
  470.     }
  471.     private float readFloat(SeekableStream stream)
  472.         throws IOException {
  473.         if (isBigEndian) {
  474.             return stream.readFloat();
  475.         } else {
  476.             return stream.readFloatLE();
  477.         }
  478.     }
  479.     private double readDouble(SeekableStream stream)
  480.         throws IOException {
  481.         if (isBigEndian) {
  482.             return stream.readDouble();
  483.         } else {
  484.             return stream.readDoubleLE();
  485.         }
  486.     }
  487.     private static int readUnsignedShort(SeekableStream stream,
  488.                                          boolean isBigEndian)
  489.         throws IOException {
  490.         if (isBigEndian) {
  491.             return stream.readUnsignedShort();
  492.         } else {
  493.             return stream.readUnsignedShortLE();
  494.         }
  495.     }
  496.     private static long readUnsignedInt(SeekableStream stream,
  497.                                         boolean isBigEndian) 
  498.         throws IOException {
  499.         if (isBigEndian) {
  500.             return stream.readUnsignedInt();
  501.         } else {
  502.             return stream.readUnsignedIntLE();
  503.         }
  504.     }
  505.     // Utilities
  506.     /**
  507.      * Returns the number of image directories (subimages) stored in a
  508.      * given TIFF file, represented by a <code>SeekableStream</code>.
  509.      */
  510.     public static int getNumDirectories(SeekableStream stream)
  511.         throws IOException{
  512.         long pointer = stream.getFilePointer(); // Save stream pointer
  513.         stream.seek(0L);
  514.         int endian = stream.readUnsignedShort();
  515.         if (!isValidEndianTag(endian)) {
  516.             throw new 
  517. IllegalArgumentException(JaiI18N.getString("TIFFDirectory1"));
  518.         }
  519.         boolean isBigEndian = (endian == 0x4d4d);
  520.         int magic = readUnsignedShort(stream, isBigEndian);
  521.         if (magic != 42) {
  522.             throw new 
  523. IllegalArgumentException(JaiI18N.getString("TIFFDirectory2"));
  524.         }
  525.         
  526.         stream.seek(4L);
  527.         long offset = readUnsignedInt(stream, isBigEndian);
  528.         int numDirectories = 0;
  529.         while (offset != 0L) {
  530.             ++numDirectories;
  531.             stream.seek(offset);
  532.             int entries = readUnsignedShort(stream, isBigEndian);
  533.             stream.skip(12*entries);
  534.             offset = readUnsignedInt(stream, isBigEndian);
  535.         }
  536.       
  537.         stream.seek(pointer); // Reset stream pointer
  538.         return numDirectories;
  539.     }
  540.     /**
  541.      * Returns a boolean indicating whether the byte order used in the
  542.      * the TIFF file is big-endian (i.e. whether the byte order is from  
  543.      * the most significant to the least significant)
  544.      */
  545.     public boolean isBigEndian() {
  546. return isBigEndian;
  547.     }
  548.     /**
  549.      * Returns the offset of the IFD corresponding to this
  550.      * <code>TIFFDirectory</code>.
  551.      */
  552.     public long getIFDOffset() {
  553.         return IFDOffset;
  554.     }
  555.     /**
  556.      * Returns the offset of the next IFD after the IFD corresponding to this
  557.      * <code>TIFFDirectory</code>.
  558.      */
  559.     public long getNextIFDOffset() {
  560.         return nextIFDOffset;
  561.     }
  562. }