content: package edu.stanford.rsl.conrad.io;
  
  import java.awt.image.ColorModel;
  import java.awt.image.IndexColorModel;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.zip.GZIPInputStream;
  
  import edu.stanford.rsl.conrad.data.numeric.Grid2D;
  import edu.stanford.rsl.conrad.pipeline.IndividualImagePipelineFilteringTool;
  import edu.stanford.rsl.conrad.pipeline.ProjectionSource;
  import edu.stanford.rsl.conrad.utils.CONRAD;
  import ij.IJ;
  import ij.LookUpTable;
  import ij.Macro;
  import ij.Prefs;
  import ij.io.FileInfo;
  import ij.io.ImageReader;
  import ij.io.RandomAccessStream;
  import ij.process.ByteProcessor;
  import ij.process.ColorProcessor;
  import ij.process.ImageProcessor;
  import ij.process.ShortProcessor;
  
  /**
   * Class to model an abstract projection source which accesses the file system to stream the data.
   * 
   * @author akmaier
   *
   */
  public abstract class FileProjectionSource implements ProjectionSource {
  
  	protected FileInfo fi = null;
  	protected long skip;
  	protected int currentIndex = -1;
  	protected boolean showProgress;
  	protected ImageReader reader;
  	protected InputStream is;
  	protected ColorModel cm;
  
  	
  
  
  	public static ProjectionSource [] getProjectionSources(){
  		ProjectionSource [] sources = {new TiffProjectionSource(), new ZipProjectionSource(), new SEQProjectionSource(), new DicomProjectionSource(), new NRRDProjectionSource(), new DennerleinProjectionSource()};
  		return sources;
  	}
  
  	public static ProjectionSource openProjectionStream(String filename) throws IOException {
  		ProjectionSource [] sources = getProjectionSources();
  		ProjectionSource source = null;
  		for (int i = 0; i < sources.length; i++){
  			try {
  				sources[i].initStream(filename);
  				source = sources[i];
  				break;
  			} catch (Exception e) {
  				// May throw some exception, if it is the wrong file type.
  				CONRAD.log(sources[i] +" failed because " +e.getLocalizedMessage());
  				// e.printStackTrace();
  			}
  		}
  		if (source == null) {
  			throw new IOException("No matching file format found.");
  		}
  		return source;	
  	}
  
  	public synchronized void getNextProjection(IndividualImagePipelineFilteringTool tool){
  		
  		Grid2D grid = getNextProjection();
  		if (grid != null){
  			tool.setImageProcessor(grid);
  			tool.setImageIndex(getCurrentProjectionNumber());
  		} else {
  			tool.setImageProcessor(null);
  			tool.setImageIndex(-1);
  		}
  	}
  
  	public void setShowProgress(boolean showProgress) {
  		this.showProgress = showProgress;
  	}
  
  	public boolean isShowProgress() {
  		return showProgress;
  	}
  
  	public int getCurrentProjectionNumber(){
  		return currentIndex;
  	}
  
  	public Grid2D getNextProjection() {
  		ImageProcessor ip = null;
  		Object pixels = reader.readPixels(is, skip);
  		if (pixels == null) {
  			return null;
  		}
  		if (pixels instanceof byte[])
  			ip = new ByteProcessor(fi.width, fi.height, (byte[]) pixels, cm);
  		else if (pixels instanceof short[])
  			ip = new ShortProcessor(fi.width, fi.height, (short[]) pixels, cm);
  		else if (pixels instanceof int[])
  			ip = new ColorProcessor(fi.width, fi.height, (int[]) pixels);
  		else if (pixels instanceof float[]) {
  			// no conversion needed
  		} else
  			throw new IllegalArgumentException("Unknown stack type");
  			
  		skip = fi.gapBetweenImages;
  		currentIndex++;
  		if (showProgress){
  			IJ.showProgress((0.0 + currentIndex) / fi.nImages);
  		}
  		if(ip != null)
  			pixels = ip.toFloat(0, null).getPixels();
  		Grid2D grid = new Grid2D((float [])pixels, fi.width, fi.height);
  		
  		return grid;
  	}
  
  
  	protected void init() {
  		cm = createColorModel(fi);
  		//if (fi.nImages>1){
  			initStack();
  		//}
  	}
  
  
  	/** Returns an IndexColorModel for the image specified by this FileInfo. */
  	protected ColorModel createColorModel(FileInfo fi) {
  		if (fi.fileType==FileInfo.COLOR8 && fi.lutSize>0)
  			return new IndexColorModel(8, fi.lutSize, fi.reds, fi.greens, fi.blues);
  		else
  			return LookUpTable.createGrayscaleColorModel(fi.whiteIsZero);
  	}
  
  
  
  	/** Opens a stack of images. */
  	protected void initStack() {
  		skip = fi.getOffset();
  		try {
  			reader = new ImageReader(fi);
  			is = createInputStream(fi);
  			if (is==null) return ;
  		}
  		catch (Exception e) {
  			CONRAD.log("" + e);
  			e.printStackTrace();
  		}
  		catch(OutOfMemoryError e) {
  			e.printStackTrace();
  			IJ.outOfMemory(fi.fileName);
  		}
  	}
  
  	/** Returns an InputStream for the image described by this FileInfo. */
  	protected InputStream createInputStream(FileInfo fi) throws IOException, MalformedURLException {
  		InputStream is = null;
  		boolean gzip = fi.fileName!=null && (fi.fileName.endsWith(".gz")||fi.fileName.endsWith(".GZ"));
  		if (fi.inputStream!=null)
  			is = fi.inputStream;
  		else if (fi.url!=null && !fi.url.equals(""))
  			is = new URL(fi.url+fi.fileName).openStream();
  		else {
  			if (fi.directory.length()>0 && !fi.directory.endsWith(Prefs.separator))
  				fi.directory += Prefs.separator;
  			File f = new File(fi.directory + fi.fileName);
  			if (gzip) fi.compression = FileInfo.COMPRESSION_UNKNOWN;
  			if (f==null || f.isDirectory() || !validateFileInfo(f, fi))
  				is = null;
  			else
  				is = new FileInputStream(f);
  		}
  		if (is!=null) {
  			if (fi.compression>=FileInfo.LZW)
  				is = new RandomAccessStream(is);
  			else if (gzip)
  				is = new GZIPInputStream(is, 50000);
  		}
  		return is;
  	}
  
  	protected static boolean validateFileInfo(File f, FileInfo fi) {
  		long offset = fi.getOffset();
  		long length = 0;
  		if (fi.width<=0 || fi.height<0) {
  			error("Width or height <= 0.", fi, offset, length);
  			return false;
  		}
  		if (offset>=0 && offset<1000L)
  			return true;
  		if (offset<0L) {
  			error("Offset is negative.", fi, offset, length);
  			return false;
  		}
  		if (fi.fileType==FileInfo.BITMAP || fi.compression!=FileInfo.COMPRESSION_NONE)
  			return true;
  		length = f.length();
  		long size = fi.width*fi.height*fi.getBytesPerPixel();
  		size = fi.nImages>1?size:size/4;
  		if (fi.height==1) size = 0; // allows plugins to read info of unknown length at end of file
  		if (offset+size>length) {
  			error("Offset + image size > file length.", fi, offset, length);
  			return false;
  		}
  		return true;
  	}
  
  	protected static void error(String msg, FileInfo fi, long offset, long length) {
  		IJ.error("FileOpener", "FileInfo parameter error. \n"
  				+msg + "\n \n"
  				+"  Width: " + fi.width + "\n"
  				+"  Height: " + fi.height + "\n"
  				+"  Offset: " + offset + "\n"
  				+"  Bytes/pixel: " + fi.getBytesPerPixel() + "\n"
  				+(length>0?"  File length: " + length + "\n":"")
  		);
  	}
  
  
  
  	/** Reads the pixel data from an image described by a FileInfo object. */
  	protected Object readPixels(FileInfo fi) {
  		Object pixels = null;
  		try {
  			InputStream is = createInputStream(fi);
  			if (is==null)
  				return null;
  			ImageReader reader = new ImageReader(fi);
  			pixels = reader.readPixels(is);
  			is.close();
  		}
  		catch (Exception e) {
  			if (!Macro.MACRO_CANCELED.equals(e.getMessage()))
  				IJ.handleException(e);
  		}
  		return pixels;
  	}
  
  	public void close() throws IOException{
  		is.close();
  	}
  
  	/**
  	 * converts a byte sequence to integer
  	 * @param values the byte array
  	 * @param index the index in 32bit integer metric
  	 * @return the integer
  	 */
  	public long convertToUnsignedInt(byte[] values, int index) {
  		int offset = index * 4;  
  		//System.out.println(values[offset +0] + " " + values[offset +1] + " " + values[offset +2] + " " +values[offset +3]);
  		int val = values[offset+3];
  		if (val < 0) val += 256;
  		long value = val;
  		value = value << 8;
  		val = values[offset+2];
  		if (val < 0) val += 256;
  		value += val;
  		value = value << 8;
  		val = values[offset+1];
  		if (val < 0) val += 256;
  		value += val;
  		value = value << 8;
  		val = values[offset];
  		if (val < 0) val += 256;
  		value += val;
  		return value;
  	}
  	
  	/**
  	 * converts a byte sequence to integer
  	 * @param values the byte array
  	 * @param index the index in 32bit integer metric
  	 * @return the integer
  	 */
  	public int convertToUnsignedShort(byte[] values, int index) {
  		int offset = index * 2;  
  		//System.out.println(values[offset +0] + " " + values[offset +1] + " " + values[offset +2] + " " +values[offset +3]);
  		int val = values[offset+1];
  		if (val < 0) val += 256;
  		int value = val;
  		value = value << 8;
  		val = values[offset];
  		if (val < 0) val += 256;
  		value += val;
  		return value;
  	}
  	
  
  }
  
  /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */