package ij.plugin;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.io.*;
import java.awt.*;
import java.io.*;
import java.util.Properties;
public class FileInfoVirtualStack extends VirtualStack implements PlugIn {
private FileInfo[] info;
private int nImages;
public FileInfoVirtualStack() {}
public FileInfoVirtualStack(FileInfo fi) {
info = new FileInfo[1];
info[0] = fi;
ImagePlus imp = open();
if (imp!=null)
imp.show();
}
public FileInfoVirtualStack(FileInfo fi, boolean show) {
info = new FileInfo[1];
info[0] = fi;
ImagePlus imp = open();
if (imp!=null && show)
imp.show();
}
public FileInfoVirtualStack(FileInfo[] fi) {
info = fi;
nImages = info.length;
}
public static ImagePlus openVirtual(String path) {
OpenDialog od = new OpenDialog("Open TIFF", path);
String name = od.getFileName();
String dir = od.getDirectory();
if (name==null)
return null;
FileInfoVirtualStack stack = new FileInfoVirtualStack();
stack.init(dir, name);
if (stack.info==null)
return null;
else
return stack.open();
}
public void run(String arg) {
OpenDialog od = new OpenDialog("Open TIFF", arg);
String name = od.getFileName();
String dir = od.getDirectory();
if (name==null)
return;
init(dir, name);
if (info==null)
return;
ImagePlus imp = open();
if (imp!=null)
imp.show();
}
private void init(String dir, String name) {
if (name.endsWith(".zip")) {
IJ.error("Virtual Stack", "ZIP compressed stacks not supported");
return;
}
TiffDecoder td = new TiffDecoder(dir, name);
if (IJ.debugMode) td.enableDebugging();
IJ.showStatus("Decoding TIFF header...");
try {
info = td.getTiffInfo();
} catch (IOException e) {
String msg = e.getMessage();
if (msg==null||msg.equals("")) msg = ""+e;
IJ.error("TiffDecoder", msg);
return;
}
if (info==null || info.length==0) {
IJ.error("Virtual Stack", "This does not appear to be a TIFF stack");
return;
}
if (IJ.debugMode)
IJ.log(info[0].debugInfo);
}
private ImagePlus open() {
FileInfo fi = info[0];
int n = fi.nImages;
if (info.length==1 && n>1) {
long bytesPerImage = fi.width*fi.height*fi.getBytesPerPixel();
if (fi.fileType==FileInfo.GRAY12_UNSIGNED)
bytesPerImage = (int)(1.5*fi.width)*fi.height;
n = validateNImages(fi, bytesPerImage);
info = new FileInfo[n];
for (int i=0; i<n; i++) {
info[i] = (FileInfo)fi.clone();
info[i].nImages = 1;
info[i].longOffset = fi.getOffset() + i*(bytesPerImage + fi.getGap());
}
}
nImages = info.length;
FileOpener fo = new FileOpener(info[0]);
ImagePlus imp = fo.openImage();
if (nImages==1 && fi.fileType==FileInfo.RGB48)
return imp;
Properties props = fo.decodeDescriptionString(fi);
ImagePlus imp2 = new ImagePlus(fi.fileName, this);
imp2.setDisplayRange(imp.getDisplayRangeMin(),imp.getDisplayRangeMax());
imp2.setFileInfo(fi);
if (imp!=null && props!=null) {
setBitDepth(imp.getBitDepth());
imp2.setCalibration(imp.getCalibration());
imp2.setOverlay(imp.getOverlay());
if (fi.info!=null)
imp2.setProperty("Info", fi.info);
int channels = getInt(props,"channels");
int slices = getInt(props,"slices");
int frames = getInt(props,"frames");
if (channels*slices*frames==nImages) {
imp2.setDimensions(channels, slices, frames);
if (getBoolean(props, "hyperstack"))
imp2.setOpenAsHyperStack(true);
}
if (channels>1 && fi.description!=null) {
int mode = IJ.COMPOSITE;
if (fi.description.indexOf("mode=color")!=-1)
mode = IJ.COLOR;
else if (fi.description.indexOf("mode=gray")!=-1)
mode = IJ.GRAYSCALE;
imp2 = new CompositeImage(imp2, mode);
}
}
return imp2;
}
private int validateNImages(FileInfo fi, long bytesPerImage) {
File f = new File(fi.getFilePath());
if (!f.exists())
return fi.nImages;
long fileLength = f.length();
for (int i=fi.nImages-1; i>=0; i--) {
long offset = fi.getOffset() + i*(bytesPerImage+fi.getGap());
if (offset+bytesPerImage<=fileLength)
return i+1;
}
return fi.nImages;
}
int getInt(Properties props, String key) {
Double n = getNumber(props, key);
return n!=null?(int)n.doubleValue():1;
}
Double getNumber(Properties props, String key) {
String s = props.getProperty(key);
if (s!=null) {
try {
return Double.valueOf(s);
} catch (NumberFormatException e) {}
}
return null;
}
boolean getBoolean(Properties props, String key) {
String s = props.getProperty(key);
return s!=null&&s.equals("true")?true:false;
}
public void deleteSlice(int n) {
if (n<1 || n>nImages)
throw new IllegalArgumentException("Argument out of range: "+n);
if (nImages<1) return;
for (int i=n; i<nImages; i++)
info[i-1] = info[i];
info[nImages-1] = null;
nImages--;
}
public ImageProcessor getProcessor(int n) {
n = translate(n); if (n<1 || n>nImages)
throw new IllegalArgumentException("Argument out of range: "+n);
info[n-1].nImages = 1; ImageProcessor ip = null;
if (IJ.debugMode) {
long t0 = System.currentTimeMillis();
FileOpener fo = new FileOpener(info[n-1]);
ip = fo.openProcessor();
IJ.log("FileInfoVirtualStack: "+n+", offset="+info[n-1].getOffset()+", "+(System.currentTimeMillis()-t0)+"ms");
} else {
FileOpener fo = new FileOpener(info[n-1]);
if (info[n-1].fileType==FileInfo.RGB48) {
ImagePlus imp = fo.openImage();
if (info[n-1].sliceNumber>0)
imp.setSlice(info[n-1].sliceNumber);
ip = imp.getProcessor();
} else
ip = fo.openProcessor();
}
if (ip!=null) {
if (cTable!=null)
ip.setCalibrationTable(cTable);
return ip;
} else {
int w=getWidth(), h=getHeight();
IJ.log("Read error or file not found ("+n+"): "+info[n-1].directory+info[n-1].fileName);
switch (getBitDepth()) {
case 8: return new ByteProcessor(w, h);
case 16: return new ShortProcessor(w, h);
case 24: return new ColorProcessor(w, h);
case 32: return new FloatProcessor(w, h);
default: return null;
}
}
}
public int size() {
return getSize();
}
public int getSize() {
return nImages;
}
public String getSliceLabel(int n) {
if (n<1 || n>nImages)
throw new IllegalArgumentException("Argument out of range: "+n);
if (info[0].sliceLabels==null || info[0].sliceLabels.length!=nImages)
return null;
else
return info[0].sliceLabels[n-1];
}
public int getWidth() {
return info[0].width;
}
public int getHeight() {
return info[0].height;
}
public synchronized void addImage(FileInfo fileInfo) {
nImages++;
if (info==null)
info = new FileInfo[250];
if (nImages==info.length) {
FileInfo[] tmp = new FileInfo[nImages*2];
System.arraycopy(info, 0, tmp, 0, nImages);
info = tmp;
}
info[nImages-1] = fileInfo;
}
@Override
public String getDirectory() {
if (info!=null && info.length>0)
return info[0].directory;
else
return null;
}
@Override
public String getFileName(int n) {
int index = n - 1;
if (index>=0 && info!=null && info.length>index)
return info[index].fileName;
else
return null;
}
}