# HG changeset patch # User robcast # Date 1126727947 -7200 # Node ID 5ded9e06cd5713e4acbad117c0b3dce0eabf77e7 # Parent 269d1028dfb70218ecdd1822a66f77f05f70b7e7 Servlet version 1.5.9b - added mo=png option to enforce PNG output - set higher JPEG quality for mo=q2 (only ImageLoaderDocuImage) diff -r 269d1028dfb7 -r 5ded9e06cd57 servlet/src/digilib/image/ImageLoaderDocuImage.java --- 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; + } + } } diff -r 269d1028dfb7 -r 5ded9e06cd57 servlet/src/digilib/image/ImageOps.java --- 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 diff -r 269d1028dfb7 -r 5ded9e06cd57 servlet/src/digilib/servlet/DigilibImageWorker.java --- 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 { diff -r 269d1028dfb7 -r 5ded9e06cd57 servlet/src/digilib/servlet/Scaler.java --- 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()) {