changeset 86:997ba69afb81

New version 1.8b1. With directory and file information cache. With enhanceRGB method for color correction.
author robcast
date Sun, 09 Mar 2003 21:37:27 +0100
parents 4e6757e8ccd4
children 5d44cd2481a5
files servlet/src/digilib/image/DocuImage.java servlet/src/digilib/image/DocuImageImpl.java servlet/src/digilib/image/ImageLoaderDocuImage.java servlet/src/digilib/image/JAIDocuImage.java servlet/src/digilib/io/DocuDirCache.java servlet/src/digilib/io/DocuDirectory.java servlet/src/digilib/io/DocuFile.java servlet/src/digilib/io/DocuFileset.java servlet/src/digilib/io/FileOps.java servlet/src/digilib/servlet/DigilibConfiguration.java servlet/src/digilib/servlet/DigilibRequest.java servlet/src/digilib/servlet/DocumentBean.java servlet/src/digilib/servlet/Scaler.java
diffstat 13 files changed, 1123 insertions(+), 193 deletions(-) [+]
line wrap: on
line diff
--- a/servlet/src/digilib/image/DocuImage.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/image/DocuImage.java	Sun Mar 09 21:37:27 2003 +0100
@@ -22,8 +22,10 @@
 
 import java.awt.Rectangle;
 import java.io.File;
+import java.io.IOException;
 import java.io.OutputStream;
 
+import digilib.io.DocuFile;
 import digilib.io.FileOpException;
 
 /** The basic class for the representation of a digilib image.
@@ -64,18 +66,16 @@
 	public void loadSubimage(File f, Rectangle region, int subsample)
 		throws FileOpException;
 
-	/** This DocuImage support the preloadImage operation for getWidth/getHeight.
+	/** Checks the size and type of the DocuFile f.
 	 * 
-	 * @return boolean
+	 * The image size and type of the DocuFile f is determined and stored in
+	 * the DocuFile object. Returns true if successfull.
+	 * 
+	 * @param f DocuFile to be checked.
+	 * @return boolean true if check was successfull.
+	 * @throws FileOpException Exception thrown on error.
 	 */
-	public boolean isPreloadSupported();
-
-	/** Preload image file into a state to use getWidth/getHeight.
-	 * 
-	 * @param f
-	 * @throws FileOpException
-	 */
-	public void preloadImage(File f) throws FileOpException;
+	public boolean checkFile(DocuFile f) throws IOException;
 
 	/** Writes the current image to a ServletResponse.
 	 *
@@ -103,6 +103,12 @@
 	 */
 	public int getHeight();
 
+	/** The mime-type of the current image.
+	 * 
+	 * @return String the mime-type of this image.
+	 */
+	public String getMimetype();
+
 	/** Crops the current image.
 	 * 
 	 * Cuts out a region of the size <code>width</code> x <code>height</code> at
@@ -175,7 +181,7 @@
 	 */
 	public void mirror(double angle) throws ImageOpException;
 
-	/** Enhaces brightness and contrast of the current image.
+	/** Enhances brightness and contrast of the current image.
 	 * 
 	 * Replaces the current image with a brightness and contrast enhanced image.
 	 * Contrast is enhanced by multiplying the pixel value with the constant
@@ -186,7 +192,22 @@
 	 * @param add additive constant for brightness enhancement
 	 * @throws ImageOpException
 	 */
-	public void enhance(double mult, double add) throws ImageOpException;
+	public void enhance(float mult, float add) throws ImageOpException;
+
+	/** Manipulates the colors of the current image.
+	 * 
+	 * Replaces the current image with a color modified image.
+	 * For the red, green and blue color channels all pixel values are multiplied
+	 * by the constant
+	 * <code>m</code> and added to the constant
+	 * <code>a</code>. Operation: p1 = (p0*m)+a.
+	 * 
+	 * @param rgbm multiplicative constants for red, green, blue
+	 * @param rgba additive constant for red, green, blue
+	 * @throws ImageOpException
+	 */
+	public void enhanceRGB(float[] rgbm, float[] rgba)
+		throws ImageOpException;
 
 	/**
 	 * Returns the interpolation quality.
--- a/servlet/src/digilib/image/DocuImageImpl.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/image/DocuImageImpl.java	Sun Mar 09 21:37:27 2003 +0100
@@ -20,11 +20,15 @@
 
 package digilib.image;
 
+import java.awt.Dimension;
 import java.awt.Rectangle;
 import java.io.File;
+import java.io.IOException;
 
 import digilib.Utils;
+import digilib.io.DocuFile;
 import digilib.io.FileOpException;
+import digilib.io.FileOps;
 
 /** Simple abstract implementation of the <code>DocuImage</code> interface.
  *
@@ -42,8 +46,11 @@
 	/** Interpolation quality. */
 	protected int quality = 0;
 	
-	// epsilon for float comparisons
+	/** epsilon for float comparisons. */
 	public final double epsilon = 1e-5;
+	
+	/** image mime-type */
+	protected String mimeType = null;
 
 	/** Default constructor. */
 	public DocuImageImpl() {
@@ -116,6 +123,23 @@
 		scale(scale);
 	}
 	
+	/* this is a rather stupid implementation, eventually loading the whole file. */
+	public boolean checkFile(DocuFile f) throws IOException {
+		loadImage(f.getFile());
+		int w = getWidth();
+		int h = getHeight();
+		Dimension s = new Dimension(w, h);
+		f.setSize(s);
+		String m = FileOps.mimeForFile(f.getFile());
+		mimeType = m;
+		f.setMimetype(m);
+		return true;
+	}
+
+	public String getMimetype() {
+		return mimeType;
+	}
+
 	public void rotate(double angle) throws ImageOpException {
 		// just a do-nothing implementation
 	}
@@ -124,21 +148,12 @@
 		// just a do-nothing implementation
 	}
 
-	public void enhance(double mult, double add) throws ImageOpException {
-		// just a do-nothing implementation
-	}
-
-	public void preloadImage(File f) throws FileOpException {
+	public void enhance(float mult, float add) throws ImageOpException {
 		// just a do-nothing implementation
 	}
 
-	public boolean isPreloadSupported() {
-		// preload per default not supported
-		return false;
-	}
-
 	public boolean isSubimageSupported() {
-		// partial loading per default not supported
+		// partial loading not supported per default
 		return false;
 	}
 
@@ -147,4 +162,9 @@
 		// empty implementation
 	}
 
+	public void enhanceRGB(float[] rgbm, float[] rgba)
+		throws ImageOpException {
+		// emtpy implementation
+	}
+
 }
--- a/servlet/src/digilib/image/ImageLoaderDocuImage.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/image/ImageLoaderDocuImage.java	Sun Mar 09 21:37:27 2003 +0100
@@ -20,6 +20,7 @@
 
 package digilib.image;
 
+import java.awt.Dimension;
 import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
 import java.awt.image.AffineTransformOp;
@@ -35,22 +36,21 @@
 import javax.imageio.ImageReader;
 import javax.imageio.stream.ImageInputStream;
 
+import digilib.io.DocuFile;
 import digilib.io.FileOpException;
+import digilib.io.FileOps;
 
 /** Implementation of DocuImage using the ImageLoader API of Java 1.4 and Java2D. */
 public class ImageLoaderDocuImage extends DocuImageImpl {
 
-	private BufferedImage img;
-
-	private int interpol;
-
-	// ImageIO image reader
-	ImageReader reader;
-
-	/* preload is supported. */
-	public boolean isPreloadSupported() {
-		return true;
-	}
+	/** image object */
+	protected BufferedImage img;
+	/** interpolation type */
+	protected int interpol;
+	/** ImageIO image reader */
+	protected ImageReader reader;
+	/** File that was read */
+	protected File imgFile;
 
 	/* loadSubimage is supported. */
 	public boolean isSubimageSupported() {
@@ -68,7 +68,7 @@
 			interpol = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
 		}
 	}
-	
+
 	public int getHeight() {
 		int h = 0;
 		try {
@@ -97,9 +97,7 @@
 		return w;
 	}
 
-	/**
-	 *  load image file
-	 */
+	/* load image file */
 	public void loadImage(File f) throws FileOpException {
 		util.dprintln(10, "loadImage!");
 		System.gc();
@@ -114,7 +112,9 @@
 		}
 	}
 
-	/* Get an ImageReader for the image file. */
+	/** Get an ImageReader for the image file.
+	 * 
+	 */
 	public void preloadImage(File f) throws FileOpException {
 		System.gc();
 		try {
@@ -130,6 +130,7 @@
 			util.dprintln(3, "ERROR(loadImage): unable to load file");
 			throw new FileOpException("Unable to load File!");
 		}
+		imgFile = f;
 	}
 
 	/* Load an image file into the Object. */
@@ -137,7 +138,7 @@
 		throws FileOpException {
 		System.gc();
 		try {
-			if (reader == null) {
+			if ((reader == null) || (imgFile != f)) {
 				preloadImage(f);
 			}
 			// set up reader parameters
@@ -156,9 +157,7 @@
 		}
 	}
 
-	/**
-	 *  write image of type mt to Stream
-	 */
+	/* write image of type mt to Stream */
 	public void writeImage(String mt, OutputStream ostream)
 		throws FileOpException {
 		util.dprintln(10, "writeImage!");
@@ -220,102 +219,139 @@
 		img = croppedImg;
 	}
 
-	public void enhance(double mult, double add) throws ImageOpException {
-		/* The number of constants must match the number of bands in the image.
-		 * We only handle 1 (greyscale) or 3 (RGB) bands.
+	public void enhance(float mult, float add) throws ImageOpException {
+		/* Only one constant should work regardless of the number of bands 
+		 * according to the JDK spec.
+		 * Doesn't work on JDK 1.4 for OSX and Linux (at least).
 		 */
-		float[] dm;
-		float[] da;
-		int ncol = img.getColorModel().getNumColorComponents(); 
-		if (ncol == 3) {
-			float[] f1 = {(float)mult, (float)mult, (float)mult};
-			dm = f1;
-			float[] f2 = {(float)add, (float)add, (float)add};
-			da = f2;
-		} else if (ncol == 1) {
-			float[] f1 = {(float)mult};
-			dm = f1;
-			float[] f2 = {(float)add};
-			da = f2;
-		} else {
-			util.dprintln(2, "ERROR(enhance): unknown number of color bands ("+ncol+")");
-			return;
+		/*		RescaleOp scaleOp =
+					new RescaleOp(
+						(float)mult, (float)add,
+						null);
+				scaleOp.filter(img, img);
+		*/
+		/* The number of constants must match the number of bands in the image.
+		 */
+		int ncol = img.getColorModel().getNumColorComponents();
+		float[] dm = new float[ncol];
+		float[] da = new float[ncol];
+		for (int i = 0; i < ncol; i++) {
+			dm[i] = (float) mult;
+			da[i] = (float) add;
 		}
-		RescaleOp scaleOp =
-			new RescaleOp(
-				dm, da,
-				null);
+		RescaleOp scaleOp = new RescaleOp(dm, da, null);
 		scaleOp.filter(img, img);
 
-		/* Operation with only one constant should work regardless of the number of bands 
-		 * according to the JDK spec.
-		 * Doesn't work on JDK 1.4 for OSX PR10 (at least).
-		 */
-		/*
-		RescaleOp scaleOp =
-			new RescaleOp(
-				(float)mult, (float)add,
-				null);
-		scaleOp.filter(img, img);
-		*/
 	}
-	
-	public void rotate(double angle) throws ImageOpException {
-		// setup rotation
-		double rangle = Math.toRadians(angle);
-		double x = getWidth()/2;
-		double y = getHeight()/2;
-		AffineTransformOp rotOp =
-			new AffineTransformOp(
-				AffineTransform.getRotateInstance(rangle, x, y),
-				interpol);
-		BufferedImage rotImg = rotOp.filter(img, null);
 
-		if (rotImg == null) {
-			util.dprintln(2, "ERROR: error in rotate");
-			throw new ImageOpException("Unable to rotate");
+	public void enhanceRGB(float[] rgbm, float[] rgba)
+		throws ImageOpException {
+		/* The number of constants must match the number of bands in the image.
+		 * We do only 3 (RGB) bands.
+		 */
+		int ncol = img.getColorModel().getNumColorComponents();
+		if ((ncol != 3) || (rgbm.length != 3) || (rgba.length != 3)) {
+			util.dprintln(
+				2,
+				"ERROR(enhance): unknown number of color bands or coefficients ("
+					+ ncol
+					+ ")");
+			return;
 		}
-		img = rotImg;
+		RescaleOp scaleOp = new RescaleOp(rgbOrdered(rgbm), rgbOrdered(rgba), null);
+		scaleOp.filter(img, img);
+	}
+
+	/** Ensures that the array f is in the right order to map the images RGB components. 
+	 */
+	public float[] rgbOrdered(float[] fa) {
+		float[] fb = new float[3];
+		int t = img.getType();
+		if ((t == BufferedImage.TYPE_3BYTE_BGR)
+			|| (t == BufferedImage.TYPE_4BYTE_ABGR)
+			|| (t == BufferedImage.TYPE_4BYTE_ABGR_PRE)) {
+			// BGR Type (actually it looks like RBG...)
+			fb[0] = fa[0];
+			fb[1] = fa[2];
+			fb[2] = fa[1];
+		} else {
+			fb = fa;
+		}
+		return fb;
 	}
 
-	public void mirror(double angle) throws ImageOpException {
-		// setup mirror
-		double mx = 1;
-		double my = 1;
-		double tx = 0;
-		double ty = 0;
-		if (Math.abs(angle - 0) < epsilon) {
-			// 0 degree
-			mx = -1;
-			tx = getWidth();
-		} else if (Math.abs(angle - 90) < epsilon) {
-			// 90 degree
-			my = -1;
-			ty = getHeight();
-		} else if (Math.abs(angle - 180) < epsilon) {
-			// 180 degree
-			mx = -1;
-			tx = getWidth();
-		} else if (Math.abs(angle - 270) < epsilon) {
-			// 270 degree
-			my = -1;
-			ty = getHeight();
-		} else if (Math.abs(angle - 360) < epsilon) {
-			// 360 degree
-			mx = -1;
-			tx = getWidth();
-		}
-		AffineTransformOp mirOp =
-			new AffineTransformOp(
-				new AffineTransform(mx, 0 , 0, my, tx, ty),
-				interpol);
-		BufferedImage mirImg = mirOp.filter(img, null);
+public void rotate(double angle) throws ImageOpException {
+	// setup rotation
+	double rangle = Math.toRadians(angle);
+	double x = getWidth() / 2;
+	double y = getHeight() / 2;
+	AffineTransformOp rotOp =
+		new AffineTransformOp(
+			AffineTransform.getRotateInstance(rangle, x, y),
+			interpol);
+	BufferedImage rotImg = rotOp.filter(img, null);
+
+	if (rotImg == null) {
+		util.dprintln(2, "ERROR: error in rotate");
+		throw new ImageOpException("Unable to rotate");
+	}
+	img = rotImg;
+}
 
-		if (mirImg == null) {
-			util.dprintln(2, "ERROR: error in mirror");
-			throw new ImageOpException("Unable to mirror");
-		}
-		img = mirImg;
+public void mirror(double angle) throws ImageOpException {
+	// setup mirror
+	double mx = 1;
+	double my = 1;
+	double tx = 0;
+	double ty = 0;
+	if (Math.abs(angle - 0) < epsilon) {
+		// 0 degree
+		mx = -1;
+		tx = getWidth();
+	} else if (Math.abs(angle - 90) < epsilon) {
+		// 90 degree
+		my = -1;
+		ty = getHeight();
+	} else if (Math.abs(angle - 180) < epsilon) {
+		// 180 degree
+		mx = -1;
+		tx = getWidth();
+	} else if (Math.abs(angle - 270) < epsilon) {
+		// 270 degree
+		my = -1;
+		ty = getHeight();
+	} else if (Math.abs(angle - 360) < epsilon) {
+		// 360 degree
+		mx = -1;
+		tx = getWidth();
 	}
+	AffineTransformOp mirOp =
+		new AffineTransformOp(
+			new AffineTransform(mx, 0, 0, my, tx, ty),
+			interpol);
+	BufferedImage mirImg = mirOp.filter(img, null);
+
+	if (mirImg == null) {
+		util.dprintln(2, "ERROR: error in mirror");
+		throw new ImageOpException("Unable to mirror");
+	}
+	img = mirImg;
+}
+
+/* check image size and type and store in DocuFile f */
+public boolean checkFile(DocuFile f) throws IOException {
+	// see if f is already loaded
+	if ((reader == null) || (imgFile != f.getFile())) {
+		preloadImage(f.getFile());
+	}
+	Dimension d = new Dimension();
+	d.setSize(reader.getWidth(0), reader.getHeight(0));
+	f.setSize(d);
+//	String t = reader.getFormatName();
+	String t = FileOps.mimeForFile(f.getFile());
+	f.setMimetype(t);
+	f.setChecked(true);
+	return true;
+}
 
 }
--- a/servlet/src/digilib/image/JAIDocuImage.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/image/JAIDocuImage.java	Sun Mar 09 21:37:27 2003 +0100
@@ -318,7 +318,7 @@
 	}
 
 	/* contrast and brightness enhancement */
-	public void enhance(double mult, double add) throws ImageOpException {
+	public void enhance(float mult, float add) throws ImageOpException {
 		RenderedImage enhImg;
 		double[] ma = { mult };
 		double[] aa = { add };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/io/DocuDirCache.java	Sun Mar 09 21:37:27 2003 +0100
@@ -0,0 +1,214 @@
+/* DocuDirCache.java
+
+  Digital Image Library servlet components
+
+  Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de)
+
+  This program is free software; you can redistribute  it and/or modify it
+  under  the terms of  the GNU General  Public License as published by the
+  Free Software Foundation;  either version 2 of the  License, or (at your
+  option) any later version.
+   
+  Please read license.txt for the full details. A copy of the GPL
+  may be found at http://www.gnu.org/copyleft/lgpl.html
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ * Created on 03.03.2003
+ */
+
+package digilib.io;
+
+import java.io.File;
+import java.util.Hashtable;
+
+/**
+ * @author casties
+ */
+public class DocuDirCache extends Hashtable {
+
+	// names of base directories
+	private String[] baseDirNames = null;
+	// number of files in the whole cache (not reliable)
+	private long numFiles = 0;
+	// number of cache hits
+	private long hits = 0;
+	// number of cache misses
+	private long misses = 0;
+
+	/* 
+	 * inherited constructors
+	 */
+	public DocuDirCache(int initialCapacity, float loadFactor) {
+		super(initialCapacity, loadFactor);
+	}
+
+	public DocuDirCache(int initialCapacity) {
+		super(initialCapacity);
+	}
+
+	public DocuDirCache() {
+		super();
+	}
+
+	/* 
+	 * new and exiting stuff 
+	 */
+
+	/** Constructor with array of base directory names.
+	 *  
+	 * @param bd base directory names
+	 */
+	public DocuDirCache(String[] bd) {
+		super();
+		baseDirNames = bd;
+	}
+
+	/** Add a DocuDirectory to the cache.
+	 * 
+	 * @param newdir
+	 */
+	public void put(DocuDirectory newdir) {
+		String s = newdir.getDirName();
+		if (containsKey(s)) {
+			System.out.println("Baah, duplicate key in DocuDirectory.put!");
+		} else {
+			super.put(s, newdir);
+			numFiles += newdir.size();
+		}
+	}
+
+	public DocuFileset getFileset(String fn, int in) {
+		DocuDirectory dd;
+		// file number is 1-based, vector index is 0-based
+		int n = in - 1;
+		// first, assume fn is a directory and look in the cache
+		dd = (DocuDirectory) get(fn);
+		if (dd == null) {
+			// cache miss
+			misses++;
+			// see if it's a directory
+			File f = new File(baseDirNames[0] + fn);
+			if (f.isDirectory()) {
+				dd = new DocuDirectory(fn, baseDirNames);
+				if (dd.isValid()) {
+					// add to the cache
+					put(dd);
+				}
+			} else {
+				// maybe it's a file
+				if (f.canRead()) {
+					// try the parent directory in the cache
+					dd = (DocuDirectory) get(f.getParent());
+					if (dd == null) {
+						// try to read from disk
+						dd = new DocuDirectory(f.getParent(), baseDirNames);
+						if (dd.isValid()) {
+							// add to the cache
+							put(dd);
+						} else {
+							// invalid path
+							return null;
+						}
+					}
+					// get the file's index
+					n = dd.indexOf(f.getName());
+				}
+			}
+		} else {
+			// cache hit
+			hits++;
+		}
+		dd.refresh();
+		if (dd.isValid()) {
+			try {
+				return (DocuFileset) dd.elementAt(n);
+			} catch (ArrayIndexOutOfBoundsException e) {
+			}
+		}
+		return null;
+	}
+
+	public DocuDirectory getDirectory(String fn) {
+		DocuDirectory dd;
+		// first, assume fn is a directory and look in the cache
+		dd = (DocuDirectory) get(fn);
+		if (dd == null) {
+			// cache miss
+			misses++;
+			// see if it's a directory
+			File f = new File(baseDirNames[0] + fn);
+			if (f.isDirectory()) {
+				dd = new DocuDirectory(fn, baseDirNames);
+				if (dd.isValid()) {
+					// add to the cache
+					put(dd);
+				}
+			} else {
+				// maybe it's a file
+				if (f.canRead()) {
+					// try the parent directory in the cache
+					dd = (DocuDirectory) get(f.getParent());
+					if (dd == null) {
+						// try to read from disk
+						dd = new DocuDirectory(f.getParent(), baseDirNames);
+						if (dd.isValid()) {
+							// add to the cache
+							put(dd);
+						} else {
+							// invalid path
+							return null;
+						}
+					}
+				}
+			}
+		} else {
+			// cache hit
+			hits++;
+		}
+		dd.refresh();
+		if (dd.isValid()) {
+			return dd;
+		}
+		return null;
+	}
+
+	/**
+	 * @return String[]
+	 */
+	public String[] getBaseDirNames() {
+		return baseDirNames;
+	}
+
+	/**
+	 * @return long
+	 */
+	public long getNumFiles() {
+		return numFiles;
+	}
+
+	/**
+	 * Sets the baseDirNames.
+	 * @param baseDirNames The baseDirNames to set
+	 */
+	public void setBaseDirNames(String[] baseDirNames) {
+		this.baseDirNames = baseDirNames;
+	}
+
+	/**
+	 * @return long
+	 */
+	public long getHits() {
+		return hits;
+	}
+
+	/**
+	 * @return long
+	 */
+	public long getMisses() {
+		return misses;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/io/DocuDirectory.java	Sun Mar 09 21:37:27 2003 +0100
@@ -0,0 +1,263 @@
+/* DocuDirectory -- Directory of DocuFilesets.
+
+  Digital Image Library servlet components
+
+  Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de)
+
+  This program is free software; you can redistribute  it and/or modify it
+  under  the terms of  the GNU General  Public License as published by the
+  Free Software Foundation;  either version 2 of the  License, or (at your
+  option) any later version.
+   
+  Please read license.txt for the full details. A copy of the GPL
+  may be found at http://www.gnu.org/copyleft/lgpl.html
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ * Created on 25.02.2003
+ */
+
+package digilib.io;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * @author casties
+ */
+public class DocuDirectory extends Vector {
+
+	// directory object is valid (has been read)
+	private boolean isValid = false;
+	// names of base directories
+	private String[] baseDirNames = null;
+	// directory name (digilib canonical form)
+	private String dirName = null;
+	// default/hires directory
+	private File dir = null;
+	// directory metadata
+	private Hashtable dirMeta = null;
+	// time of last access of this object (not the filesystem)
+	private long objectATime = 0;
+	// time the file system directory was last modified
+	private long dirMTime = 0;
+
+	/*
+	 * inherited stuff
+	 */
+
+	public DocuDirectory(int initialCapacity, int capacityIncrement) {
+		super(initialCapacity, capacityIncrement);
+	}
+
+	public DocuDirectory(int initialCapacity) {
+		super(initialCapacity);
+	}
+
+	public DocuDirectory(Collection c) {
+		super(c);
+	}
+
+	/*
+	 * new stuff
+	 */
+
+	/** Constructor with directory path and set of base directories.
+	 * 
+	 * Reads the directory at the given path appended to the base directories.
+	 * 
+	 * @see readDir
+	 *  
+	 * @param path digilib directory path name
+	 * @param bd array of base directory names
+	 */
+	public DocuDirectory(String path, String[] bd) {
+		super();
+		dirName = path;
+		baseDirNames = bd;
+		readDir();
+	}
+
+	/** Read the directory and fill this object.
+	 * 
+	 * Clears the Vector and (re)reads all files.
+	 * 
+	 * @return boolean the directory exists
+	 */
+	public boolean readDir() {
+		// clear directory first
+		clear();
+		isValid = false;
+		// number of base dirs
+		int nb = baseDirNames.length;
+		// array of base dirs
+		File[] dirs = new File[nb];
+		// the first directory has to exist
+		dir = new File(baseDirNames[0] + dirName);
+
+		if (dir.isDirectory()) {
+			// fill array with the remaining directories
+			for (int j = 1; j < nb; j++) {
+				File d = new File(baseDirNames[j] + dirName);
+				if (d.isDirectory()) {
+					dirs[j] = d;
+				}
+			}
+
+			File[] fl = dir.listFiles(new FileOps.ImageFileFilter());
+			if (fl == null) {
+				// not a directory
+				return false;
+			}
+			// number of image files
+			int nf = fl.length;
+			if (nf > 0) {
+				// resize Vector
+				this.ensureCapacity(nf);
+
+				// sort the file names alphabetically and iterate the list
+				Arrays.sort(fl);
+				for (int i = 0; i < nf; i++) {
+					String fn = fl[i].getName();
+					// add the first DocuFile to a new DocuFileset 
+					DocuFileset fs = new DocuFileset(nb);
+					fs.add(new DocuFile(fl[i]));
+					// iterate the remaining base directories
+					for (int j = 1; j < nb; j++) {
+						if (dirs[j] == null) {
+							continue;
+						}
+						File f = new File(dirs[j], fn);
+						// if the file exists, add to the DocuFileset
+						if (f.canRead()) {
+							fs.add(new DocuFile(f));
+						}
+					}
+					// add the fileset to our Vector
+					add(fs);
+					fs.setParent(this);
+				}
+			}
+			dirMTime = dir.lastModified();
+			isValid = true;
+		}
+		return isValid;
+
+	}
+
+	/** Check to see if the directory has been modified and reread if necessary.
+	 * 
+	 * @return boolean the directory is valid
+	 */
+	public boolean refresh() {
+		if (isValid) {
+			if (dir.lastModified() > dirMTime) {
+				// on-disk modification time is more recent
+				readDir();
+			}
+			touch();
+		}
+		return isValid;
+	}
+
+	/** Read directory metadata. 
+	 * 
+	 */
+	public void readMeta() {
+		// check for directory metadata...
+	}
+
+	/** Update access time.
+	 * 
+	 * @return long time of last access.
+	 */
+	public long touch() {
+		long t = objectATime;
+		objectATime = System.currentTimeMillis();
+		return t;
+	}
+
+	/** Searches for the file with the name <code>fn</code>.
+	 * 
+	 * Searches the directory for the file with the name <code>fn</code> and returns 
+	 * its index. Returns -1 if the file cannot be found. 
+	 *  
+	 * @param fn filename
+	 * @return int index of file <code>fn</code>
+	 */
+	public int indexOf(String fn) {
+		// linear search -> worst performance
+		for (int i = 0; i < elementCount; i++) {
+			DocuFileset fs = (DocuFileset) get(i);
+			if (fs.getName().equals(fn)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+
+	/** Finds the DocuFileset with the name <code>fn</code>.
+	 * 
+	 * Searches the directory for the DocuFileset with the name <code>fn</code> and returns 
+	 * it. Returns null if the file cannot be found. 
+	 *  
+	 * @param fn filename
+	 * @return DocuFileset
+	 */
+	public DocuFileset find(String fn) {
+		int i = indexOf(fn);
+		if (i >= 0) {
+			return (DocuFileset) get(i);
+		}
+		return null;
+	}
+
+	/**
+	 * @return String
+	 */
+	public String getDirName() {
+		return dirName;
+	}
+
+	/**
+	 * @return boolean
+	 */
+	public boolean isValid() {
+		return isValid;
+	}
+
+	/**
+	 * @return long
+	 */
+	public long getAccessTime() {
+		return objectATime;
+	}
+
+	/**
+	 * @return Hashtable
+	 */
+	public Hashtable getDirMeta() {
+		return dirMeta;
+	}
+
+	/**
+	 * @return long
+	 */
+	public long getDirMTime() {
+		return dirMTime;
+	}
+
+	/**
+	 * Sets the dirMeta.
+	 * @param dirMeta The dirMeta to set
+	 */
+	public void setDirMeta(Hashtable dirMeta) {
+		this.dirMeta = dirMeta;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/io/DocuFile.java	Sun Mar 09 21:37:27 2003 +0100
@@ -0,0 +1,130 @@
+/* DocuFile.java -- digilib image file class.
+
+  Digital Image Library servlet components
+
+  Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de)
+
+  This program is free software; you can redistribute  it and/or modify it
+  under  the terms of  the GNU General  Public License as published by the
+  Free Software Foundation;  either version 2 of the  License, or (at your
+  option) any later version.
+   
+  Please read license.txt for the full details. A copy of the GPL
+  may be found at http://www.gnu.org/copyleft/lgpl.html
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ * Created on 25.02.2003
+ */
+ 
+package digilib.io;
+
+import java.awt.Dimension;
+import java.io.File;
+
+/**
+ * @author casties
+ */
+public class DocuFile {
+	
+	// file object
+	private File file = null;
+	// parent DocuFileset
+	private DocuFileset parent = null;
+	// mime file type
+	private String mimetype = null;
+	// image size in pixels
+	private Dimension pixelSize = null;
+	// image size and type are valid
+	private boolean checked = false;
+
+	public DocuFile(File f) {
+		file = f;
+	}
+	
+	public String getName() {
+		if (file != null) {
+			return file.getName();
+		}
+		return null;
+	}
+
+	/**
+	 * @return File
+	 */
+	public File getFile() {
+		return file;
+	}
+
+	/**
+	 * @return Dimension
+	 */
+	public Dimension getSize() {
+		return pixelSize;
+	}
+
+	/**
+	 * @return String
+	 */
+	public String getMimetype() {
+		return mimetype;
+	}
+
+	/**
+	 * Sets the file.
+	 * @param file The file to set
+	 */
+	public void setFile(File f) {
+		this.file = f;
+		mimetype = FileOps.mimeForFile(f);
+	}
+
+	/**
+	 * Sets the imageSize.
+	 * @param imageSize The imageSize to set
+	 */
+	public void setSize(Dimension imageSize) {
+		this.pixelSize = imageSize;
+	}
+
+	/**
+	 * Sets the mimetype.
+	 * @param mimetype The mimetype to set
+	 */
+	public void setMimetype(String mimetype) {
+		this.mimetype = mimetype;
+	}
+
+	/**
+	 * @return DocuFileset
+	 */
+	public DocuFileset getParent() {
+		return parent;
+	}
+
+	/**
+	 * Sets the parent.
+	 * @param parent The parent to set
+	 */
+	public void setParent(DocuFileset parent) {
+		this.parent = parent;
+	}
+
+	/**
+	 * @return boolean
+	 */
+	public boolean isChecked() {
+		return checked;
+	}
+
+	/**
+	 * Sets the checked.
+	 * @param checked The checked to set
+	 */
+	public void setChecked(boolean checked) {
+		this.checked = checked;
+	}
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/io/DocuFileset.java	Sun Mar 09 21:37:27 2003 +0100
@@ -0,0 +1,85 @@
+/* DocuFileset -- digilib image file info class.
+
+  Digital Image Library servlet components
+
+  Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de)
+
+  This program is free software; you can redistribute  it and/or modify it
+  under  the terms of  the GNU General  Public License as published by the
+  Free Software Foundation;  either version 2 of the  License, or (at your
+  option) any later version.
+   
+  Please read license.txt for the full details. A copy of the GPL
+  may be found at http://www.gnu.org/copyleft/lgpl.html
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+package digilib.io;
+
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * @author casties
+ */
+public class DocuFileset extends Vector {
+
+	// metadata
+	private Hashtable fileMeta = null;
+	// parent directory
+	private DocuDirectory parent = null;
+
+	public DocuFileset(int initialCapacity, int capacityIncrement) {
+		super(initialCapacity, capacityIncrement);
+	}
+
+	public DocuFileset(int initialCapacity) {
+		super(initialCapacity);
+	}
+
+	public DocuFileset() {
+		super();
+	}
+
+	public DocuFileset(Collection c) {
+		super(c);
+	}
+
+	/* (non-Javadoc)
+	 * @see java.util.Collection#add(java.lang.Object)
+	 */
+	public synchronized boolean add(DocuFile f) {
+		f.setParent(this);
+		return super.add(f);
+	}
+
+	public void readMeta() {
+		// check for file metadata...
+	}
+
+	public String getName() {
+		if (this.elementCount > 0) {
+			return ((DocuFile) firstElement()).getName();
+		}
+		return null;
+	}
+	/**
+	 * @return DocuDirectory
+	 */
+	public DocuDirectory getParent() {
+		return parent;
+	}
+
+	/**
+	 * Sets the parent.
+	 * @param parent The parent to set
+	 */
+	public void setParent(DocuDirectory parent) {
+		this.parent = parent;
+	}
+
+}
--- a/servlet/src/digilib/io/FileOps.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/io/FileOps.java	Sun Mar 09 21:37:27 2003 +0100
@@ -78,7 +78,7 @@
     }
     // if fn is a directory name then open directory
     if (f.isDirectory()) {
-      File[] fl = f.listFiles(new ImgFileFilter());
+      File[] fl = f.listFiles(new ImageFileFilter());
       Arrays.sort(fl);
       if ((n > 0) && (n <= fl.length)) {
          return fl[n - 1];
@@ -102,7 +102,7 @@
     }
     // if fn is a directory name then return the number of files
     if (f.isDirectory()) {
-      return f.listFiles(new ImgFileFilter()).length;
+      return f.listFiles(new ImageFileFilter()).length;
     }
     // then fn must be something strange...
     return 0;
@@ -161,7 +161,7 @@
   /**
    *  FileFilter for image types (helper class for getFile)
    */
-  private class ImgFileFilter implements FileFilter {
+  static class ImageFileFilter implements FileFilter {
 
     public boolean accept(File f) {
       if (f.isFile()) {
--- a/servlet/src/digilib/servlet/DigilibConfiguration.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/servlet/DigilibConfiguration.java	Sun Mar 09 21:37:27 2003 +0100
@@ -20,13 +20,20 @@
 
 package digilib.servlet;
 
-import digilib.auth.*;
-import digilib.image.*;
-import digilib.io.XMLListLoader;
+import java.io.File;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+
 import digilib.Utils;
-import javax.servlet.*;
-import java.util.*;
-import java.io.*;
+import digilib.auth.AuthOps;
+import digilib.auth.XMLAuthOps;
+import digilib.image.DocuImage;
+import digilib.image.DocuImageImpl;
+import digilib.io.DocuDirCache;
+import digilib.io.XMLListLoader;
 
 /** Class to hold the digilib servlet configuration parameters.
  * The parameters can be read from the digilib-config file and be passed to
@@ -54,12 +61,9 @@
 	// image file to send if access is denied
 	private String denyImgFileName = "/docuserver/images/icons/denied.gif";
 	private String denyImgParam = "denied-image";
-	// base directories in order of preference (prescaled versions first)
+	// base directories in order of preference (prescaled versions last)
 	private String[] baseDirs =
-		{
-			"/docuserver/scaled/small",
-			"/docuserver/images",
-			"/docuserver/scans/quellen" };
+		{ "/docuserver/images", "/docuserver/scaled/small" };
 	private String baseDirParam = "basedir-list";
 	// use authentication information
 	private boolean useAuthentication = true;
@@ -89,6 +93,8 @@
 	// degree of subsampling on image load
 	private float subsampleDistance = 0;
 	private String subsampleDistanceParam = "subsample-distance";
+	// DocuDirCache instance
+	private DocuDirCache dirCache = null;
 
 	/** Constructor taking a ServletConfig.
 	 * Reads the config file location from an init parameter and loads the
@@ -125,7 +131,7 @@
 		/* 
 		 * read parameters
 		 */
-		 
+
 		// debugLevel
 		debugLevel = tryToGetInitParam(debugLevelParam, debugLevel);
 		util.setDebugLevel(debugLevel);
@@ -142,10 +148,12 @@
 		String baseDirList =
 			tryToGetInitParam(
 				baseDirParam,
-				"/docuserver/scaled/small:/docuserver/images:/docuserver/scans/quellen");
+				"/docuserver/images/:/docuserver/scaled/small/");
 		// split list into directories
 		String[] sa = splitPathArray(baseDirList);
 		baseDirs = (sa != null) ? sa : baseDirs;
+		// directory cache
+		dirCache = new DocuDirCache(baseDirs);
 		// useAuthentication
 		useAuthentication = tryToGetInitParam(useAuthParam, useAuthentication);
 		if (useAuthentication) {
@@ -156,7 +164,8 @@
 			authOp = new XMLAuthOps(util, authConfPath);
 		}
 		// subsampleDistance
-		subsampleDistance = tryToGetInitParam(subsampleDistanceParam, subsampleDistance);
+		subsampleDistance =
+			tryToGetInitParam(subsampleDistanceParam, subsampleDistance);
 	}
 
 	/**
@@ -174,7 +183,13 @@
 		// add directories into array
 		String[] pathArray = new String[n];
 		for (int i = 0; i < n; i++) {
-			pathArray[i] = dirs.nextToken();
+			String s = dirs.nextToken();
+			// make shure the dir name ends with a directory separator
+			if (s.endsWith(File.separator)) {
+				pathArray[i] = s;
+			} else {
+				pathArray[i] = s + File.separator;
+			}
 		}
 		return pathArray;
 	}
@@ -336,7 +351,7 @@
 		String s = "";
 		java.util.Iterator i = java.util.Arrays.asList(baseDirs).iterator();
 		while (i.hasNext()) {
-			s += ( i.next() + "; ");
+			s += (i.next() + "; ");
 		}
 		return s;
 	}
@@ -347,6 +362,7 @@
 	 */
 	public void setBaseDirs(String[] baseDirs) {
 		this.baseDirs = baseDirs;
+		dirCache = new DocuDirCache(baseDirs);
 	}
 
 	/**
@@ -476,4 +492,19 @@
 		this.subsampleDistance = subsampleDistance;
 	}
 
+	/**
+	 * @return DocuDirCache
+	 */
+	public DocuDirCache getDirCache() {
+		return dirCache;
+	}
+
+	/**
+	 * Sets the dirCache.
+	 * @param dirCache The dirCache to set
+	 */
+	public void setDirCache(DocuDirCache dirCache) {
+		this.dirCache = dirCache;
+	}
+
 }
--- a/servlet/src/digilib/servlet/DigilibRequest.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/servlet/DigilibRequest.java	Sun Mar 09 21:37:27 2003 +0100
@@ -26,6 +26,7 @@
 
 package digilib.servlet;
 
+import java.io.File;
 import java.util.StringTokenizer;
 
 import javax.servlet.ServletRequest;
@@ -73,12 +74,16 @@
 	private String pt_s;
 	private String baseURL; // base URL (from http:// to below /servlet)
 	private float rot; // rotation angle in degrees
-	private String rot_s; 
+	private String rot_s;
 	private float cont; // contrast enhancement factor
-	private String cont_s;	
+	private String cont_s;
 	private float brgt; // brightness enhancement factor
-	private String brgt_s;	
-	
+	private String brgt_s;
+	private float[] rgbm; // color multiplicative factors
+	private String rgbm_s;
+	private float[] rgba; // color additive factors
+	private String rgba_s;
+
 	private DocuImage image; // internal DocuImage instance for this request
 	private ServletRequest servletRequest; // internal ServletRequest
 
@@ -109,7 +114,7 @@
 		String qs = ((HttpServletRequest) request).getQueryString();
 		if (request.getParameter("fn") != null) {
 			setWithParamRequest(request);
-		} else if ((qs != null)&&(qs.indexOf("&") > -1)) {
+		} else if ((qs != null) && (qs.indexOf("&") > -1)) {
 			setWithParamRequest(request);
 		} else {
 			setWithOldString(qs);
@@ -313,6 +318,12 @@
 		if (brgt_s != null) {
 			s += "&brgt=" + brgt_s;
 		}
+		if (rgbm_s != null) {
+			s += "&rgbm=" + rgbm_s;
+		}
+		if (rgba_s != null) {
+			s += "&reda=" + rgba_s;
+		}
 		if (pt_s != null) {
 			s += "&pt=" + pt_s;
 		}
@@ -401,6 +412,14 @@
 		if (s != null) {
 			setBrgt(s);
 		}
+		s = request.getParameter("rgbm");
+		if (s != null) {
+			setRgbm(s);
+		}
+		s = request.getParameter("rgba");
+		if (s != null) {
+			setRgba(s);
+		}
 		s = request.getParameter("pt");
 		if (s != null) {
 			setPt(s);
@@ -441,6 +460,10 @@
 		cont_s = null;
 		brgt = 0;
 		brgt_s = null;
+		rgbm = null;
+		rgbm_s = null;
+		rgba = null;
+		rgba_s = null;
 		baseURL = null;
 		image = null;
 		servletRequest = null;
@@ -476,6 +499,9 @@
 		cont_s = null;
 		brgt = 0;
 		brgt_s = null;
+		rgbm = null;
+		rgbm_s = null;
+		rgba = null;
 		baseURL = null;
 		image = null;
 		servletRequest = null;
@@ -498,14 +524,18 @@
 	/** The image file path to be accessed.
 	 * 
 	 * The mage file path is assembled from the servlets RequestPath and
-	 * Parameter fn.
+	 * Parameter fn. The file path never starts with a directory separator.
 	 * 
-	 * @return String the effektive filepath.
+	 * @return String the effective filepath.
 	 */
 	public String getFilePath() {
 		String s = getRequestPath();
 		s += getFn();
-		return s;
+		if (s.startsWith(File.separator)) {
+			return s.substring(1);
+		} else {
+			return s;
+		}
 	}
 
 	/* Property getter and setter */
@@ -918,7 +948,7 @@
 			//util.dprintln(4, "trytoGetParam(int) failed on param "+s);
 		}
 	}
-	
+
 	/**
 	 * Returns the brgt.
 	 * @return float
@@ -944,4 +974,63 @@
 			//util.dprintln(4, "trytoGetParam(int) failed on param "+s);
 		}
 	}
+
+	/**
+	 * @return float[]
+	 */
+	public float[] getRgba() {
+		return rgba;
+	}
+
+	/**
+	 * @return float[]
+	 */
+	public float[] getRgbm() {
+		return rgbm;
+	}
+
+	/**
+	 * Sets the rgba.
+	 * @param rgba The rgba to set
+	 */
+	public void setRgba(float[] rgba) {
+		this.rgba = rgba;
+	}
+	public void setRgba(String s) {
+		try {
+			String[] sa = s.split("/");
+			float[] fa = new float[3];
+			for (int i = 0; i < 3; i++) {
+				float f = Float.parseFloat(sa[i]);
+				fa[i] = f;
+			}
+			this.rgba_s = s;
+			this.rgba = fa;
+		} catch (Exception e) {
+			//util.dprintln(4, "trytoGetParam(int) failed on param "+s);
+		}
+	}
+
+	/**
+	 * Sets the rgbm.
+	 * @param rgbm The rgbm to set
+	 */
+	public void setRgbm(float[] rgbm) {
+		this.rgbm = rgbm;
+	}
+	public void setRgbm(String s) {
+		try {
+			String[] sa = s.split("/");
+			float[] fa = new float[3];
+			for (int i = 0; i < 3; i++) {
+				float f = Float.parseFloat(sa[i]);
+				fa[i] = f;
+			}
+			this.rgbm_s = s;
+			this.rgbm = fa;
+		} catch (Exception e) {
+			//util.dprintln(4, "trytoGetParam(int) failed on param "+s);
+		}
+	}
+
 }
--- a/servlet/src/digilib/servlet/DocumentBean.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/servlet/DocumentBean.java	Sun Mar 09 21:37:27 2003 +0100
@@ -38,6 +38,8 @@
 	private FileOps fileOp = new FileOps(util);
 	// use authorization database
 	private boolean useAuthentication = true;
+	// DocuDirCache
+	private DocuDirCache dirCache = null;
 
 	// DigilibConfiguration object
 	private DigilibConfiguration dlConfig;
@@ -77,6 +79,8 @@
 
 		// get util
 		util = dlConfig.getUtil();
+		// get cache
+		dirCache = dlConfig.getDirCache();
 
 		/*
 		 *  authentication
@@ -130,7 +134,7 @@
 		HttpServletResponse response)
 		throws Exception {
 		util.dprintln(10, "doAuthentication");
-		if (! useAuthentication) {
+		if (!useAuthentication) {
 			// shortcut if no authentication
 			return true;
 		}
@@ -168,10 +172,11 @@
 	 */
 	public int getNumPages(DigilibRequest request) throws Exception {
 		util.dprintln(10, "getNumPages");
-		return fileOp.getNumFilesVariant(
-			dlConfig.getBaseDirs(),
-			"/" + request.getFilePath(),
-			true);
+		DocuDirectory dd = dirCache.getDirectory(request.getFilePath());
+		if (dd != null) {
+			return dd.size();
+		}
+		return 0;
 	}
 
 	/**
--- a/servlet/src/digilib/servlet/Scaler.java	Thu Feb 27 15:07:29 2003 +0100
+++ b/servlet/src/digilib/servlet/Scaler.java	Sun Mar 09 21:37:27 2003 +0100
@@ -20,6 +20,7 @@
 
 package digilib.servlet;
 
+import java.awt.Dimension;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
@@ -39,6 +40,9 @@
 import digilib.auth.AuthOps;
 import digilib.image.DocuImage;
 import digilib.image.ImageOpException;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuFile;
+import digilib.io.DocuFileset;
 import digilib.io.FileOpException;
 import digilib.io.FileOps;
 
@@ -52,7 +56,7 @@
 public class Scaler extends HttpServlet {
 
 	// digilib servlet version (for all components)
-	public static final String dlVersion = "1.6b3";
+	public static final String dlVersion = "1.8b1";
 
 	// Utils instance with debuglevel
 	Utils util;
@@ -62,6 +66,8 @@
 	AuthOps authOp;
 	// ServletOps instance
 	ServletOps servletOp;
+	// DocuDirCache instance
+	DocuDirCache dirCache;
 
 	// DigilibParameters instance
 	DigilibConfiguration dlConfig;
@@ -84,11 +90,12 @@
 		// see if there is a Configuration instance
 		dlConfig =
 			(DigilibConfiguration) context.getAttribute(
-				"digilib.servlet.parameters");
+				"digilib.servlet.configuration");
 		if (dlConfig == null) {
 			// create new Configuration
 			try {
 				dlConfig = new DigilibConfiguration(config);
+				context.setAttribute("digilib.servlet.configuration", dlConfig);
 			} catch (Exception e) {
 				throw new ServletException(e);
 			}
@@ -104,6 +111,8 @@
 		fileOp = new FileOps(util);
 		// AuthOps instance
 		servletOp = new ServletOps(util);
+		// DocuDirCache instance
+		dirCache = dlConfig.getDirCache();
 	}
 
 	/** Process the HTTP Get request*/
@@ -192,9 +201,12 @@
 		// rotation angle
 		double paramROT = dlRequest.getRot();
 		// contrast enhancement
-		double paramCONT = dlRequest.getCont();
+		float paramCONT = dlRequest.getCont();
 		// brightness enhancement
-		double paramBRGT = dlRequest.getBrgt();
+		float paramBRGT = dlRequest.getBrgt();
+		// color modification
+		float[] paramRGBM = dlRequest.getRgbm();
+		float[] paramRGBA = dlRequest.getRgba();
 
 		/* operation mode: "fit": always fit to page, 
 		 * "clip": send original resolution cropped, "file": send whole file (if
@@ -249,7 +261,7 @@
 		//"big" try for all file/image actions
 		try {
 
-			// DocuImage instance
+			// new DocuImage instance
 			DocuImage docuImage = dlConfig.getDocuImageInstance();
 			if (docuImage == null) {
 				throw new ImageOpException("Unable to load DocuImage class!");
@@ -297,17 +309,20 @@
 			}
 
 			// find the file
-			File fileToLoad =
-				fileOp.getFileVariant(
-					dlConfig.getBaseDirs(),
-					loadPathName,
-					dlRequest.getPn(),
-					preScaledFirst);
+			DocuFile fileToLoad;
+			DocuFileset fileset = dirCache.getFileset(loadPathName, dlRequest.getPn());
+			// simplistic selection of resolution
+			if (preScaledFirst) {
+				fileToLoad = (DocuFile)fileset.lastElement();
+			} else {
+				fileToLoad = (DocuFile)fileset.firstElement();
+			}
+			util.dprintln(1, "Loading: " + fileToLoad.getFile());
 
-			util.dprintln(1, "Loading: " + fileToLoad);
-
-			// get the source image type (if it's known)
-			mimeType = FileOps.mimeForFile(fileToLoad);
+			// check the source image
+			docuImage.checkFile(fileToLoad);
+			// get the source image type
+			mimeType = fileToLoad.getMimetype();
 
 			/* if autoScale and not zoomed and source is GIF/PNG 
 			 * then send as is.
@@ -320,7 +335,7 @@
 
 				util.dprintln(1, "Sending File as is.");
 
-				servletOp.sendFile(fileToLoad, response);
+				servletOp.sendFile(fileToLoad.getFile(), response);
 
 				util.dprintln(
 					1,
@@ -330,22 +345,24 @@
 				return;
 			}
 
-			// finally load the file
-			if (docuImage.isPreloadSupported()) {
-				// only preload if supported
-				docuImage.preloadImage(fileToLoad);
-			} else {
-				docuImage.loadImage(fileToLoad);
-			}
-
 			/*
 			 *  crop and scale the image
 			 */
 
+			int imgWidth = 0;
+			int imgHeight = 0;
 			// get image size
-			int imgWidth = docuImage.getWidth();
-			int imgHeight = docuImage.getHeight();
-
+			if (fileToLoad.getSize() == null) {
+				// size unknown so far 
+				imgWidth = docuImage.getWidth();
+				imgHeight = docuImage.getHeight();
+				// remember size
+				fileToLoad.setSize(new Dimension(imgWidth, imgHeight));
+			} else {
+				imgWidth = fileToLoad.getSize().width;
+				imgHeight = fileToLoad.getSize().height;
+			}
+			
 			util.dprintln(2, "IMG: " + imgWidth + "x" + imgHeight);
 			util.dprintln(
 				2,
@@ -520,7 +537,7 @@
 				}
 
 				docuImage.loadSubimage(
-					fileToLoad,
+					fileToLoad.getFile(),
 					imgArea.getBounds(),
 					(int) subsamp);
 
@@ -535,6 +552,8 @@
 				docuImage.scale(scaleXY);
 
 			} else {
+				// else load the whole file
+				docuImage.loadImage(fileToLoad.getFile());
 				docuImage.crop(
 					(int) areaXoff,
 					(int) areaYoff,
@@ -558,9 +577,26 @@
 			// contrast and brightness enhancement
 			if ((paramCONT != 0) || (paramBRGT != 0)) {
 				double mult = Math.pow(2, paramCONT);
-				docuImage.enhance(mult, paramBRGT);
+				docuImage.enhance((float)mult, (float)paramBRGT);
 			}
 
+			// color modification
+			if ((paramRGBM != null) || (paramRGBA != null)) {
+				// make shure we actually have two arrays 
+				if (paramRGBM == null) {
+					paramRGBM = new float[3];
+				}
+				if (paramRGBA == null) {
+					paramRGBA = new float[3];
+				}
+				// calculate "contrast" values
+				float[] mult = new float[3];
+				for (int i = 0; i < 3; i++) {
+					mult[i] = (float)Math.pow(2, (double)paramRGBM[i]);
+				}
+				docuImage.enhanceRGB(mult, paramRGBA);
+			}
+			
 			util.dprintln(
 				2,
 				"time " + (System.currentTimeMillis() - startTime) + "ms");