1 /* 2 * Copyright 2007 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 17 package com.google.zxing; 18 19 import com.google.zxing.aztec.AztecReader; 20 import com.google.zxing.datamatrix.DataMatrixReader; 21 import com.google.zxing.maxicode.MaxiCodeReader; 22 import com.google.zxing.oned.MultiFormatOneDReader; 23 import com.google.zxing.pdf417.PDF417Reader; 24 import com.google.zxing.qrcode.QRCodeReader; 25 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Map; 29 30 /** 31 * MultiFormatReader is a convenience class and the main entry point into the library for most uses. 32 * By default it attempts to decode all barcode formats that the library supports. Optionally, you 33 * can provide a hints object to request different behavior, for example only decoding QR codes. 34 * 35 * @author Sean Owen 36 * @author dswitkin@google.com (Daniel Switkin) 37 */ 38 public final class MultiFormatReader implements Reader { 39 40 private static final Reader[] EMPTY_READER_ARRAY = new Reader[0]; 41 42 private Map<DecodeHintType,?> hints; 43 private Reader[] readers; 44 45 /** 46 * This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it 47 * passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly. 48 * Use setHints() followed by decodeWithState() for continuous scan applications. 49 * 50 * @param image The pixel data to decode 51 * @return The contents of the image 52 * @throws NotFoundException Any errors which occurred 53 */ 54 @Override decode(BinaryBitmap image)55 public Result decode(BinaryBitmap image) throws NotFoundException { 56 setHints(null); 57 return decodeInternal(image); 58 } 59 60 /** 61 * Decode an image using the hints provided. Does not honor existing state. 62 * 63 * @param image The pixel data to decode 64 * @param hints The hints to use, clearing the previous state. 65 * @return The contents of the image 66 * @throws NotFoundException Any errors which occurred 67 */ 68 @Override decode(BinaryBitmap image, Map<DecodeHintType,?> hints)69 public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException { 70 setHints(hints); 71 return decodeInternal(image); 72 } 73 74 /** 75 * Decode an image using the state set up by calling setHints() previously. Continuous scan 76 * clients will get a <b>large</b> speed increase by using this instead of decode(). 77 * 78 * @param image The pixel data to decode 79 * @return The contents of the image 80 * @throws NotFoundException Any errors which occurred 81 */ decodeWithState(BinaryBitmap image)82 public Result decodeWithState(BinaryBitmap image) throws NotFoundException { 83 // Make sure to set up the default state so we don't crash 84 if (readers == null) { 85 setHints(null); 86 } 87 return decodeInternal(image); 88 } 89 90 /** 91 * This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls 92 * to decodeWithState(image) can reuse the same set of readers without reallocating memory. This 93 * is important for performance in continuous scan clients. 94 * 95 * @param hints The set of hints to use for subsequent calls to decode(image) 96 */ setHints(Map<DecodeHintType,?> hints)97 public void setHints(Map<DecodeHintType,?> hints) { 98 this.hints = hints; 99 100 boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); 101 @SuppressWarnings("unchecked") 102 Collection<BarcodeFormat> formats = 103 hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS); 104 Collection<Reader> readers = new ArrayList<>(); 105 if (formats != null) { 106 boolean addOneDReader = 107 formats.contains(BarcodeFormat.UPC_A) || 108 formats.contains(BarcodeFormat.UPC_E) || 109 formats.contains(BarcodeFormat.EAN_13) || 110 formats.contains(BarcodeFormat.EAN_8) || 111 formats.contains(BarcodeFormat.CODABAR) || 112 formats.contains(BarcodeFormat.CODE_39) || 113 formats.contains(BarcodeFormat.CODE_93) || 114 formats.contains(BarcodeFormat.CODE_128) || 115 formats.contains(BarcodeFormat.ITF) || 116 formats.contains(BarcodeFormat.RSS_14) || 117 formats.contains(BarcodeFormat.RSS_EXPANDED); 118 // Put 1D readers upfront in "normal" mode 119 if (addOneDReader && !tryHarder) { 120 readers.add(new MultiFormatOneDReader(hints)); 121 } 122 if (formats.contains(BarcodeFormat.QR_CODE)) { 123 readers.add(new QRCodeReader()); 124 } 125 if (formats.contains(BarcodeFormat.DATA_MATRIX)) { 126 readers.add(new DataMatrixReader()); 127 } 128 if (formats.contains(BarcodeFormat.AZTEC)) { 129 readers.add(new AztecReader()); 130 } 131 if (formats.contains(BarcodeFormat.PDF_417)) { 132 readers.add(new PDF417Reader()); 133 } 134 if (formats.contains(BarcodeFormat.MAXICODE)) { 135 readers.add(new MaxiCodeReader()); 136 } 137 // At end in "try harder" mode 138 if (addOneDReader && tryHarder) { 139 readers.add(new MultiFormatOneDReader(hints)); 140 } 141 } 142 if (readers.isEmpty()) { 143 if (!tryHarder) { 144 readers.add(new MultiFormatOneDReader(hints)); 145 } 146 147 readers.add(new QRCodeReader()); 148 readers.add(new DataMatrixReader()); 149 readers.add(new AztecReader()); 150 readers.add(new PDF417Reader()); 151 readers.add(new MaxiCodeReader()); 152 153 if (tryHarder) { 154 readers.add(new MultiFormatOneDReader(hints)); 155 } 156 } 157 this.readers = readers.toArray(EMPTY_READER_ARRAY); 158 } 159 160 @Override reset()161 public void reset() { 162 if (readers != null) { 163 for (Reader reader : readers) { 164 reader.reset(); 165 } 166 } 167 } 168 decodeInternal(BinaryBitmap image)169 private Result decodeInternal(BinaryBitmap image) throws NotFoundException { 170 if (readers != null) { 171 for (Reader reader : readers) { 172 if (Thread.currentThread().isInterrupted()) { 173 throw NotFoundException.getNotFoundInstance(); 174 } 175 try { 176 return reader.decode(image, hints); 177 } catch (ReaderException re) { 178 // continue 179 } 180 } 181 if (hints != null && hints.containsKey(DecodeHintType.ALSO_INVERTED)) { 182 // Calling all readers again with inverted image 183 image.getBlackMatrix().flip(); 184 for (Reader reader : readers) { 185 if (Thread.currentThread().isInterrupted()) { 186 throw NotFoundException.getNotFoundInstance(); 187 } 188 try { 189 return reader.decode(image, hints); 190 } catch (ReaderException re) { 191 // continue 192 } 193 } 194 } 195 } 196 throw NotFoundException.getNotFoundInstance(); 197 } 198 199 } 200