2e16a73a3dc97e1bcf4f969e8b8d364dd63c2f5d
[zxing.git] / core / src / com / google / zxing / common / BaseMonochromeBitmapSource.java
1 /*
2  * Copyright 2008 ZXing authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.zxing.common;
17
18 import com.google.zxing.MonochromeBitmapSource;
19 import com.google.zxing.BlackPointEstimationMethod;
20 import com.google.zxing.ReaderException;
21
22 /**
23  * @author dswitkin@google.com (Daniel Switkin)
24  */
25 public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSource {
26
27   private final int height;
28   private final int width;
29   private int blackPoint;
30   private BlackPointEstimationMethod lastMethod;
31   private int lastArgument;
32   private int[] luminances = null;
33
34   protected BaseMonochromeBitmapSource(int width, int height) {
35     this.height = height;
36     this.width = width;
37     blackPoint = 0x7F;
38     lastMethod = null;
39     lastArgument = 0;
40   }
41
42   private void initLuminances() {
43     if (luminances == null) {
44       int max = width > height ? width : height;
45       luminances = new int[max];
46     }
47   }
48
49   public boolean isBlack(int x, int y) {
50     return getLuminance(x, y) < blackPoint;
51   }
52
53   public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) {
54     if (row == null || row.getSize() < getWidth) {
55       row = new BitArray(getWidth);
56     } else {
57       row.clear();
58     }
59
60     // Reuse the same int array each time
61     initLuminances();
62     luminances = getLuminanceRow(y, luminances);
63
64     // If the current decoder calculated the blackPoint based on one row, assume we're trying to
65     // decode a 1D barcode, and apply some sharpening.
66     if (lastMethod.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {
67       int left = luminances[startX];
68       int center = luminances[startX + 1];
69       for (int x = 1; x < getWidth - 1; x++) {
70         int right = luminances[startX + x + 1];
71         // Simple -1 4 -1 box filter with a weight of 2
72         int luminance = ((center << 2) - left - right) >> 1;
73         if (luminance < blackPoint) {
74           row.set(x);
75         }
76         left = center;
77         center = right;
78       }
79     } else {
80       for (int x = 0; x < getWidth; x++) {
81         if (luminances[startX + x] < blackPoint) {
82           row.set(x);
83         }
84       }
85     }
86     return row;
87   }
88
89   public BitArray getBlackColumn(int x, BitArray column, int startY, int getHeight) {
90     if (column == null || column.getSize() < getHeight) {
91       column = new BitArray(getHeight);
92     } else {
93       column.clear();
94     }
95
96     // Reuse the same int array each time
97     initLuminances();
98     luminances = getLuminanceColumn(x, luminances);
99
100     // We don't handle "row sampling" specially here
101     for (int y = 0; y < getHeight; y++) {
102       if (luminances[startY + y] < blackPoint) {
103         column.set(y);
104       }
105     }
106     return column;
107   }
108
109   public void estimateBlackPoint(BlackPointEstimationMethod method, int argument)
110       throws ReaderException {
111     if (!method.equals(lastMethod) || argument != lastArgument) {
112       blackPoint = BlackPointEstimator.estimate(this, method, argument);
113       lastMethod = method;
114       lastArgument = argument;
115     }
116   }
117
118   public BlackPointEstimationMethod getLastEstimationMethod() {
119     return lastMethod;
120   }
121
122   public MonochromeBitmapSource rotateCounterClockwise() {
123     throw new IllegalArgumentException("Rotate not supported");
124   }
125
126   public boolean isRotateSupported() {
127     return false;
128   }
129
130   public final int getHeight() {
131     return height;
132   }
133
134   public final int getWidth() {
135     return width;
136   }
137
138   public String toString() {
139     StringBuffer result = new StringBuffer(height * (width + 1));
140     BitArray row = new BitArray(width);
141     for (int i = 0; i < height; i++) {
142       row = getBlackRow(i, row, 0, width);
143       for (int j = 0; j < width; j++) {
144         result.append(row.get(j) ? "X " : "  ");
145       }
146       result.append('\n');
147     }
148     return result.toString();
149   }
150
151
152   // These methods below should not need to exist because they are defined in the interface that
153   // this abstract class implements. However this seems to cause problems on some Nokias.
154   // So we write these redundant declarations.
155
156   /**
157    * Retrieves the luminance at the pixel x,y in the bitmap. This method is only used for estimating
158    * the black point and implementing getBlackRow() - it is not meant for decoding, hence it is not
159    * part of MonochromeBitmapSource itself, and is protected.
160    *
161    * @param x The x coordinate in the image.
162    * @param y The y coordinate in the image.
163    * @return The luminance value between 0 and 255.
164    */
165   public abstract int getLuminance(int x, int y);
166
167   /**
168    * This is the main mechanism for retrieving luminance data. It is dramatically more efficient
169    * than repeatedly calling getLuminance(). As above, this is not meant for decoders.
170    *
171    * @param y The row to fetch
172    * @param row The array to write luminance values into. It is <b>strongly</b> suggested that you
173    *            allocate this yourself, making sure row.length >= getWidth(), and reuse the same
174    *            array on subsequent calls for performance. If you pass null, you will be flogged,
175    *            but then I will take pity on you and allocate a sufficient array internally.
176    * @return The array containing the luminance data. This is the same as row if it was usable.
177    */
178   public abstract int[] getLuminanceRow(int y, int[] row);
179
180   /**
181    * The same as getLuminanceRow(), but for columns.
182    *
183    * @param x The column to fetch
184    * @param column The array to write luminance values into. See above.
185    * @return The array containing the luminance data.
186    */
187   public abstract int[] getLuminanceColumn(int x, int[] column);
188
189 }