Changeset 1212


Ignore:
Timestamp:
08/02/06 16:40:07 (14 years ago)
Author:
curtis
Message:

Add overwriteIFDValue method for surgically altering IFD directory entries.
Largely intended for fast updating of embedded OME-XML in ImageDescription tag.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/loci/formats/TiffTools.java

    r1195 r1212  
    4141  // -- Constants -- 
    4242 
    43   public static final boolean DEBUG = false; 
     43  public static final boolean DEBUG = true; 
    4444 
    4545  // non-IFD tags (for internal use) 
     
    5959  public static final int FLOAT = 11; 
    6060  public static final int DOUBLE = 12; 
     61 
     62  public static final int[] BYTES_PER_ELEMENT = { 
     63    -1, // invalid type 
     64    1, // BYTE 
     65    1, // ASCII 
     66    2, // SHORT 
     67    4, // LONG 
     68    8, // RATIONAL 
     69    1, // SBYTE 
     70    1, // UNDEFINED 
     71    2, // SSHORT 
     72    4, // SLONG 
     73    8, // SRATIONAL 
     74    4, // FLOAT 
     75    8, // DOUBLE 
     76  }; 
    6177 
    6278  // IFD tags 
     
    15031519 
    15041520 
     1521  // -- IFD writing methods -- 
     1522 
     1523  /** 
     1524   * Writes the given IFD value to the given output object. 
     1525   * @param ifdOut output object for writing IFD stream 
     1526   * @param extraBuf buffer to which "extra" IFD information should be written 
     1527   * @param extraOut data output wrapper for extraBuf (passed for efficiency) 
     1528   * @param offset global offset to use for IFD offset values 
     1529   * @param tag IFD tag to write 
     1530   * @param value IFD value to write 
     1531   */ 
     1532  public static void writeIFDValue(DataOutput ifdOut, 
     1533    ByteArrayOutputStream extraBuf, DataOutputStream extraOut, int offset, 
     1534    int tag, Object value) throws FormatException, IOException 
     1535  { 
     1536    // convert singleton objects into arrays, for simplicity 
     1537    if (value instanceof Short) { 
     1538      value = new short[] {((Short) value).shortValue()}; 
     1539    } 
     1540    else if (value instanceof Integer) { 
     1541      value = new int[] {((Integer) value).intValue()}; 
     1542    } 
     1543    else if (value instanceof Long) { 
     1544      value = new long[] {((Long) value).longValue()}; 
     1545    } 
     1546    else if (value instanceof TiffRational) { 
     1547      value = new TiffRational[] {(TiffRational) value}; 
     1548    } 
     1549    else if (value instanceof Float) { 
     1550      value = new float[] {((Float) value).floatValue()}; 
     1551    } 
     1552    else if (value instanceof Double) { 
     1553      value = new double[] {((Double) value).doubleValue()}; 
     1554    } 
     1555 
     1556    // write directory entry to output buffers 
     1557    ifdOut.writeShort(tag); // tag 
     1558    if (value instanceof short[]) { // BYTE 
     1559      short[] q = (short[]) value; 
     1560      ifdOut.writeShort(BYTE); // type 
     1561      ifdOut.writeInt(q.length); // count 
     1562      if (q.length <= 4) { 
     1563        for (int i=0; i<q.length; i++) ifdOut.writeByte(q[i]); // value(s) 
     1564        for (int i=q.length; i<4; i++) ifdOut.writeByte(0); // padding 
     1565      } 
     1566      else { 
     1567        ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1568        for (int i=0; i<q.length; i++) extraOut.writeByte(q[i]); // values 
     1569      } 
     1570    } 
     1571    else if (value instanceof String) { // ASCII 
     1572      char[] q = ((String) value).toCharArray(); 
     1573      ifdOut.writeShort(ASCII); // type 
     1574      ifdOut.writeInt(q.length + 1); // count 
     1575      if (q.length < 4) { 
     1576        for (int i=0; i<q.length; i++) ifdOut.writeByte(q[i]); // value(s) 
     1577        for (int i=q.length; i<4; i++) ifdOut.writeByte(0); // padding 
     1578      } 
     1579      else { 
     1580        ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1581        for (int i=0; i<q.length; i++) extraOut.writeByte(q[i]); // values 
     1582        extraOut.writeByte(0); // concluding NULL byte 
     1583      } 
     1584    } 
     1585    else if (value instanceof int[]) { // SHORT 
     1586      int[] q = (int[]) value; 
     1587      ifdOut.writeShort(SHORT); // type 
     1588      ifdOut.writeInt(q.length); // count 
     1589      if (q.length <= 2) { 
     1590        for (int i=0; i<q.length; i++) ifdOut.writeShort(q[i]); // value(s) 
     1591        for (int i=q.length; i<2; i++) ifdOut.writeShort(0); // padding 
     1592      } 
     1593      else { 
     1594        ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1595        for (int i=0; i<q.length; i++) extraOut.writeShort(q[i]); // values 
     1596      } 
     1597    } 
     1598    else if (value instanceof long[]) { // LONG 
     1599      long[] q = (long[]) value; 
     1600      ifdOut.writeShort(LONG); // type 
     1601      ifdOut.writeInt(q.length); // count 
     1602      if (q.length <= 1) { 
     1603        if (q.length == 1) ifdOut.writeInt((int) q[0]); // value 
     1604        else ifdOut.writeInt(0); // padding 
     1605      } 
     1606      else { 
     1607        ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1608        for (int i=0; i<q.length; i++) { 
     1609          extraOut.writeInt((int) q[i]); // values 
     1610        } 
     1611      } 
     1612    } 
     1613    else if (value instanceof TiffRational[]) { // RATIONAL 
     1614      TiffRational[] q = (TiffRational[]) value; 
     1615      ifdOut.writeShort(RATIONAL); // type 
     1616      ifdOut.writeInt(q.length); // count 
     1617      ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1618      for (int i=0; i<q.length; i++) { 
     1619        extraOut.writeInt((int) q[i].getNumerator()); // values 
     1620        extraOut.writeInt((int) q[i].getDenominator()); // values 
     1621      } 
     1622    } 
     1623    else if (value instanceof float[]) { // FLOAT 
     1624      float[] q = (float[]) value; 
     1625      ifdOut.writeShort(FLOAT); // type 
     1626      ifdOut.writeInt(q.length); // count 
     1627      if (q.length <= 1) { 
     1628        if (q.length == 1) ifdOut.writeFloat(q[0]); // value 
     1629        else ifdOut.writeInt(0); // padding 
     1630      } 
     1631      else { 
     1632        ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1633        for (int i=0; i<q.length; i++) extraOut.writeFloat(q[i]); // values 
     1634      } 
     1635    } 
     1636    else if (value instanceof double[]) { // DOUBLE 
     1637      double[] q = (double[]) value; 
     1638      ifdOut.writeShort(DOUBLE); // type 
     1639      ifdOut.writeInt(q.length); // count 
     1640      ifdOut.writeInt(offset + extraBuf.size()); // offset 
     1641      for (int i=0; i<q.length; i++) extraOut.writeDouble(q[i]); // values 
     1642    } 
     1643    else { 
     1644      throw new FormatException("Unknown IFD value type (" + 
     1645        value.getClass().getName() + ")"); 
     1646    } 
     1647  } 
     1648 
     1649  /** 
     1650   * Surgically overwrites an existing IFD value with the given one. This 
     1651   * method requires that the IFD directory entry already exist. It 
     1652   * intelligently updates the count field of the entry to match the new 
     1653   * length. If the new length is longer than the old length, it appends the 
     1654   * new data to the end of the file and updates the offset field; if not, or 
     1655   * if the old data is already at the end of the file, it overwrites the old 
     1656   * data in place. 
     1657   */ 
     1658  public static void overwriteIFDValue(RandomAccessFile raf, 
     1659    int ifd, int tag, Object value) throws FormatException, IOException 
     1660  { 
     1661    if (DEBUG) { 
     1662      debug("overwriteIFDValue (ifd=" + ifd + "; tag=" + tag + "; value=" + 
     1663        value + ")"); 
     1664    } 
     1665    byte[] header = new byte[4]; 
     1666    raf.seek(0); 
     1667    raf.readFully(header); 
     1668    if (!isValidHeader(header)) { 
     1669      throw new FormatException("Invalid TIFF header"); 
     1670    } 
     1671    boolean little = header[0] == LITTLE && header[1] == LITTLE; // II 
     1672    int offset = 4; // offset to the IFD 
     1673    short num = 0; // number of directory entries 
     1674 
     1675    // skip to the correct IFD 
     1676    for (int i=0; i<=ifd; i++) { 
     1677      offset = raf.readInt(); 
     1678      if (offset <= 0) { 
     1679        throw new FormatException("No such IFD (" + ifd + " of " + i + ")"); 
     1680      } 
     1681      raf.seek(offset); 
     1682      num = raf.readShort(); 
     1683      if (i < ifd) raf.seek(offset + 2 + 12 * num); 
     1684    } 
     1685 
     1686    // search directory entries for proper tag 
     1687    for (int i=0; i<num; i++) { 
     1688      int oldTag = DataTools.read2UnsignedBytes(raf, little); 
     1689      int oldType = DataTools.read2UnsignedBytes(raf, little); 
     1690      int oldCount = DataTools.read4SignedBytes(raf, little); 
     1691      int oldOffset = DataTools.read4SignedBytes(raf, little); 
     1692      if (oldTag == tag) { 
     1693        // write new value to buffers 
     1694        ByteArrayOutputStream ifdBuf = new ByteArrayOutputStream(14); 
     1695        DataOutputStream ifdOut = new DataOutputStream(ifdBuf); 
     1696        ByteArrayOutputStream extraBuf = new ByteArrayOutputStream(); 
     1697        DataOutputStream extraOut = new DataOutputStream(extraBuf); 
     1698        writeIFDValue(ifdOut, extraBuf, extraOut, oldOffset, tag, value); 
     1699        byte[] bytes = ifdBuf.toByteArray(); 
     1700        byte[] extra = extraBuf.toByteArray(); 
     1701 
     1702        // extract new directory entry parameters 
     1703        int newTag = DataTools.bytesToInt(bytes, 0, 2, little); 
     1704        int newType = DataTools.bytesToInt(bytes, 2, 2, little); 
     1705        int newCount = DataTools.bytesToInt(bytes, 4, little); 
     1706        int newOffset = DataTools.bytesToInt(bytes, 8, little); 
     1707 
     1708        // determine the best way to overwrite the old entry 
     1709        if (extra.length == 0) { 
     1710          // new entry is inline; if old entry wasn't, old data is orphaned 
     1711          // do not override new offset value since data is inline 
     1712          if (DEBUG) debug("overwriteIFDValue: new entry is inline"); 
     1713        } 
     1714        else if (newCount <= oldCount) { 
     1715          // new entry is as small or smaller than old entry; overwrite it 
     1716          newOffset = oldOffset; 
     1717          if (DEBUG) debug("overwriteIFDValue: new entry is <= old entry"); 
     1718        } 
     1719        else if (oldOffset + BYTES_PER_ELEMENT[oldTag] == raf.length()) { 
     1720          // old entry was already at EOF; overwrite it 
     1721          newOffset = oldOffset; 
     1722          if (DEBUG) debug("overwriteIFDValue: old entry is at EOF"); 
     1723        } 
     1724        else { 
     1725          // old entry was elsewhere; append to EOF, orphaning old entry 
     1726          newOffset = (int) raf.length(); 
     1727          if (DEBUG) debug("overwriteIFDValue: old entry will be orphaned"); 
     1728        } 
     1729 
     1730        // overwrite old entry 
     1731        raf.seek(raf.getFilePointer() - 10); // jump back 
     1732        DataTools.writeShort(raf, newType, little); 
     1733        DataTools.writeInt(raf, newCount, little); 
     1734        DataTools.writeInt(raf, newOffset, little); 
     1735        if (extra.length > 0) { 
     1736          raf.seek(newOffset); 
     1737          raf.write(extra); 
     1738        } 
     1739      } 
     1740    } 
     1741 
     1742    throw new FormatException("Tag not found (" + getIFDTagName(tag) + ")"); 
     1743  } 
     1744 
     1745 
    15051746  // -- Image writing methods -- 
    15061747 
     
    16981939        debug("writeImage: writing " + sk + " (value=" + sv + ")"); 
    16991940      } 
    1700  
    1701       // convert singleton objects into arrays, for simplicity 
    1702       if (value instanceof Short) { 
    1703         value = new short[] {((Short) value).shortValue()}; 
    1704       } 
    1705       else if (value instanceof Integer) { 
    1706         value = new int[] {((Integer) value).intValue()}; 
    1707       } 
    1708       else if (value instanceof Long) { 
    1709         value = new long[] {((Long) value).longValue()}; 
    1710       } 
    1711       else if (value instanceof TiffRational) { 
    1712         value = new TiffRational[] {(TiffRational) value}; 
    1713       } 
    1714       else if (value instanceof Float) { 
    1715         value = new float[] {((Float) value).floatValue()}; 
    1716       } 
    1717       else if (value instanceof Double) { 
    1718         value = new double[] {((Double) value).doubleValue()}; 
    1719       } 
    1720  
    1721       // write directory entry to output buffers 
    1722       ifdOut.writeShort(((Integer) key).intValue()); // tag 
    1723       if (value instanceof short[]) { // BYTE 
    1724         short[] q = (short[]) value; 
    1725         ifdOut.writeShort(BYTE); // type 
    1726         ifdOut.writeInt(q.length); // count 
    1727         if (q.length <= 4) { 
    1728           for (int i=0; i<q.length; i++) ifdOut.writeByte(q[i]); // value(s) 
    1729           for (int i=q.length; i<4; i++) ifdOut.writeByte(0); // padding 
    1730         } 
    1731         else { 
    1732           ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1733           for (int i=0; i<q.length; i++) extraOut.writeByte(q[i]); // values 
    1734         } 
    1735       } 
    1736       else if (value instanceof String) { // ASCII 
    1737         char[] q = ((String) value).toCharArray(); 
    1738         ifdOut.writeShort(ASCII); // type 
    1739         ifdOut.writeInt(q.length + 1); // count 
    1740         if (q.length < 4) { 
    1741           for (int i=0; i<q.length; i++) ifdOut.writeByte(q[i]); // value(s) 
    1742           for (int i=q.length; i<4; i++) ifdOut.writeByte(0); // padding 
    1743         } 
    1744         else { 
    1745           ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1746           for (int i=0; i<q.length; i++) extraOut.writeByte(q[i]); // values 
    1747           extraOut.writeByte(0); // concluding NULL byte 
    1748         } 
    1749       } 
    1750       else if (value instanceof int[]) { // SHORT 
    1751         int[] q = (int[]) value; 
    1752         ifdOut.writeShort(SHORT); // type 
    1753         ifdOut.writeInt(q.length); // count 
    1754         if (q.length <= 2) { 
    1755           for (int i=0; i<q.length; i++) ifdOut.writeShort(q[i]); // value(s) 
    1756           for (int i=q.length; i<2; i++) ifdOut.writeShort(0); // padding 
    1757         } 
    1758         else { 
    1759           ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1760           for (int i=0; i<q.length; i++) extraOut.writeShort(q[i]); // values 
    1761         } 
    1762       } 
    1763       else if (value instanceof long[]) { // LONG 
    1764         long[] q = (long[]) value; 
    1765         ifdOut.writeShort(LONG); // type 
    1766         ifdOut.writeInt(q.length); // count 
    1767         if (q.length <= 1) { 
    1768           if (q.length == 1) ifdOut.writeInt((int) q[0]); // value 
    1769           else ifdOut.writeInt(0); // padding 
    1770         } 
    1771         else { 
    1772           ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1773           for (int i=0; i<q.length; i++) { 
    1774             extraOut.writeInt((int) q[i]); // values 
    1775           } 
    1776         } 
    1777       } 
    1778       else if (value instanceof TiffRational[]) { // RATIONAL 
    1779         TiffRational[] q = (TiffRational[]) value; 
    1780         ifdOut.writeShort(RATIONAL); // type 
    1781         ifdOut.writeInt(q.length); // count 
    1782         ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1783         for (int i=0; i<q.length; i++) { 
    1784           extraOut.writeInt((int) q[i].getNumerator()); // values 
    1785           extraOut.writeInt((int) q[i].getDenominator()); // values 
    1786         } 
    1787       } 
    1788       else if (value instanceof float[]) { // FLOAT 
    1789         float[] q = (float[]) value; 
    1790         ifdOut.writeShort(FLOAT); // type 
    1791         ifdOut.writeInt(q.length); // count 
    1792         if (q.length <= 1) { 
    1793           if (q.length == 1) ifdOut.writeFloat(q[0]); // value 
    1794           else ifdOut.writeInt(0); // padding 
    1795         } 
    1796         else { 
    1797           ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1798           for (int i=0; i<q.length; i++) extraOut.writeFloat(q[i]); // values 
    1799         } 
    1800       } 
    1801       else if (value instanceof double[]) { // DOUBLE 
    1802         double[] q = (double[]) value; 
    1803         ifdOut.writeShort(DOUBLE); // type 
    1804         ifdOut.writeInt(q.length); // count 
    1805         ifdOut.writeInt(offset + extraBuf.size()); // offset 
    1806         for (int i=0; i<q.length; i++) extraOut.writeDouble(q[i]); // values 
    1807       } 
    1808       else { 
    1809         throw new FormatException("Unknown IFD value type (" + 
    1810           value.getClass().getName() + ")"); 
    1811       } 
     1941      writeIFDValue(ifdOut, extraBuf, extraOut, offset, 
     1942        ((Integer) key).intValue(), value); 
    18121943    } 
    18131944    ifdOut.writeInt(last ? 0 : offset + extraBuf.size()); // offset to next IFD 
Note: See TracChangeset for help on using the changeset viewer.