Use TRY_HARDER hint in javase CommandLineRunner. TRY_HARDER now tries rotating the image when dealing with 1D barcodes. Clarified and fixed value type of several items in the "hints" Hashtables.

git-svn-id: https://zxing.googlecode.com/svn/trunk@235 59b500cc-1b3d-0410-9834-0bbf25fbcc57
diff --git a/android/src/com/google/zxing/client/android/YUVMonochromeBitmapSource.java b/android/src/com/google/zxing/client/android/YUVMonochromeBitmapSource.java
index e2614c1..3df1855 100755
--- a/android/src/com/google/zxing/client/android/YUVMonochromeBitmapSource.java
+++ b/android/src/com/google/zxing/client/android/YUVMonochromeBitmapSource.java
@@ -114,4 +114,12 @@
     return lastMethod;
   }
 
+  public MonochromeBitmapSource rotateCounterClockwise() {
+    throw new IllegalStateException("Rotate not supported");
+  }
+
+  public boolean isRotatedSupported() {
+    return false;
+  }
+
 }
\ No newline at end of file
diff --git a/core/src/com/google/zxing/DecodeHintType.java b/core/src/com/google/zxing/DecodeHintType.java
index 8ab4bac..a8e1c83 100644
--- a/core/src/com/google/zxing/DecodeHintType.java
+++ b/core/src/com/google/zxing/DecodeHintType.java
@@ -21,26 +21,35 @@
  * more quickly or accurately decode it. It is up to implementations to decide what,
  * if anything, to do with the information that is supplied.
  *
- * @author srowen@google.com (Sean Owen), dswitkin@google.com (Daniel Switkin)
+ * @author srowen@google.com (Sean Owen)
+ * @author dswitkin@google.com (Daniel Switkin)
  * @see Reader#decode(MonochromeBitmapSource, java.util.Hashtable)
  */
 public final class DecodeHintType {
 
   // No, we can't use an enum here. J2ME doesn't support it.
 
-  /** Unspecified, application-specific hint. */
+  /**
+   * Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
+   */
   public static final DecodeHintType OTHER = new DecodeHintType();
 
-  /** Image is a pure monochrome image of a barcode. */
+  /**
+   * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
+   * use {@link Boolean#TRUE}.
+   */
   public static final DecodeHintType PURE_BARCODE = new DecodeHintType();
 
   /**
    * Image is known to be of one of a few possible formats.
-   * Maps to a collection of {@link BarcodeFormat}s.
+   * Maps to a {@link java.util.Vector} of {@link BarcodeFormat}s.
    */
   public static final DecodeHintType POSSIBLE_FORMATS = new DecodeHintType();
 
-  /** Spend more time to try to find a barcode; optimize for accuracy, not speed. */
+  /**
+   * Spend more time to try to find a barcode; optimize for accuracy, not speed.
+   * Doesn't matter what it maps to; use {@link Boolean#TRUE}.
+   */
   public static final DecodeHintType TRY_HARDER = new DecodeHintType();
 
   private DecodeHintType() {
diff --git a/core/src/com/google/zxing/MonochromeBitmapSource.java b/core/src/com/google/zxing/MonochromeBitmapSource.java
index 180970e..799a6e1 100644
--- a/core/src/com/google/zxing/MonochromeBitmapSource.java
+++ b/core/src/com/google/zxing/MonochromeBitmapSource.java
@@ -76,4 +76,23 @@
    */
   BlackPointEstimationMethod getLastEstimationMethod();
 
+  /**
+   * <p>Optional operation which returns an implementation based on the same underlying
+   * image, but which behaves as if the underlying image had been rotated 90 degrees
+   * counterclockwise. This is useful in the context of 1D barcodes and the
+   * {@link DecodeHintType#TRY_HARDER} decode hint, and is only intended to be
+   * used in non-resource-constrained environments. Hence, implementations
+   * of this class which are only used in resource-constrained mobile environments
+   * don't have a need to implement this.</p>
+   *
+   * @throws IllegalStateException if not supported
+   */
+  MonochromeBitmapSource rotateCounterClockwise();
+
+  /**
+   * @return true iff rotation is supported
+   * @see #rotateCounterClockwise()
+   */
+  boolean isRotatedSupported();
+
 }
diff --git a/core/src/com/google/zxing/MultiFormatReader.java b/core/src/com/google/zxing/MultiFormatReader.java
index 9d1fc02..3032422 100644
--- a/core/src/com/google/zxing/MultiFormatReader.java
+++ b/core/src/com/google/zxing/MultiFormatReader.java
@@ -37,7 +37,7 @@
 
   public Result decode(MonochromeBitmapSource image, Hashtable hints) throws ReaderException {
 
-    Hashtable possibleFormats = hints == null ? null : (Hashtable) hints.get(DecodeHintType.POSSIBLE_FORMATS);
+    Vector possibleFormats = hints == null ? null : (Vector) hints.get(DecodeHintType.POSSIBLE_FORMATS);
     Vector readers = new Vector();
     if (possibleFormats != null) {
       if (possibleFormats.contains(BarcodeFormat.UPC_A) ||
diff --git a/core/src/com/google/zxing/oned/AbstractOneDReader.java b/core/src/com/google/zxing/oned/AbstractOneDReader.java
index f107a48..a543f31 100644
--- a/core/src/com/google/zxing/oned/AbstractOneDReader.java
+++ b/core/src/com/google/zxing/oned/AbstractOneDReader.java
@@ -39,8 +39,20 @@
   }
 
   public final Result decode(MonochromeBitmapSource image, Hashtable hints) throws ReaderException {
+    boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
+    try {
+      return doDecode(image, hints, tryHarder);
+    } catch (ReaderException re) {
+      if (tryHarder && image.isRotatedSupported()) {
+        MonochromeBitmapSource rotatedImage = image.rotateCounterClockwise();
+        return doDecode(rotatedImage, hints, tryHarder);        
+      } else {
+        throw re;
+      }
+    }
+  }
 
-    boolean tryHarder = hints != null && hints.contains(DecodeHintType.TRY_HARDER);
+  private Result doDecode(MonochromeBitmapSource image, Hashtable hints, boolean tryHarder) throws ReaderException {
 
     int width = image.getWidth();
     int height = image.getHeight();
diff --git a/core/src/com/google/zxing/oned/MultiFormatOneDReader.java b/core/src/com/google/zxing/oned/MultiFormatOneDReader.java
index 0963745..df47b89 100644
--- a/core/src/com/google/zxing/oned/MultiFormatOneDReader.java
+++ b/core/src/com/google/zxing/oned/MultiFormatOneDReader.java
@@ -33,7 +33,7 @@
 
   public Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException {
 
-    Hashtable possibleFormats = hints == null ? null : (Hashtable) hints.get(DecodeHintType.POSSIBLE_FORMATS);
+    Vector possibleFormats = hints == null ? null : (Vector) hints.get(DecodeHintType.POSSIBLE_FORMATS);
     Vector readers = new Vector();
     if (possibleFormats != null) {
       if (possibleFormats.contains(BarcodeFormat.EAN_13) ||
diff --git a/core/src/com/google/zxing/oned/MultiFormatUPCEANReader.java b/core/src/com/google/zxing/oned/MultiFormatUPCEANReader.java
index dff4ad5..835504b 100644
--- a/core/src/com/google/zxing/oned/MultiFormatUPCEANReader.java
+++ b/core/src/com/google/zxing/oned/MultiFormatUPCEANReader.java
@@ -35,7 +35,7 @@
 public final class MultiFormatUPCEANReader extends AbstractOneDReader {
 
   public Result decodeRow(int rowNumber, BitArray row, Hashtable hints) throws ReaderException {
-    Hashtable possibleFormats = hints == null ? null : (Hashtable) hints.get(DecodeHintType.POSSIBLE_FORMATS);
+    Vector possibleFormats = hints == null ? null : (Vector) hints.get(DecodeHintType.POSSIBLE_FORMATS);
     Vector readers = new Vector();
     if (possibleFormats != null) {
       if (possibleFormats.contains(BarcodeFormat.EAN_13)) {
diff --git a/javame/src/com/google/zxing/client/j2me/LCDUIImageMonochromeBitmapSource.java b/javame/src/com/google/zxing/client/j2me/LCDUIImageMonochromeBitmapSource.java
index 6430971..3f1e374 100644
--- a/javame/src/com/google/zxing/client/j2me/LCDUIImageMonochromeBitmapSource.java
+++ b/javame/src/com/google/zxing/client/j2me/LCDUIImageMonochromeBitmapSource.java
@@ -108,6 +108,14 @@
     return lastMethod;
   }
 
+  public MonochromeBitmapSource rotateCounterClockwise() {
+    throw new IllegalStateException("Rotate not supported");
+  }
+
+  public boolean isRotatedSupported() {
+    return false;
+  }
+
   /**
    * Extracts luminance from a pixel from this source. By default, the source is assumed to use RGB,
    * so this implementation computes luminance is a function of a red, green and blue components as
diff --git a/javase/src/com/google/zxing/client/j2se/BufferedImageMonochromeBitmapSource.java b/javase/src/com/google/zxing/client/j2se/BufferedImageMonochromeBitmapSource.java
index e17dc25..92174bb 100644
--- a/javase/src/com/google/zxing/client/j2se/BufferedImageMonochromeBitmapSource.java
+++ b/javase/src/com/google/zxing/client/j2se/BufferedImageMonochromeBitmapSource.java
@@ -21,7 +21,10 @@
 import com.google.zxing.common.BitArray;
 import com.google.zxing.common.BlackPointEstimator;
 
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
 import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
 
 /**
  * <p>An implementation based upon {@link BufferedImage}. This provides access to the
@@ -112,6 +115,19 @@
     return lastMethod;
   }
 
+  public MonochromeBitmapSource rotateCounterClockwise() {
+    // 90 degrees counterclockwise:
+    AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, image.getHeight());
+    BufferedImageOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+    BufferedImage rotatedImage = new BufferedImage(image.getHeight(), image.getWidth(), image.getType());
+    op.filter(image, rotatedImage);
+    return new BufferedImageMonochromeBitmapSource(rotatedImage);
+  }
+
+  public boolean isRotatedSupported() {
+    return true;
+  }
+
   /**
    * Extracts luminance from a pixel from this source. By default, the source is assumed to use RGB,
    * so this implementation computes luminance is a function of a red, green and blue components as
diff --git a/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java b/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java
index a6d2335..d27b3a4 100644
--- a/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java
+++ b/javase/src/com/google/zxing/client/j2se/CommandLineRunner.java
@@ -16,6 +16,7 @@
 
 package com.google.zxing.client.j2se;
 
+import com.google.zxing.DecodeHintType;
 import com.google.zxing.MultiFormatReader;
 import com.google.zxing.ReaderException;
 
@@ -24,6 +25,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
+import java.util.Hashtable;
 
 /**
  * <p>Simply attempts to decode the barcode in the image indicated by the single argument
@@ -62,8 +64,10 @@
       return false;
     }
     try {
+      Hashtable hints = new Hashtable();
+      hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
       BufferedImageMonochromeBitmapSource source = new BufferedImageMonochromeBitmapSource(image);
-      String result = new MultiFormatReader().decode(source).getText();
+      String result = new MultiFormatReader().decode(source, hints).getText();
       System.out.println(uri.toString() + ": " + result);
       return true;
     } catch (ReaderException e) {