Changed the order of the BaseMonochromeBitmapSource constructor arguments to be width...
[zxing.git] / core / src / com / google / zxing / common / BaseMonochromeBitmapSource.java
index e15abb2..0e6699e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Google Inc.
+ * Copyright 2008 ZXing authors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,20 +24,34 @@ import com.google.zxing.ReaderException;
  */
 public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSource {
 
-  private int blackPoint;
-  private BlackPointEstimationMethod lastMethod;
-  private int lastArgument;
-
   private static final int LUMINANCE_BITS = 5;
   private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
   private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
 
-  public BaseMonochromeBitmapSource() {
+  private final int height;
+  private final int width;
+  private int blackPoint;
+  private BlackPointEstimationMethod lastMethod;
+  private int lastArgument;
+  private int[] luminances;
+
+  protected BaseMonochromeBitmapSource(int width, int height) {
+    this.height = height;
+    this.width = width;
     blackPoint = 0x7F;
     lastMethod = null;
     lastArgument = 0;
   }
 
+  private void initLuminances() {
+    if (luminances == null) {
+      int width = getWidth();
+      int height = getHeight();
+      int max = width > height ? width : height;
+      luminances = new int[max];
+    }
+  }
+
   public boolean isBlack(int x, int y) {
     return getLuminance(x, y) < blackPoint;
   }
@@ -49,15 +63,17 @@ public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSour
       row.clear();
     }
 
+    // Reuse the same int array each time
+    initLuminances();
+    luminances = getLuminanceRow(y, luminances);
+
     // If the current decoder calculated the blackPoint based on one row, assume we're trying to
     // decode a 1D barcode, and apply some sharpening.
-    // TODO: We may want to add a fifth parameter to request the amount of shapening to be done.
-    cacheRowForLuminance(y);
-    if (lastMethod == BlackPointEstimationMethod.ROW_SAMPLING) {
-      int left = getLuminance(startX, y);
-      int center = getLuminance(startX + 1, y);
+    if (lastMethod.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {
+      int left = luminances[startX];
+      int center = luminances[startX + 1];
       for (int x = 1; x < getWidth - 1; x++) {
-        int right = getLuminance(startX + x + 1, y);
+        int right = luminances[startX + x + 1];
         // Simple -1 4 -1 box filter with a weight of 2
         int luminance = ((center << 2) - left - right) >> 1;
         if (luminance < blackPoint) {
@@ -68,7 +84,7 @@ public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSour
       }
     } else {
       for (int x = 0; x < getWidth; x++) {
-        if (getLuminance(startX + x, y) < blackPoint) {
+        if (luminances[startX + x] < blackPoint) {
           row.set(x);
         }
       }
@@ -76,10 +92,25 @@ public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSour
     return row;
   }
 
-  public abstract int getHeight();
-  public abstract int getWidth();
-  public abstract int getLuminance(int x, int y);
-  public abstract void cacheRowForLuminance(int y);
+  public BitArray getBlackColumn(int x, BitArray column, int startY, int getHeight) {
+    if (column == null || column.getSize() < getHeight) {
+      column = new BitArray(getHeight);
+    } else {
+      column.clear();
+    }
+
+    // Reuse the same int array each time
+    initLuminances();
+    luminances = getLuminanceColumn(x, luminances);
+
+    // We don't handle "row sampling" specially here
+    for (int y = 0; y < getHeight; y++) {
+      if (luminances[startY + y] < blackPoint) {
+        column.set(y);
+      }
+    }
+    return column;
+  }
 
   public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
     if (!method.equals(lastMethod) || argument != lastArgument) {
@@ -98,10 +129,10 @@ public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSour
         if (argument < 0 || argument >= height) {
           throw new IllegalArgumentException("Row is not within the image: " + argument);
         }
-        cacheRowForLuminance(argument);
+        initLuminances();
+        luminances = getLuminanceRow(argument, luminances);
         for (int x = 0; x < width; x++) {
-          int luminance = getLuminance(x, argument);
-          histogram[luminance >> LUMINANCE_SHIFT]++;
+          histogram[luminances[x] >> LUMINANCE_SHIFT]++;
         }
       } else {
         throw new IllegalArgumentException("Unknown method: " + method);
@@ -117,12 +148,56 @@ public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSour
   }
 
   public MonochromeBitmapSource rotateCounterClockwise() {
-    throw new IllegalStateException("Rotate not supported");
+    throw new IllegalArgumentException("Rotate not supported");
   }
 
   public boolean isRotateSupported() {
     return false;
   }
 
+  public final int getHeight() {
+    return height;
+  }
+
+  public final int getWidth() {
+    return width;
+  }
+
+  // These methods below should not need to exist because they are defined in the interface that
+  // this abstract class implements. However this seems to cause problems on some Nokias.
+  // So we write these redundant declarations.
+
+  /**
+   * Retrieves the luminance at the pixel x,y in the bitmap. This method is only used for estimating
+   * the black point and implementing getBlackRow() - it is not meant for decoding, hence it is not
+   * part of MonochromeBitmapSource itself, and is protected.
+   *
+   * @param x The x coordinate in the image.
+   * @param y The y coordinate in the image.
+   * @return The luminance value between 0 and 255.
+   */
+  protected abstract int getLuminance(int x, int y);
+
+  /**
+   * This is the main mechanism for retrieving luminance data. It is dramatically more efficient
+   * than repeatedly calling getLuminance(). As above, this is not meant for decoders.
+   *
+   * @param y The row to fetch
+   * @param row The array to write luminance values into. It is <b>strongly</b> suggested that you
+   *            allocate this yourself, making sure row.length >= getWidth(), and reuse the same
+   *            array on subsequent calls for performance. If you pass null, you will be flogged,
+   *            but then I will take pity on you and allocate a sufficient array internally.
+   * @return The array containing the luminance data. This is the same as row if it was usable.
+   */
+  protected abstract int[] getLuminanceRow(int y, int[] row);
+
+  /**
+   * The same as getLuminanceRow(), but for columns.
+   *
+   * @param x The column to fetch
+   * @param column The array to write luminance values into. See above.
+   * @return The array containing the luminance data.
+   */
+  protected abstract int[] getLuminanceColumn(int x, int[] column);
 
 }