2 * Copyright 2008 ZXing authors
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.google.zxing.common;
18 import com.google.zxing.MonochromeBitmapSource;
19 import com.google.zxing.BlackPointEstimationMethod;
20 import com.google.zxing.ReaderException;
23 * @author dswitkin@google.com (Daniel Switkin)
25 public abstract class BaseMonochromeBitmapSource implements MonochromeBitmapSource {
27 private static final int LUMINANCE_BITS = 5;
28 private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS;
29 private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS;
31 private final int height;
32 private final int width;
33 private int blackPoint;
34 private BlackPointEstimationMethod lastMethod;
35 private int lastArgument;
36 private int[] luminances;
38 protected BaseMonochromeBitmapSource(int height, int width) {
46 private void initLuminances() {
47 if (luminances == null) {
48 int width = getWidth();
49 int height = getHeight();
50 int max = width > height ? width : height;
51 luminances = new int[max];
55 public boolean isBlack(int x, int y) {
56 return getLuminance(x, y) < blackPoint;
59 public BitArray getBlackRow(int y, BitArray row, int startX, int getWidth) {
60 if (row == null || row.getSize() < getWidth) {
61 row = new BitArray(getWidth);
66 // Reuse the same int array each time
68 luminances = getLuminanceRow(y, luminances);
70 // If the current decoder calculated the blackPoint based on one row, assume we're trying to
71 // decode a 1D barcode, and apply some sharpening.
72 if (lastMethod.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {
73 int left = luminances[startX];
74 int center = luminances[startX + 1];
75 for (int x = 1; x < getWidth - 1; x++) {
76 int right = luminances[startX + x + 1];
77 // Simple -1 4 -1 box filter with a weight of 2
78 int luminance = ((center << 2) - left - right) >> 1;
79 if (luminance < blackPoint) {
86 for (int x = 0; x < getWidth; x++) {
87 if (luminances[startX + x] < blackPoint) {
95 public BitArray getBlackColumn(int x, BitArray column, int startY, int getHeight) {
96 if (column == null || column.getSize() < getHeight) {
97 column = new BitArray(getHeight);
102 // Reuse the same int array each time
104 luminances = getLuminanceColumn(x, luminances);
106 // We don't handle "row sampling" specially here
107 for (int y = 0; y < getHeight; y++) {
108 if (luminances[startY + y] < blackPoint) {
115 public void estimateBlackPoint(BlackPointEstimationMethod method, int argument) throws ReaderException {
116 if (!method.equals(lastMethod) || argument != lastArgument) {
117 int width = getWidth();
118 int height = getHeight();
119 int[] histogram = new int[LUMINANCE_BUCKETS];
120 if (method.equals(BlackPointEstimationMethod.TWO_D_SAMPLING)) {
121 int minDimension = width < height ? width : height;
122 int startX = (width - minDimension) >> 1;
123 int startY = (height - minDimension) >> 1;
124 for (int n = 0; n < minDimension; n++) {
125 int luminance = getLuminance(startX + n, startY + n);
126 histogram[luminance >> LUMINANCE_SHIFT]++;
128 } else if (method.equals(BlackPointEstimationMethod.ROW_SAMPLING)) {
129 if (argument < 0 || argument >= height) {
130 throw new IllegalArgumentException("Row is not within the image: " + argument);
133 luminances = getLuminanceRow(argument, luminances);
134 for (int x = 0; x < width; x++) {
135 histogram[luminances[x] >> LUMINANCE_SHIFT]++;
138 throw new IllegalArgumentException("Unknown method: " + method);
140 blackPoint = BlackPointEstimator.estimate(histogram) << LUMINANCE_SHIFT;
142 lastArgument = argument;
146 public BlackPointEstimationMethod getLastEstimationMethod() {
150 public MonochromeBitmapSource rotateCounterClockwise() {
151 throw new IllegalArgumentException("Rotate not supported");
154 public boolean isRotateSupported() {
158 public final int getHeight() {
162 public final int getWidth() {
166 // These methods below should not need to exist because they are defined in the interface that
167 // this abstract class implements. However this seems to cause problems on some Nokias.
168 // So we write these redundant declarations.
171 * Retrieves the luminance at the pixel x,y in the bitmap. This method is only used for estimating
172 * the black point and implementing getBlackRow() - it is not meant for decoding, hence it is not
173 * part of MonochromeBitmapSource itself, and is protected.
175 * @param x The x coordinate in the image.
176 * @param y The y coordinate in the image.
177 * @return The luminance value between 0 and 255.
179 protected abstract int getLuminance(int x, int y);
182 * This is the main mechanism for retrieving luminance data. It is dramatically more efficient
183 * than repeatedly calling getLuminance(). As above, this is not meant for decoders.
185 * @param y The row to fetch
186 * @param row The array to write luminance values into. It is <b>strongly</b> suggested that you
187 * allocate this yourself, making sure row.length >= getWidth(), and reuse the same
188 * array on subsequent calls for performance. If you pass null, you will be flogged,
189 * but then I will take pity on you and allocate a sufficient array internally.
190 * @return The array containing the luminance data. This is the same as row if it was usable.
192 protected abstract int[] getLuminanceRow(int y, int[] row);
195 * The same as getLuminanceRow(), but for columns.
197 * @param x The column to fetch
198 * @param column The array to write luminance values into. See above.
199 * @return The array containing the luminance data.
201 protected abstract int[] getLuminanceColumn(int x, int[] column);