mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
C# port, add datamatrix code
git-svn-id: https://zxing.googlecode.com/svn/trunk@820 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
57afc8d154
commit
1c4f85ee24
|
@ -16,6 +16,7 @@ using System;
|
|||
using System.Collections;
|
||||
using com.google.zxing.qrcode;
|
||||
using com.google.zxing.oned;
|
||||
using com.google.zxing.datamatrix;
|
||||
|
||||
namespace com.google.zxing
|
||||
{
|
||||
|
@ -116,7 +117,7 @@ namespace com.google.zxing
|
|||
}
|
||||
// TODO re-enable once Data Matrix is ready
|
||||
if (possibleFormats.Contains(BarcodeFormat.DATAMATRIX)) {
|
||||
//readers.Add(new DataMatrixReader());
|
||||
readers.Add(new DataMatrixReader());
|
||||
}
|
||||
// At end in "try harder" mode
|
||||
if (addOneDReader && tryHarder)
|
||||
|
@ -129,14 +130,14 @@ namespace com.google.zxing
|
|||
{
|
||||
if (!tryHarder)
|
||||
{
|
||||
//readers.Add(new MultiFormatOneDReader(hints));
|
||||
readers.Add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
readers.Add(new QRCodeReader());
|
||||
// TODO re-enable once Data Matrix is ready
|
||||
// readers.addElement(new DataMatrixReader());
|
||||
readers.Add(new DataMatrixReader());
|
||||
if (tryHarder)
|
||||
{
|
||||
//readers.Add(new MultiFormatOneDReader(hints));
|
||||
readers.Add(new MultiFormatOneDReader(hints));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
BIN
csharp/bin/Release/com.google.zxing.dll
Normal file
BIN
csharp/bin/Release/com.google.zxing.dll
Normal file
Binary file not shown.
BIN
csharp/bin/Release/com.google.zxing.pdb
Normal file
BIN
csharp/bin/Release/com.google.zxing.pdb
Normal file
Binary file not shown.
118
csharp/datamatrix/DataMatrixReader.cs
Normal file
118
csharp/datamatrix/DataMatrixReader.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using com.google.zxing;
|
||||
using com.google.zxing.common;
|
||||
using com.google.zxing.datamatrix.decoder;
|
||||
using com.google.zxing.datamatrix.detector;
|
||||
|
||||
namespace com.google.zxing.datamatrix
|
||||
{
|
||||
public sealed class DataMatrixReader
|
||||
{
|
||||
private static ResultPoint[] NO_POINTS = new ResultPoint[0];
|
||||
private Decoder decoder = new Decoder();
|
||||
|
||||
/**
|
||||
* Locates and decodes a Data Matrix code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the Data Matrix code
|
||||
* @throws ReaderException if a Data Matrix code cannot be found, or cannot be decoded
|
||||
*/
|
||||
public Result decode(MonochromeBitmapSource image) {
|
||||
return decode(image, null);
|
||||
}
|
||||
|
||||
public Result decode(MonochromeBitmapSource image, System.Collections.Hashtable hints)
|
||||
{
|
||||
DecoderResult decoderResult;
|
||||
ResultPoint[] points;
|
||||
if (hints != null && hints.ContainsKey(DecodeHintType.PURE_BARCODE)) {
|
||||
BitMatrix bits = extractPureBits(image);
|
||||
decoderResult = decoder.decode(bits);
|
||||
points = NO_POINTS;
|
||||
} else {
|
||||
DetectorResult detectorResult = new Detector(image).detect();
|
||||
decoderResult = decoder.decode(detectorResult.getBits());
|
||||
points = detectorResult.getPoints();
|
||||
}
|
||||
Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.DATAMATRIX);
|
||||
if (decoderResult.getByteSegments() != null) {
|
||||
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, decoderResult.getByteSegments());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a Data Matrix code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a Data Matrix code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*/
|
||||
private static BitMatrix extractPureBits(MonochromeBitmapSource image) {
|
||||
// Now need to determine module size in pixels
|
||||
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
int minDimension = Math.Min(height, width);
|
||||
|
||||
// First, skip white border by tracking diagonally from the top left down and to the right:
|
||||
int borderWidth = 0;
|
||||
while (borderWidth < minDimension && !image.isBlack(borderWidth, borderWidth)) {
|
||||
borderWidth++;
|
||||
}
|
||||
if (borderWidth == minDimension) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
// And then keep tracking across the top-left black module to determine module size
|
||||
int moduleEnd = borderWidth + 1;
|
||||
while (moduleEnd < width && image.isBlack(moduleEnd, borderWidth)) {
|
||||
moduleEnd++;
|
||||
}
|
||||
if (moduleEnd == width) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
int moduleSize = moduleEnd - borderWidth;
|
||||
|
||||
// And now find where the bottommost black module on the first column ends
|
||||
int columnEndOfSymbol = height - 1;
|
||||
while (columnEndOfSymbol >= 0 && !image.isBlack(borderWidth, columnEndOfSymbol)) {
|
||||
columnEndOfSymbol--;
|
||||
}
|
||||
if (columnEndOfSymbol < 0) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
columnEndOfSymbol++;
|
||||
|
||||
// Make sure width of barcode is a multiple of module size
|
||||
if ((columnEndOfSymbol - borderWidth) % moduleSize != 0) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
int dimension = (columnEndOfSymbol - borderWidth) / moduleSize;
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
borderWidth += moduleSize >> 1;
|
||||
|
||||
int sampleDimension = borderWidth + (dimension - 1) * moduleSize;
|
||||
if (sampleDimension >= width || sampleDimension >= height) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
// Now just read off the bits
|
||||
BitMatrix bits = new BitMatrix(dimension);
|
||||
for (int i = 0; i < dimension; i++) {
|
||||
int iOffset = borderWidth + i * moduleSize;
|
||||
for (int j = 0; j < dimension; j++) {
|
||||
if (image.isBlack(borderWidth + j * moduleSize, iOffset)) {
|
||||
bits.set(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
}
|
||||
}
|
450
csharp/datamatrix/decoder/BitMatrixParser.cs
Normal file
450
csharp/datamatrix/decoder/BitMatrixParser.cs
Normal file
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using com.google.zxing.common;
|
||||
|
||||
namespace com.google.zxing.datamatrix.decoder
|
||||
{
|
||||
/**
|
||||
* @author bbrown@google.com (Brian Brown)
|
||||
*/
|
||||
public sealed class BitMatrixParser
|
||||
{
|
||||
|
||||
private BitMatrix mappingBitMatrix;
|
||||
private BitMatrix readMappingMatrix;
|
||||
private Version version;
|
||||
|
||||
/**
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
* @throws ReaderException if dimension is < 10 or > 144 or not 0 mod 2
|
||||
*/
|
||||
public BitMatrixParser(BitMatrix bitMatrix) {
|
||||
int dimension = bitMatrix.getDimension();
|
||||
if (dimension < 10 || dimension > 144 || (dimension & 0x01) != 0) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
version = readVersion(bitMatrix);
|
||||
this.mappingBitMatrix = extractDataRegion(bitMatrix);
|
||||
// TODO(bbrown): Make this work for rectangular symbols
|
||||
this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getDimension());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates the version object based on the dimension of the original bit matrix from
|
||||
* the datamatrix code.</p>
|
||||
*
|
||||
* <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
|
||||
*
|
||||
* @param bitMatrix Original {@link BitMatrix} including alignment patterns
|
||||
* @return {@link Version} encapsulating the Data Matrix Code's "version"
|
||||
* @throws ReaderException if the dimensions of the mapping matrix are not valid
|
||||
* Data Matrix dimensions.
|
||||
*/
|
||||
public Version readVersion(BitMatrix bitMatrix) {
|
||||
|
||||
if (version != null) {
|
||||
return version;
|
||||
}
|
||||
|
||||
// TODO(bbrown): make this work for rectangular dimensions as well.
|
||||
int numRows = bitMatrix.getDimension();
|
||||
int numColumns = numRows;
|
||||
|
||||
return Version.getVersionForDimensions(numRows, numColumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns)
|
||||
* in the correct order in order to reconstitute the codewords bytes contained within the
|
||||
* Data Matrix Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the Data Matrix Code
|
||||
* @throws ReaderException if the exact number of bytes expected is not read
|
||||
*/
|
||||
public sbyte[] readCodewords() {
|
||||
|
||||
sbyte[] result = new sbyte[version.getTotalCodewords()];
|
||||
int resultOffset = 0;
|
||||
|
||||
int row = 4;
|
||||
int column = 0;
|
||||
// TODO(bbrown): Data Matrix can be rectangular, assuming square for now
|
||||
int numRows = mappingBitMatrix.getDimension();
|
||||
int numColumns = numRows;
|
||||
|
||||
bool corner1Read = false;
|
||||
bool corner2Read = false;
|
||||
bool corner3Read = false;
|
||||
bool corner4Read = false;
|
||||
|
||||
// Read all of the codewords
|
||||
do {
|
||||
// Check the four corner cases
|
||||
if ((row == numRows) && (column == 0) && !corner1Read) {
|
||||
result[resultOffset++] = (sbyte) readCorner1(numRows, numColumns);
|
||||
row -= 2;
|
||||
column +=2;
|
||||
corner1Read = true;
|
||||
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x03) != 0) && !corner2Read) {
|
||||
result[resultOffset++] = (sbyte)readCorner2(numRows, numColumns);
|
||||
row -= 2;
|
||||
column +=2;
|
||||
corner2Read = true;
|
||||
} else if ((row == numRows+4) && (column == 2) && ((numColumns & 0x07) == 0) && !corner3Read) {
|
||||
result[resultOffset++] = (sbyte)readCorner3(numRows, numColumns);
|
||||
row -= 2;
|
||||
column +=2;
|
||||
corner3Read = true;
|
||||
} else if ((row == numRows-2) && (column == 0) && ((numColumns & 0x07) == 4) && !corner4Read) {
|
||||
result[resultOffset++] = (sbyte)readCorner4(numRows, numColumns);
|
||||
row -= 2;
|
||||
column +=2;
|
||||
corner4Read = true;
|
||||
} else {
|
||||
// Sweep upward diagonally to the right
|
||||
do {
|
||||
if ((row < numRows) && (column >= 0) && !readMappingMatrix.get(row, column)) {
|
||||
result[resultOffset++] = (sbyte)readUtah(row, column, numRows, numColumns);
|
||||
}
|
||||
row -= 2;
|
||||
column +=2;
|
||||
} while ((row >= 0) && (column < numColumns));
|
||||
row += 1;
|
||||
column +=3;
|
||||
|
||||
// Sweep downward diagonally to the left
|
||||
do {
|
||||
if ((row >= 0) && (column < numColumns) && !readMappingMatrix.get(row, column)) {
|
||||
result[resultOffset++] = (sbyte) readUtah(row, column, numRows, numColumns);
|
||||
}
|
||||
row += 2;
|
||||
column -=2;
|
||||
} while ((row < numRows) && (column >= 0));
|
||||
row += 3;
|
||||
column +=1;
|
||||
}
|
||||
} while ((row < numRows) || (column < numColumns));
|
||||
|
||||
if (resultOffset != version.getTotalCodewords()) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
|
||||
*
|
||||
* @param row Row to read in the mapping matrix
|
||||
* @param column Column to read in the mapping matrix
|
||||
* @param numRows Number of rows in the mapping matrix
|
||||
* @param numColumns Number of columns in the mapping matrix
|
||||
* @return value of the given bit in the mapping matrix
|
||||
*/
|
||||
bool readModule(int row, int column, int numRows, int numColumns) {
|
||||
// Adjust the row and column indices based on boundary wrapping
|
||||
if (row < 0) {
|
||||
row += numRows;
|
||||
column += 4 - ((numRows + 4) & 0x07);
|
||||
}
|
||||
if (column < 0) {
|
||||
column += numColumns;
|
||||
row += 4 - ((numColumns + 4) & 0x07);
|
||||
}
|
||||
readMappingMatrix.set(row, column);
|
||||
return mappingBitMatrix.get(row, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the 8 bits of the standard utah shaped pattern.</p>
|
||||
*
|
||||
* <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
|
||||
*
|
||||
* @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
|
||||
* @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern
|
||||
* @param numRows Number of rows in the mapping matrix
|
||||
* @param numColumns Number of columns in the mapping matrix
|
||||
* @return byte from the utah shape
|
||||
*/
|
||||
int readUtah(int row, int column, int numRows, int numColumns) {
|
||||
int currentByte = 0;
|
||||
if (readModule(row - 2, column - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row - 2, column - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row - 1, column - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row - 1, column - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row - 1, column, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row, column - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row, column - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(row, column, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
return currentByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the 8 bits of the special corner condition 1.</p>
|
||||
*
|
||||
* <p>See ISO 16022:2006, Figure F.3</p>
|
||||
*
|
||||
* @param numRows Number of rows in the mapping matrix
|
||||
* @param numColumns Number of columns in the mapping matrix
|
||||
* @return byte from the Corner condition 1
|
||||
*/
|
||||
int readCorner1(int numRows, int numColumns) {
|
||||
int currentByte = 0;
|
||||
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 1, 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 1, 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
return currentByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the 8 bits of the special corner condition 2.</p>
|
||||
*
|
||||
* <p>See ISO 16022:2006, Figure F.4</p>
|
||||
*
|
||||
* @param numRows Number of rows in the mapping matrix
|
||||
* @param numColumns Number of columns in the mapping matrix
|
||||
* @return byte from the Corner condition 2
|
||||
*/
|
||||
int readCorner2(int numRows, int numColumns) {
|
||||
int currentByte = 0;
|
||||
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 4, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
return currentByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the 8 bits of the special corner condition 3.</p>
|
||||
*
|
||||
* <p>See ISO 16022:2006, Figure F.5</p>
|
||||
*
|
||||
* @param numRows Number of rows in the mapping matrix
|
||||
* @param numColumns Number of columns in the mapping matrix
|
||||
* @return byte from the Corner condition 3
|
||||
*/
|
||||
int readCorner3(int numRows, int numColumns) {
|
||||
int currentByte = 0;
|
||||
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 1, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 3, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(1, numColumns - 3, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(1, numColumns - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
return currentByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the 8 bits of the special corner condition 4.</p>
|
||||
*
|
||||
* <p>See ISO 16022:2006, Figure F.6</p>
|
||||
*
|
||||
* @param numRows Number of rows in the mapping matrix
|
||||
* @param numColumns Number of columns in the mapping matrix
|
||||
* @return byte from the Corner condition 4
|
||||
*/
|
||||
int readCorner4(int numRows, int numColumns) {
|
||||
int currentByte = 0;
|
||||
if (readModule(numRows - 3, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 2, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(numRows - 1, 0, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 2, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(0, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(1, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(2, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
currentByte <<= 1;
|
||||
if (readModule(3, numColumns - 1, numRows, numColumns)) {
|
||||
currentByte |= 1;
|
||||
}
|
||||
return currentByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Extracts the data region from a {@link BitMatrix} that contains
|
||||
* alignment patterns.</p>
|
||||
*
|
||||
* @param bitMatrix Original {@link BitMatrix} with alignment patterns
|
||||
* @return BitMatrix that has the alignment patterns removed
|
||||
*/
|
||||
BitMatrix extractDataRegion(BitMatrix bitMatrix) {
|
||||
int symbolSizeRows = version.getSymbolSizeRows();
|
||||
int symbolSizeColumns = version.getSymbolSizeColumns();
|
||||
|
||||
// TODO(bbrown): Make this work with rectangular codes
|
||||
if (bitMatrix.getDimension() != symbolSizeRows) {
|
||||
throw new ArgumentException("Dimension of bitMarix must match the version size");
|
||||
}
|
||||
|
||||
int dataRegionSizeRows = version.getDataRegionSizeRows();
|
||||
int dataRegionSizeColumns = version.getDataRegionSizeColumns();
|
||||
|
||||
int numDataRegionsRow = symbolSizeRows / dataRegionSizeRows;
|
||||
int numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns;
|
||||
|
||||
int sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows;
|
||||
//int sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns;
|
||||
|
||||
// TODO(bbrown): Make this work with rectangular codes
|
||||
BitMatrix bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionRow);
|
||||
for (int dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) {
|
||||
int dataRegionRowOffset = dataRegionRow * dataRegionSizeRows;
|
||||
for (int dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) {
|
||||
int dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns;
|
||||
for (int i = 0; i < dataRegionSizeRows; ++i) {
|
||||
int readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i;
|
||||
int writeRowOffset = dataRegionRowOffset + i;
|
||||
for (int j = 0; j < dataRegionSizeColumns; ++j) {
|
||||
int readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j;
|
||||
if (bitMatrix.get(readRowOffset, readColumnOffset)) {
|
||||
int writeColumnOffset = dataRegionColumnOffset + j;
|
||||
bitMatrixWithoutAlignment.set(writeRowOffset, writeColumnOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitMatrixWithoutAlignment;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
124
csharp/datamatrix/decoder/DataBlock.cs
Normal file
124
csharp/datamatrix/decoder/DataBlock.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace com.google.zxing.datamatrix.decoder
|
||||
{
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
|
||||
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
* is represented by an instance of this class.</p>
|
||||
*
|
||||
* @author bbrown@google.com (Brian Brown)
|
||||
*/
|
||||
public sealed class DataBlock
|
||||
{
|
||||
private int numDataCodewords;
|
||||
private sbyte[] codewords;
|
||||
|
||||
private DataBlock(int numDataCodewords, sbyte[] codewords) {
|
||||
this.numDataCodewords = numDataCodewords;
|
||||
this.codewords = codewords;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them.
|
||||
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||
* method will separate the data into original blocks.</p>
|
||||
*
|
||||
* @param rawCodewords bytes as read directly from the Data Matrix Code
|
||||
* @param version version of the Data Matrix Code
|
||||
* @return {@link DataBlock}s containing original bytes, "de-interleaved" from representation in the
|
||||
* Data Matrix Code
|
||||
*/
|
||||
public static DataBlock[] getDataBlocks(sbyte[] rawCodewords,
|
||||
Version version) {
|
||||
// Figure out the number and size of data blocks used by this version
|
||||
Version.ECBlocks ecBlocks = version.getECBlocks();
|
||||
|
||||
// First count the total number of data blocks
|
||||
int totalBlocks = 0;
|
||||
Version.ECB[] ecBlockArray = ecBlocks.getECBlocks();
|
||||
for (int i = 0; i < ecBlockArray.Length; i++) {
|
||||
totalBlocks += ecBlockArray[i].getCount();
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
DataBlock[] result = new DataBlock[totalBlocks];
|
||||
int numResultBlocks = 0;
|
||||
for (int j = 0; j < ecBlockArray.Length; j++) {
|
||||
Version.ECB ecBlock = ecBlockArray[j];
|
||||
for (int i = 0; i < ecBlock.getCount(); i++) {
|
||||
int numDataCodewords = ecBlock.getDataCodewords();
|
||||
int numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords;
|
||||
result[numResultBlocks++] = new DataBlock(numDataCodewords, new sbyte[numBlockCodewords]);
|
||||
}
|
||||
}
|
||||
|
||||
// All blocks have the same amount of data, except that the last n
|
||||
// (where n may be 0) have 1 less byte. Figure out where these start.
|
||||
// TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144
|
||||
int longerBlocksTotalCodewords = result[0].codewords.Length;
|
||||
//int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1;
|
||||
|
||||
int longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords();
|
||||
int shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1;
|
||||
// The last elements of result may be 1 element shorter for 144 matrix
|
||||
// first fill out as many elements as all of them have minus 1
|
||||
int rawCodewordsOffset = 0;
|
||||
for (int i = 0; i < shorterBlocksNumDataCodewords; i++) {
|
||||
for (int j = 0; j < numResultBlocks; j++) {
|
||||
result[j].codewords[i] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out the last data block in the longer ones
|
||||
bool specialVersion = version.getVersionNumber() == 24;
|
||||
int numLongerBlocks = specialVersion ? 8 : numResultBlocks;
|
||||
for (int j = 0; j < numLongerBlocks; j++) {
|
||||
result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
|
||||
// Now add in error correction blocks
|
||||
int max = result[0].codewords.Length;
|
||||
for (int i = longerBlocksNumDataCodewords; i < max; i++) {
|
||||
for (int j = 0; j < numResultBlocks; j++) {
|
||||
int iOffset = (specialVersion && j > 7) ? i - 1 : i;
|
||||
result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
|
||||
if (rawCodewordsOffset != rawCodewords.Length) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getNumDataCodewords() {
|
||||
return numDataCodewords;
|
||||
}
|
||||
|
||||
public sbyte[] getCodewords() {
|
||||
return codewords;
|
||||
}
|
||||
}
|
||||
}
|
449
csharp/datamatrix/decoder/DecodedBitStreamParser.cs
Normal file
449
csharp/datamatrix/decoder/DecodedBitStreamParser.cs
Normal file
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using com.google.zxing.common;
|
||||
|
||||
namespace com.google.zxing.datamatrix.decoder
|
||||
{
|
||||
/**
|
||||
* <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
|
||||
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
* is represented by an instance of this class.</p>
|
||||
*
|
||||
* @author bbrown@google.com (Brian Brown)
|
||||
*/
|
||||
public sealed class DecodedBitStreamParser
|
||||
{
|
||||
/**
|
||||
* See ISO 16022:2006, Annex C Table C.1
|
||||
* The C40 Basic Character Set (*'s used for placeholders for the shift values)
|
||||
*/
|
||||
private static char[] C40_BASIC_SET_CHARS = {
|
||||
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'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'
|
||||
};
|
||||
|
||||
private static char[] C40_SHIFT2_SET_CHARS = {
|
||||
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.',
|
||||
'/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_'
|
||||
};
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, Annex C Table C.2
|
||||
* The Text Basic Character Set (*'s used for placeholders for the shift values)
|
||||
*/
|
||||
private static char[] TEXT_BASIC_SET_CHARS = {
|
||||
'*', '*', '*', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'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'
|
||||
};
|
||||
|
||||
private static char[] TEXT_SHIFT3_SET_CHARS = {
|
||||
'\'', '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', '{', '|', '}', '~', (char) 127
|
||||
};
|
||||
|
||||
private const int PAD_ENCODE = 0; // Not really an encoding
|
||||
private const int ASCII_ENCODE = 1;
|
||||
private const int C40_ENCODE = 2;
|
||||
private const int TEXT_ENCODE = 3;
|
||||
private const int ANSIX12_ENCODE = 4;
|
||||
private const int EDIFACT_ENCODE = 5;
|
||||
private const int BASE256_ENCODE = 6;
|
||||
|
||||
private DecodedBitStreamParser() {
|
||||
}
|
||||
|
||||
public static DecoderResult decode(sbyte[] bytes) {
|
||||
BitSource bits = new BitSource(bytes);
|
||||
StringBuilder result = new StringBuilder();
|
||||
StringBuilder resultTrailer = new StringBuilder(0);
|
||||
System.Collections.ArrayList byteSegments = new System.Collections.ArrayList(1);
|
||||
int mode = ASCII_ENCODE;
|
||||
do {
|
||||
if (mode == ASCII_ENCODE) {
|
||||
mode = decodeAsciiSegment(bits, result, resultTrailer);
|
||||
} else {
|
||||
switch (mode) {
|
||||
case C40_ENCODE:
|
||||
decodeC40Segment(bits, result);
|
||||
break;
|
||||
case TEXT_ENCODE:
|
||||
decodeTextSegment(bits, result);
|
||||
break;
|
||||
case ANSIX12_ENCODE:
|
||||
decodeAnsiX12Segment(bits, result);
|
||||
break;
|
||||
case EDIFACT_ENCODE:
|
||||
decodeEdifactSegment(bits, result);
|
||||
break;
|
||||
case BASE256_ENCODE:
|
||||
decodeBase256Segment(bits, result, byteSegments);
|
||||
break;
|
||||
default:
|
||||
throw new ReaderException();
|
||||
}
|
||||
mode = ASCII_ENCODE;
|
||||
}
|
||||
} while (mode != PAD_ENCODE && bits.available() > 0);
|
||||
if (resultTrailer.Length > 0) {
|
||||
result.Append(resultTrailer);
|
||||
}
|
||||
return new DecoderResult(bytes, result.ToString(), int.Equals(byteSegments.Count,0) ? null : byteSegments);
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
|
||||
*/
|
||||
private static int decodeAsciiSegment(BitSource bits, StringBuilder result, StringBuilder resultTrailer)
|
||||
{
|
||||
bool upperShift = false;
|
||||
do {
|
||||
int oneByte = bits.readBits(8);
|
||||
if (oneByte == 0) {
|
||||
throw new ReaderException();
|
||||
} else if (oneByte <= 128) { // ASCII data (ASCII value + 1)
|
||||
oneByte = upperShift ? (oneByte + 128) : oneByte;
|
||||
upperShift = false;
|
||||
result.Append((char) (oneByte - 1));
|
||||
return ASCII_ENCODE;
|
||||
} else if (oneByte == 129) { // Pad
|
||||
return PAD_ENCODE;
|
||||
} else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130)
|
||||
int value = oneByte - 130;
|
||||
if (value < 10) { // padd with '0' for single digit values
|
||||
result.Append('0');
|
||||
}
|
||||
result.Append(value);
|
||||
} else if (oneByte == 230) { // Latch to C40 encodation
|
||||
return C40_ENCODE;
|
||||
} else if (oneByte == 231) { // Latch to Base 256 encodation
|
||||
return BASE256_ENCODE;
|
||||
} else if (oneByte == 232) { // FNC1
|
||||
throw new ReaderException();
|
||||
} else if (oneByte == 233) { // Structured Append
|
||||
throw new ReaderException();
|
||||
} else if (oneByte == 234) { // Reader Programming
|
||||
throw new ReaderException();
|
||||
} else if (oneByte == 235) { // Upper Shift (shift to Extended ASCII)
|
||||
upperShift = true;
|
||||
} else if (oneByte == 236) { // 05 Macro
|
||||
result.Append("[)>\u001E05\u001D");
|
||||
resultTrailer.Insert(0, "\u001E\u0004");
|
||||
} else if (oneByte == 237) { // 06 Macro
|
||||
result.Append("[)>\u001E06\u001D");
|
||||
resultTrailer.Insert(0, "\u001E\u0004");
|
||||
} else if (oneByte == 238) { // Latch to ANSI X12 encodation
|
||||
return ANSIX12_ENCODE;
|
||||
} else if (oneByte == 239) { // Latch to Text encodation
|
||||
return TEXT_ENCODE;
|
||||
} else if (oneByte == 240) { // Latch to EDIFACT encodation
|
||||
return EDIFACT_ENCODE;
|
||||
} else if (oneByte == 241) { // ECI Character
|
||||
// TODO(bbrown): I think we need to support ECI
|
||||
throw new ReaderException();
|
||||
} else if (oneByte >= 242) { // Not to be used in ASCII encodation
|
||||
throw new ReaderException();
|
||||
}
|
||||
} while (bits.available() > 0);
|
||||
return ASCII_ENCODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
|
||||
*/
|
||||
private static void decodeC40Segment(BitSource bits, StringBuilder result) {
|
||||
// Three C40 values are encoded in a 16-bit value as
|
||||
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||
// TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time
|
||||
bool upperShift = false;
|
||||
|
||||
int[] cValues = new int[3];
|
||||
do {
|
||||
// If there is only one byte left then it will be encoded as ASCII
|
||||
if (bits.available() == 8) {
|
||||
return;
|
||||
}
|
||||
int firstByte = bits.readBits(8);
|
||||
if (firstByte == 254) { // Unlatch codeword
|
||||
return;
|
||||
}
|
||||
|
||||
parseTwoBytes(firstByte, bits.readBits(8), cValues);
|
||||
|
||||
int shift = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int cValue = cValues[i];
|
||||
switch (shift) {
|
||||
case 0:
|
||||
if (cValue < 3) {
|
||||
shift = cValue + 1;
|
||||
} else {
|
||||
if (upperShift) {
|
||||
result.Append((char) (C40_BASIC_SET_CHARS[cValue] + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(C40_BASIC_SET_CHARS[cValue]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (upperShift) {
|
||||
result.Append((char) (cValue + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(cValue);
|
||||
}
|
||||
shift = 0;
|
||||
break;
|
||||
case 2:
|
||||
if (cValue < 27) {
|
||||
if (upperShift) {
|
||||
result.Append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(C40_SHIFT2_SET_CHARS[cValue]);
|
||||
}
|
||||
} else if (cValue == 27) { // FNC1
|
||||
throw new ReaderException();
|
||||
} else if (cValue == 30) { // Upper Shift
|
||||
upperShift = true;
|
||||
} else {
|
||||
throw new ReaderException();
|
||||
}
|
||||
shift = 0;
|
||||
break;
|
||||
case 3:
|
||||
if (upperShift) {
|
||||
result.Append((char) (cValue + 224));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append((char) (cValue + 96));
|
||||
}
|
||||
shift = 0;
|
||||
break;
|
||||
default:
|
||||
throw new ReaderException();
|
||||
}
|
||||
}
|
||||
} while (bits.available() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
|
||||
*/
|
||||
private static void decodeTextSegment(BitSource bits, StringBuilder result) {
|
||||
// Three Text values are encoded in a 16-bit value as
|
||||
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||
// TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
|
||||
bool upperShift = false;
|
||||
|
||||
int[] cValues = new int[3];
|
||||
do {
|
||||
// If there is only one byte left then it will be encoded as ASCII
|
||||
if (bits.available() == 8) {
|
||||
return;
|
||||
}
|
||||
int firstByte = bits.readBits(8);
|
||||
if (firstByte == 254) { // Unlatch codeword
|
||||
return;
|
||||
}
|
||||
|
||||
parseTwoBytes(firstByte, bits.readBits(8), cValues);
|
||||
|
||||
int shift = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int cValue = cValues[i];
|
||||
switch (shift) {
|
||||
case 0:
|
||||
if (cValue < 3) {
|
||||
shift = cValue + 1;
|
||||
} else {
|
||||
if (upperShift) {
|
||||
result.Append((char) (TEXT_BASIC_SET_CHARS[cValue] + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(TEXT_BASIC_SET_CHARS[cValue]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (upperShift) {
|
||||
result.Append((char) (cValue + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(cValue);
|
||||
}
|
||||
shift = 0;
|
||||
break;
|
||||
case 2:
|
||||
// Shift 2 for Text is the same encoding as C40
|
||||
if (cValue < 27) {
|
||||
if (upperShift) {
|
||||
result.Append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(C40_SHIFT2_SET_CHARS[cValue]);
|
||||
}
|
||||
} else if (cValue == 27) { // FNC1
|
||||
throw new ReaderException();
|
||||
} else if (cValue == 30) { // Upper Shift
|
||||
upperShift = true;
|
||||
} else {
|
||||
throw new ReaderException();
|
||||
}
|
||||
shift = 0;
|
||||
break;
|
||||
case 3:
|
||||
if (upperShift) {
|
||||
result.Append((char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128));
|
||||
upperShift = false;
|
||||
} else {
|
||||
result.Append(TEXT_SHIFT3_SET_CHARS[cValue]);
|
||||
}
|
||||
shift = 0;
|
||||
break;
|
||||
default:
|
||||
throw new ReaderException();
|
||||
}
|
||||
}
|
||||
} while (bits.available() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, 5.2.7
|
||||
*/
|
||||
private static void decodeAnsiX12Segment(BitSource bits, StringBuilder result) {
|
||||
// Three ANSI X12 values are encoded in a 16-bit value as
|
||||
// (1600 * C1) + (40 * C2) + C3 + 1
|
||||
|
||||
int[] cValues = new int[3];
|
||||
do {
|
||||
// If there is only one byte left then it will be encoded as ASCII
|
||||
if (bits.available() == 8) {
|
||||
return;
|
||||
}
|
||||
int firstByte = bits.readBits(8);
|
||||
if (firstByte == 254) { // Unlatch codeword
|
||||
return;
|
||||
}
|
||||
|
||||
parseTwoBytes(firstByte, bits.readBits(8), cValues);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int cValue = cValues[i];
|
||||
if (cValue == 0) { // X12 segment terminator <CR>
|
||||
result.Append('\r');
|
||||
} else if (cValue == 1) { // X12 segment separator *
|
||||
result.Append('*');
|
||||
} else if (cValue == 2) { // X12 sub-element separator >
|
||||
result.Append('>');
|
||||
} else if (cValue == 3) { // space
|
||||
result.Append(' ');
|
||||
} else if (cValue < 14) { // 0 - 9
|
||||
result.Append((char) (cValue + 44));
|
||||
} else if (cValue < 40) { // A - Z
|
||||
result.Append((char) (cValue + 51));
|
||||
} else {
|
||||
throw new ReaderException();
|
||||
}
|
||||
}
|
||||
} while (bits.available() > 0);
|
||||
}
|
||||
|
||||
private static void parseTwoBytes(int firstByte, int secondByte, int[] result) {
|
||||
int fullBitValue = (firstByte << 8) + secondByte - 1;
|
||||
int temp = fullBitValue / 1600;
|
||||
result[0] = temp;
|
||||
fullBitValue -= temp * 1600;
|
||||
temp = fullBitValue / 40;
|
||||
result[1] = temp;
|
||||
result[2] = fullBitValue - temp * 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, 5.2.8 and Annex C Table C.3
|
||||
*/
|
||||
private static void decodeEdifactSegment(BitSource bits, StringBuilder result) {
|
||||
bool unlatch = false;
|
||||
do {
|
||||
// If there is only two or less bytes left then it will be encoded as ASCII
|
||||
if (bits.available() <= 16) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int edifactValue = bits.readBits(6);
|
||||
|
||||
// Check for the unlatch character
|
||||
if (edifactValue == 0x2B67) { // 011111
|
||||
unlatch = true;
|
||||
// If we encounter the unlatch code then continue reading because the Codeword triple
|
||||
// is padded with 0's
|
||||
}
|
||||
|
||||
if (!unlatch) {
|
||||
if ((edifactValue & 32) == 0) { // no 1 in the leading (6th) bit
|
||||
edifactValue |= 64; // Add a leading 01 to the 6 bit binary value
|
||||
}
|
||||
result.Append(edifactValue);
|
||||
}
|
||||
}
|
||||
} while (!unlatch && bits.available() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, 5.2.9 and Annex B, B.2
|
||||
*/
|
||||
private static void decodeBase256Segment(BitSource bits, StringBuilder result, System.Collections.ArrayList byteSegments) {
|
||||
// Figure out how long the Base 256 Segment is.
|
||||
int d1 = bits.readBits(8);
|
||||
int count;
|
||||
if (d1 == 0) { // Read the remainder of the symbol
|
||||
count = bits.available() / 8;
|
||||
} else if (d1 < 250) {
|
||||
count = d1;
|
||||
} else {
|
||||
count = 250 * (d1 - 249) + bits.readBits(8);
|
||||
}
|
||||
byte[] bytes = new byte[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
bytes[i] = unrandomize255State(bits.readBits(8), i);
|
||||
}
|
||||
byteSegments.Add(bytes);
|
||||
try {
|
||||
result.Append(System.Text.Encoding.GetEncoding("iso-8859-1").GetString(bytes));
|
||||
} catch (Exception uee) {
|
||||
throw new Exception("Platform does not support required encoding: " + uee);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006, Annex B, B.2
|
||||
*/
|
||||
private static byte unrandomize255State(int randomizedBase256Codeword,
|
||||
int base256CodewordPosition) {
|
||||
int pseudoRandomNumber = ((149 * base256CodewordPosition) % 255) + 1;
|
||||
int tempVariable = randomizedBase256Codeword - pseudoRandomNumber;
|
||||
return (byte) (tempVariable >= 0 ? tempVariable : (tempVariable + 256));
|
||||
}
|
||||
}
|
||||
}
|
132
csharp/datamatrix/decoder/Decoder.cs
Normal file
132
csharp/datamatrix/decoder/Decoder.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using com.google.zxing.common;
|
||||
using com.google.zxing.common.reedsolomon;
|
||||
|
||||
namespace com.google.zxing.datamatrix.decoder
|
||||
{
|
||||
|
||||
/**
|
||||
* <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
|
||||
* the Data Matrix Code from an image.</p>
|
||||
*
|
||||
* @author bbrown@google.com (Brian Brown)
|
||||
*/
|
||||
public sealed class Decoder
|
||||
{
|
||||
private ReedSolomonDecoder rsDecoder;
|
||||
public Decoder() {
|
||||
rsDecoder = new ReedSolomonDecoder(GF256.DATA_MATRIX_FIELD);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param image booleans representing white/black Data Matrix Code modules
|
||||
* @return text and bytes encoded within the Data Matrix Code
|
||||
* @throws ReaderException if the Data Matrix Code cannot be decoded
|
||||
*/
|
||||
public DecoderResult decode(bool[][] image) {
|
||||
int dimension = image.Length;
|
||||
BitMatrix bits = new BitMatrix(dimension);
|
||||
for (int i = 0; i < dimension; i++) {
|
||||
for (int j = 0; j < dimension; j++) {
|
||||
if (image[i][j]) {
|
||||
bits.set(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return decode(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken
|
||||
* to mean a black module.</p>
|
||||
*
|
||||
* @param bits booleans representing white/black Data Matrix Code modules
|
||||
* @return text and bytes encoded within the Data Matrix Code
|
||||
* @throws ReaderException if the Data Matrix Code cannot be decoded
|
||||
*/
|
||||
public DecoderResult decode(BitMatrix bits) {
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
BitMatrixParser parser = new BitMatrixParser(bits);
|
||||
Version version = parser.readVersion(bits);
|
||||
|
||||
// Read codewords
|
||||
sbyte[] codewords = parser.readCodewords();
|
||||
// Separate into data blocks
|
||||
DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version);
|
||||
|
||||
// Count total number of data bytes
|
||||
int totalBytes = 0;
|
||||
for (int i = 0; i < dataBlocks.Length; i++) {
|
||||
totalBytes += dataBlocks[i].getNumDataCodewords();
|
||||
}
|
||||
sbyte[] resultBytes = new sbyte[totalBytes];
|
||||
int resultOffset = 0;
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
for (int j = 0; j < dataBlocks.Length; j++) {
|
||||
DataBlock dataBlock = dataBlocks[j];
|
||||
sbyte[] codewordBytes = dataBlock.getCodewords();
|
||||
int numDataCodewords = dataBlock.getNumDataCodewords();
|
||||
correctErrors(codewordBytes, numDataCodewords);
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
resultBytes[resultOffset++] = codewordBytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser.decode(resultBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param codewordBytes data and error correction codewords
|
||||
* @param numDataCodewords number of codewords that are data bytes
|
||||
* @throws ReaderException if error correction fails
|
||||
*/
|
||||
private void correctErrors(sbyte[] codewordBytes, int numDataCodewords) {
|
||||
int numCodewords = codewordBytes.Length;
|
||||
// First read into an array of ints
|
||||
int[] codewordsInts = new int[numCodewords];
|
||||
for (int i = 0; i < numCodewords; i++) {
|
||||
codewordsInts[i] = codewordBytes[i] & 0xFF;
|
||||
}
|
||||
int numECCodewords = codewordBytes.Length - numDataCodewords;
|
||||
try {
|
||||
rsDecoder.decode(codewordsInts, numECCodewords);
|
||||
} catch (ReedSolomonException rse) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for (int i = 0; i < numDataCodewords; i++) {
|
||||
codewordBytes[i] = (sbyte) codewordsInts[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
222
csharp/datamatrix/decoder/Version.cs
Normal file
222
csharp/datamatrix/decoder/Version.cs
Normal file
|
@ -0,0 +1,222 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace com.google.zxing.datamatrix.decoder
|
||||
{
|
||||
public sealed class Version
|
||||
{
|
||||
private static Version[] VERSIONS = buildVersions();
|
||||
private int versionNumber;
|
||||
private int symbolSizeRows;
|
||||
private int symbolSizeColumns;
|
||||
private int dataRegionSizeRows;
|
||||
private int dataRegionSizeColumns;
|
||||
private ECBlocks ecBlocks;
|
||||
private int totalCodewords;
|
||||
|
||||
private Version(int versionNumber,
|
||||
int symbolSizeRows,
|
||||
int symbolSizeColumns,
|
||||
int dataRegionSizeRows,
|
||||
int dataRegionSizeColumns,
|
||||
ECBlocks ecBlocks) {
|
||||
this.versionNumber = versionNumber;
|
||||
this.symbolSizeRows = symbolSizeRows;
|
||||
this.symbolSizeColumns = symbolSizeColumns;
|
||||
this.dataRegionSizeRows = dataRegionSizeRows;
|
||||
this.dataRegionSizeColumns = dataRegionSizeColumns;
|
||||
this.ecBlocks = ecBlocks;
|
||||
|
||||
// Calculate the total number of codewords
|
||||
int total = 0;
|
||||
int ecCodewords = ecBlocks.getECCodewords();
|
||||
ECB[] ecbArray = ecBlocks.getECBlocks();
|
||||
for (int i = 0; i < ecbArray.Length; i++) {
|
||||
ECB ecBlock = ecbArray[i];
|
||||
total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords);
|
||||
}
|
||||
this.totalCodewords = total;
|
||||
}
|
||||
|
||||
public int getVersionNumber() {
|
||||
return versionNumber;
|
||||
}
|
||||
|
||||
public int getSymbolSizeRows() {
|
||||
return symbolSizeRows;
|
||||
}
|
||||
|
||||
public int getSymbolSizeColumns() {
|
||||
return symbolSizeColumns;
|
||||
}
|
||||
|
||||
public int getDataRegionSizeRows() {
|
||||
return dataRegionSizeRows;
|
||||
}
|
||||
|
||||
public int getDataRegionSizeColumns() {
|
||||
return dataRegionSizeColumns;
|
||||
}
|
||||
|
||||
public int getTotalCodewords() {
|
||||
return totalCodewords;
|
||||
}
|
||||
|
||||
public ECBlocks getECBlocks() {
|
||||
return ecBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deduces version information from Data Matrix dimensions.</p>
|
||||
*
|
||||
* @param numRows Number of rows in modules
|
||||
* @param numColumns Number of columns in modules
|
||||
* @return {@link Version} for a Data Matrix Code of those dimensions
|
||||
* @throws ReaderException if dimensions do correspond to a valid Data Matrix size
|
||||
*/
|
||||
public static Version getVersionForDimensions(int numRows, int numColumns) {
|
||||
if ((numRows & 0x01) != 0 || (numColumns & 0x01) != 0) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
// TODO(bbrown): This is doing a linear search through the array of versions.
|
||||
// If we interleave the rectangular versions with the square versions we could
|
||||
// do a binary search.
|
||||
int numVersions = VERSIONS.Length;
|
||||
for (int i = 0; i < numVersions; ++i){
|
||||
Version version = VERSIONS[i];
|
||||
if (version.symbolSizeRows == numRows && version.symbolSizeColumns == numColumns) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
|
||||
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
|
||||
* each set of blocks. It also holds the number of error-correction codewords per block since it
|
||||
* will be the same across all blocks within one version.</p>
|
||||
*/
|
||||
public class ECBlocks {
|
||||
private int ecCodewords;
|
||||
private ECB[] ecBlocks;
|
||||
|
||||
public ECBlocks(int ecCodewords, ECB ecBlocks) {
|
||||
this.ecCodewords = ecCodewords;
|
||||
this.ecBlocks = new ECB[] { ecBlocks };
|
||||
}
|
||||
|
||||
public ECBlocks(int ecCodewords, ECB ecBlocks1, ECB ecBlocks2) {
|
||||
this.ecCodewords = ecCodewords;
|
||||
this.ecBlocks = new ECB[] { ecBlocks1, ecBlocks2 };
|
||||
}
|
||||
|
||||
public int getECCodewords() {
|
||||
return ecCodewords;
|
||||
}
|
||||
|
||||
public ECB[] getECBlocks() {
|
||||
return ecBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
|
||||
* This includes the number of data codewords, and the number of times a block with these
|
||||
* parameters is used consecutively in the Data Matrix code version's format.</p>
|
||||
*/
|
||||
public class ECB {
|
||||
private int count;
|
||||
private int dataCodewords;
|
||||
|
||||
public ECB(int count, int dataCodewords) {
|
||||
this.count = count;
|
||||
this.dataCodewords = dataCodewords;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getDataCodewords() {
|
||||
return dataCodewords;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return versionNumber.ToString();
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 16022:2006 5.5.1 Table 7
|
||||
*/
|
||||
private static Version[] buildVersions() {
|
||||
return new Version[]{
|
||||
new Version(1, 10, 10, 8, 8,
|
||||
new ECBlocks(5, new ECB(1, 3))),
|
||||
new Version(2, 12, 12, 10, 10,
|
||||
new ECBlocks(7, new ECB(1, 5))),
|
||||
new Version(3, 14, 14, 12, 12,
|
||||
new ECBlocks(10, new ECB(1, 8))),
|
||||
new Version(4, 16, 16, 14, 14,
|
||||
new ECBlocks(12, new ECB(1, 12))),
|
||||
new Version(5, 18, 18, 16, 16,
|
||||
new ECBlocks(14, new ECB(1, 18))),
|
||||
new Version(6, 20, 20, 18, 18,
|
||||
new ECBlocks(18, new ECB(1, 22))),
|
||||
new Version(7, 22, 22, 20, 20,
|
||||
new ECBlocks(20, new ECB(1, 30))),
|
||||
new Version(8, 24, 24, 22, 22,
|
||||
new ECBlocks(24, new ECB(1, 36))),
|
||||
new Version(9, 26, 26, 24, 24,
|
||||
new ECBlocks(28, new ECB(1, 44))),
|
||||
new Version(10, 32, 32, 14, 14,
|
||||
new ECBlocks(36, new ECB(1, 62))),
|
||||
new Version(11, 36, 36, 16, 16,
|
||||
new ECBlocks(42, new ECB(1, 86))),
|
||||
new Version(12, 40, 40, 18, 18,
|
||||
new ECBlocks(48, new ECB(1, 114))),
|
||||
new Version(13, 44, 44, 20, 20,
|
||||
new ECBlocks(56, new ECB(1, 144))),
|
||||
new Version(14, 48, 48, 22, 22,
|
||||
new ECBlocks(68, new ECB(1, 174))),
|
||||
new Version(15, 52, 52, 24, 24,
|
||||
new ECBlocks(42, new ECB(2, 102))),
|
||||
new Version(16, 64, 64, 14, 14,
|
||||
new ECBlocks(56, new ECB(2, 140))),
|
||||
new Version(17, 72, 72, 16, 16,
|
||||
new ECBlocks(36, new ECB(4, 92))),
|
||||
new Version(18, 80, 80, 18, 18,
|
||||
new ECBlocks(48, new ECB(4, 114))),
|
||||
new Version(19, 88, 88, 20, 20,
|
||||
new ECBlocks(56, new ECB(4, 144))),
|
||||
new Version(20, 96, 96, 22, 22,
|
||||
new ECBlocks(68, new ECB(4, 174))),
|
||||
new Version(21, 104, 104, 24, 24,
|
||||
new ECBlocks(56, new ECB(6, 136))),
|
||||
new Version(22, 120, 120, 18, 18,
|
||||
new ECBlocks(68, new ECB(6, 175))),
|
||||
new Version(23, 132, 132, 20, 20,
|
||||
new ECBlocks(62, new ECB(8, 163))),
|
||||
new Version(24, 144, 144, 22, 22,
|
||||
new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))),
|
||||
new Version(25, 8, 18, 6, 16,
|
||||
new ECBlocks(7, new ECB(1, 5))),
|
||||
new Version(26, 8, 32, 6, 14,
|
||||
new ECBlocks(11, new ECB(1, 10))),
|
||||
new Version(27, 12, 26, 10, 24,
|
||||
new ECBlocks(14, new ECB(1, 16))),
|
||||
new Version(28, 12, 36, 10, 16,
|
||||
new ECBlocks(18, new ECB(1, 22))),
|
||||
new Version(29, 16, 36, 10, 16,
|
||||
new ECBlocks(24, new ECB(1, 32))),
|
||||
new Version(30, 16, 48, 14, 22,
|
||||
new ECBlocks(28, new ECB(1, 49)))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
424
csharp/datamatrix/detector/Detector.cs
Normal file
424
csharp/datamatrix/detector/Detector.cs
Normal file
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using com.google.zxing;
|
||||
using com.google.zxing.common;
|
||||
|
||||
namespace com.google.zxing.datamatrix.detector
|
||||
{
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code
|
||||
* is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public sealed class Detector
|
||||
{
|
||||
private static int MAX_MODULES = 32;
|
||||
|
||||
// Trick to avoid creating new int objects below -- a sort of crude copy of
|
||||
// the int.valueOf(int) optimization added in Java 5, not in J2ME
|
||||
private static int[] intS =
|
||||
{0, 1, 2, 3, 4};
|
||||
|
||||
private MonochromeBitmapSource image;
|
||||
|
||||
public Detector(MonochromeBitmapSource image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a Data Matrix Code in an image.</p>
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws ReaderException if no Data Matrix Code can be found
|
||||
*/
|
||||
public DetectorResult detect() {
|
||||
|
||||
if (!BlackPointEstimationMethod.TWO_D_SAMPLING.Equals(image.getLastEstimationMethod())) {
|
||||
image.estimateBlackPoint(BlackPointEstimationMethod.TWO_D_SAMPLING, 0);
|
||||
}
|
||||
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
int halfHeight = height >> 1;
|
||||
int halfWidth = width >> 1;
|
||||
int iSkip = Math.Max(1, height / (MAX_MODULES << 3));
|
||||
int jSkip = Math.Max(1, width / (MAX_MODULES << 3));
|
||||
|
||||
int minI = 0;
|
||||
int maxI = height;
|
||||
int minJ = 0;
|
||||
int maxJ = width;
|
||||
ResultPoint pointA = findCornerFromCenter(halfHeight, -iSkip, minI, maxI, halfWidth, 0, minJ, maxJ, halfWidth >> 1);
|
||||
minI = (int) pointA.getY() - 1;
|
||||
ResultPoint pointB = findCornerFromCenter(halfHeight, 0, minI, maxI, halfWidth, -jSkip, minJ, maxJ, halfHeight >> 1);
|
||||
minJ = (int) pointB.getX() - 1;
|
||||
ResultPoint pointC = findCornerFromCenter(halfHeight, 0, minI, maxI, halfWidth, jSkip, minJ, maxJ, halfHeight >> 1);
|
||||
maxJ = (int) pointC.getX() + 1;
|
||||
ResultPoint pointD = findCornerFromCenter(halfHeight, iSkip, minI, maxI, halfWidth, 0, minJ, maxJ, halfWidth >> 1);
|
||||
maxI = (int) pointD.getY() + 1;
|
||||
// Go try to find point A again with better information -- might have been off at first.
|
||||
pointA = findCornerFromCenter(halfHeight, -iSkip, minI, maxI, halfWidth, 0, minJ, maxJ, halfWidth >> 2);
|
||||
|
||||
// Point A and D are across the diagonal from one another,
|
||||
// as are B and C. Figure out which are the solid black lines
|
||||
// by counting transitions
|
||||
System.Collections.ArrayList transitions = new System.Collections.ArrayList(4);
|
||||
transitions.Add(transitionsBetween(pointA, pointB));
|
||||
transitions.Add(transitionsBetween(pointA, pointC));
|
||||
transitions.Add(transitionsBetween(pointB, pointD));
|
||||
transitions.Add(transitionsBetween(pointC, pointD));
|
||||
Collections.insertionSort(transitions, new ResultPointsAndTransitionsComparator());
|
||||
|
||||
// Sort by number of transitions. First two will be the two solid sides; last two
|
||||
// will be the two alternating black/white sides
|
||||
ResultPointsAndTransitions lSideOne = (ResultPointsAndTransitions) transitions[0];
|
||||
ResultPointsAndTransitions lSideTwo = (ResultPointsAndTransitions) transitions[1];
|
||||
|
||||
// Figure out which point is their intersection by tallying up the number of times we see the
|
||||
// endpoints in the four endpoints. One will show up twice.
|
||||
System.Collections.Hashtable pointCount = new System.Collections.Hashtable();
|
||||
increment(pointCount, lSideOne.getFrom());
|
||||
increment(pointCount, lSideOne.getTo());
|
||||
increment(pointCount, lSideTwo.getFrom());
|
||||
increment(pointCount, lSideTwo.getTo());
|
||||
|
||||
ResultPoint maybeTopLeft = null;
|
||||
ResultPoint bottomLeft = null;
|
||||
ResultPoint maybeBottomRight = null;
|
||||
System.Collections.IEnumerator points = pointCount.GetEnumerator();
|
||||
|
||||
while (points.MoveNext()) {
|
||||
ResultPoint point = (ResultPoint) points.Current;
|
||||
int value = (int) pointCount[point];
|
||||
if (value == 2) {
|
||||
bottomLeft = point; // this is definitely the bottom left, then -- end of two L sides
|
||||
} else {
|
||||
// Otherwise it's either top left or bottom right -- just assign the two arbitrarily now
|
||||
if (maybeTopLeft == null) {
|
||||
maybeTopLeft = point;
|
||||
} else {
|
||||
maybeBottomRight = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maybeTopLeft == null || bottomLeft == null || maybeBottomRight == null) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
// Bottom left is correct but top left and bottom right might be switched
|
||||
ResultPoint[] corners = { maybeTopLeft, bottomLeft, maybeBottomRight };
|
||||
// Use the dot product trick to sort them out
|
||||
GenericResultPoint.orderBestPatterns(corners);
|
||||
|
||||
// Now we know which is which:
|
||||
ResultPoint bottomRight = corners[0];
|
||||
bottomLeft = corners[1];
|
||||
ResultPoint topLeft = corners[2];
|
||||
|
||||
// Which point didn't we find in relation to the "L" sides? that's the top right corner
|
||||
ResultPoint topRight;
|
||||
if (!pointCount.ContainsKey(pointA)) {
|
||||
topRight = pointA;
|
||||
} else if (!pointCount.ContainsKey(pointB)) {
|
||||
topRight = pointB;
|
||||
} else if (!pointCount.ContainsKey(pointC)) {
|
||||
topRight = pointC;
|
||||
} else {
|
||||
topRight = pointD;
|
||||
}
|
||||
|
||||
// Next determine the dimension by tracing along the top or right side and counting black/white
|
||||
// transitions. Since we start inside a black module, we should see a number of transitions
|
||||
// equal to 1 less than the code dimension. Well, actually 2 less, because we are going to
|
||||
// end on a black module:
|
||||
|
||||
// The top right point is actually the corner of a module, which is one of the two black modules
|
||||
// adjacent to the white module at the top right. Tracing to that corner from either the top left
|
||||
// or bottom right should work here, but, one will be more reliable since it's traced straight
|
||||
// up or across, rather than at a slight angle. We use dot products to figure out which is
|
||||
// better to use:
|
||||
int dimension;
|
||||
if (GenericResultPoint.crossProductZ(bottomLeft, bottomRight, topRight) <
|
||||
GenericResultPoint.crossProductZ(topRight, topLeft, bottomLeft)) {
|
||||
dimension = transitionsBetween(topLeft, topRight).getTransitions();
|
||||
} else {
|
||||
dimension = transitionsBetween(bottomRight, topRight).getTransitions();
|
||||
}
|
||||
dimension += 2;
|
||||
|
||||
BitMatrix bits = sampleGrid(image, topLeft, bottomLeft, bottomRight, dimension);
|
||||
return new DetectorResult(bits, new ResultPoint[] {pointA, pointB, pointC, pointD});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
|
||||
* point which should be within the barcode.
|
||||
*
|
||||
* @param centerI center's i componennt (vertical)
|
||||
* @param di change in i per step. If scanning up this is negative; down, positive; left or right, 0
|
||||
* @param minI minimum value of i to search through (meaningless when di == 0)
|
||||
* @param maxI maximum value of i
|
||||
* @param centerJ center's j component (horizontal)
|
||||
* @param dj same as di but change in j per step instead
|
||||
* @param minJ see minI
|
||||
* @param maxJ see minJ
|
||||
* @param maxWhiteRun maximum run of white pixels that can still be considered to be within
|
||||
* the barcode
|
||||
* @return a {@link ResultPoint} encapsulating the corner that was found
|
||||
* @throws ReaderException if such a point cannot be found
|
||||
*/
|
||||
private ResultPoint findCornerFromCenter(int centerI, int di, int minI, int maxI,
|
||||
int centerJ, int dj, int minJ, int maxJ,
|
||||
int maxWhiteRun) {
|
||||
int[] lastRange = null;
|
||||
for (int i = centerI, j = centerJ;
|
||||
i < maxI && i >= minI && j < maxJ && j >= minJ;
|
||||
i += di, j += dj) {
|
||||
int[] range;
|
||||
if (dj == 0) {
|
||||
// horizontal slices, up and down
|
||||
range = blackWhiteRange(i, maxWhiteRun, minJ, maxJ, true);
|
||||
} else {
|
||||
// vertical slices, left and right
|
||||
range = blackWhiteRange(j, maxWhiteRun, minI, maxI, false);
|
||||
}
|
||||
if (range == null) {
|
||||
if (lastRange == null) {
|
||||
throw new ReaderException();
|
||||
}
|
||||
// lastRange was found
|
||||
if (dj == 0) {
|
||||
int lastI = i - di;
|
||||
if (lastRange[0] < centerJ) {
|
||||
if (lastRange[1] > centerJ) {
|
||||
// straddle, choose one or the other based on direction
|
||||
return new GenericResultPoint(di > 0 ? lastRange[0] : lastRange[1], lastI);
|
||||
}
|
||||
return new GenericResultPoint(lastRange[0], lastI);
|
||||
} else {
|
||||
return new GenericResultPoint(lastRange[1], lastI);
|
||||
}
|
||||
} else {
|
||||
int lastJ = j - dj;
|
||||
if (lastRange[0] < centerI) {
|
||||
if (lastRange[1] > centerI) {
|
||||
return new GenericResultPoint(lastJ, dj < 0 ? lastRange[0] : lastRange[1]);
|
||||
}
|
||||
return new GenericResultPoint(lastJ, lastRange[0]);
|
||||
} else {
|
||||
return new GenericResultPoint(lastJ, lastRange[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastRange = range;
|
||||
}
|
||||
throw new ReaderException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the int associated with a key by one.
|
||||
*/
|
||||
private static void increment(System.Collections.Hashtable table, ResultPoint key) {
|
||||
int value = (int) table[key];
|
||||
table[key] = value.Equals(null) ? intS[1] : intS[value + 1];
|
||||
//table.put(key, value == null ? intS[1] : intS[value.intValue() + 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the start and end of a region of pixels, either horizontally or vertically, that could be
|
||||
* part of a Data Matrix barcode.
|
||||
*
|
||||
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) where
|
||||
* we are scanning. If scanning vertically it's the colummn, the fixed horizontal location
|
||||
* @param maxWhiteRun largest run of white pixels that can still be considered part of the barcode region
|
||||
* @param minDim minimum pixel location, horizontally or vertically, to consider
|
||||
* @param maxDim maximum pixel location, horizontally or vertically, to consider
|
||||
* @param horizontal if true, we're scanning left-right, instead of up-down
|
||||
* @return int[] with start and end of found range, or null if no such range is found (e.g. only white was found)
|
||||
*/
|
||||
private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, bool horizontal) {
|
||||
|
||||
int center = (minDim + maxDim) / 2;
|
||||
|
||||
BitArray rowOrColumn = horizontal ? image.getBlackRow(fixedDimension, null, 0, image.getWidth())
|
||||
: image.getBlackColumn(fixedDimension, null, 0, image.getHeight());
|
||||
|
||||
// Scan left/up first
|
||||
int start = center;
|
||||
while (start >= minDim) {
|
||||
if (rowOrColumn.get(start)) {
|
||||
start--;
|
||||
} else {
|
||||
int whiteRunStart = start;
|
||||
do {
|
||||
start--;
|
||||
} while (start >= minDim && !rowOrColumn.get(start));
|
||||
int whiteRunSize = whiteRunStart - start;
|
||||
if (start < minDim || whiteRunSize > maxWhiteRun) {
|
||||
start = whiteRunStart + 1; // back up
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
start++;
|
||||
|
||||
// Then try right/down
|
||||
int end = center;
|
||||
while (end < maxDim) {
|
||||
if (rowOrColumn.get(end)) {
|
||||
end++;
|
||||
} else {
|
||||
int whiteRunStart = end;
|
||||
do {
|
||||
end++;
|
||||
} while (end < maxDim && !rowOrColumn.get(end));
|
||||
int whiteRunSize = end - whiteRunStart;
|
||||
if (end >= maxDim || whiteRunSize > maxWhiteRun) {
|
||||
end = whiteRunStart - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
end--;
|
||||
|
||||
if (end > start) {
|
||||
return new int[] { start, end };
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BitMatrix sampleGrid(MonochromeBitmapSource image,
|
||||
ResultPoint topLeft,
|
||||
ResultPoint bottomLeft,
|
||||
ResultPoint bottomRight,
|
||||
int dimension) {
|
||||
|
||||
// We make up the top right point for now, based on the others.
|
||||
// TODO: we actually found a fourth corner above and figured out which of two modules
|
||||
// it was the corner of. We could use that here and adjust for perspective distortion.
|
||||
float topRightX = (bottomRight.getX() - bottomLeft.getX()) + topLeft.getX();
|
||||
float topRightY = (bottomRight.getY() - bottomLeft.getY()) + topLeft.getY();
|
||||
|
||||
// Note that unlike in the QR Code sampler, we didn't find the center of modules, but the
|
||||
// very corners. So there is no 0.5f here; 0.0f is right.
|
||||
GridSampler sampler = GridSampler.Instance;
|
||||
return sampler.sampleGrid(
|
||||
image,
|
||||
dimension,
|
||||
0.0f,
|
||||
0.0f,
|
||||
dimension,
|
||||
0.0f,
|
||||
dimension,
|
||||
dimension,
|
||||
0.0f,
|
||||
dimension,
|
||||
topLeft.getX(),
|
||||
topLeft.getY(),
|
||||
topRightX,
|
||||
topRightY,
|
||||
bottomRight.getX(),
|
||||
bottomRight.getY(),
|
||||
bottomLeft.getX(),
|
||||
bottomLeft.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of black/white transitions between two points, using something like Bresenham's algorithm.
|
||||
*/
|
||||
private ResultPointsAndTransitions transitionsBetween(ResultPoint from, ResultPoint to) {
|
||||
// See QR Code Detector, sizeOfBlackWhiteBlackRun()
|
||||
int fromX = (int) from.getX();
|
||||
int fromY = (int) from.getY();
|
||||
int toX = (int) to.getX();
|
||||
int toY = (int) to.getY();
|
||||
bool steep = Math.Abs(toY - fromY) > Math.Abs(toX - fromX);
|
||||
if (steep) {
|
||||
int temp = fromX;
|
||||
fromX = fromY;
|
||||
fromY = temp;
|
||||
temp = toX;
|
||||
toX = toY;
|
||||
toY = temp;
|
||||
}
|
||||
|
||||
int dx = Math.Abs(toX - fromX);
|
||||
int dy = Math.Abs(toY - fromY);
|
||||
int error = -dx >> 1;
|
||||
int ystep = fromY < toY ? 1 : -1;
|
||||
int xstep = fromX < toX ? 1 : -1;
|
||||
int transitions = 0;
|
||||
bool inBlack = image.isBlack(steep ? fromY : fromX, steep ? fromX : fromY);
|
||||
for (int x = fromX, y = fromY; x != toX; x += xstep) {
|
||||
bool isBlack = image.isBlack(steep ? y : x, steep ? x : y);
|
||||
if (isBlack == !inBlack) {
|
||||
transitions++;
|
||||
inBlack = isBlack;
|
||||
}
|
||||
error += dy;
|
||||
if (error > 0) {
|
||||
y += ystep;
|
||||
error -= dx;
|
||||
}
|
||||
}
|
||||
return new ResultPointsAndTransitions(from, to, transitions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply encapsulates two points and a number of transitions between them.
|
||||
*/
|
||||
private class ResultPointsAndTransitions {
|
||||
private ResultPoint from;
|
||||
private ResultPoint to;
|
||||
private int transitions;
|
||||
|
||||
public ResultPointsAndTransitions(ResultPoint from, ResultPoint to, int transitions) {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.transitions = transitions;
|
||||
}
|
||||
|
||||
public ResultPoint getFrom() {
|
||||
return from;
|
||||
}
|
||||
public ResultPoint getTo() {
|
||||
return to;
|
||||
}
|
||||
public int getTransitions() {
|
||||
return transitions;
|
||||
}
|
||||
public String toString() {
|
||||
return from + "/" + to + '/' + transitions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders ResultPointsAndTransitions by number of transitions, ascending.
|
||||
*/
|
||||
private class ResultPointsAndTransitionsComparator : Comparator {
|
||||
public int compare(Object o1, Object o2) {
|
||||
return ((ResultPointsAndTransitions) o1).getTransitions() - ((ResultPointsAndTransitions) o2).getTransitions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
csharp/obj/Release/com.google.zxing.dll
Normal file
BIN
csharp/obj/Release/com.google.zxing.dll
Normal file
Binary file not shown.
BIN
csharp/obj/Release/com.google.zxing.pdb
Normal file
BIN
csharp/obj/Release/com.google.zxing.pdb
Normal file
Binary file not shown.
5
csharp/obj/Release/zxing.csproj.FileListAbsolute.txt
Normal file
5
csharp/obj/Release/zxing.csproj.FileListAbsolute.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
C:\projects\my open source project\zxingsharp\obj\Release\ResolveAssemblyReference.cache
|
||||
C:\projects\my open source project\zxingsharp\bin\Release\com.google.zxing.dll
|
||||
C:\projects\my open source project\zxingsharp\bin\Release\com.google.zxing.pdb
|
||||
C:\projects\my open source project\zxingsharp\obj\Release\com.google.zxing.dll
|
||||
C:\projects\my open source project\zxingsharp\obj\Release\com.google.zxing.pdb
|
|
@ -71,6 +71,13 @@
|
|||
<Compile Include="common\reedsolomon\ReedSolomonDecoder.cs" />
|
||||
<Compile Include="common\reedsolomon\ReedSolomonEncoder.cs" />
|
||||
<Compile Include="common\reedsolomon\ReedSolomonException.cs" />
|
||||
<Compile Include="datamatrix\DataMatrixReader.cs" />
|
||||
<Compile Include="datamatrix\decoder\BitMatrixParser.cs" />
|
||||
<Compile Include="datamatrix\decoder\DataBlock.cs" />
|
||||
<Compile Include="datamatrix\decoder\DecodedBitStreamParser.cs" />
|
||||
<Compile Include="datamatrix\decoder\Decoder.cs" />
|
||||
<Compile Include="datamatrix\decoder\Version.cs" />
|
||||
<Compile Include="datamatrix\detector\Detector.cs" />
|
||||
<Compile Include="DecodeHintType.cs" />
|
||||
<Compile Include="EncodeHintType.cs" />
|
||||
<Compile Include="oned\MultiFormatOneDReader.cs" />
|
||||
|
|
BIN
csharp/zxing.suo
Normal file
BIN
csharp/zxing.suo
Normal file
Binary file not shown.
Loading…
Reference in a new issue