changeset 352:5ded9e06cd57

Servlet version 1.5.9b - added mo=png option to enforce PNG output - set higher JPEG quality for mo=q2 (only ImageLoaderDocuImage)
author robcast
date Wed, 14 Sep 2005 21:59:07 +0200
parents 269d1028dfb7
children 25095f85ba7c
files servlet/src/digilib/image/ImageLoaderDocuImage.java servlet/src/digilib/image/ImageOps.java servlet/src/digilib/servlet/DigilibImageWorker.java servlet/src/digilib/servlet/Scaler.java
diffstat 4 files changed, 118 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/servlet/src/digilib/image/ImageLoaderDocuImage.java	Wed Aug 10 11:58:05 2005 +0200
+++ b/servlet/src/digilib/image/ImageLoaderDocuImage.java	Wed Sep 14 21:59:07 2005 +0200
@@ -33,12 +33,18 @@
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.util.Iterator;
+import java.util.Locale;
 
+import javax.imageio.IIOImage;
 import javax.imageio.ImageIO;
 import javax.imageio.ImageReadParam;
 import javax.imageio.ImageReader;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
 import javax.imageio.stream.FileImageInputStream;
 import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.ImageOutputStream;
 
 import digilib.io.FileOpException;
 import digilib.io.ImageFile;
@@ -66,7 +72,7 @@
 	public void setQuality(int qual) {
 		quality = qual;
 		renderHint = new RenderingHints(null);
-		//hint.put(RenderingHints.KEY_ANTIALIASING,
+		// hint.put(RenderingHints.KEY_ANTIALIASING,
 		// RenderingHints.VALUE_ANTIALIAS_OFF);
 		// setup interpolation quality
 		if (qual > 0) {
@@ -111,7 +117,7 @@
 	/* load image file */
 	public void loadImage(ImageFile f) throws FileOpException {
 		logger.debug("loadImage " + f.getFile());
-		//System.gc();
+		// System.gc();
 		try {
 			img = ImageIO.read(f.getFile());
 			if (img == null) {
@@ -134,10 +140,10 @@
 			// clean up old reader
 			dispose();
 		}
-		//System.gc();
+		// System.gc();
 		RandomAccessFile rf = new RandomAccessFile(f.getFile(), "r");
 		ImageInputStream istream = new FileImageInputStream(rf);
-		//Iterator readers = ImageIO.getImageReaders(istream);
+		// Iterator readers = ImageIO.getImageReaders(istream);
 		String mt = f.getMimetype();
 		logger.debug("File type:" + mt);
 		Iterator readers = ImageIO.getImageReadersByMIMEType(mt);
@@ -150,7 +156,7 @@
 		while (readers.hasNext()) {
 			logger.debug("ImageIO: next reader: " + readers.next().getClass());
 		}
-		//*/
+		// */
 		reader.setInput(istream);
 		imgFile = f.getFile();
 		return reader;
@@ -160,7 +166,7 @@
 	public void loadSubimage(ImageFile f, Rectangle region, int prescale)
 			throws FileOpException {
 		logger.debug("loadSubimage");
-		//System.gc();
+		// System.gc();
 		try {
 			if ((reader == null) || (imgFile != f.getFile())) {
 				getReader(f);
@@ -189,41 +195,64 @@
 		logger.debug("writeImage");
 		try {
 			// setup output
-			String type = "png";
+			ImageWriter writer = null;
+			ImageOutputStream imgout = ImageIO.createImageOutputStream(ostream);
 			if (mt == "image/jpeg") {
-				type = "jpeg";
+				/*
+				 * JPEG doesn't do transparency so we have to convert any RGBA
+				 * image to RGB :-( *Java2D BUG*
+				 */
+				if (img.getColorModel().hasAlpha()) {
+					logger.debug("BARF: JPEG with transparency!!");
+					int w = img.getWidth();
+					int h = img.getHeight();
+					// BufferedImage.TYPE_INT_RGB seems to be fastest (JDK1.4.1,
+					// OSX)
+					int destType = BufferedImage.TYPE_INT_RGB;
+					BufferedImage img2 = new BufferedImage(w, h, destType);
+					img2.createGraphics().drawImage(img, null, 0, 0);
+					img = img2;
+				}
+				writer = (ImageWriter) ImageIO.getImageWritersByFormatName(
+						"jpeg").next();
+				if (writer == null) {
+					throw new FileOpException("Unable to get JPEG writer");
+				}
+				ImageWriteParam param = writer.getDefaultWriteParam();
+				if (quality > 1) {
+					// change JPEG compression quality
+					if (param.getCompressionMode() != ImageWriteParam.MODE_EXPLICIT) {
+						// work around problem with J2RE 1.4 JPEG writer
+						logger.debug("creating new ImageWriteParam");
+						param = new CompressibleJPEGImageWriteParam(param
+								.getLocale());
+						param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+					}
+					logger.debug("JPEG qual before: "
+							+ Float.toString(param.getCompressionQuality()));
+					param.setCompressionQuality(0.9f);
+					logger.debug("JPEG qual now: "
+							+ Float.toString(param.getCompressionQuality()));
+				}
+				writer.setOutput(imgout);
+				// render output
+				logger.debug("writing");
+				writer.write(null, new IIOImage(img, null, null), param);
 			} else if (mt == "image/png") {
-				type = "png";
+				// render output
+				writer = (ImageWriter) ImageIO.getImageWritersByFormatName(
+						"png").next();
+				if (writer == null) {
+					throw new FileOpException("Unable to get PNG writer");
+				}
+				writer.setOutput(imgout);
+				logger.debug("writing");
+				writer.write(img);
 			} else {
 				// unknown mime type
 				throw new FileOpException("Unknown mime type: " + mt);
 			}
 
-			/*
-			 * JPEG doesn't do transparency so we have to convert any RGBA image
-			 * to RGB :-( *Java2D BUG*
-			 */
-			if ((type == "jpeg") && (img.getColorModel().hasAlpha())) {
-				logger.debug("BARF: JPEG with transparency!!");
-				int w = img.getWidth();
-				int h = img.getHeight();
-				// BufferedImage.TYPE_INT_RGB seems to be fastest (JDK1.4.1,
-				// OSX)
-				int destType = BufferedImage.TYPE_INT_RGB;
-				BufferedImage img2 = new BufferedImage(w, h, destType);
-				img2.createGraphics().drawImage(img, null, 0, 0);
-				img = img2;
-			}
-
-			// render output
-			logger.debug("writing");
-			if (ImageIO.write(img, type, ostream)) {
-				// writing was OK
-				return;
-			} else {
-				throw new FileOpException(
-						"Error writing image: Unknown image format!");
-			}
 		} catch (IOException e) {
 			throw new FileOpException("Error writing image.");
 		}
@@ -255,7 +284,7 @@
 		if (scaledImg == null) {
 			throw new ImageOpException("Unable to scale");
 		}
-		//DEBUG
+		// DEBUG
 		logger.debug("SCALE: " + scale + " ->" + scaledImg.getWidth() + "x"
 				+ scaledImg.getHeight());
 		img = scaledImg;
@@ -263,7 +292,7 @@
 	}
 
 	public void blur(int radius) throws ImageOpException {
-		//DEBUG
+		// DEBUG
 		logger.debug("blur: " + radius);
 		// minimum radius is 2
 		int klen = Math.max(radius, 2);
@@ -298,8 +327,8 @@
 		BufferedImage croppedImg = img.getSubimage(x_off, y_off, width, height);
 		logger.debug("CROP:" + croppedImg.getWidth() + "x"
 				+ croppedImg.getHeight());
-		//DEBUG
-		//    util.dprintln(2, " time
+		// DEBUG
+		// util.dprintln(2, " time
 		// "+(System.currentTimeMillis()-startTime)+"ms");
 		if (croppedImg == null) {
 			throw new ImageOpException("Unable to crop");
@@ -413,7 +442,7 @@
 		rotOp = new AffineTransformOp(trafo, renderHint);
 		BufferedImage rotImg = rotOp.filter(img, null);
 		// calculate new bounding box
-		//Rectangle2D bounds = rotOp.getBounds2D(img);
+		// Rectangle2D bounds = rotOp.getBounds2D(img);
 		if (rotImg == null) {
 			throw new ImageOpException("Unable to rotate");
 		}
@@ -477,4 +506,25 @@
 		img = null;
 	}
 
+	/**
+	 * Modified JPEGImageWriteParam that accepts a compression quality to work
+	 * around problem with J2RE 1.4.
+	 * 
+	 * @author casties
+	 * 
+	 */
+	public class CompressibleJPEGImageWriteParam extends JPEGImageWriteParam {
+
+		public CompressibleJPEGImageWriteParam(Locale arg0) {
+			super(arg0);
+			// TODO Auto-generated constructor stub
+		}
+
+		public void setCompressionQuality(float quality) {
+			if (quality < 0.0F || quality > 1.0F) {
+				throw new IllegalArgumentException("Quality out-of-bounds!");
+			}
+			this.compressionQuality = quality;
+		}
+	}
 }
--- a/servlet/src/digilib/image/ImageOps.java	Wed Aug 10 11:58:05 2005 +0200
+++ b/servlet/src/digilib/image/ImageOps.java	Wed Sep 14 21:59:07 2005 +0200
@@ -46,6 +46,11 @@
 
 	private static Logger logger = Logger.getLogger(ImageOps.class);
 
+	public static int TYPE_AUTO = 0;
+	public static int TYPE_JPEG = 1;
+	public static int TYPE_PNG = 2;
+	
+	
 	/** Check image size and type and store in ImageFile f */
 	public static boolean checkFile(ImageFile imgf) throws IOException {
 		// fileset to store the information
--- a/servlet/src/digilib/servlet/DigilibImageWorker.java	Wed Aug 10 11:58:05 2005 +0200
+++ b/servlet/src/digilib/servlet/DigilibImageWorker.java	Wed Sep 14 21:59:07 2005 +0200
@@ -28,6 +28,7 @@
 
 import digilib.image.DocuImage;
 import digilib.image.ImageOpException;
+import digilib.image.ImageOps;
 import digilib.io.FileOpException;
 import digilib.io.ImageFile;
 
@@ -81,7 +82,7 @@
 
 	boolean wholeRotArea;
 
-	private boolean forceJPEG;
+	int forceType;
 
 	/**
 	 * @param dlConfig
@@ -100,7 +101,7 @@
 	 * @param innerUserImgArea
 	 * @param minSubsample
 	 * @param wholeRotArea
-	 * @param forceJPEG
+	 * @param forceType
 	 */
 	public DigilibImageWorker(DigilibConfiguration dlConfig,
 			HttpServletResponse response, String mimeType, int scaleQual,
@@ -108,7 +109,7 @@
 			float paramBRGT, float[] paramRGBM, float[] paramRGBA,
 			ImageFile fileToLoad, float scaleXY, Rectangle2D outerUserImgArea,
 			Rectangle2D innerUserImgArea, float minSubsample,
-			boolean wholeRotArea, boolean forceJPEG) {
+			boolean wholeRotArea, int forceType) {
 		super();
 		this.dlConfig = dlConfig;
 		this.response = response;
@@ -126,7 +127,7 @@
 		this.innerUserImgArea = innerUserImgArea;
 		this.minSubsample = minSubsample;
 		this.wholeRotArea = wholeRotArea;
-		this.forceJPEG = forceJPEG;
+		this.forceType = forceType;
 	}
 
 	/*
@@ -246,10 +247,16 @@
 
 		/* write the resulting image */
 
-		// setup output -- if source is JPG then dest will be JPG else it's
-		// PNG
-		if (forceJPEG
-				|| (mimeType.equals("image/jpeg") || mimeType
+		// setup output -- if output type is forced use that otherwise  
+		// if source is JPG then dest will be JPG else it's PNG
+		if (forceType != ImageOps.TYPE_AUTO) {
+			if (forceType == ImageOps.TYPE_JPEG) {
+				mimeType = "image/jpeg";				
+			}
+			if (forceType == ImageOps.TYPE_PNG) {
+				mimeType = "image/png";				
+			}
+		} else if ((mimeType.equals("image/jpeg") || mimeType
 						.equals("image/jp2"))) {
 			mimeType = "image/jpeg";
 		} else {
--- a/servlet/src/digilib/servlet/Scaler.java	Wed Aug 10 11:58:05 2005 +0200
+++ b/servlet/src/digilib/servlet/Scaler.java	Wed Sep 14 21:59:07 2005 +0200
@@ -59,7 +59,7 @@
 	private static final long serialVersionUID = -325080527268912852L;
 
 	/** digilib servlet version (for all components) */
-	public static final String dlVersion = "1.5.8b";
+	public static final String dlVersion = "1.5.9b";
 
 	/** logger for accounting requests */
 	private static Logger accountlog = Logger.getLogger("account.request");
@@ -231,8 +231,8 @@
 		boolean loresOnly = false;
 		// use hires images only
 		boolean hiresOnly = false;
-		// send the image always as JPEG
-		boolean forceJPEG = false;
+		// send the image always as a specific type (e.g. JPEG or PNG)
+		int forceType = ImageOps.TYPE_AUTO;
 		// interpolation to use for scaling
 		int scaleQual = defaultQuality;
 		// send html error message (or image file)
@@ -343,7 +343,11 @@
 		}
 		// operation mode: "jpg": always use JPEG
 		if (dlRequest.hasOption("mo", "jpg")) {
-			forceJPEG = true;
+			forceType = ImageOps.TYPE_JPEG;
+		}
+		// operation mode: "png": always use PNG
+		if (dlRequest.hasOption("mo", "png")) {
+			forceType = ImageOps.TYPE_PNG;
 		}
 
 		// check with the maximum allowed size (if set)
@@ -636,7 +640,7 @@
 					mimeType, scaleQual, dlRequest, paramROT, paramCONT,
 					paramBRGT, paramRGBM, paramRGBA, fileToLoad, scaleXY,
 					outerUserImgArea, innerUserImgArea, minSubsample,
-					wholeRotArea, forceJPEG);
+					wholeRotArea, forceType);
 
 			job.run();
 			if (job.hasError()) {