changeset 1520:71069c3dfb30

DocuImage hacks are now configurable through docuimage-hacks parameter in config.
author robcast
date Fri, 17 Jun 2016 19:08:39 +0200
parents 6f53339a8a39
children d830c76eebc5
files common/src/main/java/digilib/conf/DigilibConfiguration.java common/src/main/java/digilib/image/DocuImage.java common/src/main/java/digilib/image/DocuImageImpl.java common/src/main/java/digilib/image/ImageLoaderDocuImage.java
diffstat 4 files changed, 106 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/common/src/main/java/digilib/conf/DigilibConfiguration.java	Thu Jun 16 20:38:54 2016 +0200
+++ b/common/src/main/java/digilib/conf/DigilibConfiguration.java	Fri Jun 17 19:08:39 2016 +0200
@@ -80,8 +80,10 @@
         newParameter("digilib.version", getVersion(), null, 's');
         // sending image files as-is allowed
         newParameter("sendfile-allowed", Boolean.TRUE, null, 'f');
-        // Type of DocuImage instance
+        // type of DocuImage instance
         newParameter("docuimage-class", "digilib.image.ImageLoaderDocuImage", null, 'f');
+        // image hacks for DocuImage implementation
+        newParameter("docuimage-hacks", "", null, 'f');
         // degree of subsampling on image load
         newParameter("subsample-minimum", new Float(2f), null, 'f');
         // default scaling quality
@@ -175,7 +177,13 @@
             DocuImage di = DocuImageFactory.getInstance();
             config.newParameter("servlet.docuimage.class", docuImageClass, null, 's');
             config.newParameter("servlet.docuimage.version", di.getVersion(), null, 's');
-            logger.debug("DocuImage ("+docuImageClass+") "+di.getVersion()); 
+            logger.debug("DocuImage ("+docuImageClass+") "+di.getVersion());
+            // set hacks on instance
+            try {
+                docuImageClass.newInstance().setHacks(config.getAsString("docuimage-hacks"));
+            } catch (InstantiationException | IllegalAccessException e) {
+                logger.error("Error creating instance of DocuImage class!");
+            }
             // log supported formats
             StringBuilder fmts = new StringBuilder();
             Iterator<String> dlfs = di.getSupportedFormats();
--- a/common/src/main/java/digilib/image/DocuImage.java	Thu Jun 16 20:38:54 2016 +0200
+++ b/common/src/main/java/digilib/image/DocuImage.java	Fri Jun 17 19:08:39 2016 +0200
@@ -308,4 +308,14 @@
      */
     public String getVersion();
 
+    /**
+     * Set implementation specific image hacks.
+     * 
+     * Sets static class members. Needs to be called only once per class.  
+     * 
+     * Format: comma separated key=value pairs (no spaces!).
+     * 
+     * @param hackString
+     */
+    public void setHacks(String hackString);
 }
--- a/common/src/main/java/digilib/image/DocuImageImpl.java	Thu Jun 16 20:38:54 2016 +0200
+++ b/common/src/main/java/digilib/image/DocuImageImpl.java	Fri Jun 17 19:08:39 2016 +0200
@@ -220,4 +220,12 @@
 
     public abstract void writeImage(String mt, OutputStream ostream) throws ImageOpException, FileOpException;
 
+    /* (non-Javadoc)
+     * @see digilib.image.DocuImage#SetHacks(java.lang.String)
+     */
+    @Override
+    public void setHacks(String hacks) {
+        // doing nothing
+    }
+
 }
--- a/common/src/main/java/digilib/image/ImageLoaderDocuImage.java	Thu Jun 16 20:38:54 2016 +0200
+++ b/common/src/main/java/digilib/image/ImageLoaderDocuImage.java	Fri Jun 17 19:08:39 2016 +0200
@@ -48,7 +48,9 @@
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.util.Arrays;
+import java.util.EnumMap;
 import java.util.Iterator;
+import java.util.Map.Entry;
 
 import javax.imageio.IIOImage;
 import javax.imageio.ImageIO;
@@ -72,7 +74,7 @@
 public class ImageLoaderDocuImage extends ImageInfoDocuImage {
 
     /** DocuImage version */
-    public static final String version = "ImageLoaderDocuImage 2.1.9";
+    public static final String version = "ImageLoaderDocuImage 2.2.0";
 
     /** image object */
     protected BufferedImage img;
@@ -87,7 +89,7 @@
     protected RenderingHints renderHint = null;
 
     /** convolution kernels for blur() */
-    protected static Kernel[] convolutionKernels = { 
+    protected static Kernel[] convolutionKernels = {
             null, new Kernel(1, 1, new float[] { 1f }),
             new Kernel(2, 2, new float[] { 0.25f, 0.25f, 
                                            0.25f, 0.25f }),
@@ -95,25 +97,39 @@
                                            1f / 9f, 1f / 9f, 1f / 9f, 
                                            1f / 9f, 1f / 9f, 1f / 9f }) };
 
-    /* lookup tables for inverting images (byte) */
+    /** lookup table for inverting images (byte) */
     protected static LookupTable invertSingleByteTable;
+    /** lookup table for inverting images (byte) */
     protected static LookupTable invertRgbaByteTable;
-    protected static boolean needsInvertRgba = false;
-    /* RescaleOp for contrast/brightness operation */
-    protected static boolean needsRescaleRgba = false;
-    /* lookup table for false-color */
+    /** lookup table for false-color */
     protected static LookupTable mapBgrByteTable;
-    protected static boolean needsMapBgr = false;
-    /** set destination type to sRGB when loading if available */
-    protected static boolean setDestSrgb = false;
-    /** set destination type to sRGB when loading if available, even for non-RGB images */
-    protected static boolean setDestSrgbForNonRgb = false;
-    /** set destination type for blur operation */
-    protected static boolean setDestForBlur = true;
-    /** set destination type for scale operation */
-    protected static boolean setDestForScale = true;
 
+    /** hacks for specific platform bugs */ 
+    protected static enum Hacks {
+        /** table for LookupOp for inversion needs four channels including alpha */
+        needsInvertRgba,
+        /** table for RescaleOp for enhance needs four channels */
+        needsRescaleRgba,
+        /** destination image type for LookupOp(mapBgrByteTable) needs to be (A)BGR */ 
+        needsMapBgr, 
+        /** set destination type to sRGB (if available) when loading  */
+        setDestSrgb, 
+        /** set destination type to sRGB (if available) when loading, even for non-RGB images */
+        setDestSrgbForNonRgb, 
+        /** set destination type for blur operation */
+        setDestForBlur, 
+        /** set destination type for scale operation */
+        setDestForScale
+    }
+    
+    /** active hacks */
+    protected static EnumMap<Hacks, Boolean> imageHacks = new EnumMap<Hacks, Boolean>(Hacks.class);
+    
     static {
+        // init imageHacks
+        for (Hacks h : Hacks.values()) {
+            imageHacks.put(h, false);
+        }
         /*
          * create static lookup tables
          */
@@ -156,24 +172,57 @@
         if ((os.startsWith("Linux"))
             || (os.startsWith("Mac OS X") && osver.startsWith("10.7"))) {
             // GRAB(WTF?) works for Linux JDK1.6 with transparency
-            needsInvertRgba = true;
+            imageHacks.put(Hacks.needsInvertRgba, true);
             invertRgbaByteTable = new ByteLookupTable(0, new byte[][] { invertByte, invertByte, orderedByte, invertByte });
-            needsRescaleRgba = true;
-            needsMapBgr = true;
+            imageHacks.put(Hacks.needsRescaleRgba, true);
+            imageHacks.put(Hacks.needsMapBgr, true);
         } else if ((os.startsWith("Mac OS X") && (osver.startsWith("10.5") || osver.startsWith("10.6"))) 
             || (os.startsWith("Windows"))) {
-            needsRescaleRgba = true;
+            imageHacks.put(Hacks.needsRescaleRgba, true);
         }
         // this hopefully works for all
         mapBgrByteTable = new ByteLookupTable(0, new byte[][] { mapR, mapG, mapB });
-        logger.debug("ImageIO Hacks: needsRescaleRgba="+needsRescaleRgba+" needsInvertRgba="+needsInvertRgba+
-                " needsMapBgr="+needsMapBgr+" setDestSrgb="+setDestSrgb+" setDestSrgbForNonRgb="+setDestSrgbForNonRgb);
+        imageHacks.put(Hacks.setDestForBlur, true);
+        imageHacks.put(Hacks.setDestForScale, true);
+        // print current hacks
+        StringBuffer msg = new StringBuffer("Default DocuImage Hacks: ");
+        for (Entry<Hacks, Boolean> kv : imageHacks.entrySet()) {
+            msg.append(kv.getKey() + "=" + kv.getValue() + " ");
+        }
+        logger.debug(msg);
     }
 
     /** the size of the current image */
     protected ImageSize imageSize;
 
     /* (non-Javadoc)
+     * @see digilib.image.DocuImageImpl#setHacks(java.lang.String)
+     */
+    @Override
+    public void setHacks(String hackString) {
+        if (hackString == null || hackString.isEmpty()) {
+            return;
+        }
+        // read hack values from String
+        try {
+            for (String h : hackString.split(",")) {
+                String[] hs = h.split("=");
+                Hacks key = Hacks.valueOf(hs[0]);
+                Boolean val = hs[1].equals("true");
+                imageHacks.put(key, val);
+            }
+        } catch (Exception e) {
+            logger.error("Error setting docuimage hacks!", e);
+        }
+        // print current hacks
+        StringBuffer msg = new StringBuffer("DocuImage Hacks: ");
+        for (Entry<Hacks, Boolean> kv : imageHacks.entrySet()) {
+            msg.append(kv.getKey() + "=" + kv.getValue() + " ");
+        }
+        logger.debug(msg);
+    }
+    
+    /* (non-Javadoc)
      * @see digilib.image.DocuImageImpl#getVersion()
      */
     public String getVersion() {
@@ -378,7 +427,7 @@
             if (prescale > 1) {
                 readParam.setSourceSubsampling(prescale, prescale, 0, 0);
             }
-			if (ImageLoaderDocuImage.setDestSrgb) {
+			if (imageHacks.get(Hacks.setDestSrgb)) {
 				/*
 				 * try to set target color space to sRGB
 				 */
@@ -387,7 +436,7 @@
 					ColorModel cm = type.getColorModel();
 					ColorSpace cs = cm.getColorSpace();
 					logger.debug("loadSubimage: possible color model:" + cm + " color space:" + cs);
-					if (cs.getNumComponents() < 3 && !ImageLoaderDocuImage.setDestSrgbForNonRgb) {
+					if (cs.getNumComponents() < 3 && !imageHacks.get(Hacks.setDestSrgbForNonRgb)) {
 						// if the first type is not RGB do nothing
 						logger.debug("loadSubimage: image is not RGB " + type);
 						break;
@@ -552,7 +601,7 @@
         logger.debug("scaled from " + imgW + "x" + imgH + " img=" + img);
         AffineTransformOp scaleOp = new AffineTransformOp(AffineTransform.getScaleInstance(scaleX, scaleY), renderHint);
         BufferedImage dest = null;
-        if (setDestForScale) {
+        if (imageHacks.get(Hacks.setDestForScale)) {
             // keep image type unless we know its unsuitable
             int imgType = img.getType();
             if (imgType != BufferedImage.TYPE_CUSTOM && imgType != BufferedImage.TYPE_BYTE_BINARY 
@@ -598,7 +647,7 @@
         // blur with convolve operation
         ConvolveOp blurOp = new ConvolveOp(blur, ConvolveOp.EDGE_NO_OP, renderHint);
         BufferedImage dest = null;
-        if (setDestForBlur) {
+        if (imageHacks.get(Hacks.setDestForBlur)) {
             int imgType = img.getType();
             // keep image type unless we know its unsuitable (formerly only for 3BYTE_BGR *Java2D BUG*)
             if (imgType != BufferedImage.TYPE_CUSTOM && imgType != BufferedImage.TYPE_BYTE_BINARY 
@@ -698,7 +747,7 @@
     public void enhance(float mult, float add) throws ImageOpException {
         RescaleOp op = null;
         logger.debug("enhance: img=" + img);
-        if (needsRescaleRgba) {
+        if (imageHacks.get(Hacks.needsRescaleRgba)) {
             /*
              * 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
@@ -817,7 +866,7 @@
                 logger.debug("Color op: unable to invert");
                 return;
             }
-            if (needsInvertRgba && cm.hasAlpha()) {
+            if (imageHacks.get(Hacks.needsInvertRgba) && cm.hasAlpha()) {
                 // fix for some cases
                 invtbl = invertRgbaByteTable;
             } else {
@@ -835,7 +884,7 @@
             ColorConvertOp grayOp = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), renderHint);
             // create new 3-channel image
             int destType = BufferedImage.TYPE_INT_RGB;
-            if (needsMapBgr) {
+            if (imageHacks.get(Hacks.needsMapBgr)) {
                 // special case for funny Java2D implementations
                 if (img.getColorModel().hasAlpha()) {
                     destType = BufferedImage.TYPE_4BYTE_ABGR_PRE;