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:
mfzpeyo 2009-01-10 12:21:17 +00:00
parent 57afc8d154
commit 1c4f85ee24
15 changed files with 1936 additions and 4 deletions

View file

@ -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));
}
}
}

Binary file not shown.

Binary file not shown.

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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));
}
}
}

View 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];
}
}
}
}

View 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)))
};
}
}
}

View 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();
}
}
}
}

Binary file not shown.

Binary file not shown.

View 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

View file

@ -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

Binary file not shown.