mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
move java files
This commit is contained in:
77
port_src/core/DONE/BarcodeFormat.java
Normal file
77
port_src/core/DONE/BarcodeFormat.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Enumerates barcode formats known to this package. Please keep alphabetized.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum BarcodeFormat {
|
||||
|
||||
/** Aztec 2D barcode format. */
|
||||
AZTEC,
|
||||
|
||||
/** CODABAR 1D format. */
|
||||
CODABAR,
|
||||
|
||||
/** Code 39 1D format. */
|
||||
CODE_39,
|
||||
|
||||
/** Code 93 1D format. */
|
||||
CODE_93,
|
||||
|
||||
/** Code 128 1D format. */
|
||||
CODE_128,
|
||||
|
||||
/** Data Matrix 2D barcode format. */
|
||||
DATA_MATRIX,
|
||||
|
||||
/** EAN-8 1D format. */
|
||||
EAN_8,
|
||||
|
||||
/** EAN-13 1D format. */
|
||||
EAN_13,
|
||||
|
||||
/** ITF (Interleaved Two of Five) 1D format. */
|
||||
ITF,
|
||||
|
||||
/** MaxiCode 2D barcode format. */
|
||||
MAXICODE,
|
||||
|
||||
/** PDF417 format. */
|
||||
PDF_417,
|
||||
|
||||
/** QR Code 2D barcode format. */
|
||||
QR_CODE,
|
||||
|
||||
/** RSS 14 */
|
||||
RSS_14,
|
||||
|
||||
/** RSS EXPANDED */
|
||||
RSS_EXPANDED,
|
||||
|
||||
/** UPC-A 1D format. */
|
||||
UPC_A,
|
||||
|
||||
/** UPC-E 1D format. */
|
||||
UPC_E,
|
||||
|
||||
/** UPC/EAN extension format. Not a stand-alone format. */
|
||||
UPC_EAN_EXTENSION
|
||||
|
||||
}
|
||||
87
port_src/core/DONE/Binarizer.java
Normal file
87
port_src/core/DONE/Binarizer.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
|
||||
* It allows the algorithm to vary polymorphically, for example allowing a very expensive
|
||||
* thresholding technique for servers and a fast one for mobile. It also permits the implementation
|
||||
* to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public abstract class Binarizer {
|
||||
|
||||
private final LuminanceSource source;
|
||||
|
||||
protected Binarizer(LuminanceSource source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public final LuminanceSource getLuminanceSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||
* at a time if needed.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public abstract BitMatrix getBlackMatrix() throws NotFoundException;
|
||||
|
||||
/**
|
||||
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||
*
|
||||
* @param source The LuminanceSource this Binarizer will operate on.
|
||||
* @return A new concrete Binarizer implementation object.
|
||||
*/
|
||||
public abstract Binarizer createBinarizer(LuminanceSource source);
|
||||
|
||||
public final int getWidth() {
|
||||
return source.getWidth();
|
||||
}
|
||||
|
||||
public final int getHeight() {
|
||||
return source.getHeight();
|
||||
}
|
||||
|
||||
}
|
||||
150
port_src/core/DONE/BinaryBitmap.java
Normal file
150
port_src/core/DONE/BinaryBitmap.java
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
|
||||
* accept a BinaryBitmap and attempt to decode it.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class BinaryBitmap {
|
||||
|
||||
private final Binarizer binarizer;
|
||||
private BitMatrix matrix;
|
||||
|
||||
public BinaryBitmap(Binarizer binarizer) {
|
||||
if (binarizer == null) {
|
||||
throw new IllegalArgumentException("Binarizer must be non-null.");
|
||||
}
|
||||
this.binarizer = binarizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return binarizer.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return binarizer.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public BitArray getBlackRow(int y, BitArray row) throws NotFoundException {
|
||||
return binarizer.getBlackRow(y, row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public BitMatrix getBlackMatrix() throws NotFoundException {
|
||||
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||
// reasons for this:
|
||||
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||
// 1D Reader finds a barcode before the 2D Readers run.
|
||||
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||
if (matrix == null) {
|
||||
matrix = binarizer.getBlackMatrix();
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap can be cropped.
|
||||
*/
|
||||
public boolean isCropSupported() {
|
||||
return binarizer.getLuminanceSource().isCropSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
public BinaryBitmap crop(int left, int top, int width, int height) {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height);
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap supports counter-clockwise rotation.
|
||||
*/
|
||||
public boolean isRotateSupported() {
|
||||
return binarizer.getLuminanceSource().isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public BinaryBitmap rotateCounterClockwise() {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise();
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public BinaryBitmap rotateCounterClockwise45() {
|
||||
LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise45();
|
||||
return new BinaryBitmap(binarizer.createBinarizer(newSource));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return getBlackMatrix().toString();
|
||||
} catch (NotFoundException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
47
port_src/core/DONE/ChecksumException.java
Normal file
47
port_src/core/DONE/ChecksumException.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was successfully detected and decoded, but
|
||||
* was not returned because its checksum feature failed.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class ChecksumException extends ReaderException {
|
||||
|
||||
private static final ChecksumException INSTANCE = new ChecksumException();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private ChecksumException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private ChecksumException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static ChecksumException getChecksumInstance() {
|
||||
return isStackTrace ? new ChecksumException() : INSTANCE;
|
||||
}
|
||||
|
||||
public static ChecksumException getChecksumInstance(Throwable cause) {
|
||||
return isStackTrace ? new ChecksumException(cause) : INSTANCE;
|
||||
}
|
||||
}
|
||||
128
port_src/core/DONE/DecodeHintType.java
Normal file
128
port_src/core/DONE/DecodeHintType.java
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Encapsulates a type of hint that a caller may pass to a barcode reader to help it
|
||||
* 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 Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @see Reader#decode(BinaryBitmap,java.util.Map)
|
||||
*/
|
||||
public enum DecodeHintType {
|
||||
|
||||
/**
|
||||
* Unspecified, application-specific hint. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
OTHER(Object.class),
|
||||
|
||||
/**
|
||||
* Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
PURE_BARCODE(Void.class),
|
||||
|
||||
/**
|
||||
* Image is known to be of one of a few possible formats.
|
||||
* Maps to a {@link List} of {@link BarcodeFormat}s.
|
||||
*/
|
||||
POSSIBLE_FORMATS(List.class),
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
TRY_HARDER(Void.class),
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use when decoding, where applicable (type String)
|
||||
*/
|
||||
CHARACTER_SET(String.class),
|
||||
|
||||
/**
|
||||
* Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}.
|
||||
*/
|
||||
ALLOWED_LENGTHS(int[].class),
|
||||
|
||||
/**
|
||||
* Assume Code 39 codes employ a check digit. Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ASSUME_CODE_39_CHECK_DIGIT(Void.class),
|
||||
|
||||
/**
|
||||
* Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
|
||||
* For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
|
||||
* use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ASSUME_GS1(Void.class),
|
||||
|
||||
/**
|
||||
* If true, return the start and end digits in a Codabar barcode instead of stripping them. They
|
||||
* are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
|
||||
* to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
RETURN_CODABAR_START_END(Void.class),
|
||||
|
||||
/**
|
||||
* The caller needs to be notified via callback when a possible {@link ResultPoint}
|
||||
* is found. Maps to a {@link ResultPointCallback}.
|
||||
*/
|
||||
NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class),
|
||||
|
||||
|
||||
/**
|
||||
* Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
|
||||
* Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5].
|
||||
* If it is optional to have an extension, do not set this hint. If this is set,
|
||||
* and a UPC or EAN barcode is found but an extension is not, then no result will be returned
|
||||
* at all.
|
||||
*/
|
||||
ALLOWED_EAN_EXTENSIONS(int[].class),
|
||||
|
||||
/**
|
||||
* If true, also tries to decode as inverted image. All configured decoders are simply called a
|
||||
* second time with an inverted image. Doesn't matter what it maps to; use {@link Boolean#TRUE}.
|
||||
*/
|
||||
ALSO_INVERTED(Void.class),
|
||||
|
||||
// End of enumeration values.
|
||||
;
|
||||
|
||||
/**
|
||||
* Data type the hint is expecting.
|
||||
* Among the possible values the {@link Void} stands out as being used for
|
||||
* hints that do not expect a value to be supplied (flag hints). Such hints
|
||||
* will possibly have their value ignored, or replaced by a
|
||||
* {@link Boolean#TRUE}. Hint suppliers should probably use
|
||||
* {@link Boolean#TRUE} as directed by the actual hint documentation.
|
||||
*/
|
||||
private final Class<?> valueType;
|
||||
|
||||
DecodeHintType(Class<?> valueType) {
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public Class<?> getValueType() {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
}
|
||||
62
port_src/core/DONE/Dimension.java
Normal file
62
port_src/core/DONE/Dimension.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Simply encapsulates a width and height.
|
||||
*/
|
||||
public final class Dimension {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public Dimension(int width, int height) {
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other instanceof Dimension) {
|
||||
Dimension d = (Dimension) other;
|
||||
return width == d.width && height == d.height;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return width * 32713 + height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return width + "x" + height;
|
||||
}
|
||||
|
||||
}
|
||||
177
port_src/core/DONE/EncodeHintType.java
Normal file
177
port_src/core/DONE/EncodeHintType.java
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* These are a set of hints that you may pass to Writers to specify their behavior.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public enum EncodeHintType {
|
||||
|
||||
/**
|
||||
* Specifies what degree of error correction to use, for example in QR Codes.
|
||||
* Type depends on the encoder. For example for QR codes it's type
|
||||
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
|
||||
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
|
||||
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
|
||||
* In all cases, it can also be a {@link String} representation of the desired value as well.
|
||||
* Note: an Aztec symbol should have a minimum of 25% EC words.
|
||||
*/
|
||||
ERROR_CORRECTION,
|
||||
|
||||
/**
|
||||
* Specifies what character encoding to use where applicable (type {@link String})
|
||||
*/
|
||||
CHARACTER_SET,
|
||||
|
||||
/**
|
||||
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
|
||||
*/
|
||||
DATA_MATRIX_SHAPE,
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for Data Matrix (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
* The compact encoding mode also supports the encoding of characters that are not in the ISO-8859-1
|
||||
* character set via ECIs.
|
||||
* Please note that in that case, the most compact character encoding is chosen for characters in
|
||||
* the input that are not in the ISO-8859-1 character set. Based on experience, some scanners do not
|
||||
* support encodings like cp-1256 (Arabic). In such cases the encoding can be forced to UTF-8 by
|
||||
* means of the {@link #CHARACTER_SET} encoding hint.
|
||||
* Compact encoding also provides GS1-FNC1 support when {@link #GS1_FORMAT} is selected. In this case
|
||||
* group-separator character (ASCII 29 decimal) can be used to encode the positions of FNC1 codewords
|
||||
* for the purpose of delimiting AIs.
|
||||
* This option and {@link #FORCE_C40} are mutually exclusive.
|
||||
*/
|
||||
DATA_MATRIX_COMPACT,
|
||||
|
||||
/**
|
||||
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated use width/height params in
|
||||
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
MIN_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
|
||||
*
|
||||
* @deprecated without replacement
|
||||
*/
|
||||
@Deprecated
|
||||
MAX_SIZE,
|
||||
|
||||
/**
|
||||
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
|
||||
* by format; for example it controls margin before and after the barcode horizontally for
|
||||
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
MARGIN,
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String} value).
|
||||
*/
|
||||
PDF417_COMPACT,
|
||||
|
||||
/**
|
||||
* Specifies what compaction mode to use for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
|
||||
* enum values).
|
||||
*/
|
||||
PDF417_COMPACTION,
|
||||
|
||||
/**
|
||||
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
|
||||
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
|
||||
*/
|
||||
PDF417_DIMENSIONS,
|
||||
|
||||
/**
|
||||
* Specifies whether to automatically insert ECIs when encoding PDF417 (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String} value).
|
||||
* Please note that in that case, the most compact character encoding is chosen for characters in
|
||||
* the input that are not in the ISO-8859-1 character set. Based on experience, some scanners do not
|
||||
* support encodings like cp-1256 (Arabic). In such cases the encoding can be forced to UTF-8 by
|
||||
* means of the {@link #CHARACTER_SET} encoding hint.
|
||||
*/
|
||||
PDF417_AUTO_ECI,
|
||||
|
||||
/**
|
||||
* Specifies the required number of layers for an Aztec code.
|
||||
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
|
||||
* 0 indicates to use the minimum number of layers (the default).
|
||||
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
AZTEC_LAYERS,
|
||||
|
||||
/**
|
||||
* Specifies the exact version of QR code to be encoded.
|
||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
QR_VERSION,
|
||||
|
||||
/**
|
||||
* Specifies the QR code mask pattern to be used. Allowed values are
|
||||
* 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select
|
||||
* the optimal mask pattern.
|
||||
* * (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||
*/
|
||||
QR_MASK_PATTERN,
|
||||
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for QR code (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
* Please note that when compaction is performed, the most compact character encoding is chosen
|
||||
* for characters in the input that are not in the ISO-8859-1 character set. Based on experience,
|
||||
* some scanners do not support encodings like cp-1256 (Arabic). In such cases the encoding can
|
||||
* be forced to UTF-8 by means of the {@link #CHARACTER_SET} encoding hint.
|
||||
*/
|
||||
QR_COMPACT,
|
||||
|
||||
/**
|
||||
* Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
*/
|
||||
GS1_FORMAT,
|
||||
|
||||
/**
|
||||
* Forces which encoding will be used. Currently only used for Code-128 code sets (Type {@link String}).
|
||||
* Valid values are "A", "B", "C".
|
||||
* This option and {@link #CODE128_COMPACT} are mutually exclusive.
|
||||
*/
|
||||
FORCE_CODE_SET,
|
||||
|
||||
/**
|
||||
* Forces C40 encoding for data-matrix (type {@link Boolean}, or "true" or "false") {@link String } value). This
|
||||
* option and {@link #DATA_MATRIX_COMPACT} are mutually exclusive.
|
||||
*/
|
||||
FORCE_C40,
|
||||
|
||||
/**
|
||||
* Specifies whether to use compact mode for Code-128 code (type {@link Boolean}, or "true" or "false"
|
||||
* {@link String } value).
|
||||
* This can yield slightly smaller bar codes. This option and {@link #FORCE_CODE_SET} are mutually
|
||||
* exclusive.
|
||||
*/
|
||||
CODE128_COMPACT,
|
||||
|
||||
}
|
||||
47
port_src/core/DONE/FormatException.java
Normal file
47
port_src/core/DONE/FormatException.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was successfully detected, but some aspect of
|
||||
* the content did not conform to the barcode's format rules. This could have
|
||||
* been due to a mis-detection.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class FormatException extends ReaderException {
|
||||
|
||||
private static final FormatException INSTANCE = new FormatException();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private FormatException() {
|
||||
}
|
||||
|
||||
private FormatException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public static FormatException getFormatInstance() {
|
||||
return isStackTrace ? new FormatException() : INSTANCE;
|
||||
}
|
||||
|
||||
public static FormatException getFormatInstance(Throwable cause) {
|
||||
return isStackTrace ? new FormatException(cause) : INSTANCE;
|
||||
}
|
||||
}
|
||||
88
port_src/core/DONE/InvertedLuminanceSource.java
Normal file
88
port_src/core/DONE/InvertedLuminanceSource.java
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class InvertedLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final LuminanceSource delegate;
|
||||
|
||||
public InvertedLuminanceSource(LuminanceSource delegate) {
|
||||
super(delegate.getWidth(), delegate.getHeight());
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
row = delegate.getRow(y, row);
|
||||
int width = getWidth();
|
||||
for (int i = 0; i < width; i++) {
|
||||
row[i] = (byte) (255 - (row[i] & 0xFF));
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
byte[] matrix = delegate.getMatrix();
|
||||
int length = getWidth() * getHeight();
|
||||
byte[] invertedMatrix = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
invertedMatrix[i] = (byte) (255 - (matrix[i] & 0xFF));
|
||||
}
|
||||
return invertedMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return delegate.isCropSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new InvertedLuminanceSource(delegate.crop(left, top, width, height));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRotateSupported() {
|
||||
return delegate.isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return original delegate {@link LuminanceSource} since invert undoes itself
|
||||
*/
|
||||
@Override
|
||||
public LuminanceSource invert() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
return new InvertedLuminanceSource(delegate.rotateCounterClockwise());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource rotateCounterClockwise45() {
|
||||
return new InvertedLuminanceSource(delegate.rotateCounterClockwise45());
|
||||
}
|
||||
|
||||
}
|
||||
157
port_src/core/DONE/LuminanceSource.java
Normal file
157
port_src/core/DONE/LuminanceSource.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* The purpose of this class hierarchy is to abstract different bitmap implementations across
|
||||
* platforms into a standard interface for requesting greyscale luminance values. The interface
|
||||
* only provides immutable methods; therefore crop and rotation create copies. This is to ensure
|
||||
* that one Reader does not modify the original luminance source and leave it in an unknown state
|
||||
* for other Readers in the chain.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public abstract class LuminanceSource {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
protected LuminanceSource(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches one row of luminance data from the underlying platform's bitmap. Values range from
|
||||
* 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
|
||||
* to bitwise and with 0xff for each value. It is preferable for implementations of this method
|
||||
* to only fetch this row rather than the whole image, since no 2D Readers may be installed and
|
||||
* getMatrix() may never be called.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0,getHeight())
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* Always use the returned object, and ignore the .length of the array.
|
||||
* @return An array containing the luminance data.
|
||||
*/
|
||||
public abstract byte[] getRow(int y, byte[] row);
|
||||
|
||||
/**
|
||||
* Fetches luminance data for the underlying bitmap. Values should be fetched using:
|
||||
* {@code int luminance = array[y * width + x] & 0xff}
|
||||
*
|
||||
* @return A row-major 2D array of luminance values. Do not use result.length as it may be
|
||||
* larger than width * height bytes on some platforms. Do not modify the contents
|
||||
* of the result.
|
||||
*/
|
||||
public abstract byte[] getMatrix();
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public final int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public final int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports cropping.
|
||||
*/
|
||||
public boolean isCropSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
throw new UnsupportedOperationException("This luminance source does not support cropping.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports counter-clockwise rotation.
|
||||
*/
|
||||
public boolean isRotateSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*/
|
||||
public LuminanceSource invert() {
|
||||
return new InvertedLuminanceSource(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public LuminanceSource rotateCounterClockwise() {
|
||||
throw new UnsupportedOperationException("This luminance source does not support rotation by 90 degrees.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public LuminanceSource rotateCounterClockwise45() {
|
||||
throw new UnsupportedOperationException("This luminance source does not support rotation by 45 degrees.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
byte[] row = new byte[width];
|
||||
StringBuilder result = new StringBuilder(height * (width + 1));
|
||||
for (int y = 0; y < height; y++) {
|
||||
row = getRow(y, row);
|
||||
for (int x = 0; x < width; x++) {
|
||||
int luminance = row[x] & 0xFF;
|
||||
char c;
|
||||
if (luminance < 0x40) {
|
||||
c = '#';
|
||||
} else if (luminance < 0x80) {
|
||||
c = '+';
|
||||
} else if (luminance < 0xC0) {
|
||||
c = '.';
|
||||
} else {
|
||||
c = ' ';
|
||||
}
|
||||
result.append(c);
|
||||
}
|
||||
result.append('\n');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
199
port_src/core/DONE/MultiFormatReader.java
Normal file
199
port_src/core/DONE/MultiFormatReader.java
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.aztec.AztecReader;
|
||||
import com.google.zxing.datamatrix.DataMatrixReader;
|
||||
import com.google.zxing.maxicode.MaxiCodeReader;
|
||||
import com.google.zxing.oned.MultiFormatOneDReader;
|
||||
import com.google.zxing.pdf417.PDF417Reader;
|
||||
import com.google.zxing.qrcode.QRCodeReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MultiFormatReader is a convenience class and the main entry point into the library for most uses.
|
||||
* By default it attempts to decode all barcode formats that the library supports. Optionally, you
|
||||
* can provide a hints object to request different behavior, for example only decoding QR codes.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class MultiFormatReader implements Reader {
|
||||
|
||||
private static final Reader[] EMPTY_READER_ARRAY = new Reader[0];
|
||||
|
||||
private Map<DecodeHintType,?> hints;
|
||||
private Reader[] readers;
|
||||
|
||||
/**
|
||||
* This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it
|
||||
* passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly.
|
||||
* Use setHints() followed by decodeWithState() for continuous scan applications.
|
||||
*
|
||||
* @param image The pixel data to decode
|
||||
* @return The contents of the image
|
||||
* @throws NotFoundException Any errors which occurred
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException {
|
||||
setHints(null);
|
||||
return decodeInternal(image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an image using the hints provided. Does not honor existing state.
|
||||
*
|
||||
* @param image The pixel data to decode
|
||||
* @param hints The hints to use, clearing the previous state.
|
||||
* @return The contents of the image
|
||||
* @throws NotFoundException Any errors which occurred
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException {
|
||||
setHints(hints);
|
||||
return decodeInternal(image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an image using the state set up by calling setHints() previously. Continuous scan
|
||||
* clients will get a <b>large</b> speed increase by using this instead of decode().
|
||||
*
|
||||
* @param image The pixel data to decode
|
||||
* @return The contents of the image
|
||||
* @throws NotFoundException Any errors which occurred
|
||||
*/
|
||||
public Result decodeWithState(BinaryBitmap image) throws NotFoundException {
|
||||
// Make sure to set up the default state so we don't crash
|
||||
if (readers == null) {
|
||||
setHints(null);
|
||||
}
|
||||
return decodeInternal(image);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls
|
||||
* to decodeWithState(image) can reuse the same set of readers without reallocating memory. This
|
||||
* is important for performance in continuous scan clients.
|
||||
*
|
||||
* @param hints The set of hints to use for subsequent calls to decode(image)
|
||||
*/
|
||||
public void setHints(Map<DecodeHintType,?> hints) {
|
||||
this.hints = hints;
|
||||
|
||||
boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER);
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<BarcodeFormat> formats =
|
||||
hints == null ? null : (Collection<BarcodeFormat>) hints.get(DecodeHintType.POSSIBLE_FORMATS);
|
||||
Collection<Reader> readers = new ArrayList<>();
|
||||
if (formats != null) {
|
||||
boolean addOneDReader =
|
||||
formats.contains(BarcodeFormat.UPC_A) ||
|
||||
formats.contains(BarcodeFormat.UPC_E) ||
|
||||
formats.contains(BarcodeFormat.EAN_13) ||
|
||||
formats.contains(BarcodeFormat.EAN_8) ||
|
||||
formats.contains(BarcodeFormat.CODABAR) ||
|
||||
formats.contains(BarcodeFormat.CODE_39) ||
|
||||
formats.contains(BarcodeFormat.CODE_93) ||
|
||||
formats.contains(BarcodeFormat.CODE_128) ||
|
||||
formats.contains(BarcodeFormat.ITF) ||
|
||||
formats.contains(BarcodeFormat.RSS_14) ||
|
||||
formats.contains(BarcodeFormat.RSS_EXPANDED);
|
||||
// Put 1D readers upfront in "normal" mode
|
||||
if (addOneDReader && !tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.QR_CODE)) {
|
||||
readers.add(new QRCodeReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.DATA_MATRIX)) {
|
||||
readers.add(new DataMatrixReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.AZTEC)) {
|
||||
readers.add(new AztecReader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.PDF_417)) {
|
||||
readers.add(new PDF417Reader());
|
||||
}
|
||||
if (formats.contains(BarcodeFormat.MAXICODE)) {
|
||||
readers.add(new MaxiCodeReader());
|
||||
}
|
||||
// At end in "try harder" mode
|
||||
if (addOneDReader && tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
}
|
||||
if (readers.isEmpty()) {
|
||||
if (!tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
|
||||
readers.add(new QRCodeReader());
|
||||
readers.add(new DataMatrixReader());
|
||||
readers.add(new AztecReader());
|
||||
readers.add(new PDF417Reader());
|
||||
readers.add(new MaxiCodeReader());
|
||||
|
||||
if (tryHarder) {
|
||||
readers.add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
}
|
||||
this.readers = readers.toArray(EMPTY_READER_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (readers != null) {
|
||||
for (Reader reader : readers) {
|
||||
reader.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Result decodeInternal(BinaryBitmap image) throws NotFoundException {
|
||||
if (readers != null) {
|
||||
for (Reader reader : readers) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
try {
|
||||
return reader.decode(image, hints);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
if (hints != null && hints.containsKey(DecodeHintType.ALSO_INVERTED)) {
|
||||
// Calling all readers again with inverted image
|
||||
image.getBlackMatrix().flip();
|
||||
for (Reader reader : readers) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
try {
|
||||
return reader.decode(image, hints);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
}
|
||||
105
port_src/core/DONE/MultiFormatWriter.java
Normal file
105
port_src/core/DONE/MultiFormatWriter.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.aztec.AztecWriter;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.datamatrix.DataMatrixWriter;
|
||||
import com.google.zxing.oned.CodaBarWriter;
|
||||
import com.google.zxing.oned.Code128Writer;
|
||||
import com.google.zxing.oned.Code39Writer;
|
||||
import com.google.zxing.oned.Code93Writer;
|
||||
import com.google.zxing.oned.EAN13Writer;
|
||||
import com.google.zxing.oned.EAN8Writer;
|
||||
import com.google.zxing.oned.ITFWriter;
|
||||
import com.google.zxing.oned.UPCAWriter;
|
||||
import com.google.zxing.oned.UPCEWriter;
|
||||
import com.google.zxing.pdf417.PDF417Writer;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a factory class which finds the appropriate Writer subclass for the BarcodeFormat
|
||||
* requested and encodes the barcode with the supplied contents.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class MultiFormatWriter implements Writer {
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height) throws WriterException {
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width, int height,
|
||||
Map<EncodeHintType,?> hints) throws WriterException {
|
||||
|
||||
Writer writer;
|
||||
switch (format) {
|
||||
case EAN_8:
|
||||
writer = new EAN8Writer();
|
||||
break;
|
||||
case UPC_E:
|
||||
writer = new UPCEWriter();
|
||||
break;
|
||||
case EAN_13:
|
||||
writer = new EAN13Writer();
|
||||
break;
|
||||
case UPC_A:
|
||||
writer = new UPCAWriter();
|
||||
break;
|
||||
case QR_CODE:
|
||||
writer = new QRCodeWriter();
|
||||
break;
|
||||
case CODE_39:
|
||||
writer = new Code39Writer();
|
||||
break;
|
||||
case CODE_93:
|
||||
writer = new Code93Writer();
|
||||
break;
|
||||
case CODE_128:
|
||||
writer = new Code128Writer();
|
||||
break;
|
||||
case ITF:
|
||||
writer = new ITFWriter();
|
||||
break;
|
||||
case PDF_417:
|
||||
writer = new PDF417Writer();
|
||||
break;
|
||||
case CODABAR:
|
||||
writer = new CodaBarWriter();
|
||||
break;
|
||||
case DATA_MATRIX:
|
||||
writer = new DataMatrixWriter();
|
||||
break;
|
||||
case AZTEC:
|
||||
writer = new AztecWriter();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("No encoder available for format " + format);
|
||||
}
|
||||
return writer.encode(contents, format, width, height, hints);
|
||||
}
|
||||
|
||||
}
|
||||
40
port_src/core/DONE/NotFoundException.java
Normal file
40
port_src/core/DONE/NotFoundException.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was not found in the image. It might have been
|
||||
* partially detected but could not be confirmed.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class NotFoundException extends ReaderException {
|
||||
|
||||
private static final NotFoundException INSTANCE = new NotFoundException();
|
||||
static {
|
||||
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
|
||||
}
|
||||
|
||||
private NotFoundException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public static NotFoundException getNotFoundInstance() {
|
||||
return isStackTrace ? new NotFoundException() : INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
168
port_src/core/DONE/PlanarYUVLuminanceSource.java
Normal file
168
port_src/core/DONE/PlanarYUVLuminanceSource.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* This object extends LuminanceSource around an array of YUV data returned from the camera driver,
|
||||
* with the option to crop to a rectangle within the full data. This can be used to exclude
|
||||
* superfluous pixels around the perimeter and speed up decoding.
|
||||
*
|
||||
* It works for any pixel format where the Y channel is planar and appears first, including
|
||||
* YCbCr_420_SP and YCbCr_422_SP.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class PlanarYUVLuminanceSource extends LuminanceSource {
|
||||
|
||||
private static final int THUMBNAIL_SCALE_FACTOR = 2;
|
||||
|
||||
private final byte[] yuvData;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public PlanarYUVLuminanceSource(byte[] yuvData,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height,
|
||||
boolean reverseHorizontal) {
|
||||
super(width, height);
|
||||
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
|
||||
this.yuvData = yuvData;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
if (reverseHorizontal) {
|
||||
reverseHorizontal(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
int width = getWidth();
|
||||
if (row == null || row.length < width) {
|
||||
row = new byte[width];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(yuvData, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return yuvData;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(yuvData, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new PlanarYUVLuminanceSource(yuvData,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height,
|
||||
false);
|
||||
}
|
||||
|
||||
public int[] renderThumbnail() {
|
||||
int width = getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
int height = getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
int[] pixels = new int[width * height];
|
||||
byte[] yuv = yuvData;
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
for (int x = 0; x < width; x++) {
|
||||
int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
|
||||
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
|
||||
}
|
||||
inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailWidth() {
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
public int getThumbnailHeight() {
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
private void reverseHorizontal(int width, int height) {
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
136
port_src/core/DONE/RGBLuminanceSource.java
Normal file
136
port_src/core/DONE/RGBLuminanceSource.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* This class is used to help decode images from files which arrive as RGB data from
|
||||
* an ARGB pixel array. It does not support rotation.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Betaminos
|
||||
*/
|
||||
public final class RGBLuminanceSource extends LuminanceSource {
|
||||
|
||||
private final byte[] luminances;
|
||||
private final int dataWidth;
|
||||
private final int dataHeight;
|
||||
private final int left;
|
||||
private final int top;
|
||||
|
||||
public RGBLuminanceSource(int width, int height, int[] pixels) {
|
||||
super(width, height);
|
||||
|
||||
dataWidth = width;
|
||||
dataHeight = height;
|
||||
left = 0;
|
||||
top = 0;
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
//
|
||||
// Total number of pixels suffices, can ignore shape
|
||||
int size = width * height;
|
||||
luminances = new byte[size];
|
||||
for (int offset = 0; offset < size; offset++) {
|
||||
int pixel = pixels[offset];
|
||||
int r = (pixel >> 16) & 0xff; // red
|
||||
int g2 = (pixel >> 7) & 0x1fe; // 2 * green
|
||||
int b = pixel & 0xff; // blue
|
||||
// Calculate green-favouring average cheaply
|
||||
luminances[offset] = (byte) ((r + g2 + b) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
private RGBLuminanceSource(byte[] pixels,
|
||||
int dataWidth,
|
||||
int dataHeight,
|
||||
int left,
|
||||
int top,
|
||||
int width,
|
||||
int height) {
|
||||
super(width, height);
|
||||
if (left + width > dataWidth || top + height > dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
this.luminances = pixels;
|
||||
this.dataWidth = dataWidth;
|
||||
this.dataHeight = dataHeight;
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow(int y, byte[] row) {
|
||||
if (y < 0 || y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
int width = getWidth();
|
||||
if (row == null || row.length < width) {
|
||||
row = new byte[width];
|
||||
}
|
||||
int offset = (y + top) * dataWidth + left;
|
||||
System.arraycopy(luminances, offset, row, 0, width);
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMatrix() {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if (width == dataWidth && height == dataHeight) {
|
||||
return luminances;
|
||||
}
|
||||
|
||||
int area = width * height;
|
||||
byte[] matrix = new byte[area];
|
||||
int inputOffset = top * dataWidth + left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if (width == dataWidth) {
|
||||
System.arraycopy(luminances, inputOffset, matrix, 0, area);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
for (int y = 0; y < height; y++) {
|
||||
int outputOffset = y * width;
|
||||
System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);
|
||||
inputOffset += dataWidth;
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LuminanceSource crop(int left, int top, int width, int height) {
|
||||
return new RGBLuminanceSource(luminances,
|
||||
dataWidth,
|
||||
dataHeight,
|
||||
this.left + left,
|
||||
this.top + top,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
}
|
||||
69
port_src/core/DONE/Reader.java
Normal file
69
port_src/core/DONE/Reader.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementations of this interface can decode an image of a barcode in some format into
|
||||
* the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can
|
||||
* decode a QR code. The decoder may optionally receive hints from the caller which may help
|
||||
* it decode more quickly or accurately.
|
||||
*
|
||||
* See {@link MultiFormatReader}, which attempts to determine what barcode
|
||||
* format is present within the image as well, and then decodes it accordingly.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public interface Reader {
|
||||
|
||||
/**
|
||||
* Locates and decodes a barcode in some format within an image.
|
||||
*
|
||||
* @param image image of barcode to decode
|
||||
* @return String which the barcode encodes
|
||||
* @throws NotFoundException if no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException;
|
||||
|
||||
/**
|
||||
* Locates and decodes a barcode in some format within an image. This method also accepts
|
||||
* hints, each possibly associated to some data, which may help the implementation decode.
|
||||
*
|
||||
* @param image image of barcode to decode
|
||||
* @param hints passed as a {@link Map} from {@link DecodeHintType}
|
||||
* to arbitrary data. The
|
||||
* meaning of the data depends upon the hint type. The implementation may or may not do
|
||||
* anything with these hints.
|
||||
* @return String which the barcode encodes
|
||||
* @throws NotFoundException if no potential barcode is found
|
||||
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
|
||||
* @throws FormatException if a potential barcode is found but format is invalid
|
||||
*/
|
||||
Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, ChecksumException, FormatException;
|
||||
|
||||
/**
|
||||
* Resets any internal state the implementation has after a decode, to prepare it
|
||||
* for reuse.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
}
|
||||
58
port_src/core/DONE/ReaderException.java
Normal file
58
port_src/core/DONE/ReaderException.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* The general exception class throw when something goes wrong during decoding of a barcode.
|
||||
* This includes, but is not limited to, failing checksums / error correction algorithms, being
|
||||
* unable to locate finder timing patterns, and so on.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public abstract class ReaderException extends Exception {
|
||||
|
||||
// disable stack traces when not running inside test units
|
||||
protected static boolean isStackTrace =
|
||||
System.getProperty("surefire.test.class.path") != null;
|
||||
protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0];
|
||||
|
||||
ReaderException() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ReaderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
// Prevent stack traces from being taken
|
||||
@Override
|
||||
public final synchronized Throwable fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing only. Controls whether library exception classes include stack traces or not.
|
||||
* Defaults to false, unless running in the project's unit testing harness.
|
||||
*
|
||||
* @param enabled if true, enables stack traces in library exception classes
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public static void setStackTrace(boolean enabled) {
|
||||
isStackTrace = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
153
port_src/core/DONE/Result.java
Normal file
153
port_src/core/DONE/Result.java
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of decoding a barcode within an image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class Result {
|
||||
|
||||
private final String text;
|
||||
private final byte[] rawBytes;
|
||||
private final int numBits;
|
||||
private ResultPoint[] resultPoints;
|
||||
private final BarcodeFormat format;
|
||||
private Map<ResultMetadataType,Object> resultMetadata;
|
||||
private final long timestamp;
|
||||
|
||||
public Result(String text,
|
||||
byte[] rawBytes,
|
||||
ResultPoint[] resultPoints,
|
||||
BarcodeFormat format) {
|
||||
this(text, rawBytes, resultPoints, format, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Result(String text,
|
||||
byte[] rawBytes,
|
||||
ResultPoint[] resultPoints,
|
||||
BarcodeFormat format,
|
||||
long timestamp) {
|
||||
this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length,
|
||||
resultPoints, format, timestamp);
|
||||
}
|
||||
|
||||
public Result(String text,
|
||||
byte[] rawBytes,
|
||||
int numBits,
|
||||
ResultPoint[] resultPoints,
|
||||
BarcodeFormat format,
|
||||
long timestamp) {
|
||||
this.text = text;
|
||||
this.rawBytes = rawBytes;
|
||||
this.numBits = numBits;
|
||||
this.resultPoints = resultPoints;
|
||||
this.format = format;
|
||||
this.resultMetadata = null;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw text encoded by the barcode
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
|
||||
*/
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public int getNumBits() {
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return points related to the barcode in the image. These are typically points
|
||||
* identifying finder patterns or the corners of the barcode. The exact meaning is
|
||||
* specific to the type of barcode that was decoded.
|
||||
*/
|
||||
public ResultPoint[] getResultPoints() {
|
||||
return resultPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded
|
||||
*/
|
||||
public BarcodeFormat getBarcodeFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
|
||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||
* like orientation.
|
||||
*/
|
||||
public Map<ResultMetadataType,Object> getResultMetadata() {
|
||||
return resultMetadata;
|
||||
}
|
||||
|
||||
public void putMetadata(ResultMetadataType type, Object value) {
|
||||
if (resultMetadata == null) {
|
||||
resultMetadata = new EnumMap<>(ResultMetadataType.class);
|
||||
}
|
||||
resultMetadata.put(type, value);
|
||||
}
|
||||
|
||||
public void putAllMetadata(Map<ResultMetadataType,Object> metadata) {
|
||||
if (metadata != null) {
|
||||
if (resultMetadata == null) {
|
||||
resultMetadata = metadata;
|
||||
} else {
|
||||
resultMetadata.putAll(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addResultPoints(ResultPoint[] newPoints) {
|
||||
ResultPoint[] oldPoints = resultPoints;
|
||||
if (oldPoints == null) {
|
||||
resultPoints = newPoints;
|
||||
} else if (newPoints != null && newPoints.length > 0) {
|
||||
ResultPoint[] allPoints = new ResultPoint[oldPoints.length + newPoints.length];
|
||||
System.arraycopy(oldPoints, 0, allPoints, 0, oldPoints.length);
|
||||
System.arraycopy(newPoints, 0, allPoints, oldPoints.length, newPoints.length);
|
||||
resultPoints = allPoints;
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
103
port_src/core/DONE/ResultMetadataType.java
Normal file
103
port_src/core/DONE/ResultMetadataType.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Represents some type of metadata about the result of the decoding that the decoder
|
||||
* wishes to communicate back to the caller.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public enum ResultMetadataType {
|
||||
|
||||
/**
|
||||
* Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
|
||||
*/
|
||||
OTHER,
|
||||
|
||||
/**
|
||||
* Denotes the likely approximate orientation of the barcode in the image. This value
|
||||
* is given as degrees rotated clockwise from the normal, upright orientation.
|
||||
* For example a 1D barcode which was found by reading top-to-bottom would be
|
||||
* said to have orientation "90". This key maps to an {@link Integer} whose
|
||||
* value is in the range [0,360).
|
||||
*/
|
||||
ORIENTATION,
|
||||
|
||||
/**
|
||||
* <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
|
||||
* which is sometimes used to encode binary data. While {@link Result} makes available
|
||||
* the complete raw bytes in the barcode for these formats, it does not offer the bytes
|
||||
* from the byte segments alone.</p>
|
||||
*
|
||||
* <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
|
||||
* raw bytes in the byte segments in the barcode, in order.</p>
|
||||
*/
|
||||
BYTE_SEGMENTS,
|
||||
|
||||
/**
|
||||
* Error correction level used, if applicable. The value type depends on the
|
||||
* format, but is typically a String.
|
||||
*/
|
||||
ERROR_CORRECTION_LEVEL,
|
||||
|
||||
/**
|
||||
* For some periodicals, indicates the issue number as an {@link Integer}.
|
||||
*/
|
||||
ISSUE_NUMBER,
|
||||
|
||||
/**
|
||||
* For some products, indicates the suggested retail price in the barcode as a
|
||||
* formatted {@link String}.
|
||||
*/
|
||||
SUGGESTED_PRICE,
|
||||
|
||||
/**
|
||||
* For some products, the possible country of manufacture as a {@link String} denoting the
|
||||
* ISO country code. Some map to multiple possible countries, like "US/CA".
|
||||
*/
|
||||
POSSIBLE_COUNTRY,
|
||||
|
||||
/**
|
||||
* For some products, the extension text
|
||||
*/
|
||||
UPC_EAN_EXTENSION,
|
||||
|
||||
/**
|
||||
* PDF417-specific metadata
|
||||
*/
|
||||
PDF417_EXTRA_METADATA,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* sequence number is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_SEQUENCE,
|
||||
|
||||
/**
|
||||
* If the code format supports structured append and the current scanned code is part of one then the
|
||||
* parity is given with it.
|
||||
*/
|
||||
STRUCTURED_APPEND_PARITY,
|
||||
|
||||
/**
|
||||
* Barcode Symbology Identifier.
|
||||
* Note: According to the GS1 specification the identifier may have to replace a leading FNC1/GS character
|
||||
* when prepending to the barcode content.
|
||||
*/
|
||||
SYMBOLOGY_IDENTIFIER,
|
||||
}
|
||||
130
port_src/core/DONE/ResultPoint.java
Normal file
130
port_src/core/DONE/ResultPoint.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2007 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.detector.MathUtils;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
|
||||
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public class ResultPoint {
|
||||
|
||||
private final float x;
|
||||
private final float y;
|
||||
|
||||
public ResultPoint(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public final float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public final float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object other) {
|
||||
if (other instanceof ResultPoint) {
|
||||
ResultPoint otherPoint = (ResultPoint) other;
|
||||
return x == otherPoint.x && y == otherPoint.y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
return "(" + x + ',' + y + ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
|
||||
* and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
||||
*
|
||||
* @param patterns array of three {@code ResultPoint} to order
|
||||
*/
|
||||
public static void orderBestPatterns(ResultPoint[] patterns) {
|
||||
|
||||
// Find distances between pattern centers
|
||||
float zeroOneDistance = distance(patterns[0], patterns[1]);
|
||||
float oneTwoDistance = distance(patterns[1], patterns[2]);
|
||||
float zeroTwoDistance = distance(patterns[0], patterns[2]);
|
||||
|
||||
ResultPoint pointA;
|
||||
ResultPoint pointB;
|
||||
ResultPoint pointC;
|
||||
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) {
|
||||
pointB = patterns[0];
|
||||
pointA = patterns[1];
|
||||
pointC = patterns[2];
|
||||
} else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) {
|
||||
pointB = patterns[1];
|
||||
pointA = patterns[0];
|
||||
pointC = patterns[2];
|
||||
} else {
|
||||
pointB = patterns[2];
|
||||
pointA = patterns[0];
|
||||
pointC = patterns[1];
|
||||
}
|
||||
|
||||
// Use cross product to figure out whether A and C are correct or flipped.
|
||||
// This asks whether BC x BA has a positive z component, which is the arrangement
|
||||
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
||||
// should swap A and C.
|
||||
if (crossProductZ(pointA, pointB, pointC) < 0.0f) {
|
||||
ResultPoint temp = pointA;
|
||||
pointA = pointC;
|
||||
pointC = temp;
|
||||
}
|
||||
|
||||
patterns[0] = pointA;
|
||||
patterns[1] = pointB;
|
||||
patterns[2] = pointC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pattern1 first pattern
|
||||
* @param pattern2 second pattern
|
||||
* @return distance between two points
|
||||
*/
|
||||
public static float distance(ResultPoint pattern1, ResultPoint pattern2) {
|
||||
return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the z component of the cross product between vectors BC and BA.
|
||||
*/
|
||||
private static float crossProductZ(ResultPoint pointA,
|
||||
ResultPoint pointB,
|
||||
ResultPoint pointC) {
|
||||
float bX = pointB.x;
|
||||
float bY = pointB.y;
|
||||
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
|
||||
}
|
||||
|
||||
}
|
||||
29
port_src/core/DONE/ResultPointCallback.java
Normal file
29
port_src/core/DONE/ResultPointCallback.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2009 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* Callback which is invoked when a possible result point (significant
|
||||
* point in the barcode image such as a corner) is found.
|
||||
*
|
||||
* @see DecodeHintType#NEED_RESULT_POINT_CALLBACK
|
||||
*/
|
||||
public interface ResultPointCallback {
|
||||
|
||||
void foundPossibleResultPoint(ResultPoint point);
|
||||
|
||||
}
|
||||
59
port_src/core/DONE/Writer.java
Normal file
59
port_src/core/DONE/Writer.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The base class for all objects which encode/generate a barcode image.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public interface Writer {
|
||||
|
||||
/**
|
||||
* Encode a barcode using the default settings.
|
||||
*
|
||||
* @param contents The contents to encode in the barcode
|
||||
* @param format The barcode format to generate
|
||||
* @param width The preferred width in pixels
|
||||
* @param height The preferred height in pixels
|
||||
* @return {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
|
||||
throws WriterException;
|
||||
|
||||
/**
|
||||
* @param contents The contents to encode in the barcode
|
||||
* @param format The barcode format to generate
|
||||
* @param width The preferred width in pixels
|
||||
* @param height The preferred height in pixels
|
||||
* @param hints Additional parameters to supply to the encoder
|
||||
* @return {@link BitMatrix} representing encoded barcode image
|
||||
* @throws WriterException if contents cannot be encoded legally in a format
|
||||
*/
|
||||
BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height,
|
||||
Map<EncodeHintType,?> hints)
|
||||
throws WriterException;
|
||||
|
||||
}
|
||||
38
port_src/core/DONE/WriterException.java
Normal file
38
port_src/core/DONE/WriterException.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing;
|
||||
|
||||
/**
|
||||
* A base class which covers the range of exceptions which may occur when encoding a barcode using
|
||||
* the Writer framework.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class WriterException extends Exception {
|
||||
|
||||
public WriterException() {
|
||||
}
|
||||
|
||||
public WriterException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public WriterException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
||||
58
port_src/core/DONE/aztec/AztecDetectorResult.java
Normal file
58
port_src/core/DONE/aztec/AztecDetectorResult.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.DetectorResult;
|
||||
|
||||
/**
|
||||
* <p>Extends {@link DetectorResult} with more information specific to the Aztec format,
|
||||
* like the number of layers and whether it's compact.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class AztecDetectorResult extends DetectorResult {
|
||||
|
||||
private final boolean compact;
|
||||
private final int nbDatablocks;
|
||||
private final int nbLayers;
|
||||
|
||||
public AztecDetectorResult(BitMatrix bits,
|
||||
ResultPoint[] points,
|
||||
boolean compact,
|
||||
int nbDatablocks,
|
||||
int nbLayers) {
|
||||
super(bits, points);
|
||||
this.compact = compact;
|
||||
this.nbDatablocks = nbDatablocks;
|
||||
this.nbLayers = nbLayers;
|
||||
}
|
||||
|
||||
public int getNbLayers() {
|
||||
return nbLayers;
|
||||
}
|
||||
|
||||
public int getNbDatablocks() {
|
||||
return nbDatablocks;
|
||||
}
|
||||
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
}
|
||||
123
port_src/core/DONE/aztec/AztecReader.java
Normal file
123
port_src/core/DONE/aztec/AztecReader.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Reader;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultMetadataType;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
import com.google.zxing.aztec.decoder.Decoder;
|
||||
import com.google.zxing.aztec.detector.Detector;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode Aztec codes in an image.
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class AztecReader implements Reader {
|
||||
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws NotFoundException if a Data Matrix code cannot be found
|
||||
* @throws FormatException if a Data Matrix code cannot be decoded
|
||||
*/
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image) throws NotFoundException, FormatException {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
|
||||
throws NotFoundException, FormatException {
|
||||
|
||||
NotFoundException notFoundException = null;
|
||||
FormatException formatException = null;
|
||||
Detector detector = new Detector(image.getBlackMatrix());
|
||||
ResultPoint[] points = null;
|
||||
DecoderResult decoderResult = null;
|
||||
try {
|
||||
AztecDetectorResult detectorResult = detector.detect(false);
|
||||
points = detectorResult.getPoints();
|
||||
decoderResult = new Decoder().decode(detectorResult);
|
||||
} catch (NotFoundException e) {
|
||||
notFoundException = e;
|
||||
} catch (FormatException e) {
|
||||
formatException = e;
|
||||
}
|
||||
if (decoderResult == null) {
|
||||
try {
|
||||
AztecDetectorResult detectorResult = detector.detect(true);
|
||||
points = detectorResult.getPoints();
|
||||
decoderResult = new Decoder().decode(detectorResult);
|
||||
} catch (NotFoundException | FormatException e) {
|
||||
if (notFoundException != null) {
|
||||
throw notFoundException;
|
||||
}
|
||||
if (formatException != null) {
|
||||
throw formatException;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (hints != null) {
|
||||
ResultPointCallback rpcb = (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
|
||||
if (rpcb != null) {
|
||||
for (ResultPoint point : points) {
|
||||
rpcb.foundPossibleResultPoint(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result result = new Result(decoderResult.getText(),
|
||||
decoderResult.getRawBytes(),
|
||||
decoderResult.getNumBits(),
|
||||
points,
|
||||
BarcodeFormat.AZTEC,
|
||||
System.currentTimeMillis());
|
||||
|
||||
List<byte[]> byteSegments = decoderResult.getByteSegments();
|
||||
if (byteSegments != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
|
||||
}
|
||||
String ecLevel = decoderResult.getECLevel();
|
||||
if (ecLevel != null) {
|
||||
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
}
|
||||
result.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]z" + decoderResult.getSymbologyModifier());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
94
port_src/core/DONE/aztec/AztecWriter.java
Normal file
94
port_src/core/DONE/aztec/AztecWriter.java
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.Writer;
|
||||
import com.google.zxing.aztec.encoder.AztecCode;
|
||||
import com.google.zxing.aztec.encoder.Encoder;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Renders an Aztec code as a {@link BitMatrix}.
|
||||
*/
|
||||
public final class AztecWriter implements Writer {
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, Map<EncodeHintType,?> hints) {
|
||||
Charset charset = null; // Do not add any ECI code by default
|
||||
int eccPercent = Encoder.DEFAULT_EC_PERCENT;
|
||||
int layers = Encoder.DEFAULT_AZTEC_LAYERS;
|
||||
if (hints != null) {
|
||||
if (hints.containsKey(EncodeHintType.CHARACTER_SET)) {
|
||||
charset = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toString());
|
||||
}
|
||||
if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
|
||||
eccPercent = Integer.parseInt(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
|
||||
}
|
||||
if (hints.containsKey(EncodeHintType.AZTEC_LAYERS)) {
|
||||
layers = Integer.parseInt(hints.get(EncodeHintType.AZTEC_LAYERS).toString());
|
||||
}
|
||||
}
|
||||
return encode(contents, format, width, height, charset, eccPercent, layers);
|
||||
}
|
||||
|
||||
private static BitMatrix encode(String contents, BarcodeFormat format,
|
||||
int width, int height,
|
||||
Charset charset, int eccPercent, int layers) {
|
||||
if (format != BarcodeFormat.AZTEC) {
|
||||
throw new IllegalArgumentException("Can only encode AZTEC, but got " + format);
|
||||
}
|
||||
AztecCode aztec = Encoder.encode(contents, eccPercent, layers, charset);
|
||||
return renderResult(aztec, width, height);
|
||||
}
|
||||
|
||||
private static BitMatrix renderResult(AztecCode code, int width, int height) {
|
||||
BitMatrix input = code.getMatrix();
|
||||
if (input == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int inputWidth = input.getWidth();
|
||||
int inputHeight = input.getHeight();
|
||||
int outputWidth = Math.max(width, inputWidth);
|
||||
int outputHeight = Math.max(height, inputHeight);
|
||||
|
||||
int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
|
||||
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
|
||||
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
|
||||
|
||||
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
|
||||
|
||||
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
|
||||
// Write the contents of this row of the barcode
|
||||
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
|
||||
if (input.get(inputX, inputY)) {
|
||||
output.setRegion(outputX, outputY, multiple, multiple);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
443
port_src/core/DONE/aztec/decoder/Decoder.java
Normal file
443
port_src/core/DONE/aztec/decoder/Decoder.java
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.decoder;
|
||||
|
||||
import com.google.zxing.FormatException;
|
||||
import com.google.zxing.aztec.AztecDetectorResult;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.CharacterSetECI;
|
||||
import com.google.zxing.common.DecoderResult;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* <p>The main class which implements Aztec Code decoding -- as opposed to locating and extracting
|
||||
* the Aztec Code from an image.</p>
|
||||
*
|
||||
* @author David Olivier
|
||||
*/
|
||||
public final class Decoder {
|
||||
|
||||
private enum Table {
|
||||
UPPER,
|
||||
LOWER,
|
||||
MIXED,
|
||||
DIGIT,
|
||||
PUNCT,
|
||||
BINARY
|
||||
}
|
||||
|
||||
private static final String[] UPPER_TABLE = {
|
||||
"CTRL_PS", " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] LOWER_TABLE = {
|
||||
"CTRL_PS", " ", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
|
||||
"q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] MIXED_TABLE = {
|
||||
"CTRL_PS", " ", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\b", "\t", "\n",
|
||||
"\13", "\f", "\r", "\33", "\34", "\35", "\36", "\37", "@", "\\", "^", "_",
|
||||
"`", "|", "~", "\177", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS"
|
||||
};
|
||||
|
||||
private static final String[] PUNCT_TABLE = {
|
||||
"FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")",
|
||||
"*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL"
|
||||
};
|
||||
|
||||
private static final String[] DIGIT_TABLE = {
|
||||
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", "CTRL_US"
|
||||
};
|
||||
|
||||
private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1;
|
||||
|
||||
private AztecDetectorResult ddata;
|
||||
|
||||
public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatException {
|
||||
ddata = detectorResult;
|
||||
BitMatrix matrix = detectorResult.getBits();
|
||||
boolean[] rawbits = extractBits(matrix);
|
||||
CorrectedBitsResult correctedBits = correctBits(rawbits);
|
||||
byte[] rawBytes = convertBoolArrayToByteArray(correctedBits.correctBits);
|
||||
String result = getEncodedData(correctedBits.correctBits);
|
||||
DecoderResult decoderResult =
|
||||
new DecoderResult(rawBytes, result, null, String.format("%d%%", correctedBits.ecLevel));
|
||||
decoderResult.setNumBits(correctedBits.correctBits.length);
|
||||
return decoderResult;
|
||||
}
|
||||
|
||||
// This method is used for testing the high-level encoder
|
||||
public static String highLevelDecode(boolean[] correctedBits) throws FormatException {
|
||||
return getEncodedData(correctedBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string encoded in the aztec code bits
|
||||
*
|
||||
* @return the decoded string
|
||||
*/
|
||||
private static String getEncodedData(boolean[] correctedBits) throws FormatException {
|
||||
int endIndex = correctedBits.length;
|
||||
Table latchTable = Table.UPPER; // table most recently latched to
|
||||
Table shiftTable = Table.UPPER; // table to use for the next read
|
||||
|
||||
// Final decoded string result
|
||||
// (correctedBits-5) / 4 is an upper bound on the size (all-digit result)
|
||||
StringBuilder result = new StringBuilder((correctedBits.length - 5) / 4);
|
||||
|
||||
// Intermediary buffer of decoded bytes, which is decoded into a string and flushed
|
||||
// when character encoding changes (ECI) or input ends.
|
||||
ByteArrayOutputStream decodedBytes = new ByteArrayOutputStream();
|
||||
Charset encoding = DEFAULT_ENCODING;
|
||||
|
||||
int index = 0;
|
||||
while (index < endIndex) {
|
||||
if (shiftTable == Table.BINARY) {
|
||||
if (endIndex - index < 5) {
|
||||
break;
|
||||
}
|
||||
int length = readCode(correctedBits, index, 5);
|
||||
index += 5;
|
||||
if (length == 0) {
|
||||
if (endIndex - index < 11) {
|
||||
break;
|
||||
}
|
||||
length = readCode(correctedBits, index, 11) + 31;
|
||||
index += 11;
|
||||
}
|
||||
for (int charCount = 0; charCount < length; charCount++) {
|
||||
if (endIndex - index < 8) {
|
||||
index = endIndex; // Force outer loop to exit
|
||||
break;
|
||||
}
|
||||
int code = readCode(correctedBits, index, 8);
|
||||
decodedBytes.write((byte) code);
|
||||
index += 8;
|
||||
}
|
||||
// Go back to whatever mode we had been in
|
||||
shiftTable = latchTable;
|
||||
} else {
|
||||
int size = shiftTable == Table.DIGIT ? 4 : 5;
|
||||
if (endIndex - index < size) {
|
||||
break;
|
||||
}
|
||||
int code = readCode(correctedBits, index, size);
|
||||
index += size;
|
||||
String str = getCharacter(shiftTable, code);
|
||||
if ("FLG(n)".equals(str)) {
|
||||
if (endIndex - index < 3) {
|
||||
break;
|
||||
}
|
||||
int n = readCode(correctedBits, index, 3);
|
||||
index += 3;
|
||||
// flush bytes, FLG changes state
|
||||
try {
|
||||
result.append(decodedBytes.toString(encoding.name()));
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new IllegalStateException(uee);
|
||||
}
|
||||
decodedBytes.reset();
|
||||
switch (n) {
|
||||
case 0:
|
||||
result.append((char) 29); // translate FNC1 as ASCII 29
|
||||
break;
|
||||
case 7:
|
||||
throw FormatException.getFormatInstance(); // FLG(7) is reserved and illegal
|
||||
default:
|
||||
// ECI is decimal integer encoded as 1-6 codes in DIGIT mode
|
||||
int eci = 0;
|
||||
if (endIndex - index < 4 * n) {
|
||||
break;
|
||||
}
|
||||
while (n-- > 0) {
|
||||
int nextDigit = readCode(correctedBits, index, 4);
|
||||
index += 4;
|
||||
if (nextDigit < 2 || nextDigit > 11) {
|
||||
throw FormatException.getFormatInstance(); // Not a decimal digit
|
||||
}
|
||||
eci = eci * 10 + (nextDigit - 2);
|
||||
}
|
||||
CharacterSetECI charsetECI = CharacterSetECI.getCharacterSetECIByValue(eci);
|
||||
if (charsetECI == null) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
encoding = charsetECI.getCharset();
|
||||
}
|
||||
// Go back to whatever mode we had been in
|
||||
shiftTable = latchTable;
|
||||
} else if (str.startsWith("CTRL_")) {
|
||||
// Table changes
|
||||
// ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked.
|
||||
// That's including when that mode is a shift.
|
||||
// Our test case dlusbs.png for issue #642 exercises that.
|
||||
latchTable = shiftTable; // Latch the current mode, so as to return to Upper after U/S B/S
|
||||
shiftTable = getTable(str.charAt(5));
|
||||
if (str.charAt(6) == 'L') {
|
||||
latchTable = shiftTable;
|
||||
}
|
||||
} else {
|
||||
// Though stored as a table of strings for convenience, codes actually represent 1 or 2 *bytes*.
|
||||
byte[] b = str.getBytes(StandardCharsets.US_ASCII);
|
||||
decodedBytes.write(b, 0, b.length);
|
||||
// Go back to whatever mode we had been in
|
||||
shiftTable = latchTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
result.append(decodedBytes.toString(encoding.name()));
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
// can't happen
|
||||
throw new IllegalStateException(uee);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the table corresponding to the char passed
|
||||
*/
|
||||
private static Table getTable(char t) {
|
||||
switch (t) {
|
||||
case 'L':
|
||||
return Table.LOWER;
|
||||
case 'P':
|
||||
return Table.PUNCT;
|
||||
case 'M':
|
||||
return Table.MIXED;
|
||||
case 'D':
|
||||
return Table.DIGIT;
|
||||
case 'B':
|
||||
return Table.BINARY;
|
||||
case 'U':
|
||||
default:
|
||||
return Table.UPPER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character (or string) corresponding to the passed code in the given table
|
||||
*
|
||||
* @param table the table used
|
||||
* @param code the code of the character
|
||||
*/
|
||||
private static String getCharacter(Table table, int code) {
|
||||
switch (table) {
|
||||
case UPPER:
|
||||
return UPPER_TABLE[code];
|
||||
case LOWER:
|
||||
return LOWER_TABLE[code];
|
||||
case MIXED:
|
||||
return MIXED_TABLE[code];
|
||||
case PUNCT:
|
||||
return PUNCT_TABLE[code];
|
||||
case DIGIT:
|
||||
return DIGIT_TABLE[code];
|
||||
default:
|
||||
// Should not reach here.
|
||||
throw new IllegalStateException("Bad table");
|
||||
}
|
||||
}
|
||||
|
||||
static final class CorrectedBitsResult {
|
||||
private final boolean[] correctBits;
|
||||
private final int ecLevel;
|
||||
|
||||
CorrectedBitsResult(boolean[] correctBits, int ecLevel) {
|
||||
this.correctBits = correctBits;
|
||||
this.ecLevel = ecLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Performs RS error correction on an array of bits.</p>
|
||||
*
|
||||
* @return the corrected array
|
||||
* @throws FormatException if the input contains too many errors
|
||||
*/
|
||||
private CorrectedBitsResult correctBits(boolean[] rawbits) throws FormatException {
|
||||
GenericGF gf;
|
||||
int codewordSize;
|
||||
|
||||
if (ddata.getNbLayers() <= 2) {
|
||||
codewordSize = 6;
|
||||
gf = GenericGF.AZTEC_DATA_6;
|
||||
} else if (ddata.getNbLayers() <= 8) {
|
||||
codewordSize = 8;
|
||||
gf = GenericGF.AZTEC_DATA_8;
|
||||
} else if (ddata.getNbLayers() <= 22) {
|
||||
codewordSize = 10;
|
||||
gf = GenericGF.AZTEC_DATA_10;
|
||||
} else {
|
||||
codewordSize = 12;
|
||||
gf = GenericGF.AZTEC_DATA_12;
|
||||
}
|
||||
|
||||
int numDataCodewords = ddata.getNbDatablocks();
|
||||
int numCodewords = rawbits.length / codewordSize;
|
||||
if (numCodewords < numDataCodewords) {
|
||||
throw FormatException.getFormatInstance();
|
||||
}
|
||||
int offset = rawbits.length % codewordSize;
|
||||
|
||||
int[] dataWords = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++, offset += codewordSize) {
|
||||
dataWords[i] = readCode(rawbits, offset, codewordSize);
|
||||
}
|
||||
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(gf);
|
||||
rsDecoder.decode(dataWords, numCodewords - numDataCodewords);
|
||||
} catch (ReedSolomonException ex) {
|
||||
throw FormatException.getFormatInstance(ex);
|
||||
}
|
||||
|
||||
// Now perform the unstuffing operation.
|
||||
// First, count how many bits are going to be thrown out as stuffing
|
||||
int mask = (1 << codewordSize) - 1;
|
||||
int stuffedBits = 0;
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
int dataWord = dataWords[i];
|
||||
if (dataWord == 0 || dataWord == mask) {
|
||||
throw FormatException.getFormatInstance();
|
||||
} else if (dataWord == 1 || dataWord == mask - 1) {
|
||||
stuffedBits++;
|
||||
}
|
||||
}
|
||||
// Now, actually unpack the bits and remove the stuffing
|
||||
boolean[] correctedBits = new boolean[numDataCodewords * codewordSize - stuffedBits];
|
||||
int index = 0;
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
int dataWord = dataWords[i];
|
||||
if (dataWord == 1 || dataWord == mask - 1) {
|
||||
// next codewordSize-1 bits are all zeros or all ones
|
||||
Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1);
|
||||
index += codewordSize - 1;
|
||||
} else {
|
||||
for (int bit = codewordSize - 1; bit >= 0; --bit) {
|
||||
correctedBits[index++] = (dataWord & (1 << bit)) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CorrectedBitsResult(correctedBits, 100 * (numCodewords - numDataCodewords) / numCodewords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of bits from an Aztec Code matrix
|
||||
*
|
||||
* @return the array of bits
|
||||
*/
|
||||
private boolean[] extractBits(BitMatrix matrix) {
|
||||
boolean compact = ddata.isCompact();
|
||||
int layers = ddata.getNbLayers();
|
||||
int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines
|
||||
int[] alignmentMap = new int[baseMatrixSize];
|
||||
boolean[] rawbits = new boolean[totalBitsInLayer(layers, compact)];
|
||||
|
||||
if (compact) {
|
||||
for (int i = 0; i < alignmentMap.length; i++) {
|
||||
alignmentMap[i] = i;
|
||||
}
|
||||
} else {
|
||||
int matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
|
||||
int origCenter = baseMatrixSize / 2;
|
||||
int center = matrixSize / 2;
|
||||
for (int i = 0; i < origCenter; i++) {
|
||||
int newOffset = i + i / 15;
|
||||
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
|
||||
alignmentMap[origCenter + i] = center + newOffset + 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0, rowOffset = 0; i < layers; i++) {
|
||||
int rowSize = (layers - i) * 4 + (compact ? 9 : 12);
|
||||
// The top-left most point of this layer is <low, low> (not including alignment lines)
|
||||
int low = i * 2;
|
||||
// The bottom-right most point of this layer is <high, high> (not including alignment lines)
|
||||
int high = baseMatrixSize - 1 - low;
|
||||
// We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows
|
||||
for (int j = 0; j < rowSize; j++) {
|
||||
int columnOffset = j * 2;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
// left column
|
||||
rawbits[rowOffset + columnOffset + k] =
|
||||
matrix.get(alignmentMap[low + k], alignmentMap[low + j]);
|
||||
// bottom row
|
||||
rawbits[rowOffset + 2 * rowSize + columnOffset + k] =
|
||||
matrix.get(alignmentMap[low + j], alignmentMap[high - k]);
|
||||
// right column
|
||||
rawbits[rowOffset + 4 * rowSize + columnOffset + k] =
|
||||
matrix.get(alignmentMap[high - k], alignmentMap[high - j]);
|
||||
// top row
|
||||
rawbits[rowOffset + 6 * rowSize + columnOffset + k] =
|
||||
matrix.get(alignmentMap[high - j], alignmentMap[low + k]);
|
||||
}
|
||||
}
|
||||
rowOffset += rowSize * 8;
|
||||
}
|
||||
return rawbits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a code of given length and at given index in an array of bits
|
||||
*/
|
||||
private static int readCode(boolean[] rawbits, int startIndex, int length) {
|
||||
int res = 0;
|
||||
for (int i = startIndex; i < startIndex + length; i++) {
|
||||
res <<= 1;
|
||||
if (rawbits[i]) {
|
||||
res |= 0x01;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a code of length 8 in an array of bits, padding with zeros
|
||||
*/
|
||||
private static byte readByte(boolean[] rawbits, int startIndex) {
|
||||
int n = rawbits.length - startIndex;
|
||||
if (n >= 8) {
|
||||
return (byte) readCode(rawbits, startIndex, 8);
|
||||
}
|
||||
return (byte) (readCode(rawbits, startIndex, n) << (8 - n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs a bit array into bytes, most significant bit first
|
||||
*/
|
||||
static byte[] convertBoolArrayToByteArray(boolean[] boolArr) {
|
||||
byte[] byteArr = new byte[(boolArr.length + 7) / 8];
|
||||
for (int i = 0; i < byteArr.length; i++) {
|
||||
byteArr[i] = readByte(boolArr, 8 * i);
|
||||
}
|
||||
return byteArr;
|
||||
}
|
||||
|
||||
private static int totalBitsInLayer(int layers, boolean compact) {
|
||||
return ((compact ? 88 : 112) + 16 * layers) * layers;
|
||||
}
|
||||
}
|
||||
603
port_src/core/DONE/aztec/detector/Detector.java
Normal file
603
port_src/core/DONE/aztec/detector/Detector.java
Normal file
@@ -0,0 +1,603 @@
|
||||
/*
|
||||
* Copyright 2010 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.detector;
|
||||
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.aztec.AztecDetectorResult;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.GridSampler;
|
||||
import com.google.zxing.common.detector.MathUtils;
|
||||
import com.google.zxing.common.detector.WhiteRectangleDetector;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonException;
|
||||
|
||||
/**
|
||||
* Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code
|
||||
* is rotated or skewed, or partially obscured.
|
||||
*
|
||||
* @author David Olivier
|
||||
* @author Frank Yellin
|
||||
*/
|
||||
public final class Detector {
|
||||
|
||||
private static final int[] EXPECTED_CORNER_BITS = {
|
||||
0xee0, // 07340 XXX .XX X.. ...
|
||||
0x1dc, // 00734 ... XXX .XX X..
|
||||
0x83b, // 04073 X.. ... XXX .XX
|
||||
0x707, // 03407 .XX X.. ... XXX
|
||||
};
|
||||
|
||||
private final BitMatrix image;
|
||||
|
||||
private boolean compact;
|
||||
private int nbLayers;
|
||||
private int nbDataBlocks;
|
||||
private int nbCenterLayers;
|
||||
private int shift;
|
||||
|
||||
public Detector(BitMatrix image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public AztecDetectorResult detect() throws NotFoundException {
|
||||
return detect(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects an Aztec Code in an image.
|
||||
*
|
||||
* @param isMirror if true, image is a mirror-image of original
|
||||
* @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code
|
||||
* @throws NotFoundException if no Aztec Code can be found
|
||||
*/
|
||||
public AztecDetectorResult detect(boolean isMirror) throws NotFoundException {
|
||||
|
||||
// 1. Get the center of the aztec matrix
|
||||
Point pCenter = getMatrixCenter();
|
||||
|
||||
// 2. Get the center points of the four diagonal points just outside the bull's eye
|
||||
// [topRight, bottomRight, bottomLeft, topLeft]
|
||||
ResultPoint[] bullsEyeCorners = getBullsEyeCorners(pCenter);
|
||||
|
||||
if (isMirror) {
|
||||
ResultPoint temp = bullsEyeCorners[0];
|
||||
bullsEyeCorners[0] = bullsEyeCorners[2];
|
||||
bullsEyeCorners[2] = temp;
|
||||
}
|
||||
|
||||
// 3. Get the size of the matrix and other parameters from the bull's eye
|
||||
extractParameters(bullsEyeCorners);
|
||||
|
||||
// 4. Sample the grid
|
||||
BitMatrix bits = sampleGrid(image,
|
||||
bullsEyeCorners[shift % 4],
|
||||
bullsEyeCorners[(shift + 1) % 4],
|
||||
bullsEyeCorners[(shift + 2) % 4],
|
||||
bullsEyeCorners[(shift + 3) % 4]);
|
||||
|
||||
// 5. Get the corners of the matrix.
|
||||
ResultPoint[] corners = getMatrixCornerPoints(bullsEyeCorners);
|
||||
|
||||
return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the number of data layers and data blocks from the layer around the bull's eye.
|
||||
*
|
||||
* @param bullsEyeCorners the array of bull's eye corners
|
||||
* @throws NotFoundException in case of too many errors or invalid parameters
|
||||
*/
|
||||
private void extractParameters(ResultPoint[] bullsEyeCorners) throws NotFoundException {
|
||||
if (!isValid(bullsEyeCorners[0]) || !isValid(bullsEyeCorners[1]) ||
|
||||
!isValid(bullsEyeCorners[2]) || !isValid(bullsEyeCorners[3])) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
int length = 2 * nbCenterLayers;
|
||||
// Get the bits around the bull's eye
|
||||
int[] sides = {
|
||||
sampleLine(bullsEyeCorners[0], bullsEyeCorners[1], length), // Right side
|
||||
sampleLine(bullsEyeCorners[1], bullsEyeCorners[2], length), // Bottom
|
||||
sampleLine(bullsEyeCorners[2], bullsEyeCorners[3], length), // Left side
|
||||
sampleLine(bullsEyeCorners[3], bullsEyeCorners[0], length) // Top
|
||||
};
|
||||
|
||||
// bullsEyeCorners[shift] is the corner of the bulls'eye that has three
|
||||
// orientation marks.
|
||||
// sides[shift] is the row/column that goes from the corner with three
|
||||
// orientation marks to the corner with two.
|
||||
shift = getRotation(sides, length);
|
||||
|
||||
// Flatten the parameter bits into a single 28- or 40-bit long
|
||||
long parameterData = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int side = sides[(shift + i) % 4];
|
||||
if (compact) {
|
||||
// Each side of the form ..XXXXXXX. where Xs are parameter data
|
||||
parameterData <<= 7;
|
||||
parameterData += (side >> 1) & 0x7F;
|
||||
} else {
|
||||
// Each side of the form ..XXXXX.XXXXX. where Xs are parameter data
|
||||
parameterData <<= 10;
|
||||
parameterData += ((side >> 2) & (0x1f << 5)) + ((side >> 1) & 0x1F);
|
||||
}
|
||||
}
|
||||
|
||||
// Corrects parameter data using RS. Returns just the data portion
|
||||
// without the error correction.
|
||||
int correctedData = getCorrectedParameterData(parameterData, compact);
|
||||
|
||||
if (compact) {
|
||||
// 8 bits: 2 bits layers and 6 bits data blocks
|
||||
nbLayers = (correctedData >> 6) + 1;
|
||||
nbDataBlocks = (correctedData & 0x3F) + 1;
|
||||
} else {
|
||||
// 16 bits: 5 bits layers and 11 bits data blocks
|
||||
nbLayers = (correctedData >> 11) + 1;
|
||||
nbDataBlocks = (correctedData & 0x7FF) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getRotation(int[] sides, int length) throws NotFoundException {
|
||||
// In a normal pattern, we expect to See
|
||||
// ** .* D A
|
||||
// * *
|
||||
//
|
||||
// . *
|
||||
// .. .. C B
|
||||
//
|
||||
// Grab the 3 bits from each of the sides the form the locator pattern and concatenate
|
||||
// into a 12-bit integer. Start with the bit at A
|
||||
int cornerBits = 0;
|
||||
for (int side : sides) {
|
||||
// XX......X where X's are orientation marks
|
||||
int t = ((side >> (length - 2)) << 1) + (side & 1);
|
||||
cornerBits = (cornerBits << 3) + t;
|
||||
}
|
||||
// Mov the bottom bit to the top, so that the three bits of the locator pattern at A are
|
||||
// together. cornerBits is now:
|
||||
// 3 orientation bits at A || 3 orientation bits at B || ... || 3 orientation bits at D
|
||||
cornerBits = ((cornerBits & 1) << 11) + (cornerBits >> 1);
|
||||
// The result shift indicates which element of BullsEyeCorners[] goes into the top-left
|
||||
// corner. Since the four rotation values have a Hamming distance of 8, we
|
||||
// can easily tolerate two errors.
|
||||
for (int shift = 0; shift < 4; shift++) {
|
||||
if (Integer.bitCount(cornerBits ^ EXPECTED_CORNER_BITS[shift]) <= 2) {
|
||||
return shift;
|
||||
}
|
||||
}
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrects the parameter bits using Reed-Solomon algorithm.
|
||||
*
|
||||
* @param parameterData parameter bits
|
||||
* @param compact true if this is a compact Aztec code
|
||||
* @throws NotFoundException if the array contains too many errors
|
||||
*/
|
||||
private static int getCorrectedParameterData(long parameterData, boolean compact) throws NotFoundException {
|
||||
int numCodewords;
|
||||
int numDataCodewords;
|
||||
|
||||
if (compact) {
|
||||
numCodewords = 7;
|
||||
numDataCodewords = 2;
|
||||
} else {
|
||||
numCodewords = 10;
|
||||
numDataCodewords = 4;
|
||||
}
|
||||
|
||||
int numECCodewords = numCodewords - numDataCodewords;
|
||||
int[] parameterWords = new int[numCodewords];
|
||||
for (int i = numCodewords - 1; i >= 0; --i) {
|
||||
parameterWords[i] = (int) parameterData & 0xF;
|
||||
parameterData >>= 4;
|
||||
}
|
||||
try {
|
||||
ReedSolomonDecoder rsDecoder = new ReedSolomonDecoder(GenericGF.AZTEC_PARAM);
|
||||
rsDecoder.decode(parameterWords, numECCodewords);
|
||||
} catch (ReedSolomonException ignored) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
// Toss the error correction. Just return the data as an integer
|
||||
int result = 0;
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
result = (result << 4) + parameterWords[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the corners of a bull-eye centered on the passed point.
|
||||
* This returns the centers of the diagonal points just outside the bull's eye
|
||||
* Returns [topRight, bottomRight, bottomLeft, topLeft]
|
||||
*
|
||||
* @param pCenter Center point
|
||||
* @return The corners of the bull-eye
|
||||
* @throws NotFoundException If no valid bull-eye can be found
|
||||
*/
|
||||
private ResultPoint[] getBullsEyeCorners(Point pCenter) throws NotFoundException {
|
||||
|
||||
Point pina = pCenter;
|
||||
Point pinb = pCenter;
|
||||
Point pinc = pCenter;
|
||||
Point pind = pCenter;
|
||||
|
||||
boolean color = true;
|
||||
|
||||
for (nbCenterLayers = 1; nbCenterLayers < 9; nbCenterLayers++) {
|
||||
Point pouta = getFirstDifferent(pina, color, 1, -1);
|
||||
Point poutb = getFirstDifferent(pinb, color, 1, 1);
|
||||
Point poutc = getFirstDifferent(pinc, color, -1, 1);
|
||||
Point poutd = getFirstDifferent(pind, color, -1, -1);
|
||||
|
||||
//d a
|
||||
//
|
||||
//c b
|
||||
|
||||
if (nbCenterLayers > 2) {
|
||||
float q = distance(poutd, pouta) * nbCenterLayers / (distance(pind, pina) * (nbCenterLayers + 2));
|
||||
if (q < 0.75 || q > 1.25 || !isWhiteOrBlackRectangle(pouta, poutb, poutc, poutd)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pina = pouta;
|
||||
pinb = poutb;
|
||||
pinc = poutc;
|
||||
pind = poutd;
|
||||
|
||||
color = !color;
|
||||
}
|
||||
|
||||
if (nbCenterLayers != 5 && nbCenterLayers != 7) {
|
||||
throw NotFoundException.getNotFoundInstance();
|
||||
}
|
||||
|
||||
compact = nbCenterLayers == 5;
|
||||
|
||||
// Expand the square by .5 pixel in each direction so that we're on the border
|
||||
// between the white square and the black square
|
||||
ResultPoint pinax = new ResultPoint(pina.getX() + 0.5f, pina.getY() - 0.5f);
|
||||
ResultPoint pinbx = new ResultPoint(pinb.getX() + 0.5f, pinb.getY() + 0.5f);
|
||||
ResultPoint pincx = new ResultPoint(pinc.getX() - 0.5f, pinc.getY() + 0.5f);
|
||||
ResultPoint pindx = new ResultPoint(pind.getX() - 0.5f, pind.getY() - 0.5f);
|
||||
|
||||
// Expand the square so that its corners are the centers of the points
|
||||
// just outside the bull's eye.
|
||||
return expandSquare(new ResultPoint[]{pinax, pinbx, pincx, pindx},
|
||||
2 * nbCenterLayers - 3,
|
||||
2 * nbCenterLayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a candidate center point of an Aztec code from an image
|
||||
*
|
||||
* @return the center point
|
||||
*/
|
||||
private Point getMatrixCenter() {
|
||||
|
||||
ResultPoint pointA;
|
||||
ResultPoint pointB;
|
||||
ResultPoint pointC;
|
||||
ResultPoint pointD;
|
||||
|
||||
//Get a white rectangle that can be the border of the matrix in center bull's eye or
|
||||
try {
|
||||
|
||||
ResultPoint[] cornerPoints = new WhiteRectangleDetector(image).detect();
|
||||
pointA = cornerPoints[0];
|
||||
pointB = cornerPoints[1];
|
||||
pointC = cornerPoints[2];
|
||||
pointD = cornerPoints[3];
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
|
||||
// This exception can be in case the initial rectangle is white
|
||||
// In that case, surely in the bull's eye, we try to expand the rectangle.
|
||||
int cx = image.getWidth() / 2;
|
||||
int cy = image.getHeight() / 2;
|
||||
pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();
|
||||
pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();
|
||||
pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();
|
||||
pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();
|
||||
|
||||
}
|
||||
|
||||
//Compute the center of the rectangle
|
||||
int cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0f);
|
||||
int cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0f);
|
||||
|
||||
// Redetermine the white rectangle starting from previously computed center.
|
||||
// This will ensure that we end up with a white rectangle in center bull's eye
|
||||
// in order to compute a more accurate center.
|
||||
try {
|
||||
ResultPoint[] cornerPoints = new WhiteRectangleDetector(image, 15, cx, cy).detect();
|
||||
pointA = cornerPoints[0];
|
||||
pointB = cornerPoints[1];
|
||||
pointC = cornerPoints[2];
|
||||
pointD = cornerPoints[3];
|
||||
} catch (NotFoundException e) {
|
||||
// This exception can be in case the initial rectangle is white
|
||||
// In that case we try to expand the rectangle.
|
||||
pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();
|
||||
pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();
|
||||
pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();
|
||||
pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();
|
||||
}
|
||||
|
||||
// Recompute the center of the rectangle
|
||||
cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0f);
|
||||
cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0f);
|
||||
|
||||
return new Point(cx, cy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Aztec code corners from the bull's eye corners and the parameters.
|
||||
*
|
||||
* @param bullsEyeCorners the array of bull's eye corners
|
||||
* @return the array of aztec code corners
|
||||
*/
|
||||
private ResultPoint[] getMatrixCornerPoints(ResultPoint[] bullsEyeCorners) {
|
||||
return expandSquare(bullsEyeCorners, 2 * nbCenterLayers, getDimension());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BitMatrix by sampling the provided image.
|
||||
* topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the
|
||||
* diagonal just outside the bull's eye.
|
||||
*/
|
||||
private BitMatrix sampleGrid(BitMatrix image,
|
||||
ResultPoint topLeft,
|
||||
ResultPoint topRight,
|
||||
ResultPoint bottomRight,
|
||||
ResultPoint bottomLeft) throws NotFoundException {
|
||||
|
||||
GridSampler sampler = GridSampler.getInstance();
|
||||
int dimension = getDimension();
|
||||
|
||||
float low = dimension / 2.0f - nbCenterLayers;
|
||||
float high = dimension / 2.0f + nbCenterLayers;
|
||||
|
||||
return sampler.sampleGrid(image,
|
||||
dimension,
|
||||
dimension,
|
||||
low, low, // topleft
|
||||
high, low, // topright
|
||||
high, high, // bottomright
|
||||
low, high, // bottomleft
|
||||
topLeft.getX(), topLeft.getY(),
|
||||
topRight.getX(), topRight.getY(),
|
||||
bottomRight.getX(), bottomRight.getY(),
|
||||
bottomLeft.getX(), bottomLeft.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples a line.
|
||||
*
|
||||
* @param p1 start point (inclusive)
|
||||
* @param p2 end point (exclusive)
|
||||
* @param size number of bits
|
||||
* @return the array of bits as an int (first bit is high-order bit of result)
|
||||
*/
|
||||
private int sampleLine(ResultPoint p1, ResultPoint p2, int size) {
|
||||
int result = 0;
|
||||
|
||||
float d = distance(p1, p2);
|
||||
float moduleSize = d / size;
|
||||
float px = p1.getX();
|
||||
float py = p1.getY();
|
||||
float dx = moduleSize * (p2.getX() - p1.getX()) / d;
|
||||
float dy = moduleSize * (p2.getY() - p1.getY()) / d;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (image.get(MathUtils.round(px + i * dx), MathUtils.round(py + i * dy))) {
|
||||
result |= 1 << (size - i - 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the border of the rectangle passed in parameter is compound of white points only
|
||||
* or black points only
|
||||
*/
|
||||
private boolean isWhiteOrBlackRectangle(Point p1,
|
||||
Point p2,
|
||||
Point p3,
|
||||
Point p4) {
|
||||
|
||||
int corr = 3;
|
||||
|
||||
p1 = new Point(Math.max(0, p1.getX() - corr), Math.min(image.getHeight() - 1, p1.getY() + corr));
|
||||
p2 = new Point(Math.max(0, p2.getX() - corr), Math.max(0, p2.getY() - corr));
|
||||
p3 = new Point(Math.min(image.getWidth() - 1, p3.getX() + corr),
|
||||
Math.max(0, Math.min(image.getHeight() - 1, p3.getY() - corr)));
|
||||
p4 = new Point(Math.min(image.getWidth() - 1, p4.getX() + corr),
|
||||
Math.min(image.getHeight() - 1, p4.getY() + corr));
|
||||
|
||||
int cInit = getColor(p4, p1);
|
||||
|
||||
if (cInit == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int c = getColor(p1, p2);
|
||||
|
||||
if (c != cInit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
c = getColor(p2, p3);
|
||||
|
||||
if (c != cInit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
c = getColor(p3, p4);
|
||||
|
||||
return c == cInit;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of a segment
|
||||
*
|
||||
* @return 1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else
|
||||
*/
|
||||
private int getColor(Point p1, Point p2) {
|
||||
float d = distance(p1, p2);
|
||||
if (d == 0.0f) {
|
||||
return 0;
|
||||
}
|
||||
float dx = (p2.getX() - p1.getX()) / d;
|
||||
float dy = (p2.getY() - p1.getY()) / d;
|
||||
int error = 0;
|
||||
|
||||
float px = p1.getX();
|
||||
float py = p1.getY();
|
||||
|
||||
boolean colorModel = image.get(p1.getX(), p1.getY());
|
||||
|
||||
int iMax = (int) Math.floor(d);
|
||||
for (int i = 0; i < iMax; i++) {
|
||||
if (image.get(MathUtils.round(px), MathUtils.round(py)) != colorModel) {
|
||||
error++;
|
||||
}
|
||||
px += dx;
|
||||
py += dy;
|
||||
}
|
||||
|
||||
float errRatio = error / d;
|
||||
|
||||
if (errRatio > 0.1f && errRatio < 0.9f) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (errRatio <= 0.1f) == colorModel ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the coordinate of the first point with a different color in the given direction
|
||||
*/
|
||||
private Point getFirstDifferent(Point init, boolean color, int dx, int dy) {
|
||||
int x = init.getX() + dx;
|
||||
int y = init.getY() + dy;
|
||||
|
||||
while (isValid(x, y) && image.get(x, y) == color) {
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
|
||||
x -= dx;
|
||||
y -= dy;
|
||||
|
||||
while (isValid(x, y) && image.get(x, y) == color) {
|
||||
x += dx;
|
||||
}
|
||||
x -= dx;
|
||||
|
||||
while (isValid(x, y) && image.get(x, y) == color) {
|
||||
y += dy;
|
||||
}
|
||||
y -= dy;
|
||||
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the square represented by the corner points by pushing out equally in all directions
|
||||
*
|
||||
* @param cornerPoints the corners of the square, which has the bull's eye at its center
|
||||
* @param oldSide the original length of the side of the square in the target bit matrix
|
||||
* @param newSide the new length of the size of the square in the target bit matrix
|
||||
* @return the corners of the expanded square
|
||||
*/
|
||||
private static ResultPoint[] expandSquare(ResultPoint[] cornerPoints, int oldSide, int newSide) {
|
||||
float ratio = newSide / (2.0f * oldSide);
|
||||
float dx = cornerPoints[0].getX() - cornerPoints[2].getX();
|
||||
float dy = cornerPoints[0].getY() - cornerPoints[2].getY();
|
||||
float centerx = (cornerPoints[0].getX() + cornerPoints[2].getX()) / 2.0f;
|
||||
float centery = (cornerPoints[0].getY() + cornerPoints[2].getY()) / 2.0f;
|
||||
|
||||
ResultPoint result0 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
|
||||
ResultPoint result2 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
|
||||
|
||||
dx = cornerPoints[1].getX() - cornerPoints[3].getX();
|
||||
dy = cornerPoints[1].getY() - cornerPoints[3].getY();
|
||||
centerx = (cornerPoints[1].getX() + cornerPoints[3].getX()) / 2.0f;
|
||||
centery = (cornerPoints[1].getY() + cornerPoints[3].getY()) / 2.0f;
|
||||
ResultPoint result1 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
|
||||
ResultPoint result3 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
|
||||
|
||||
return new ResultPoint[]{result0, result1, result2, result3};
|
||||
}
|
||||
|
||||
private boolean isValid(int x, int y) {
|
||||
return x >= 0 && x < image.getWidth() && y >= 0 && y < image.getHeight();
|
||||
}
|
||||
|
||||
private boolean isValid(ResultPoint point) {
|
||||
int x = MathUtils.round(point.getX());
|
||||
int y = MathUtils.round(point.getY());
|
||||
return isValid(x, y);
|
||||
}
|
||||
|
||||
private static float distance(Point a, Point b) {
|
||||
return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());
|
||||
}
|
||||
|
||||
private static float distance(ResultPoint a, ResultPoint b) {
|
||||
return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY());
|
||||
}
|
||||
|
||||
private int getDimension() {
|
||||
if (compact) {
|
||||
return 4 * nbLayers + 11;
|
||||
}
|
||||
return 4 * nbLayers + 2 * ((2 * nbLayers + 6) / 15) + 15;
|
||||
}
|
||||
|
||||
static final class Point {
|
||||
private final int x;
|
||||
private final int y;
|
||||
|
||||
ResultPoint toResultPoint() {
|
||||
return new ResultPoint(x, y);
|
||||
}
|
||||
|
||||
Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<" + x + ' ' + y + '>';
|
||||
}
|
||||
}
|
||||
}
|
||||
89
port_src/core/DONE/aztec/encoder/AztecCode.java
Normal file
89
port_src/core/DONE/aztec/encoder/AztecCode.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* Aztec 2D code representation
|
||||
*
|
||||
* @author Rustam Abdullaev
|
||||
*/
|
||||
public final class AztecCode {
|
||||
|
||||
private boolean compact;
|
||||
private int size;
|
||||
private int layers;
|
||||
private int codeWords;
|
||||
private BitMatrix matrix;
|
||||
|
||||
/**
|
||||
* @return {@code true} if compact instead of full mode
|
||||
*/
|
||||
public boolean isCompact() {
|
||||
return compact;
|
||||
}
|
||||
|
||||
public void setCompact(boolean compact) {
|
||||
this.compact = compact;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return size in pixels (width and height)
|
||||
*/
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of levels
|
||||
*/
|
||||
public int getLayers() {
|
||||
return layers;
|
||||
}
|
||||
|
||||
public void setLayers(int layers) {
|
||||
this.layers = layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of data codewords
|
||||
*/
|
||||
public int getCodeWords() {
|
||||
return codeWords;
|
||||
}
|
||||
|
||||
public void setCodeWords(int codeWords) {
|
||||
this.codeWords = codeWords;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the symbol image
|
||||
*/
|
||||
public BitMatrix getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public void setMatrix(BitMatrix matrix) {
|
||||
this.matrix = matrix;
|
||||
}
|
||||
|
||||
}
|
||||
61
port_src/core/DONE/aztec/encoder/BinaryShiftToken.java
Normal file
61
port_src/core/DONE/aztec/encoder/BinaryShiftToken.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
final class BinaryShiftToken extends Token {
|
||||
|
||||
private final int binaryShiftStart;
|
||||
private final int binaryShiftByteCount;
|
||||
|
||||
BinaryShiftToken(Token previous,
|
||||
int binaryShiftStart,
|
||||
int binaryShiftByteCount) {
|
||||
super(previous);
|
||||
this.binaryShiftStart = binaryShiftStart;
|
||||
this.binaryShiftByteCount = binaryShiftByteCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(BitArray bitArray, byte[] text) {
|
||||
int bsbc = binaryShiftByteCount;
|
||||
for (int i = 0; i < bsbc; i++) {
|
||||
if (i == 0 || (i == 31 && bsbc <= 62)) {
|
||||
// We need a header before the first character, and before
|
||||
// character 31 when the total byte code is <= 62
|
||||
bitArray.appendBits(31, 5); // BINARY_SHIFT
|
||||
if (bsbc > 62) {
|
||||
bitArray.appendBits(bsbc - 31, 16);
|
||||
} else if (i == 0) {
|
||||
// 1 <= binaryShiftByteCode <= 62
|
||||
bitArray.appendBits(Math.min(bsbc, 31), 5);
|
||||
} else {
|
||||
// 32 <= binaryShiftCount <= 62 and i == 31
|
||||
bitArray.appendBits(bsbc - 31, 5);
|
||||
}
|
||||
}
|
||||
bitArray.appendBits(text[binaryShiftStart + i], 8);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<" + binaryShiftStart + "::" + (binaryShiftStart + binaryShiftByteCount - 1) + '>';
|
||||
}
|
||||
|
||||
}
|
||||
404
port_src/core/DONE/aztec/encoder/Encoder.java
Normal file
404
port_src/core/DONE/aztec/encoder/Encoder.java
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.reedsolomon.GenericGF;
|
||||
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Generates Aztec 2D barcodes.
|
||||
*
|
||||
* @author Rustam Abdullaev
|
||||
*/
|
||||
public final class Encoder {
|
||||
|
||||
public static final int DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words
|
||||
public static final int DEFAULT_AZTEC_LAYERS = 0;
|
||||
private static final int MAX_NB_BITS = 32;
|
||||
private static final int MAX_NB_BITS_COMPACT = 4;
|
||||
|
||||
private static final int[] WORD_SIZE = {
|
||||
4, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
||||
12, 12, 12, 12, 12, 12, 12, 12, 12, 12
|
||||
};
|
||||
|
||||
private Encoder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given string content as an Aztec symbol (without ECI code)
|
||||
*
|
||||
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(String data) {
|
||||
return encode(data.getBytes(StandardCharsets.ISO_8859_1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given string content as an Aztec symbol (without ECI code)
|
||||
*
|
||||
* @param data input data string; must be encodable as ISO/IEC 8859-1 (Latin-1)
|
||||
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
|
||||
* a minimum of 23% + 3 words is recommended)
|
||||
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(String data, int minECCPercent, int userSpecifiedLayers) {
|
||||
return encode(data.getBytes(StandardCharsets.ISO_8859_1), minECCPercent, userSpecifiedLayers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given string content as an Aztec symbol
|
||||
*
|
||||
* @param data input data string
|
||||
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
|
||||
* a minimum of 23% + 3 words is recommended)
|
||||
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
|
||||
* @param charset character set in which to encode string using ECI; if null, no ECI code
|
||||
* will be inserted, and the string must be encodable as ISO/IEC 8859-1
|
||||
* (Latin-1), the default encoding of the symbol.
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(String data, int minECCPercent, int userSpecifiedLayers, Charset charset) {
|
||||
byte[] bytes = data.getBytes(null != charset ? charset : StandardCharsets.ISO_8859_1);
|
||||
return encode(bytes, minECCPercent, userSpecifiedLayers, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given binary content as an Aztec symbol (without ECI code)
|
||||
*
|
||||
* @param data input data string
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(byte[] data) {
|
||||
return encode(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given binary content as an Aztec symbol (without ECI code)
|
||||
*
|
||||
* @param data input data string
|
||||
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
|
||||
* a minimum of 23% + 3 words is recommended)
|
||||
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers) {
|
||||
return encode(data, minECCPercent, userSpecifiedLayers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given binary content as an Aztec symbol
|
||||
*
|
||||
* @param data input data string
|
||||
* @param minECCPercent minimal percentage of error check words (According to ISO/IEC 24778:2008,
|
||||
* a minimum of 23% + 3 words is recommended)
|
||||
* @param userSpecifiedLayers if non-zero, a user-specified value for the number of layers
|
||||
* @param charset character set to mark using ECI; if null, no ECI code will be inserted, and the
|
||||
* default encoding of ISO/IEC 8859-1 will be assuming by readers.
|
||||
* @return Aztec symbol matrix with metadata
|
||||
*/
|
||||
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers, Charset charset) {
|
||||
// High-level encode
|
||||
BitArray bits = new HighLevelEncoder(data, charset).encode();
|
||||
|
||||
// stuff bits and choose symbol size
|
||||
int eccBits = bits.getSize() * minECCPercent / 100 + 11;
|
||||
int totalSizeBits = bits.getSize() + eccBits;
|
||||
boolean compact;
|
||||
int layers;
|
||||
int totalBitsInLayer;
|
||||
int wordSize;
|
||||
BitArray stuffedBits;
|
||||
if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS) {
|
||||
compact = userSpecifiedLayers < 0;
|
||||
layers = Math.abs(userSpecifiedLayers);
|
||||
if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Illegal value %s for layers", userSpecifiedLayers));
|
||||
}
|
||||
totalBitsInLayer = totalBitsInLayer(layers, compact);
|
||||
wordSize = WORD_SIZE[layers];
|
||||
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
|
||||
stuffedBits = stuffBits(bits, wordSize);
|
||||
if (stuffedBits.getSize() + eccBits > usableBitsInLayers) {
|
||||
throw new IllegalArgumentException("Data to large for user specified layer");
|
||||
}
|
||||
if (compact && stuffedBits.getSize() > wordSize * 64) {
|
||||
// Compact format only allows 64 data words, though C4 can hold more words than that
|
||||
throw new IllegalArgumentException("Data to large for user specified layer");
|
||||
}
|
||||
} else {
|
||||
wordSize = 0;
|
||||
stuffedBits = null;
|
||||
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
|
||||
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
|
||||
// is the same size, but has more data.
|
||||
for (int i = 0; ; i++) {
|
||||
if (i > MAX_NB_BITS) {
|
||||
throw new IllegalArgumentException("Data too large for an Aztec code");
|
||||
}
|
||||
compact = i <= 3;
|
||||
layers = compact ? i + 1 : i;
|
||||
totalBitsInLayer = totalBitsInLayer(layers, compact);
|
||||
if (totalSizeBits > totalBitsInLayer) {
|
||||
continue;
|
||||
}
|
||||
// [Re]stuff the bits if this is the first opportunity, or if the
|
||||
// wordSize has changed
|
||||
if (stuffedBits == null || wordSize != WORD_SIZE[layers]) {
|
||||
wordSize = WORD_SIZE[layers];
|
||||
stuffedBits = stuffBits(bits, wordSize);
|
||||
}
|
||||
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
|
||||
if (compact && stuffedBits.getSize() > wordSize * 64) {
|
||||
// Compact format only allows 64 data words, though C4 can hold more words than that
|
||||
continue;
|
||||
}
|
||||
if (stuffedBits.getSize() + eccBits <= usableBitsInLayers) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize);
|
||||
|
||||
// generate mode message
|
||||
int messageSizeInWords = stuffedBits.getSize() / wordSize;
|
||||
BitArray modeMessage = generateModeMessage(compact, layers, messageSizeInWords);
|
||||
|
||||
// allocate symbol
|
||||
int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines
|
||||
int[] alignmentMap = new int[baseMatrixSize];
|
||||
int matrixSize;
|
||||
if (compact) {
|
||||
// no alignment marks in compact mode, alignmentMap is a no-op
|
||||
matrixSize = baseMatrixSize;
|
||||
for (int i = 0; i < alignmentMap.length; i++) {
|
||||
alignmentMap[i] = i;
|
||||
}
|
||||
} else {
|
||||
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
|
||||
int origCenter = baseMatrixSize / 2;
|
||||
int center = matrixSize / 2;
|
||||
for (int i = 0; i < origCenter; i++) {
|
||||
int newOffset = i + i / 15;
|
||||
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
|
||||
alignmentMap[origCenter + i] = center + newOffset + 1;
|
||||
}
|
||||
}
|
||||
BitMatrix matrix = new BitMatrix(matrixSize);
|
||||
|
||||
// draw data bits
|
||||
for (int i = 0, rowOffset = 0; i < layers; i++) {
|
||||
int rowSize = (layers - i) * 4 + (compact ? 9 : 12);
|
||||
for (int j = 0; j < rowSize; j++) {
|
||||
int columnOffset = j * 2;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (messageBits.get(rowOffset + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]);
|
||||
}
|
||||
if (messageBits.get(rowOffset + rowSize * 2 + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]);
|
||||
}
|
||||
if (messageBits.get(rowOffset + rowSize * 4 + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]);
|
||||
}
|
||||
if (messageBits.get(rowOffset + rowSize * 6 + columnOffset + k)) {
|
||||
matrix.set(alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
rowOffset += rowSize * 8;
|
||||
}
|
||||
|
||||
// draw mode message
|
||||
drawModeMessage(matrix, compact, matrixSize, modeMessage);
|
||||
|
||||
// draw alignment marks
|
||||
if (compact) {
|
||||
drawBullsEye(matrix, matrixSize / 2, 5);
|
||||
} else {
|
||||
drawBullsEye(matrix, matrixSize / 2, 7);
|
||||
for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16) {
|
||||
for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2) {
|
||||
matrix.set(matrixSize / 2 - j, k);
|
||||
matrix.set(matrixSize / 2 + j, k);
|
||||
matrix.set(k, matrixSize / 2 - j);
|
||||
matrix.set(k, matrixSize / 2 + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AztecCode aztec = new AztecCode();
|
||||
aztec.setCompact(compact);
|
||||
aztec.setSize(matrixSize);
|
||||
aztec.setLayers(layers);
|
||||
aztec.setCodeWords(messageSizeInWords);
|
||||
aztec.setMatrix(matrix);
|
||||
return aztec;
|
||||
}
|
||||
|
||||
private static void drawBullsEye(BitMatrix matrix, int center, int size) {
|
||||
for (int i = 0; i < size; i += 2) {
|
||||
for (int j = center - i; j <= center + i; j++) {
|
||||
matrix.set(j, center - i);
|
||||
matrix.set(j, center + i);
|
||||
matrix.set(center - i, j);
|
||||
matrix.set(center + i, j);
|
||||
}
|
||||
}
|
||||
matrix.set(center - size, center - size);
|
||||
matrix.set(center - size + 1, center - size);
|
||||
matrix.set(center - size, center - size + 1);
|
||||
matrix.set(center + size, center - size);
|
||||
matrix.set(center + size, center - size + 1);
|
||||
matrix.set(center + size, center + size - 1);
|
||||
}
|
||||
|
||||
static BitArray generateModeMessage(boolean compact, int layers, int messageSizeInWords) {
|
||||
BitArray modeMessage = new BitArray();
|
||||
if (compact) {
|
||||
modeMessage.appendBits(layers - 1, 2);
|
||||
modeMessage.appendBits(messageSizeInWords - 1, 6);
|
||||
modeMessage = generateCheckWords(modeMessage, 28, 4);
|
||||
} else {
|
||||
modeMessage.appendBits(layers - 1, 5);
|
||||
modeMessage.appendBits(messageSizeInWords - 1, 11);
|
||||
modeMessage = generateCheckWords(modeMessage, 40, 4);
|
||||
}
|
||||
return modeMessage;
|
||||
}
|
||||
|
||||
private static void drawModeMessage(BitMatrix matrix, boolean compact, int matrixSize, BitArray modeMessage) {
|
||||
int center = matrixSize / 2;
|
||||
if (compact) {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
int offset = center - 3 + i;
|
||||
if (modeMessage.get(i)) {
|
||||
matrix.set(offset, center - 5);
|
||||
}
|
||||
if (modeMessage.get(i + 7)) {
|
||||
matrix.set(center + 5, offset);
|
||||
}
|
||||
if (modeMessage.get(20 - i)) {
|
||||
matrix.set(offset, center + 5);
|
||||
}
|
||||
if (modeMessage.get(27 - i)) {
|
||||
matrix.set(center - 5, offset);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int offset = center - 5 + i + i / 5;
|
||||
if (modeMessage.get(i)) {
|
||||
matrix.set(offset, center - 7);
|
||||
}
|
||||
if (modeMessage.get(i + 10)) {
|
||||
matrix.set(center + 7, offset);
|
||||
}
|
||||
if (modeMessage.get(29 - i)) {
|
||||
matrix.set(offset, center + 7);
|
||||
}
|
||||
if (modeMessage.get(39 - i)) {
|
||||
matrix.set(center - 7, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize) {
|
||||
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
|
||||
int messageSizeInWords = bitArray.getSize() / wordSize;
|
||||
ReedSolomonEncoder rs = new ReedSolomonEncoder(getGF(wordSize));
|
||||
int totalWords = totalBits / wordSize;
|
||||
int[] messageWords = bitsToWords(bitArray, wordSize, totalWords);
|
||||
rs.encode(messageWords, totalWords - messageSizeInWords);
|
||||
int startPad = totalBits % wordSize;
|
||||
BitArray messageBits = new BitArray();
|
||||
messageBits.appendBits(0, startPad);
|
||||
for (int messageWord : messageWords) {
|
||||
messageBits.appendBits(messageWord, wordSize);
|
||||
}
|
||||
return messageBits;
|
||||
}
|
||||
|
||||
private static int[] bitsToWords(BitArray stuffedBits, int wordSize, int totalWords) {
|
||||
int[] message = new int[totalWords];
|
||||
int i;
|
||||
int n;
|
||||
for (i = 0, n = stuffedBits.getSize() / wordSize; i < n; i++) {
|
||||
int value = 0;
|
||||
for (int j = 0; j < wordSize; j++) {
|
||||
value |= stuffedBits.get(i * wordSize + j) ? (1 << wordSize - j - 1) : 0;
|
||||
}
|
||||
message[i] = value;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
private static GenericGF getGF(int wordSize) {
|
||||
switch (wordSize) {
|
||||
case 4:
|
||||
return GenericGF.AZTEC_PARAM;
|
||||
case 6:
|
||||
return GenericGF.AZTEC_DATA_6;
|
||||
case 8:
|
||||
return GenericGF.AZTEC_DATA_8;
|
||||
case 10:
|
||||
return GenericGF.AZTEC_DATA_10;
|
||||
case 12:
|
||||
return GenericGF.AZTEC_DATA_12;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported word size " + wordSize);
|
||||
}
|
||||
}
|
||||
|
||||
static BitArray stuffBits(BitArray bits, int wordSize) {
|
||||
BitArray out = new BitArray();
|
||||
|
||||
int n = bits.getSize();
|
||||
int mask = (1 << wordSize) - 2;
|
||||
for (int i = 0; i < n; i += wordSize) {
|
||||
int word = 0;
|
||||
for (int j = 0; j < wordSize; j++) {
|
||||
if (i + j >= n || bits.get(i + j)) {
|
||||
word |= 1 << (wordSize - 1 - j);
|
||||
}
|
||||
}
|
||||
if ((word & mask) == mask) {
|
||||
out.appendBits(word & mask, wordSize);
|
||||
i--;
|
||||
} else if ((word & mask) == 0) {
|
||||
out.appendBits(word | 1, wordSize);
|
||||
i--;
|
||||
} else {
|
||||
out.appendBits(word, wordSize);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int totalBitsInLayer(int layers, boolean compact) {
|
||||
return ((compact ? 88 : 112) + 16 * layers) * layers;
|
||||
}
|
||||
}
|
||||
325
port_src/core/DONE/aztec/encoder/HighLevelEncoder.java
Normal file
325
port_src/core/DONE/aztec/encoder/HighLevelEncoder.java
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
import com.google.zxing.common.CharacterSetECI;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* This produces nearly optimal encodings of text into the first-level of
|
||||
* encoding used by Aztec code.
|
||||
*
|
||||
* It uses a dynamic algorithm. For each prefix of the string, it determines
|
||||
* a set of encodings that could lead to this prefix. We repeatedly add a
|
||||
* character and generate a new set of optimal encodings until we have read
|
||||
* through the entire input.
|
||||
*
|
||||
* @author Frank Yellin
|
||||
* @author Rustam Abdullaev
|
||||
*/
|
||||
public final class HighLevelEncoder {
|
||||
|
||||
static final String[] MODE_NAMES = {"UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"};
|
||||
|
||||
static final int MODE_UPPER = 0; // 5 bits
|
||||
static final int MODE_LOWER = 1; // 5 bits
|
||||
static final int MODE_DIGIT = 2; // 4 bits
|
||||
static final int MODE_MIXED = 3; // 5 bits
|
||||
static final int MODE_PUNCT = 4; // 5 bits
|
||||
|
||||
// The Latch Table shows, for each pair of Modes, the optimal method for
|
||||
// getting from one mode to another. In the worst possible case, this can
|
||||
// be up to 14 bits. In the best possible case, we are already there!
|
||||
// The high half-word of each entry gives the number of bits.
|
||||
// The low half-word of each entry are the actual bits necessary to change
|
||||
static final int[][] LATCH_TABLE = {
|
||||
{
|
||||
0,
|
||||
(5 << 16) + 28, // UPPER -> LOWER
|
||||
(5 << 16) + 30, // UPPER -> DIGIT
|
||||
(5 << 16) + 29, // UPPER -> MIXED
|
||||
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
|
||||
0,
|
||||
(5 << 16) + 30, // LOWER -> DIGIT
|
||||
(5 << 16) + 29, // LOWER -> MIXED
|
||||
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(4 << 16) + 14, // DIGIT -> UPPER
|
||||
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
|
||||
0,
|
||||
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
|
||||
(14 << 16) + (14 << 10) + (29 << 5) + 30,
|
||||
// DIGIT -> UPPER -> MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(5 << 16) + 29, // MIXED -> UPPER
|
||||
(5 << 16) + 28, // MIXED -> LOWER
|
||||
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
|
||||
0,
|
||||
(5 << 16) + 30, // MIXED -> PUNCT
|
||||
},
|
||||
{
|
||||
(5 << 16) + 31, // PUNCT -> UPPER
|
||||
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
|
||||
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
|
||||
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
|
||||
0,
|
||||
},
|
||||
};
|
||||
|
||||
// A reverse mapping from [mode][char] to the encoding for that character
|
||||
// in that mode. An entry of 0 indicates no mapping exists.
|
||||
private static final int[][] CHAR_MAP = new int[5][256];
|
||||
static {
|
||||
CHAR_MAP[MODE_UPPER][' '] = 1;
|
||||
for (int c = 'A'; c <= 'Z'; c++) {
|
||||
CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
|
||||
}
|
||||
CHAR_MAP[MODE_LOWER][' '] = 1;
|
||||
for (int c = 'a'; c <= 'z'; c++) {
|
||||
CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
|
||||
}
|
||||
CHAR_MAP[MODE_DIGIT][' '] = 1;
|
||||
for (int c = '0'; c <= '9'; c++) {
|
||||
CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
|
||||
}
|
||||
CHAR_MAP[MODE_DIGIT][','] = 12;
|
||||
CHAR_MAP[MODE_DIGIT]['.'] = 13;
|
||||
int[] mixedTable = {
|
||||
'\0', ' ', '\1', '\2', '\3', '\4', '\5', '\6', '\7', '\b', '\t', '\n',
|
||||
'\13', '\f', '\r', '\33', '\34', '\35', '\36', '\37', '@', '\\', '^',
|
||||
'_', '`', '|', '~', '\177'
|
||||
};
|
||||
for (int i = 0; i < mixedTable.length; i++) {
|
||||
CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
|
||||
}
|
||||
int[] punctTable = {
|
||||
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
|
||||
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
|
||||
'[', ']', '{', '}'
|
||||
};
|
||||
for (int i = 0; i < punctTable.length; i++) {
|
||||
if (punctTable[i] > 0) {
|
||||
CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A map showing the available shift codes. (The shifts to BINARY are not
|
||||
// shown
|
||||
static final int[][] SHIFT_TABLE = new int[6][6]; // mode shift codes, per table
|
||||
static {
|
||||
for (int[] table : SHIFT_TABLE) {
|
||||
Arrays.fill(table, -1);
|
||||
}
|
||||
SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
|
||||
|
||||
SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
|
||||
SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
|
||||
|
||||
SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
|
||||
|
||||
SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
|
||||
SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
|
||||
}
|
||||
|
||||
private final byte[] text;
|
||||
private final Charset charset;
|
||||
|
||||
public HighLevelEncoder(byte[] text) {
|
||||
this.text = text;
|
||||
this.charset = null;
|
||||
}
|
||||
|
||||
public HighLevelEncoder(byte[] text, Charset charset) {
|
||||
this.text = text;
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return text represented by this encoder encoded as a {@link BitArray}
|
||||
*/
|
||||
public BitArray encode() {
|
||||
State initialState = State.INITIAL_STATE;
|
||||
if (charset != null) {
|
||||
CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(charset);
|
||||
if (null == eci) {
|
||||
throw new IllegalArgumentException("No ECI code for character set " + charset);
|
||||
}
|
||||
initialState = initialState.appendFLGn(eci.getValue());
|
||||
}
|
||||
Collection<State> states = Collections.singletonList(initialState);
|
||||
for (int index = 0; index < text.length; index++) {
|
||||
int pairCode;
|
||||
int nextChar = index + 1 < text.length ? text[index + 1] : 0;
|
||||
switch (text[index]) {
|
||||
case '\r':
|
||||
pairCode = nextChar == '\n' ? 2 : 0;
|
||||
break;
|
||||
case '.' :
|
||||
pairCode = nextChar == ' ' ? 3 : 0;
|
||||
break;
|
||||
case ',' :
|
||||
pairCode = nextChar == ' ' ? 4 : 0;
|
||||
break;
|
||||
case ':' :
|
||||
pairCode = nextChar == ' ' ? 5 : 0;
|
||||
break;
|
||||
default:
|
||||
pairCode = 0;
|
||||
}
|
||||
if (pairCode > 0) {
|
||||
// We have one of the four special PUNCT pairs. Treat them specially.
|
||||
// Get a new set of states for the two new characters.
|
||||
states = updateStateListForPair(states, index, pairCode);
|
||||
index++;
|
||||
} else {
|
||||
// Get a new set of states for the new character.
|
||||
states = updateStateListForChar(states, index);
|
||||
}
|
||||
}
|
||||
// We are left with a set of states. Find the shortest one.
|
||||
State minState = Collections.min(states, new Comparator<State>() {
|
||||
@Override
|
||||
public int compare(State a, State b) {
|
||||
return a.getBitCount() - b.getBitCount();
|
||||
}
|
||||
});
|
||||
// Convert it to a bit array, and return.
|
||||
return minState.toBitArray(text);
|
||||
}
|
||||
|
||||
// We update a set of states for a new character by updating each state
|
||||
// for the new character, merging the results, and then removing the
|
||||
// non-optimal states.
|
||||
private Collection<State> updateStateListForChar(Iterable<State> states, int index) {
|
||||
Collection<State> result = new LinkedList<>();
|
||||
for (State state : states) {
|
||||
updateStateForChar(state, index, result);
|
||||
}
|
||||
return simplifyStates(result);
|
||||
}
|
||||
|
||||
// Return a set of states that represent the possible ways of updating this
|
||||
// state for the next character. The resulting set of states are added to
|
||||
// the "result" list.
|
||||
private void updateStateForChar(State state, int index, Collection<State> result) {
|
||||
char ch = (char) (text[index] & 0xFF);
|
||||
boolean charInCurrentTable = CHAR_MAP[state.getMode()][ch] > 0;
|
||||
State stateNoBinary = null;
|
||||
for (int mode = 0; mode <= MODE_PUNCT; mode++) {
|
||||
int charInMode = CHAR_MAP[mode][ch];
|
||||
if (charInMode > 0) {
|
||||
if (stateNoBinary == null) {
|
||||
// Only create stateNoBinary the first time it's required.
|
||||
stateNoBinary = state.endBinaryShift(index);
|
||||
}
|
||||
// Try generating the character by latching to its mode
|
||||
if (!charInCurrentTable || mode == state.getMode() || mode == MODE_DIGIT) {
|
||||
// If the character is in the current table, we don't want to latch to
|
||||
// any other mode except possibly digit (which uses only 4 bits). Any
|
||||
// other latch would be equally successful *after* this character, and
|
||||
// so wouldn't save any bits.
|
||||
State latchState = stateNoBinary.latchAndAppend(mode, charInMode);
|
||||
result.add(latchState);
|
||||
}
|
||||
// Try generating the character by switching to its mode.
|
||||
if (!charInCurrentTable && SHIFT_TABLE[state.getMode()][mode] >= 0) {
|
||||
// It never makes sense to temporarily shift to another mode if the
|
||||
// character exists in the current mode. That can never save bits.
|
||||
State shiftState = stateNoBinary.shiftAndAppend(mode, charInMode);
|
||||
result.add(shiftState);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.getBinaryShiftByteCount() > 0 || CHAR_MAP[state.getMode()][ch] == 0) {
|
||||
// It's never worthwhile to go into binary shift mode if you're not already
|
||||
// in binary shift mode, and the character exists in your current mode.
|
||||
// That can never save bits over just outputting the char in the current mode.
|
||||
State binaryState = state.addBinaryShiftChar(index);
|
||||
result.add(binaryState);
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<State> updateStateListForPair(Iterable<State> states, int index, int pairCode) {
|
||||
Collection<State> result = new LinkedList<>();
|
||||
for (State state : states) {
|
||||
updateStateForPair(state, index, pairCode, result);
|
||||
}
|
||||
return simplifyStates(result);
|
||||
}
|
||||
|
||||
private static void updateStateForPair(State state, int index, int pairCode, Collection<State> result) {
|
||||
State stateNoBinary = state.endBinaryShift(index);
|
||||
// Possibility 1. Latch to MODE_PUNCT, and then append this code
|
||||
result.add(stateNoBinary.latchAndAppend(MODE_PUNCT, pairCode));
|
||||
if (state.getMode() != MODE_PUNCT) {
|
||||
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
|
||||
// Every state except MODE_PUNCT (handled above) can shift
|
||||
result.add(stateNoBinary.shiftAndAppend(MODE_PUNCT, pairCode));
|
||||
}
|
||||
if (pairCode == 3 || pairCode == 4) {
|
||||
// both characters are in DIGITS. Sometimes better to just add two digits
|
||||
State digitState = stateNoBinary
|
||||
.latchAndAppend(MODE_DIGIT, 16 - pairCode) // period or comma in DIGIT
|
||||
.latchAndAppend(MODE_DIGIT, 1); // space in DIGIT
|
||||
result.add(digitState);
|
||||
}
|
||||
if (state.getBinaryShiftByteCount() > 0) {
|
||||
// It only makes sense to do the characters as binary if we're already
|
||||
// in binary mode.
|
||||
State binaryState = state.addBinaryShiftChar(index).addBinaryShiftChar(index + 1);
|
||||
result.add(binaryState);
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<State> simplifyStates(Iterable<State> states) {
|
||||
Deque<State> result = new LinkedList<>();
|
||||
for (State newState : states) {
|
||||
boolean add = true;
|
||||
for (Iterator<State> iterator = result.iterator(); iterator.hasNext();) {
|
||||
State oldState = iterator.next();
|
||||
if (oldState.isBetterThanOrEqualTo(newState)) {
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
if (newState.isBetterThanOrEqualTo(oldState)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
result.addFirst(newState);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
45
port_src/core/DONE/aztec/encoder/SimpleToken.java
Normal file
45
port_src/core/DONE/aztec/encoder/SimpleToken.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
final class SimpleToken extends Token {
|
||||
|
||||
// For normal words, indicates value and bitCount
|
||||
private final short value;
|
||||
private final short bitCount;
|
||||
|
||||
SimpleToken(Token previous, int value, int bitCount) {
|
||||
super(previous);
|
||||
this.value = (short) value;
|
||||
this.bitCount = (short) bitCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
void appendTo(BitArray bitArray, byte[] text) {
|
||||
bitArray.appendBits(value, bitCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
int value = this.value & ((1 << bitCount) - 1);
|
||||
value |= 1 << bitCount;
|
||||
return '<' + Integer.toBinaryString(value | (1 << bitCount)).substring(1) + '>';
|
||||
}
|
||||
|
||||
}
|
||||
195
port_src/core/DONE/aztec/encoder/State.java
Normal file
195
port_src/core/DONE/aztec/encoder/State.java
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
/**
|
||||
* State represents all information about a sequence necessary to generate the current output.
|
||||
* Note that a state is immutable.
|
||||
*/
|
||||
final class State {
|
||||
|
||||
static final State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
|
||||
|
||||
// The current mode of the encoding (or the mode to which we'll return if
|
||||
// we're in Binary Shift mode.
|
||||
private final int mode;
|
||||
// The list of tokens that we output. If we are in Binary Shift mode, this
|
||||
// token list does *not* yet included the token for those bytes
|
||||
private final Token token;
|
||||
// If non-zero, the number of most recent bytes that should be output
|
||||
// in Binary Shift mode.
|
||||
private final int binaryShiftByteCount;
|
||||
// The total number of bits generated (including Binary Shift).
|
||||
private final int bitCount;
|
||||
private final int binaryShiftCost;
|
||||
|
||||
private State(Token token, int mode, int binaryBytes, int bitCount) {
|
||||
this.token = token;
|
||||
this.mode = mode;
|
||||
this.binaryShiftByteCount = binaryBytes;
|
||||
this.bitCount = bitCount;
|
||||
this.binaryShiftCost = calculateBinaryShiftCost(binaryBytes);
|
||||
}
|
||||
|
||||
int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
Token getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
int getBinaryShiftByteCount() {
|
||||
return binaryShiftByteCount;
|
||||
}
|
||||
|
||||
int getBitCount() {
|
||||
return bitCount;
|
||||
}
|
||||
|
||||
State appendFLGn(int eci) {
|
||||
State result = shiftAndAppend(HighLevelEncoder.MODE_PUNCT, 0); // 0: FLG(n)
|
||||
Token token = result.token;
|
||||
int bitsAdded = 3;
|
||||
if (eci < 0) {
|
||||
token = token.add(0, 3); // 0: FNC1
|
||||
} else if (eci > 999999) {
|
||||
throw new IllegalArgumentException("ECI code must be between 0 and 999999");
|
||||
} else {
|
||||
byte[] eciDigits = Integer.toString(eci).getBytes(StandardCharsets.ISO_8859_1);
|
||||
token = token.add(eciDigits.length, 3); // 1-6: number of ECI digits
|
||||
for (byte eciDigit : eciDigits) {
|
||||
token = token.add(eciDigit - '0' + 2, 4);
|
||||
}
|
||||
bitsAdded += eciDigits.length * 4;
|
||||
}
|
||||
return new State(token, mode, 0, bitCount + bitsAdded);
|
||||
}
|
||||
|
||||
// Create a new state representing this state with a latch to a (not
|
||||
// necessary different) mode, and then a code.
|
||||
State latchAndAppend(int mode, int value) {
|
||||
int bitCount = this.bitCount;
|
||||
Token token = this.token;
|
||||
if (mode != this.mode) {
|
||||
int latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode];
|
||||
token = token.add(latch & 0xFFFF, latch >> 16);
|
||||
bitCount += latch >> 16;
|
||||
}
|
||||
int latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
|
||||
token = token.add(value, latchModeBitCount);
|
||||
return new State(token, mode, 0, bitCount + latchModeBitCount);
|
||||
}
|
||||
|
||||
// Create a new state representing this state, with a temporary shift
|
||||
// to a different mode to output a single value.
|
||||
State shiftAndAppend(int mode, int value) {
|
||||
Token token = this.token;
|
||||
int thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
|
||||
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
|
||||
token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount);
|
||||
token = token.add(value, 5);
|
||||
return new State(token, this.mode, 0, this.bitCount + thisModeBitCount + 5);
|
||||
}
|
||||
|
||||
// Create a new state representing this state, but an additional character
|
||||
// output in Binary Shift mode.
|
||||
State addBinaryShiftChar(int index) {
|
||||
Token token = this.token;
|
||||
int mode = this.mode;
|
||||
int bitCount = this.bitCount;
|
||||
if (this.mode == HighLevelEncoder.MODE_PUNCT || this.mode == HighLevelEncoder.MODE_DIGIT) {
|
||||
int latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER];
|
||||
token = token.add(latch & 0xFFFF, latch >> 16);
|
||||
bitCount += latch >> 16;
|
||||
mode = HighLevelEncoder.MODE_UPPER;
|
||||
}
|
||||
int deltaBitCount =
|
||||
(binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 :
|
||||
(binaryShiftByteCount == 62) ? 9 : 8;
|
||||
State result = new State(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount);
|
||||
if (result.binaryShiftByteCount == 2047 + 31) {
|
||||
// The string is as long as it's allowed to be. We should end it.
|
||||
result = result.endBinaryShift(index + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create the state identical to this one, but we are no longer in
|
||||
// Binary Shift mode.
|
||||
State endBinaryShift(int index) {
|
||||
if (binaryShiftByteCount == 0) {
|
||||
return this;
|
||||
}
|
||||
Token token = this.token;
|
||||
token = token.addBinaryShift(index - binaryShiftByteCount, binaryShiftByteCount);
|
||||
return new State(token, mode, 0, this.bitCount);
|
||||
}
|
||||
|
||||
// Returns true if "this" state is better (or equal) to be in than "that"
|
||||
// state under all possible circumstances.
|
||||
boolean isBetterThanOrEqualTo(State other) {
|
||||
int newModeBitCount = this.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);
|
||||
if (this.binaryShiftByteCount < other.binaryShiftByteCount) {
|
||||
// add additional B/S encoding cost of other, if any
|
||||
newModeBitCount += other.binaryShiftCost - this.binaryShiftCost;
|
||||
} else if (this.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0) {
|
||||
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
|
||||
newModeBitCount += 10;
|
||||
}
|
||||
return newModeBitCount <= other.bitCount;
|
||||
}
|
||||
|
||||
BitArray toBitArray(byte[] text) {
|
||||
List<Token> symbols = new ArrayList<>();
|
||||
for (Token token = endBinaryShift(text.length).token; token != null; token = token.getPrevious()) {
|
||||
symbols.add(token);
|
||||
}
|
||||
BitArray bitArray = new BitArray();
|
||||
// Add each token to the result in forward order
|
||||
for (int i = symbols.size() - 1; i >= 0; i--) {
|
||||
symbols.get(i).appendTo(bitArray, text);
|
||||
}
|
||||
return bitArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s bits=%d bytes=%d", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
|
||||
}
|
||||
|
||||
private static int calculateBinaryShiftCost(int binaryShiftByteCount) {
|
||||
if (binaryShiftByteCount > 62) {
|
||||
return 21; // B/S with extended length
|
||||
}
|
||||
if (binaryShiftByteCount > 31) {
|
||||
return 20; // two B/S
|
||||
}
|
||||
if (binaryShiftByteCount > 0) {
|
||||
return 10; // one B/S
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
46
port_src/core/DONE/aztec/encoder/Token.java
Normal file
46
port_src/core/DONE/aztec/encoder/Token.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2013 ZXing authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.zxing.aztec.encoder;
|
||||
|
||||
import com.google.zxing.common.BitArray;
|
||||
|
||||
abstract class Token {
|
||||
|
||||
static final Token EMPTY = new SimpleToken(null, 0, 0);
|
||||
|
||||
private final Token previous;
|
||||
|
||||
Token(Token previous) {
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
final Token getPrevious() {
|
||||
return previous;
|
||||
}
|
||||
|
||||
final Token add(int value, int bitCount) {
|
||||
return new SimpleToken(this, value, bitCount);
|
||||
}
|
||||
|
||||
final Token addBinaryShift(int start, int byteCount) {
|
||||
//int bitCount = (byteCount * 8) + (byteCount <= 31 ? 10 : byteCount <= 62 ? 20 : 21);
|
||||
return new BinaryShiftToken(this, start, byteCount);
|
||||
}
|
||||
|
||||
abstract void appendTo(BitArray bitArray, byte[] text);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user