Changeset 5773 for branches/4.1


Ignore:
Timestamp:
01/07/10 11:29:03 (10 years ago)
Author:
melissa
Message:

Ported Flex/TIFF efficiency changes to 4.1 branch.

Location:
branches/4.1/components
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • branches/4.1/components/bio-formats/src/loci/formats/in/FlexReader.java

    r5720 r5773  
    3131 
    3232import loci.common.DataTools; 
     33import loci.common.FileHandle; 
    3334import loci.common.Location; 
    3435import loci.common.LogTools; 
     
    4445import loci.formats.tiff.IFD; 
    4546import loci.formats.tiff.IFDList; 
     47import loci.formats.tiff.TiffCompression; 
     48import loci.formats.tiff.TiffConstants; 
    4649import loci.formats.tiff.TiffParser; 
    4750 
     
    113116  private String plateName, plateBarcode; 
    114117  private int nRows = 0, nCols = 0; 
     118  private RandomAccessInputStream firstStream; 
    115119 
    116120  /** 
     
    121125 
    122126  private IFDList[][] ifds; 
     127  private long[][][] offsets; 
    123128 
    124129  /** Specifies the row and column index into 'flexFiles' for a given well. */ 
     
    173178    int[] pos = FormatTools.rasterToPosition(lengths, getSeries()); 
    174179 
    175     int imageNumber = getImageCount() * pos[0] + no; 
    176  
    177180    int wellRow = wellNumber[pos[1]][0]; 
    178181    int wellCol = wellNumber[pos[1]][1]; 
     
    182185    } 
    183186 
    184     IFD ifd = ifds[wellRow][wellCol].get(imageNumber); 
    185     RandomAccessInputStream s = 
    186       new RandomAccessInputStream(flexFiles[wellRow][wellCol]); 
    187     TiffParser tp = new TiffParser(s); 
     187    int imageNumber = offsets[wellRow][wellCol] == null ? 
     188      getImageCount() * pos[0] + no : 0; 
     189    IFD ifd = offsets[wellRow][wellCol] == null ? 
     190      ifds[wellRow][wellCol].get(imageNumber) : ifds[0][0].get(0); 
     191 
     192    RandomAccessInputStream s = (wellRow == 0 && wellCol == 0) ? firstStream : 
     193      new RandomAccessInputStream( 
     194        new FileHandle(flexFiles[wellRow][wellCol], "r")); 
    188195 
    189196    int nBytes = ifd.getBitsPerSample()[0] / 8; 
     197    int bpp = FormatTools.getBytesPerPixel(getPixelType()); 
     198    int planeSize = getSizeX() * getSizeY() * getRGBChannelCount() * nBytes; 
     199    double factor = 1d; 
     200 
     201    // read pixels from the file 
     202    if (ifd.getCompression() != TiffCompression.UNCOMPRESSED || nBytes != bpp || 
     203      offsets[wellRow][wellCol] == null) 
     204    { 
     205      TiffParser tp = new TiffParser(s); 
     206      tp.getSamples(ifd, buf, x, y, w, h); 
     207      factor = factors[wellRow][wellCol][imageNumber]; 
     208    } 
     209    else { 
     210      int index = getImageCount() * pos[0] + no; 
     211      long offset = index == offsets[wellRow][wellCol].length - 1 ? 
     212        s.length() : offsets[wellRow][wellCol][index + 1]; 
     213      s.seek(offset - planeSize); 
     214      readPlane(s, x, y, w, h, buf); 
     215      factor = factors[0][0][index]; 
     216    } 
     217    if (wellRow != 0 || wellCol != 0) s.close(); 
    190218 
    191219    // expand pixel values with multiplication by factor[no] 
    192     byte[] bytes = tp.getSamples(ifd, buf, x, y, w, h); 
    193     s.close(); 
    194  
    195     int bpp = FormatTools.getBytesPerPixel(getPixelType()); 
    196     int num = bytes.length / bpp; 
    197  
    198     double factor = factors[wellRow][wellCol][imageNumber]; 
     220    int num = buf.length / bpp; 
    199221 
    200222    if (factor != 1d || nBytes != bpp) { 
    201223      for (int i=num-1; i>=0; i--) { 
    202         int q = nBytes == 1 ? bytes[i] & 0xff : 
    203           DataTools.bytesToInt(bytes, i * bpp, bpp, isLittleEndian()); 
    204         q = (int) (q * factor); 
    205         DataTools.unpackBytes(q, buf, i * bpp, bpp, isLittleEndian()); 
    206       } 
    207     } 
    208     else { 
    209       System.arraycopy(bytes, 0, buf, 0, bytes.length); 
     224        int q = nBytes == 1 ? buf[i] & 0xff : 
     225          DataTools.bytesToInt(buf, i * bpp, bpp, isLittleEndian()); 
     226        if (q != 0) { 
     227          q = (int) (q * factor); 
     228          DataTools.unpackBytes(q, buf, i * bpp, bpp, isLittleEndian()); 
     229        } 
     230      } 
    210231    } 
    211232 
     
    235256      flexFiles = null; 
    236257      ifds = null; 
     258      offsets = null; 
    237259      wellNumber = null; 
     260      if (firstStream != null) firstStream.close(); 
     261      firstStream = null; 
    238262    } 
    239263  } 
     
    512536 
    513537  /** 
    514    * Returns the IFDs of the first well that has data. May not be 
     538   * Returns the number of planes in the first well that has data. May not be 
    515539   * <code>[0][0]</code> as the acquisition may have been column or row offset. 
    516    * @return List of the first well's IFDs. 
    517540   */ 
    518   private IFDList firstWellIfds() { 
     541  private int firstWellPlanes() { 
     542    for (int i=0; i<offsets.length; i++) { 
     543      for (int j=0; j<offsets[i].length; j++) { 
     544        if (offsets[i][j] != null) { 
     545          return offsets[i][j].length; 
     546        } 
     547      } 
     548    } 
     549 
    519550    for (int i = 0; i < ifds.length; i++) { 
    520551      for (int j = 0; j < ifds[i].length; j++) { 
    521         if (ifds[i][j] != null) return ifds[i][j]; 
    522       } 
    523     } 
    524     return null; 
     552        if (ifds[i][j] != null) { 
     553          return ifds[i][j].size(); 
     554        } 
     555      } 
     556    } 
     557    return 0; 
    525558  } 
    526559 
     
    534567    throws FormatException, IOException 
    535568  { 
    536     status("Parsing .flex file (well " + (wellRow + 'A') + (wellCol + 1) + ")"); 
     569    status("Parsing .flex file (well " + 
     570      ((char) (wellRow + 'A')) + (wellCol + 1) + ")"); 
    537571    debug("Parsing .flex file associated with well row " + wellRow + 
    538572      ", column " + wellCol); 
     
    561595    // parse factors from XML 
    562596    debug("Parsing XML from " + flexFiles[wellRow][wellCol]); 
     597 
     598    int nOffsets = offsets[wellRow][wellCol] != null ? 
     599      offsets[wellRow][wellCol].length : 0; 
     600 
     601    int oldWellRow = wellRow; 
     602    int oldWellCol = wellCol; 
     603 
     604    if (wellRow >= ifds.length || wellCol >= ifds[wellRow].length) { 
     605      wellRow = 0; 
     606      wellCol = 0; 
     607    } 
    563608    IFD ifd = ifds[wellRow][wellCol].get(0); 
    564609    String xml = XMLTools.sanitizeXML(ifd.getIFDStringValue(FLEX, true)); 
     
    571616    XMLTools.parseXML(xml.getBytes(), handler); 
    572617 
    573     if (firstFile) populateCoreMetadata(wellRow, wellCol, n); 
     618    if (firstFile) populateCoreMetadata(oldWellRow, oldWellCol, n); 
    574619 
    575620    int totalPlanes = getSeriesCount() * getImageCount(); 
     
    627672    throws FormatException 
    628673  { 
    629     status("Populating core metadata"); 
    630     debug("Populating core metadata for well row " + wellRow + ", column " + 
    631       wellCol); 
     674    status("Populating core metadata for well row " + wellRow + 
     675      ", column " + wellCol); 
    632676    if (getSizeC() == 0 && getSizeT() == 0) { 
    633677      Vector<String> uniqueChannels = new Vector<String>(); 
     
    664708    // of reported images 
    665709 
    666     IFDList ifdList = ifds[wellRow][wellCol]; 
     710    IFDList ifdList = wellRow < ifds.length && wellCol < ifds[wellRow].length ? 
     711      ifds[wellRow][wellCol] : ifds[0][0]; 
    667712    IFD ifd = ifdList.get(0); 
     713    int nPlanes = ifdList.size(); 
     714    if (offsets[wellRow][wellCol] != null) { 
     715      nPlanes = offsets[wellRow][wellCol].length; 
     716    } 
    668717 
    669718    core[0].imageCount = getSizeZ() * getSizeC() * getSizeT(); 
    670     if (getImageCount() * fieldCount != ifdList.size()) { 
    671       core[0].imageCount = ifdList.size() / fieldCount; 
     719    if (getImageCount() * fieldCount != nPlanes) { 
     720      core[0].imageCount = nPlanes / fieldCount; 
    672721      core[0].sizeZ = 1; 
    673722      core[0].sizeC = 1; 
    674       core[0].sizeT = ifdList.size() / fieldCount; 
     723      core[0].sizeT = nPlanes / fieldCount; 
    675724    } 
    676725    core[0].sizeX = (int) ifd.getImageWidth(); 
     
    910959 
    911960    flexFiles = new String[nRows][nCols]; 
    912     ifds = new IFDList[nRows][nCols]; 
    913     factors = new double[nRows][nCols][]; 
     961    offsets = new long[nRows][nCols][]; 
    914962    wellCount = v.size(); 
    915963    wellNumber = new int[wellCount][2]; 
     
    917965    RandomAccessInputStream s = null; 
    918966    boolean firstFile = true; 
     967    boolean compressed = false; 
     968    int nOffsets = 1; 
    919969 
    920970    int currentWell = 0; 
     
    922972      for (int col=0; col<nCols; col++) { 
    923973        flexFiles[row][col] = v.get(row + "," + col); 
    924         if (flexFiles[row][col] == null) continue; 
     974        if (flexFiles[row][col] == null) { 
     975          continue; 
     976        } 
    925977 
    926978        wellNumber[currentWell][0] = row; 
    927979        wellNumber[currentWell][1] = col; 
    928         s = new RandomAccessInputStream(flexFiles[row][col]); 
    929         status("Parsing IFDs for well " + (row + 'A') + (col + 1)); 
     980 
     981        s = new RandomAccessInputStream( 
     982          new FileHandle(flexFiles[row][col], "r")); 
     983        if (currentWell == 0) firstStream = s; 
    930984        TiffParser tp = new TiffParser(s); 
    931         ifds[row][col] = tp.getIFDs(); 
    932         s.close(); 
    933  
    934         parseFlexFile(currentWell, row, col, firstFile, store); 
     985 
     986        if (compressed || firstFile) { 
     987          status("Parsing IFDs for well " + ((char) (row + 'A')) + (col + 1)); 
     988          IFD firstIFD = tp.getFirstIFD(); 
     989          compressed = 
     990            firstIFD.getCompression() != TiffCompression.UNCOMPRESSED; 
     991 
     992          if (compressed) { 
     993            if (ifds == null) { 
     994              ifds = new IFDList[nRows][nCols]; 
     995              factors = new double[nRows][nCols][]; 
     996            } 
     997            ifds[row][col] = tp.getIFDs(false, false); 
     998            ifds[row][col].set(0, firstIFD); 
     999            parseFlexFile(currentWell, row, col, firstFile, store); 
     1000          } 
     1001          else { 
     1002            // if the pixel data is uncompressed, we can assume that 
     1003            // the pixel data for image #0 is located immediately before 
     1004            // IFD #1; as a result, we only need to parse the first IFD 
     1005            if (ifds == null) { 
     1006              ifds = new IFDList[1][1]; 
     1007              factors = new double[1][1][]; 
     1008            } 
     1009            offsets[row][col] = tp.getIFDOffsets(); 
     1010            nOffsets = offsets[row][col].length; 
     1011            ifds[0][0] = new IFDList(); 
     1012            ifds[0][0].add(firstIFD); 
     1013            parseFlexFile(currentWell, row, col, firstFile, store); 
     1014          } 
     1015        } 
     1016        else { 
     1017          // retrieve the offsets to each IFD, instead of parsing 
     1018          // all of the IFDs 
     1019          status("Retrieving IFD offsets for well " + 
     1020            ((char) (row + 'A')) + (col + 1)); 
     1021          offsets[row][col] = new long[nOffsets]; 
     1022 
     1023          // Assume that all IFDs after the first are evenly spaced. 
     1024          // TiffParser.getIFDOffsets() could be used instead, but is 
     1025          // substantially slower. 
     1026          tp.checkHeader(); 
     1027          offsets[row][col][0] = tp.getFirstOffset(); 
     1028          if (offsets[row][col].length > 1) { 
     1029            s.seek(offsets[row][col][0]); 
     1030            s.skipBytes(s.readShort() * TiffConstants.BYTES_PER_ENTRY); 
     1031            offsets[row][col][1] = s.readInt(); 
     1032            int size = FormatTools.getPlaneSize(this) + 174; 
     1033            for (int i=2; i<offsets[row][col].length; i++) { 
     1034              offsets[row][col][i] =  offsets[row][col][i - 1] + size; 
     1035            } 
     1036          } 
     1037        } 
     1038        if (currentWell != 0) s.close(); 
    9351039        if (firstFile) firstFile = false; 
    9361040        currentWell++; 
     
    10321136      else if ("Image".equals(parentQName)) { 
    10331137        if (fieldCount == 0) fieldCount = 1; 
    1034         int nImages = firstWellIfds().size() / fieldCount; 
     1138        int nImages = firstWellPlanes() / fieldCount; 
    10351139        if (nImages == 0) nImages = 1; // probably a manually altered dataset 
    10361140        int currentSeries = (nextImage - 1) / nImages; 
     
    11581262        parentQName = qName; 
    11591263        int fieldNo = Integer.parseInt(attributes.getValue("No")); 
    1160         if (fieldNo > fieldCount && fieldCount < firstWellIfds().size()) { 
     1264        if (fieldNo > fieldCount && fieldCount < firstWellPlanes()) { 
    11611265          fieldCount++; 
    11621266        } 
  • branches/4.1/components/bio-formats/src/loci/formats/tiff/IFD.java

    r5334 r5773  
    4848 
    4949  // -- Constants -- 
     50 
     51  // TODO: Investigate using Java 1.5 enums instead of int enumerations. 
     52  //       http://javahowto.blogspot.com/2008/04/java-enum-examples.html 
    5053 
    5154  // non-IFD tags (for internal use) 
     
    322325  } 
    323326 
     327  /** Gets the given directory entry value as a string (regardless of type). */ 
     328  public String getIFDTextValue(int tag) { 
     329    String value = null; 
     330    Object o = getIFDValue(tag); 
     331    if (o instanceof String[]) { 
     332      StringBuilder sb = new StringBuilder(); 
     333      String[] s = (String[]) o; 
     334      for (int i=0; i<s.length; i++) { 
     335        sb.append(s[i]); 
     336        if (i < s.length - 1) sb.append("\n"); 
     337      } 
     338      value = sb.toString(); 
     339    } 
     340    else if (o instanceof short[]) { 
     341      StringBuffer sb = new StringBuffer(); 
     342      for (short s : ((short[]) o)) { 
     343        if (!Character.isISOControl((char) s)) { 
     344          sb.append((char) s); 
     345        } 
     346        else sb.append("\n"); 
     347      } 
     348      value = sb.toString(); 
     349    } 
     350    else if (o != null) value = o.toString(); 
     351 
     352    // sanitize line feeds 
     353    if (value != null) { 
     354      value = value.replaceAll("\r\n", "\n"); // CR-LF to LF 
     355      value = value.replaceAll("\r", "\n"); // CR to LF 
     356    } 
     357 
     358    return value; 
     359  } 
     360 
    324361  /** 
    325362   * Gets the given directory entry values in long format 
     
    425462  /** Convenience method for obtaining the ImageDescription from this IFD. */ 
    426463  public String getComment() { 
    427     // extract comment 
    428     Object o = getIFDValue(IMAGE_DESCRIPTION); 
    429     String comment = null; 
    430     if (o instanceof String) comment = (String) o; 
    431     else if (o instanceof String[]) { 
    432       String[] s = (String[]) o; 
    433       if (s.length > 0) comment = s[0]; 
    434     } 
    435     else if (o != null) comment = o.toString(); 
    436  
    437     if (comment != null) { 
    438       // sanitize line feeds 
    439       comment = comment.replaceAll("\r\n", "\n"); // CR-LF to LF 
    440       comment = comment.replaceAll("\r", "\n"); // CR to LF 
    441     } 
    442     return comment; 
     464    return getIFDTextValue(IMAGE_DESCRIPTION); 
    443465  } 
    444466 
     
    559581 
    560582    while (bps % 8 != 0) bps++; 
    561     if (bps == 24) bps = 32; 
    562  
    563     if (bitFormat == 3) return FormatTools.FLOAT; 
     583    if (bps == 24 && bitFormat != 3) bps = 32; 
     584 
    564585    switch (bps) { 
    565586      case 16: 
     587        if (bitFormat == 3) return FormatTools.FLOAT; 
    566588        return bitFormat == 2 ? FormatTools.INT16 : FormatTools.UINT16; 
     589      case 24: 
     590        return FormatTools.DOUBLE; 
    567591      case 32: 
     592        if (bitFormat == 3) return FormatTools.FLOAT; 
    568593        return bitFormat == 2 ? FormatTools.INT32 : FormatTools.UINT32; 
    569594      default: 
     
    814839  /** Prints the contents of this IFD. */ 
    815840  public void printIFD() { 
     841    if (!LogTools.isDebug()) return; 
    816842    StringBuffer sb = new StringBuffer(); 
    817843    sb.append("IFD directory entry values:"); 
  • branches/4.1/components/bio-formats/src/loci/formats/tiff/TiffIFDEntry.java

    r5272 r5773  
    3535 * @author Chris Allan callan at blackcat.ca 
    3636 */ 
    37 public class TiffIFDEntry { 
     37public class TiffIFDEntry implements Comparable { 
    3838 
    3939  /** The <i>Tag</i> that identifies the field. */ 
     
    8383  public long getValueOffset() { return valueOffset; } 
    8484 
     85  public String toString() { 
     86    return "tag = " + tag + ", type = " + type + ", count = " + valueCount + 
     87      ", offset = " + valueOffset; 
     88  } 
     89 
     90  // -- Comparable API methods -- 
     91 
     92  public int compareTo(Object o) { 
     93    if (!(o instanceof TiffIFDEntry)) return 1; 
     94    long offset = ((TiffIFDEntry) o).getValueOffset(); 
     95 
     96    if (offset == getValueOffset()) return 0; 
     97    return offset < getValueOffset() ? 1 : -1; 
     98  } 
     99 
    85100} 
  • branches/4.1/components/bio-formats/src/loci/formats/tiff/TiffParser.java

    r5757 r5773  
    2525 
    2626import java.io.IOException; 
     27import java.util.Arrays; 
     28import java.util.Vector; 
    2729 
    2830import loci.common.DataTools; 
     
    128130   */ 
    129131  public IFDList getIFDs(boolean skipThumbnails) throws IOException { 
     132    return getIFDs(skipThumbnails, true); 
     133  } 
     134 
     135  /** 
     136   * Gets all IFDs within the TIFF file, or null 
     137   * if the input source is not a valid TIFF file. 
     138   * If 'skipThumbnails' is set to true, thumbnail IFDs will not be returned. 
     139   * If 'fillInEntries' is set to true, IFD entry values that are stored at 
     140   * an arbitrary offset will be read. 
     141   */ 
     142  public IFDList getIFDs(boolean skipThumbnails, boolean fillInEntries) 
     143    throws IOException 
     144  { 
    130145    // check TIFF header 
    131146    Boolean result = checkHeader(); 
     
    145160    IFDList ifds = new IFDList(); 
    146161    for (long ifdNum=0; ifdNum<ifdMax; ifdNum++) { 
    147       IFD ifd = getIFD(ifdNum, offset, bigTiff); 
     162      IFD ifd = getIFD(ifdNum, offset, bigTiff, fillInEntries); 
    148163      if (ifd == null || ifd.size() <= 2) break; 
    149164      Number subfile = (Number) ifd.getIFDValue(IFD.NEW_SUBFILE_TYPE); 
     
    165180  } 
    166181 
     182  /** Gets the offsets to every IFD in the file. */ 
     183  public long[] getIFDOffsets() throws IOException { 
     184    // check TIFF header 
     185    Boolean result = checkHeader(); 
     186    if (result == null) return null; 
     187 
     188    in.seek(2); 
     189    boolean bigTiff = in.readShort() == TiffConstants.BIG_TIFF_MAGIC_NUMBER; 
     190    int bytesPerEntry = bigTiff ? TiffConstants.BIG_TIFF_BYTES_PER_ENTRY : 
     191      TiffConstants.BYTES_PER_ENTRY; 
     192 
     193    Vector<Long> offsets = new Vector<Long>(); 
     194    long offset = getFirstOffset(bigTiff); 
     195    while (true) { 
     196      in.seek(offset); 
     197      offsets.add(offset); 
     198      int nEntries = in.readShort(); 
     199      in.skipBytes(nEntries * bytesPerEntry); 
     200      offset = getNextOffset(bigTiff, offset); 
     201      if (offset <= 0 || offset >= in.length()) break; 
     202    } 
     203 
     204    long[] f = new long[offsets.size()]; 
     205    for (int i=0; i<f.length; i++) { 
     206      f[i] = offsets.get(i).longValue(); 
     207    } 
     208 
     209    return f; 
     210  } 
     211 
    167212  /** 
    168213   * Gets the first IFD within the TIFF file, or null 
     
    232277 
    233278      // Parse the entry's "ValueOffset" 
    234       long valueOffset = getNextOffset(bigTiff, offset); 
     279      long valueOffset = getNextOffset(bigTiff, 0); 
    235280 
    236281      return new TiffIFDEntry(entryTag, entryType, valueCount, valueOffset); 
     
    266311  public IFD getIFD(long ifdNum, long offset, boolean bigTiff) 
    267312    throws IOException 
     313  { 
     314    return getIFD(ifdNum, offset, bigTiff, true); 
     315  } 
     316 
     317  /** 
     318   * Gets the IFD stored at the given offset. 
     319   * If 'fillInEntries' is set to true, IFD entry values that are stored at 
     320   * an arbitrary offset will be read. 
     321   */ 
     322  public IFD getIFD(long ifdNum, long offset, boolean bigTiff, 
     323    boolean fillInEntries) throws IOException 
    268324  { 
    269325    IFD ifd = new IFD(); 
     
    303359      Object value = null; 
    304360 
     361      long pointer = in.getFilePointer(); 
     362 
    305363      if (count > threshhold / bpe) { 
    306         long pointer = getNextOffset(bigTiff, 0); 
    307         LogTools.debug("getIFDs: seeking to offset: " + pointer); 
    308         in.seek(pointer); 
    309       } 
     364        pointer = getNextOffset(bigTiff, 0); 
     365      } 
     366 
    310367      long inputLen = in.length(); 
    311       long inputPointer = in.getFilePointer(); 
    312       if (count * bpe + inputPointer > inputLen) { 
     368      if (count * bpe + pointer > inputLen) { 
    313369        int oldCount = count; 
    314         count = (int) ((inputLen - inputPointer) / bpe); 
     370        count = (int) ((inputLen - pointer) / bpe); 
    315371        LogTools.debug("getIFDs: truncated " + (oldCount - count) + 
    316372          " array elements for tag " + tag); 
    317373      } 
    318  
    319374      if (count < 0 || count > in.length()) break; 
    320375 
    321       if (type == IFD.BYTE) { 
    322         // 8-bit unsigned integer 
    323         if (count == 1) value = new Short(in.readByte()); 
    324         else { 
    325           byte[] bytes = new byte[count]; 
    326           in.readFully(bytes); 
    327           // bytes are unsigned, so use shorts 
    328           short[] shorts = new short[count]; 
    329           for (int j=0; j<count; j++) shorts[j] = (short) (bytes[j] & 0xff); 
    330           value = shorts; 
    331         } 
    332       } 
    333       else if (type == IFD.ASCII) { 
    334         // 8-bit byte that contain a 7-bit ASCII code; 
    335         // the last byte must be NUL (binary zero) 
    336         byte[] ascii = new byte[count]; 
    337         in.read(ascii); 
    338  
    339         // count number of null terminators 
    340         int nullCount = 0; 
    341         for (int j=0; j<count; j++) { 
    342           if (ascii[j] == 0 || j == count - 1) nullCount++; 
    343         } 
    344  
    345         // convert character array to array of strings 
    346         String[] strings = nullCount == 1 ? null : new String[nullCount]; 
    347         String s = null; 
    348         int c = 0, ndx = -1; 
    349         for (int j=0; j<count; j++) { 
    350           if (ascii[j] == 0) { 
    351             s = new String(ascii, ndx + 1, j - ndx - 1); 
    352             ndx = j; 
    353           } 
    354           else if (j == count - 1) { 
    355             // handle non-null-terminated strings 
    356             s = new String(ascii, ndx + 1, j - ndx); 
    357           } 
    358           else s = null; 
    359           if (strings != null && s != null) strings[c++] = s; 
    360         } 
    361         value = strings == null ? (Object) s : strings; 
    362       } 
    363       else if (type == IFD.SHORT) { 
    364         // 16-bit (2-byte) unsigned integer 
    365         if (count == 1) value = new Integer(in.readShort() & 0xffff); 
    366         else { 
    367           int[] shorts = new int[count]; 
    368           for (int j=0; j<count; j++) { 
    369             shorts[j] = in.readShort() & 0xffff; 
    370           } 
    371           value = shorts; 
    372         } 
    373       } 
    374       else if (type == IFD.LONG || type == IFD.IFD) { 
    375         // 32-bit (4-byte) unsigned integer 
    376         if (count == 1) value = new Long(in.readInt()); 
    377         else { 
    378           long[] longs = new long[count]; 
    379           for (int j=0; j<count; j++) longs[j] = in.readInt(); 
    380           value = longs; 
    381         } 
    382       } 
    383       else if (type == IFD.LONG8 || type == IFD.SLONG8 || type == IFD.IFD8) { 
    384         if (count == 1) value = new Long(in.readLong()); 
    385         else { 
    386           long[] longs = new long[count]; 
    387           for (int j=0; j<count; j++) longs[j] = in.readLong(); 
    388           value = longs; 
    389         } 
    390       } 
    391       else if (type == IFD.RATIONAL || type == IFD.SRATIONAL) { 
    392         // Two LONGs or SLONGs: the first represents the numerator 
    393         // of a fraction; the second, the denominator 
    394         if (count == 1) value = new TiffRational(in.readInt(), in.readInt()); 
    395         else { 
    396           TiffRational[] rationals = new TiffRational[count]; 
    397           for (int j=0; j<count; j++) { 
    398             rationals[j] = new TiffRational(in.readInt(), in.readInt()); 
    399           } 
    400           value = rationals; 
    401         } 
    402       } 
    403       else if (type == IFD.SBYTE || type == IFD.UNDEFINED) { 
    404         // SBYTE: An 8-bit signed (twos-complement) integer 
    405         // UNDEFINED: An 8-bit byte that may contain anything, 
    406         // depending on the definition of the field 
    407         if (count == 1) value = new Byte(in.readByte()); 
    408         else { 
    409           byte[] sbytes = new byte[count]; 
    410           in.read(sbytes); 
    411           value = sbytes; 
    412         } 
    413       } 
    414       else if (type == IFD.SSHORT) { 
    415         // A 16-bit (2-byte) signed (twos-complement) integer 
    416         if (count == 1) value = new Short(in.readShort()); 
    417         else { 
    418           short[] sshorts = new short[count]; 
    419           for (int j=0; j<count; j++) sshorts[j] = in.readShort(); 
    420           value = sshorts; 
    421         } 
    422       } 
    423       else if (type == IFD.SLONG) { 
    424         // A 32-bit (4-byte) signed (twos-complement) integer 
    425         if (count == 1) value = new Integer(in.readInt()); 
    426         else { 
    427           int[] slongs = new int[count]; 
    428           for (int j=0; j<count; j++) slongs[j] = in.readInt(); 
    429           value = slongs; 
    430         } 
    431       } 
    432       else if (type == IFD.FLOAT) { 
    433         // Single precision (4-byte) IEEE format 
    434         if (count == 1) value = new Float(in.readFloat()); 
    435         else { 
    436           float[] floats = new float[count]; 
    437           for (int j=0; j<count; j++) floats[j] = in.readFloat(); 
    438           value = floats; 
    439         } 
    440       } 
    441       else if (type == IFD.DOUBLE) { 
    442         // Double precision (8-byte) IEEE format 
    443         if (count == 1) value = new Double(in.readDouble()); 
    444         else { 
    445           double[] doubles = new double[count]; 
    446           for (int j=0; j<count; j++) { 
    447             doubles[j] = in.readDouble(); 
    448           } 
    449           value = doubles; 
    450         } 
    451       } 
     376      TiffIFDEntry entry = new TiffIFDEntry(tag, type, count, pointer); 
     377 
     378      if (pointer != in.getFilePointer() && !fillInEntries) { 
     379        value = entry; 
     380      } 
     381      else value = getIFDValue(entry); 
     382 
    452383      if (value != null && !ifd.containsKey(new Integer(tag))) { 
    453384        ifd.put(new Integer(tag), value); 
    454385      } 
    455386    } 
     387 
    456388    in.seek(offset + baseOffset + bytesPerEntry * numEntries); 
    457389 
     
    463395 
    464396    return ifd; 
     397  } 
     398 
     399  /** Fill in IFD entries that are stored at an arbitrary offset. */ 
     400  public void fillInIFD(IFD ifd) throws IOException { 
     401    Vector<TiffIFDEntry> entries = new Vector<TiffIFDEntry>(); 
     402    for (Object key : ifd.keySet()) { 
     403      if (ifd.get(key) instanceof TiffIFDEntry) { 
     404        entries.add((TiffIFDEntry) ifd.get(key)); 
     405      } 
     406    } 
     407 
     408    TiffIFDEntry[] e = entries.toArray(new TiffIFDEntry[entries.size()]); 
     409    Arrays.sort(e); 
     410 
     411    for (TiffIFDEntry entry : e) { 
     412      ifd.put(new Integer(entry.getTag()), getIFDValue(entry)); 
     413    } 
     414  } 
     415 
     416  /** Retrieve the value corresponding to the given TiffIFDEntry. */ 
     417  public Object getIFDValue(TiffIFDEntry entry) throws IOException { 
     418    int type = entry.getType(); 
     419    int count = entry.getValueCount(); 
     420    long offset = entry.getValueOffset(); 
     421 
     422    if (offset != in.getFilePointer()) { 
     423      in.seek(offset); 
     424    } 
     425 
     426    if (type == IFD.BYTE) { 
     427      // 8-bit unsigned integer 
     428      if (count == 1) return new Short(in.readByte()); 
     429      byte[] bytes = new byte[count]; 
     430      in.readFully(bytes); 
     431      // bytes are unsigned, so use shorts 
     432      short[] shorts = new short[count]; 
     433      for (int j=0; j<count; j++) shorts[j] = (short) (bytes[j] & 0xff); 
     434      return shorts; 
     435    } 
     436    else if (type == IFD.ASCII) { 
     437      // 8-bit byte that contain a 7-bit ASCII code; 
     438      // the last byte must be NUL (binary zero) 
     439      byte[] ascii = new byte[count]; 
     440      in.read(ascii); 
     441 
     442      // count number of null terminators 
     443      int nullCount = 0; 
     444      for (int j=0; j<count; j++) { 
     445        if (ascii[j] == 0 || j == count - 1) nullCount++; 
     446      } 
     447 
     448      // convert character array to array of strings 
     449      String[] strings = nullCount == 1 ? null : new String[nullCount]; 
     450      String s = null; 
     451      int c = 0, ndx = -1; 
     452      for (int j=0; j<count; j++) { 
     453        if (ascii[j] == 0) { 
     454          s = new String(ascii, ndx + 1, j - ndx - 1); 
     455          ndx = j; 
     456        } 
     457        else if (j == count - 1) { 
     458          // handle non-null-terminated strings 
     459          s = new String(ascii, ndx + 1, j - ndx); 
     460        } 
     461        else s = null; 
     462        if (strings != null && s != null) strings[c++] = s; 
     463      } 
     464      return strings == null ? (Object) s : strings; 
     465    } 
     466    else if (type == IFD.SHORT) { 
     467      // 16-bit (2-byte) unsigned integer 
     468      if (count == 1) return new Integer(in.readShort() & 0xffff); 
     469      int[] shorts = new int[count]; 
     470      for (int j=0; j<count; j++) { 
     471        shorts[j] = in.readShort() & 0xffff; 
     472      } 
     473      return shorts; 
     474    } 
     475    else if (type == IFD.LONG || type == IFD.IFD) { 
     476      // 32-bit (4-byte) unsigned integer 
     477      if (count == 1) return new Long(in.readInt()); 
     478      long[] longs = new long[count]; 
     479      for (int j=0; j<count; j++) longs[j] = in.readInt(); 
     480      return longs; 
     481    } 
     482    else if (type == IFD.LONG8 || type == IFD.SLONG8 || type == IFD.IFD8) { 
     483      if (count == 1) return new Long(in.readLong()); 
     484      long[] longs = new long[count]; 
     485      for (int j=0; j<count; j++) longs[j] = in.readLong(); 
     486      return longs; 
     487    } 
     488    else if (type == IFD.RATIONAL || type == IFD.SRATIONAL) { 
     489      // Two LONGs or SLONGs: the first represents the numerator 
     490      // of a fraction; the second, the denominator 
     491      if (count == 1) return new TiffRational(in.readInt(), in.readInt()); 
     492      TiffRational[] rationals = new TiffRational[count]; 
     493      for (int j=0; j<count; j++) { 
     494        rationals[j] = new TiffRational(in.readInt(), in.readInt()); 
     495      } 
     496      return rationals; 
     497    } 
     498    else if (type == IFD.SBYTE || type == IFD.UNDEFINED) { 
     499      // SBYTE: An 8-bit signed (twos-complement) integer 
     500      // UNDEFINED: An 8-bit byte that may contain anything, 
     501      // depending on the definition of the field 
     502      if (count == 1) return new Byte(in.readByte()); 
     503      byte[] sbytes = new byte[count]; 
     504      in.read(sbytes); 
     505      return sbytes; 
     506    } 
     507    else if (type == IFD.SSHORT) { 
     508      // A 16-bit (2-byte) signed (twos-complement) integer 
     509      if (count == 1) return new Short(in.readShort()); 
     510      short[] sshorts = new short[count]; 
     511      for (int j=0; j<count; j++) sshorts[j] = in.readShort(); 
     512      return sshorts; 
     513    } 
     514    else if (type == IFD.SLONG) { 
     515      // A 32-bit (4-byte) signed (twos-complement) integer 
     516      if (count == 1) return new Integer(in.readInt()); 
     517      int[] slongs = new int[count]; 
     518      for (int j=0; j<count; j++) slongs[j] = in.readInt(); 
     519      return slongs; 
     520    } 
     521    else if (type == IFD.FLOAT) { 
     522      // Single precision (4-byte) IEEE format 
     523      if (count == 1) return new Float(in.readFloat()); 
     524      float[] floats = new float[count]; 
     525      for (int j=0; j<count; j++) floats[j] = in.readFloat(); 
     526      return floats; 
     527    } 
     528    else if (type == IFD.DOUBLE) { 
     529      // Double precision (8-byte) IEEE format 
     530      if (count == 1) return new Double(in.readDouble()); 
     531      double[] doubles = new double[count]; 
     532      for (int j=0; j<count; j++) { 
     533        doubles[j] = in.readDouble(); 
     534      } 
     535      return doubles; 
     536    } 
     537 
     538    return null; 
    465539  } 
    466540 
     
    577651    long tileWidth = ifd.getTileWidth(); 
    578652    long tileLength = ifd.getTileLength(); 
     653    if (tileLength <= 0) { 
     654      LogTools.debug("Tile length is " + tileLength + 
     655        "; setting it to " + height); 
     656      tileLength = height; 
     657    } 
     658 
    579659    long numTileRows = ifd.getTilesPerColumn(); 
    580660    long numTileCols = ifd.getTilesPerRow(); 
    581661 
     662    int photoInterp = ifd.getPhotometricInterpretation(); 
    582663    int planarConfig = ifd.getPlanarConfiguration(); 
    583664    int pixel = ifd.getBytesPerSample()[0]; 
     
    605686    LogTools.debug("reading image data (samplesPerPixel=" + 
    606687      samplesPerPixel + "; numSamples=" + numSamples + ")"); 
     688 
     689    int compression = ifd.getCompression(); 
     690 
     691    // special case: if we only need one tile, and that tile doesn't need 
     692    // any special handling, then we can just read it directly and return 
     693    if ((x % tileWidth) == 0 && (y % tileLength) == 0 && width == tileWidth && 
     694      height == tileLength && samplesPerPixel == 1 && 
     695      (ifd.getBitsPerSample()[0] % 8) == 0 && 
     696      photoInterp != PhotoInterp.WHITE_IS_ZERO && 
     697      photoInterp != PhotoInterp.CMYK && photoInterp != PhotoInterp.Y_CB_CR && 
     698      compression == TiffCompression.UNCOMPRESSED) 
     699    { 
     700      long[] stripOffsets = ifd.getStripOffsets(); 
     701      long[] stripByteCounts = ifd.getStripByteCounts(); 
     702 
     703      int tile = (int) ((y / tileLength) * numTileCols + (x / tileWidth)); 
     704      in.seek(stripOffsets[tile]); 
     705      in.read(buf, 0, (int) Math.min(buf.length, stripByteCounts[tile])); 
     706      return buf; 
     707    } 
    607708 
    608709    long nrows = numTileRows; 
     
    629730    } 
    630731 
     732    Region tileBounds = new Region(0, 0, (int) tileWidth, (int) tileLength); 
     733 
    631734    for (int row=0; row<numTileRows; row++) { 
    632735      for (int col=0; col<numTileCols; col++) { 
    633         Region tileBounds = new Region(col * (int) tileWidth, 
    634           (int) (row * tileLength), (int) tileWidth, (int) tileLength); 
     736        tileBounds.x = col * (int) tileWidth; 
     737        tileBounds.y = row * (int) tileLength; 
    635738 
    636739        if (!imageBounds.intersects(tileBounds)) continue; 
  • branches/4.1/components/common/src/loci/common/RandomAccessInputStream.java

    r5487 r5773  
    127127  } 
    128128 
    129   /** Constructs a random access stream around the given byte array. */ 
    130   public RandomAccessInputStream(byte[] array) throws IOException { 
    131     // this doesn't use a file descriptor, so we don't need to add it to the 
    132     // file cache 
    133     raf = new ByteArrayHandle(array); 
     129  /** Constructs a random access stream around the given handle. */ 
     130  public RandomAccessInputStream(IRandomAccess handle) throws IOException { 
     131    raf = handle; 
    134132    fp = 0; 
    135133    afp = 0; 
    136134    length = raf.length(); 
     135    fileCache.put(this, true); 
     136    openFiles++; 
     137    if (openFiles > MAX_FILES) cleanCache(); 
     138  } 
     139 
     140  /** Constructs a random access stream around the given byte array. */ 
     141  public RandomAccessInputStream(byte[] array) throws IOException { 
     142    this(new ByteArrayHandle(array)); 
    137143  } 
    138144 
     
    221227   */ 
    222228  public String findString(String... terminators) throws IOException { 
    223     return findString(DEFAULT_BLOCK_SIZE, terminators); 
    224   } 
    225  
    226   /** 
    227    * Reads a string ending with one of the given terminating substrings, 
    228    * using the specified block size for buffering. 
    229    * 
     229    return findString(true, DEFAULT_BLOCK_SIZE, terminators); 
     230  } 
     231 
     232  /** 
     233   * Reads or skips a string ending with 
     234   * one of the given terminating substrings. 
     235   * 
     236   * @param saveString Whether to collect the string from the current file 
     237   *   pointer to the terminating bytes, and return it. If false, returns null. 
    230238   * @param terminators The strings for which to search. 
     239   * 
     240   * @throws IOException If saveString flag is set 
     241   *   and the maximum search length (512 MB) is exceeded. 
     242   * 
     243   * @return The string from the initial position through the end of the 
     244   *   terminating sequence, or through the end of the stream if no 
     245   *   terminating sequence is found, or null if saveString flag is unset. 
     246   */ 
     247  public String findString(boolean saveString, String... terminators) 
     248    throws IOException 
     249  { 
     250    return findString(saveString, DEFAULT_BLOCK_SIZE, terminators); 
     251  } 
     252 
     253  /** 
     254   * Reads a string ending with one of the given terminating 
     255   * substrings, using the specified block size for buffering. 
     256   * 
    231257   * @param blockSize The block size to use when reading bytes in chunks. 
    232    * 
    233    * @throws IOException If the maximum search length (512 MB) is exceeded. 
     258   * @param terminators The strings for which to search. 
    234259   * 
    235260   * @return The string from the initial position through the end of the 
     
    240265    throws IOException 
    241266  { 
     267    return findString(true, blockSize, terminators); 
     268  } 
     269 
     270  /** 
     271   * Reads or skips a string ending with one of the given terminating 
     272   * substrings, using the specified block size for buffering. 
     273   * 
     274   * @param saveString Whether to collect the string from the current file 
     275   *   pointer to the terminating bytes, and return it. If false, returns null. 
     276   * @param blockSize The block size to use when reading bytes in chunks. 
     277   * @param terminators The strings for which to search. 
     278   * 
     279   * @throws IOException If saveString flag is set 
     280   *   and the maximum search length (512 MB) is exceeded. 
     281   * 
     282   * @return The string from the initial position through the end of the 
     283   *   terminating sequence, or through the end of the stream if no 
     284   *   terminating sequence is found, or null if saveString flag is unset. 
     285   */ 
     286  public String findString(boolean saveString, int blockSize, 
     287    String... terminators) throws IOException 
     288  { 
    242289    StringBuilder out = new StringBuilder(); 
    243290    long startPos = getFilePointer(); 
     291    long bytesDropped = 0; 
    244292    long inputLen = length(); 
    245293    long maxLen = inputLen - startPos; 
    246     if (maxLen > MAX_SEARCH_SIZE) maxLen = MAX_SEARCH_SIZE; 
     294    boolean tooLong = saveString && maxLen > MAX_SEARCH_SIZE; 
     295    if (tooLong) maxLen = MAX_SEARCH_SIZE; 
    247296    boolean match = false; 
     297    int maxTermLen = 0; 
     298    for (String term : terminators) { 
     299      int len = term.length(); 
     300      if (len > maxTermLen) maxTermLen = len; 
     301    } 
    248302 
    249303    InputStreamReader in = new InputStreamReader(this); 
    250304    char[] buf = new char[blockSize]; 
    251     int i = 0; 
    252     while (i < maxLen) { 
    253       long pos = startPos + i; 
     305    long loc = 0; 
     306    while (loc < maxLen) { 
     307      long pos = startPos + loc; 
    254308      int num = blockSize; 
    255309      if (pos + blockSize > inputLen) num = (int) (inputLen - pos); 
     310 
     311      // if we're not saving the string, drop any old, unnecessary output 
     312      if (!saveString) { 
     313        int outLen = out.length(); 
     314        if (outLen >= maxTermLen) { 
     315          int dropIndex = outLen - maxTermLen + 1; 
     316          String last = out.substring(dropIndex, outLen); 
     317          out.setLength(0); 
     318          out.append(last); 
     319          bytesDropped += dropIndex; 
     320        } 
     321      } 
    256322 
    257323      // read block from stream 
     
    262328      out.append(buf, 0, r); 
    263329 
    264       // check output 
    265       int[] indices = new int[terminators.length]; 
     330      // check output, returning smallest possible string 
     331      int min = Integer.MAX_VALUE, tagLen = 0; 
    266332      for (int t=0; t<terminators.length; t++) { 
    267         int tagLen = terminators[t].length(); 
    268         indices[t] = out.indexOf(terminators[t], i == 0 ? 0 : i - tagLen); 
    269         if (!match) { 
    270           match = indices[t] >= 0; 
     333        int len = terminators[t].length(); 
     334        int start = (int) (loc - bytesDropped - len); 
     335        int value = out.indexOf(terminators[t], start < 0 ? 0 : start); 
     336        if (value >= 0 && value < min) { 
     337          match = true; 
     338          min = value; 
     339          tagLen = len; 
    271340        } 
    272341      } 
    273342 
    274       // return smallest possible string 
    275  
    276343      if (match) { 
    277         int min = Integer.MAX_VALUE; 
    278         int minIndex = Integer.MAX_VALUE; 
    279         for (int t=0; t<indices.length; t++) { 
    280           if (indices[t] >= 0 && indices[t] < min) { 
    281             min = indices[t]; 
    282             minIndex = t; 
    283           } 
     344        // reset stream to proper location 
     345        seek(startPos + bytesDropped + min + tagLen); 
     346 
     347        // trim output string 
     348        if (saveString) { 
     349          out.setLength(min + tagLen); 
     350          return out.toString(); 
    284351        } 
    285         int tagLen = terminators[minIndex].length(); 
    286         seek(startPos + min + tagLen); // reset stream to proper location 
    287         out.setLength(min + tagLen); // trim output 
    288         break; 
    289       } 
    290  
    291       i += r; 
    292     } 
    293  
    294     if (!match) throw new IOException("Maximum search length reached."); 
    295  
    296     return out.toString(); 
     352        else return null; 
     353      } 
     354 
     355      loc += r; 
     356    } 
     357 
     358    // no match 
     359    if (tooLong) throw new IOException("Maximum search length reached."); 
     360    return null; 
    297361  } 
    298362 
     
    515579    } 
    516580 
     581    // case 1: 
     582    // file is larger than 2 GB, so automatically use the RAF 
     583 
     584    if (length() > Integer.MAX_VALUE && raf != null) { 
     585      raf.seek(afp); 
     586      return RAF; 
     587    } 
     588 
    517589    // ensure that the DIS' file pointer is actually equal to fp before 
    518590    // we try to read from the DIS 
     
    605677  /** Re-open a file that has been closed */ 
    606678  private void reopen() throws IOException { 
    607     String path = Location.getMappedId(file); 
    608     File f = new File(path).getAbsoluteFile(); 
    609  
    610     raf = Location.getHandle(file); 
    611  
     679    String path; // file path corresponding to this location 
     680    File f; // absolute file on disk corresponding to this location 
     681    raf = Location.getMappedFile(file); 
     682    boolean createDIS = true; 
     683    if (raf == null) { 
     684      // no directly mapped IRandomAccess handle; create a handle 
     685      raf = Location.getHandle(file); 
     686      path = Location.getMappedId(file); 
     687      f = new File(path).getAbsoluteFile(); 
     688    } 
     689    else { 
     690      // an IRandomAccess handle was directly mapped; 
     691      // assume it is superior and skip creation of the DIS 
     692      path = null; 
     693      f = null; 
     694    } 
    612695    if (raf == null) { 
    613696      throw new IOException("File not found: " + file); 
    614697    } 
    615  
    616698    length = raf.length(); 
    617699 
    618     if (f.exists()) { 
     700    if (f != null && f.exists()) { 
    619701      compressed = raf instanceof CompressedRandomAccess; 
    620702      BufferedInputStream bis = new BufferedInputStream( 
     
    642724    int ndx = 0; 
    643725 
    644     while (closed < toClose) { 
     726    while (closed < toClose && ndx < files.length) { 
    645727      if (!this.equals(files[ndx]) && files[ndx].file != null && 
    646728        Boolean.TRUE.equals(fileCache.get(files[ndx]))) 
Note: See TracChangeset for help on using the changeset viewer.